Sending Access reports by email as HTML - html

I have an MS Access report that contains records of clients from one table (including the email address) and linked grouped records from other tables fetched by a Query.
I want to send the content of the report to each client separately in the body of the email (not as an attachment), I am able to get the text put in the body of the email but without the formatting and without the picture in the header.
I used the following code which runs behind a click of a button. I would appreciate if anyone can help with the formatting issue AND if there is a way I can automate sending the emails for my 200+ clients without clicking the button each time (like a loop or something):
Private Sub Command70_Click()
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim appOutlook As Outlook.Application
Dim MailOutlook As Outlook.MailItem
Dim RTFBody
Set appOutlook = CreateObject("Outlook.application")
Set MailOutlook = appOutlook.CreateItem(olMailItem)
DoCmd.OutputTo acOutputReport, "report1", acFormatHTML, "Report.htm", , , , acExportQualityScreen
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.OpenTextFile("Report.htm", ForReading, False, TristateTrue)
RTFBody = f.ReadAll
f.Close
With MailOutlook
.To = Me.Email.Value
.CC = "anwarmirza.ridha#gmail.com"
.Subject = Me.CR_Number & " " & Me.English_Name & " Weekly Report"
.HTMLBody = "Dear Supplier" & Chr$(13) & Chr$(13) & _
RTFBody
.Send
End With
Set MailOutlook = Nothing
Set appOutlook = Nothing
End Sub

Since MS Access reports are specialized rich text formats, conversion to HTML is not easily available. You would need to rebuild your report with HTML markup. However, there is another approach.
Consider creating an Outlook email template (.oft) with all needed images, colors, fonts, and other formatting with placeholders such as %...% markers:
Dear %ClientName%:
Thank you for purchase of %product% for %totalsales% on %salesdate%. We appreciate your business of %years% years.
%salestable%
Best wishes,
MyCompany Management
Then, have MS Access loop through a recordset of email details and text for message body to fill in placeholders. Handle any date/current/percent formatting in SQL or VBA. Because you need a group level multi-record summary, run two loops at 1) client level and 2) sales level.
strSQL = "SELECT ClientID, ClientName, ...email details... FROM myClientsTable"
Set clientRST = CurrentDb.OpenRecordset(strSQL)
Do While Not clientRST.EOF
Set MailOutlook = appOutlook.CreateItemFromTemplate("C:\Path\To\ClientEmail.oft")
strSQL = "SELECT Col1, Col2, Col3 ...sales details..." _
& " FROM mySalesTable" _
& " WHERE ClientID = " & clientRST!ClientID
Set salesRST = CurrentDb.OpenRecordSet(strSQL)
' TABLE COLUMNS
strTable = "<table><th>"
For i = 1 to salesRST.Fields.Count
strTable = strTable & "<td>" & salesRST.Fields(i-0).Name & "</td>"
Next i
strTable = strTable & "</th>"
' TABLE ROWS
salesRST.MoveFirst
While Not salesRst.EOF
strTable = strTable & "<tr>"
For i = 1 to salesRST.Fields.Count
strTable = strTable & "<td>" & salesRST.Fields(i-0).Value & "</td>"
Next i
strTable = strTable & "</tr>"
salesRST.MoveNext
Wend
strTable = strTable & "</table>"
salesRST.Close
With MailOutlook
' DYNAMIC RECIPEINT
.To = clientRST!Email
.CC = "anwarmirza.ridha#gmail.com"
' DYNAMIC SUBJECT
.Subject = clientRST!CR_Number & " " & clientRST!English_Name & " Weekly Report"
' REPLACE PLACEHOLDERS
.HTMLBody = Replace(.HTMLBody, "%ClientName%", clientRST!ClientName)
.HTMLBody = Replace(.HTMLBody, "%product%", clientRST!product)
.HTMLBody = Replace(.HTMLBody, "%totalsales%", clientRST!totalsales)
.HTMLBody = Replace(.HTMLBody, "%salesdate%", clientRST!salesdate)
.HTMLBody = Replace(.HTMLBody, "%years%", clientRST!client_years)
' ADD SALES TABLE
.HTMLBody = Replace(.HTMLBody, "%salestable%", strTable)
.Send
End With
Set MailOutlook = Nothing
clientRST.MoveNext
Loop
clientRST.Close

Related

Automate Email based on details in spreadsheet and copy/paste tables from spreadsheet into corresponding email

Thank you for taking the time to try and help me with this project.
I have some vba that sends an email to each recipient on my spreadsheet and includes in the body of the text information from the spreadsheet. This piece of the code works great. Here's the part where I am stuck...
The workbook contains a couple tables that I would like to filter and copy/paste into each email BUT the data from each table needs to be filtered to the data that applies to each recipient.
For example:
The email is being sent to a Regional leader and includes scores for their Region overall.
I have 1 table that includes manager scores which can be filtered by Region and
on a second tab, I have a table for each Region that drills down the scores by type of service.
So for the SouthWest Regional leader, I would like to Filter table 1 to only show managers in the SouthWest Region, copy/paste that table directly into the email and then go to the Service Type tables and copy the SouthWest table and paste into the email.
The final piece I would like to accomplish is to copy the employee level details which reside on a separate tab, to a workbook and attach it to the email. This too would need to be specific to employees within each region.
I don't know if this is possible within my code or if there is a smart way to accomplish it. I appreciate any help or insight you are willing to give! I have attached an example file and below is the email code I am currently using. I also have some code that filters the data based on the region that may or may not be helpful.
Sub SendMailtoRFE()
Dim outapp As New Outlook.Application
Dim outmail As Outlook.Mailitem
Dim wks As Worksheet
Dim i As Integer
Dim sFile1 As String
Dim TempFilePath As String
Environ ("UserProfile")
Set outapp = CreateObject("outlook.application")
sFile1 = "Infographic"
TempFilePath = Environ$("temp") & "Roadside Assistance " 'FIND OUT HOW TO CLEAN UP THE NAME: "Temp" added to file name
ActiveWorkbook.Sheets(sFile1).ExportAsFixedFormat Type:=xlTypePDF, Filename:=TempFilePath & sFile1 & ".pdf"
On Error Resume Next
For i = 3 To wks.Range("A" & Rows.Count).End(xlUp).Row
Set outmail = outapp.CreateItem(olMailItem)
With outmail
.To = wks.Range("C" & i).Value
.Subject = wks.Range("A" & i).Value & " Region Roadside Assistance YTD Communication"
.HTMLBody = "Dear " & wks.Range("C" & i).Value & "," & "<br></br>" & _
"You've shared how important Roadside Assistance is for your personal auto clients. As one of the highest frequency types of losses, success or failure " & _
"here may be seen as a signal of the overall value of the program." & "<br></br><br></br>" & _
"Here are the results for clients in your area who completed a survey. Year to date, the NPS was " & FormatPercent(wks.Range("K" & i).Value, 0) & _
" based on " & wks.Range("H" & i).Value & " total responses." & _
" The overall score for all regions is " & FormatPercent(wks.Range("K12").Value, 0) & "." & "<br></br><br></br>" & _
"Below are a few additional details to help you understand your region's score. " & _
"Please follow up with any questions or concerns." & "<br></br><br></br>" & vbNewLine & _
"**Please note, the table containing MLGA scores shows only the MLGA's where 5 or more survey responses were received.**"
.Attachments.Add (TempFilePath & sFile1 & ".pdf")
.display
End With
On Error GoTo 0
Set outmail = Nothing
Next i
Set outapp = Nothing
End Sub
''Filter Region on the MLGA Tow NPS Score Tab
Sub FilterSouthWest()
Dim wks As Worksheet
Set wks = Sheets("MLGA TOW NPS Score")
With wks.Range("A2:C2")
.AutoFilter Field:=3, Criteria1:="9A"
End With
End Sub
Use .SpecialCells(xlCellTypeVisible) to set the range on the filtered table and copy/paste them into the email using WordEditor. To insert the html text create a temporary file and use .InsertFile, This converts the html formatting into word formatting. You may need to add a wait between the copy/paste action depending on the amount of data.
Option Explicit
Sub SendMailtoRFE()
'sheet names
Const PDF = "Infographic" ' attachment
Const WS_S = "MLGA TOW NPS Score" ' filtered score data
Const WS_R = "Regions" ' names and emails
Const WS_T = "Tables" ' Regions Tables
Dim ws As Worksheet, sPath As String, sPDFname As String
Dim lastrow As Long, i As Long, n As Long
' region code for filter
Dim dictRegions As Object, region
Set dictRegions = CreateObject("Scripting.Dictionary")
With dictRegions
.Add "NorthEast", "6A"
.Add "NorthWest", "7A"
.Add "SouthEast", "8A"
.Add "SouthWest", "9A"
End With
sPath = Environ$("temp") & "\"
sPDFname = sPath & "Roadside Assistance " & PDF & ".pdf"
Sheets(PDF).ExportAsFixedFormat Type:=xlTypePDF, Filename:=sPDFname
Dim outapp As Outlook.Application
Dim outmail As Outlook.Mailitem
Dim outInsp As Object, oWordDoc
Dim wsRegion As Worksheet
Dim sRegion As String, sEmailAddr As String, rngScore As Range
Dim Table1 As Range, Table2 As Range, tmpHTML As String
' scores
With Sheets(WS_S)
lastrow = .Cells(.Rows.Count, "C").End(xlUp).Row
Set rngScore = .Range("A2:G" & lastrow) ' 5 columns
End With
' open outlook
Set outapp = New Outlook.Application
' regions
Set wsRegion = Sheets(WS_R)
lastrow = wsRegion.Cells(wsRegion.Rows.Count, "A").End(xlUp).Row
For i = 3 To lastrow '
sRegion = wsRegion.Range("A" & i).Value
sEmailAddr = wsRegion.Range("C" & i).Value
tmpHTML = HTMLFile(wsRegion, i)
' region
With rngScore
.AutoFilter
.AutoFilter Field:=3, Criteria1:=dictRegions(sRegion) ' filter col C
Set Table1 = .SpecialCells(xlCellTypeVisible)
End With
' Service Type Table
Set Table2 = Sheets(WS_T).ListObjects(sRegion).Range ' Table named same as region
'Debug.Print dictRegions(sRegion), sRegion, Table1.Address, Table2.Address
Set outmail = outapp.CreateItem(olMailItem)
n = n + 1
With outmail
.To = sEmailAddr
.Subject = sRegion & " Region Roadside Assistance YTD Communication"
.Attachments.Add sPDFname
.display
End With
Set outInsp = outmail.GetInspector
Set oWordDoc = outInsp.WordEditor
'Wait 1
With oWordDoc
.Content.Delete
.Paragraphs.Add.Range.InsertFile tmpHTML, Link:=False, Attachment:=False
Table1.Copy
.Paragraphs.Add.Range.Paste
.Paragraphs.Add.Range.Text = vbCrLf ' blank line
'Wait 1
Table2.Copy
.Paragraphs.Add.Range.Paste
'Wait 1
End With
Application.CutCopyMode = False
Set oWordDoc = Nothing
Set outInsp = Nothing
Set outmail = Nothing
' delete temp html file
On Error Resume Next
Kill tmpHTML
On Error GoTo 0
'Wait 1
Next
' end
Sheets(WS_S).AutoFilterMode = False
Set outapp = Nothing
AppActivate Application.Caption ' back to excel
MsgBox n & " Emails created", vbInformation
End Sub
Function HTMLFile(ws As Worksheet, i As Long) As String
Const CSS = "p{font:14px Verdana};h1{font:14px Verdana Bold};"
' template
Dim s As String
s = "<html><style>" & CSS & "</style><h1>Dear #NAME#,</h1>" & _
"<p>You've shared how important Roadside Assistance is for your personal auto clients.<br/>" & vbLf & _
"As one of the highest frequency types of losses, success or failure " & vbLf & _
"here may be seen as a signal of the overall value of the program.</p>" & vbLf & _
"<p>Here are the results for clients in your area who completed a survey.</p> " & vbLf & _
"<li>Year to date, the NPS was <b>#NPS_YTD#</b> " & vbLf & _
"based on <b>#RESPONSES#</b> total responses.</li> " & vbLf & _
"<li>The overall score for all regions is <b>#NPS_ALL#</b>,</li>" & vbLf & _
"<p>Below are a few additional details to help you understand your region's score. " & vbLf & _
"Please follow up with any questions or concerns." & "</p>" & vbNewLine & vbLf & _
"<p><i>**Please note, the table containing MLGA scores shows only the MLGA's where 5 " & vbLf & _
"or more survey responses were received.**</i></p></html>"
s = Replace(s, "#NAME#", ws.Cells(i, "C"))
s = Replace(s, "#NPS_YTD#", FormatPercent(ws.Cells(i, "K"), 0))
s = Replace(s, "#RESPONSES#", ws.Cells(i, "H"))
s = Replace(s, "#NPS_ALL#", FormatPercent(ws.Cells(12, "K"), 0))
Dim ff: ff = FreeFile
HTMLFile = Environ$("temp") & "\" & Format(Now(), "~yyyymmddhhmmss") & ".htm"
Open HTMLFile For Output As #ff
Print #ff, s
Close #ff
End Function
Sub Wait(n As Long)
Dim t As Date
t = DateAdd("s", n, Now())
Do While Now() < t
DoEvents
Loop
End Sub

Insert picture file, using variable, into text in .HTMLBody

I'm trying to use a variable to insert an image in an e-mail.
With OutlookItm
.To = strTo
.CC = strCc
.Subject = strSubject
.HTMLBody = "Hello all, <p>In attach the <b>Filtered Trunk Report</b> and below the <b>Invoice Volume chart</b>:</p>" & _
"<center><img src = Filename:ChartFile1 ></center>" & "<p>.</p>" & _
"<center><img src = 'C:\Users\matuo\OneDrive\Desktop\Chart2.jpg'></center>" & Signature
.Display
End With
The code works for "Chart2.jpg", but I want to use a variable for the directory and file.
The picture using the variable doesn't work.
Let's image you have got a string object pathToImage which contains the image path, in that case the code may look like:
With OutlookItm
.To = strTo
.CC = strCc
.Subject = strSubject
.HTMLBody = "Hello all, <p>In attach the <b>Filtered Trunk Report</b> and below the <b>Invoice Volume chart</b>:</p>" & _
"<center><img src = " & pathToImage & " ></center>" & "<p>.</p>" & _
"<center><img src = 'C:\Users\matuo\OneDrive\Desktop\Chart2.jpg'></center>" & Signature
.Display
End With
However, a local path to image is not really a good idea. Such images will not be displayed on the recipient side because the image itself is stored on your machine. Most probably you'd need to embed an image to the email itself. To get that done you need to attach the image file and set several properties - they are actually optional, even the following code works fine:
.Attachments.Add "C:\Users\JoeSchmo\Pictures\imageName.jpg", olByValue, 0
.HTMLBody = "<BODY><IMG src=""cid:imageName.jpg"" width=200> </BODY>"
But ideally you'd need to set the PR_ATTACH_CONTENT_ID and PR_ATTACHMENT_HIDDEN properties. For example:
Function SendasAttachment(fName As String)
Dim olApp As Outlook.Application
Dim olMsg As Outlook.MailItem
Dim olAtt As Outlook.Attachments
Set olApp = Outlook.Application
Set olMsg = olApp.CreateItem(0) ' email
Set olAtt = olMsg.Attachments
Const PR_ATTACH_MIME_TAG = "http://schemas.microsoft.com/mapi/proptag/0x370E001E"
Const PR_ATTACH_CONTENT_ID = "http://schemas.microsoft.com/mapi/proptag/0x3712001E"
Const PR_ATTACHMENT_HIDDEN = "http://schemas.microsoft.com/mapi/proptag/0x7FFE000B"
' attach file
olAtt.Add (fldName & fName)
Set l_Attach = olAtt.Add(fldName & fName)
Set oPA = l_Attach.PropertyAccessor
oPA.SetProperty PR_ATTACH_MIME_TAG, "image/jpeg"
oPA.SetProperty PR_ATTACH_CONTENT_ID, "myident"
oPA.SetProperty PR_ATTACHMENT_HIDDEN, True
olMsg.PropertyAccessor.SetProperty "http://schemas.microsoft.com/mapi/id/{00062008-0000-0000-C000-000000000046}/8514000B", True
olMsg.To = "test#email.com"
msgHTMLBody = "<HTML>" & _
"<head>" & _
"</head>" & _
"<BODY>" & "Hi " & olMsg.To & ", <br /><br /> I have attached " & fName & " as you requested." & _
"<br /><img align=baseline border=1 hspace=0 src=cid:myident width='400'/>" & _
"</BODY></HTML>"
' send message
With olMsg
.Subject = "Here's that file you wanted"
.BodyFormat = olFormatHTML
.HTMLBody = msgHTMLBody
.Save
'.Display
.Send
End With
End Function
Read more about that in the Embed Images in New Messages using a Macro article.

Sending an email to a list of people from a query - Problem with loop

I have a form that shows the results of an union query that has a button with the following code. What I want the button to do is email each person on that query separately with their information. If I leave the Do Until - Loop off it creates the email perfectly. When I include the loop I get an odd message,
Run-Time error '-1834742 (ffe4010a)':
The item has been moved or deleted focused on the .bodyformat line below. I tried commenting out that line to see what happened, and the error moved to the .to line. I've looked over my Do Until Loop and checked the Microsoft page as well as a google search for that ridiculous run time error, but I'm striking out. Does anyone see what I'm missing?
Private Sub btnSend_Click()
Dim appOutLook As Outlook.Application
Dim MailOutLook As Outlook.MailItem
Dim strSubject As String
Dim strEmail As String
Dim strPDHSUM As String
Dim sqls As String
Dim MyDb As DAO.Database
Dim rsEmail As DAO.Recordset
Set MyDb = CurrentDb
Set rsEmail = MyDb.OpenRecordset("eqREPPDHSummaryZero")
Set appOutLook = CreateObject("Outlook.Application")
Set MailOutLook = appOutLook.CreateItem(olMailItem)
With rsEmail
.MoveFirst
Do Until rsEmail.EOF
strEmail = .Fields(2)
strPDHSUM = .Fields(1)
With MailOutLook
.BodyFormat = olFormatRichText
.To = strEmail
'.CC = ""
'.bcc = ""
.Subject = "PDH Summary"
.HTMLBody = "Hello!" & "<br>" & "This is an automated reminder about the Professional Development Hour requirement for PFS. Each PFS staff member is required to have 4 hours of approved professional development each year. " & "<br>" & "<br>" & "So far this year you have taken " & strPDHSUM & " PD hours." & "<br>" & "<br>" & "Additional PDH classes are held each month and can be found at the " & "<a href=https://avalidaddress.com>PDH Class Schedule on OnBase</a> " & "If you feel there is an error in this information or need assistance signing up for PDH credits, please email " & "<a href=mailto:MyEmail#whereIWork.edu>Rob Loughrey</a>." & "<Br>" & "<br>" & "Thank you," & "<br>" & "PFS Education and Quality Unit"
.Send
'.Display 'Used during testing without sending (Comment out .Send if using this line)
End With
sqls = "INSERT INTO tblEmails " _
& "(TypeofEmail, SendDateTime, EmailAddress) VALUES " _
& "('PDH Summary', Now(), '" & strEmail & "');"
DoCmd.RunSQL sqls
.MoveNext
Loop
End With
Set MyDb = Nothing
Set rsEmail = Nothing
End Sub````
Move:
Set MailOutLook = appOutLook.CreateItem(olMailItem) into recordset loop
Do Until rsEmail.EOF
Set MailOutLook = appOutLook.CreateItem(olMailItem)
Have to create as many mail items as emails sending. When it was outside For loop, just created one mail item which only worked with first mail sent, when loop was hit a second time MailOutlook was gone so code failed.

Write multi-line email by looping query and format into columns in VBA Access

I have an access query which I aim to loop through and write the contents to the email body of an outlook email. The below code does the job, however if there are a lot of lines, it doesn't look great and becomes very busy, visually.
I'd like to either, add some formatting rules which will align each variable into a "column", or make each variable a fixed length so that the pipes ("|") align (and of course making the variable fixed length would probably mean filling some with " " spaces from the left.
Alternatively, I could try sort this issue out using HTML or something? though I have no experience with that, but if this is the way to go, any advise would be greatly appreciated.
Public Sub Test()
On Error GoTo Error_Handler
Dim strbody, DateStamp
strbody = ""
DateStamp = Format(Date, "Medium Date")
Dim MyDB As DAO.Database
Dim Tem As DAO.Recordset
Set MyDB = CurrentDb()
Set Tem = MyDB.OpenRecordset("TestQuery", dbOpenForwardOnly)
With Tem
Do While Not .EOF
strbody = strbody + (CStr(![Num]) + " | " + CStr(Format(![TDate], "Medium Date")) + " | " + CStr(Format(![VDate], "Medium Date")) + " | " + CStr(Format(![QTY], "Standard")) + " | " + ![Name] & vbNewLine)
.MoveNext
Loop
End With
Tem.Close
Set Tem = Nothing
Dim OutApp As Object
Dim OutMail As Object
Dim objOutlook As Outlook.Application
Dim objEmail As Outlook.MailItem
Set objOutlook = CreateObject("Outlook.application")
Set objEmail = objOutlook.CreateItem(olMailItem)
With objEmail
.To = "test#abc.com"
.subject = "Test" & " " & DateStamp
.Body = strbody
.Display
End With
Exit_Here:
Set objOutlook = Nothing
Exit Sub
Error_Handler:
MsgBox Err & ": " & Err.Description
Resume Exit_Here
End Sub
Currently, the output in the email looks a bit like this:
1234 | 22-Aug-18 | 23-Aug-18 | 1,000.00 | testname 123
5678 | 21-Aug-18 | 22-Aug-18 | 50,000.00 | second testname of different length
The more lines the messier it gets, in addition to missing column names.
Again, any help is greatly appreciated. Thank you.
I do this with HTML mails.
I have a HTML mail template saved as .oft file, and create the mail item like this:
Dim oItem As Outlook.MailItem
Set oItem = oOutlook.CreateItemFromTemplate(TemplatePath)
The mail template contains variables like $TableGoesHere$ which are replaced by the variable data when creating the mail, e.g.
Set Tem = MyDB.OpenRecordset("TestQuery", dbOpenForwardOnly)
strTbl = OutlookHtmlTableFromRS(Tem)
oItem.HtmlBody = Replace(oItem.HtmlBody, "$TableGoesHere$", strTbl)
with the function:
' Create HTML table from a recordset, with all columns except those in <sExcl>
' Returns HTML string to insert into HTML mail
Public Function OutlookHtmlTableFromRS(RS As Recordset, Optional sExcl As String = "") As String
Dim fld As DAO.Field
Dim S As String
' Table heading row
S = "<table cellpadding='5' style='text-align:left; border: 1px solid gray; border-collapse:collapse; font-family:Calibri, Helvetica, sans-serif; font-size:11pt;'>" & _
"<tr style='padding:5px;border: 1px solid gray;'>"
For Each fld In RS.Fields
If InStr(sExcl, fld.Name) = 0 Then
S = S & "<th style='border: 1px solid gray;'>" & fld.Name & "</th>"
End If
Next fld
S = S & "</tr>"
' Data rows
Do While Not RS.EOF
S = S & "<tr style='padding:5px;'>"
For Each fld In RS.Fields
If InStr(sExcl, fld.Name) = 0 Then
S = S & "<td style='border: 1px solid gray;'>" & fld.Value & "</td>"
End If
Next fld
S = S & "</tr>"
RS.MoveNext
Loop
S = S & "</table>"
RS.Close
OutlookHtmlTableFromRS = S
End Function
Yes, HTML is the best way! :) With HTML you can create tables in Outlook and have formatting too! And HTML is pretty easy. For any formatting you want, just google it. For example, if you wanted some of the data to be right aligned, you might google "html table right align". There are many helpful websites out there for HTML.
The below VBA code is what I used to put the contents of an Access query into the body of an Outlook email.
While it may look long, it works and it's the best way to copy a query or table into an email.
I love this! My email looks really good!
Private Sub btnEmail_Click()
'This will open an email in your Outlook and compose it for you. You review and click send.
'This macro requires the reference "Microsoft Outlook 16.0 Object Library" to be enabled.
Dim f As Long, c As Long
Dim sTable As String
Dim rs As DAO.Recordset
Dim olApp As Outlook.Application
Dim olMsg As Outlook.MailItem
Dim olRecip As Recipient
Dim Recipients As Recipients
Dim qField(1 To 4) 'array
''Table Header
qField(1) = "Group Number"
qField(2) = "Group Name"
qField(3) = "PTD"
qField(4) = "Term Date"
sTable = "<table border=0 cellspacing=0 style='padding:0in 5.5pt 0in 5.5pt'><tbody>"
sTable = sTable & "<tr bgcolor=""#70ad47""><font color=""white""><b><td>Group#</td>" 'white font, bold, green fill
sTable = sTable & "<td>Group Name</td>"
sTable = sTable & "<td>PTD</td>"
sTable = sTable & "<td>Term Date</td></b></font></tr>"
''Rows
Set rs = CurrentDb.OpenRecordset("TestQuery")
Do Until rs.EOF
c = c + 1 'counter for the every other row light green
If c Mod 2 = 0 Then 'every other row light green
sTable = sTable & "<tr style=""background: #e2efd9"">"
Else
sTable = sTable & "<tr>" 'open row
End If
For f = 1 To 4 'cells
sTable = sTable & "<td>" & rs.Fields(qField(f)) & "</td>"
Next
sTable = sTable & "</tr>" 'close row
rs.MoveNext
Loop
sTable = sTable & "</tbody></table>" 'close table
rs.Close
Set rs = Nothing
''Compose Email
Set olApp = CreateObject("Outlook.Application")
Set olMsg = olApp.CreateItem(olMailItem)
olMsg.To = "test#abc.com"
For Each olRecip In olMsg.Recipients
olRecip.Resolve
Next
olMsg.Subject = "Test " & Format(Date, "Medium Date")
olMsg.Display 'This must go before the .HTMLBody line b/c that is the only way to capture the existing default signature.
olMsg.HTMLBody = "<Body><div>Hello,<br>" & _
"Please see report below.<br><br>" & sTable & olMsg.HTMLBody

Add image from Excel sheet to Outlook HTML body using Excel VBA

I am trying to add an image from an Excel sheet to an Outlook email.
I tried using a link to an image stored in a network location and on the Internet. However, not all users will have access to these locations.
Is it possible to store the image in another worksheet and then copy it into the email body?
I know the below won't work because you can't export shapes but can I do something like this?
ActiveUser = Environ$("UserName")
TempFilePath = "C:\Users\" & ActiveUser & "\Desktop\"
Sheets("Images").Shapes("PanelComparison").Export TempFilePath & "\PanelComparison.png"
panelimage = "<img src = ""TempFilePath\PanelComparison.png"" width=1000 height=720 border=0>"
The CreateEmail Sub calls the SaveToImage Sub. The SaveToImage sub grabs a range, creates a chart on a new page and then saves the picture(objChart) to a specified directory.
The LMpic string variable calls the image just saved and inputs it into the HTML body.
Public Sub CreateEmail()
Dim OutApp As Object
Dim OutMail As Object
Dim cell As Range
Dim FN, LN, EmBody, EmBody1, EmBody2, EmBody3 As String
Dim wb As Workbook
Dim ws As Worksheet
Application.ScreenUpdating = False
Set OutApp = CreateObject("Outlook.Application")
Set wb = ActiveWorkbook
Set ws = Worksheets("Sheet1")
Call SaveToImage
ws.Activate
LMpic = wb.Path & "\ClarityEmailPic.jpg'"
On Error GoTo cleanup
For Each cell In Columns("D").Cells.SpecialCells(xlCellTypeConstants)
If cell.Value Like "?*#?*.?*" Then
FN = Cells(cell.Row, "B").Value
LN = Cells(cell.Row, "A").Value
EmBody = Range("Email_Body").Value
EmBody1 = Range("Email_Body1").Value
EmBody2 = Range("Email_Body2").Value
'EmBody3 = Range("Email_Body3").Value
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = cell.Value
.Subject = "Volt Clarity Reminder "
.Importance = olImportanceHigh
.HTMLBody = "<html><br><br><br>" & _
"<table border width=300 align=center>" & _
"<tr bgcolor=#FFFFFF>" & _
"<td align=right>" & _
"<img src='" & objRange & "'>" & _
"</td>" & _
"</tr>" & _
"<tr border=0.5 height=7 bgcolor=#102561><td colspan=2></td></tr>" & _
"<tr>" & _
"<td colspan=2 bgcolor=#E6E6E6>" & _
"<body style=font-family:Arial style=backgroung-color:#FFFFFF align=center>" & _
"<p> Dear " & FN & " " & LN & "," & "</p>" & _
"<p>" & EmBody & "</p>" & _
"<p>" & EmBody2 & "<i><font color=red>" & EmBody1 & "</i></font>" & "</p>" & _
"</body></td></tr></table></html>"
.Display 'Or use Display
End With
On Error GoTo 0
Set OutMail = Nothing
End If
Next cell
cleanup:
Set OutApp = Nothing
Application.ScreenUpdating = True
End Sub
Public Sub SaveToImage()
'
' SaveToImage Macro
'
Dim DataObj As Shape
Dim objChart As Chart
Dim folderpath As String
Dim picname As String
Dim ws As Worksheet
Application.ScreenUpdating = False
Set ws = Worksheets("Sheet2")
folderpath = Application.ActiveWorkbook.Path & Application.PathSeparator 'locating & assigning current folder path
picname = "ClarityEmailPic.jpg" 'image file name
Application.ScreenUpdating = False
Call ws.Range("Picture").CopyPicture(xlScreen, xlPicture) 'copying the range as an image
Worksheets.Add(after:=Worksheets(1)).Name = "Sheet4" 'creating a new sheet to insert the chart
ActiveSheet.Shapes.AddChart.Select
Set objChart = ActiveChart
ActiveSheet.Shapes.Item(1).Width = ws.Range("Picture").Width 'making chart size match image range size
ActiveSheet.Shapes.Item(1).Height = ws.Range("Picture").Height
objChart.Paste 'pasting the range to the chart
objChart.Export (folderpath & picname) 'creating an image file with the activechart
Application.DisplayAlerts = False
ActiveWindow.SelectedSheets.Delete 'deleting sheet4
Application.DisplayAlerts = True
End Sub
In general email images are stored on a web server, with the SRC pointing to that server (http://...). They're not embedded in the email itself.