Forum Discussion
VBA to email table range based on conditions
- Sep 08, 2022
Here we go Gabriel,
First of all, we need to add an extra function to copy Table to Email body, the function are reference from: https://stackoverflow.com/questions/70921224/macro-to-copy-visible-cells-from-excel-to-email-body
The function code is here, you can paste after End sub of your original code
Function RangetoHTML(rng As Range) Dim fso As Object Dim ts As Object Dim TempFile As String Dim TempWB As Workbook TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm" 'Copy the range and create a new workbook to past the data in rng.Copy Set TempWB = Workbooks.Add(1) With TempWB.Sheets(1) .Cells(1).PasteSpecial Paste:=8 .Cells(1).PasteSpecial xlPasteValues, , False, False .Cells(1).PasteSpecial xlPasteFormats, , False, False .Cells(1).Select Application.CutCopyMode = False On Error Resume Next .DrawingObjects.Visible = True .DrawingObjects.Delete On Error GoTo 0 End With 'Publish the sheet to a htm file With TempWB.PublishObjects.Add( _ SourceType:=xlSourceRange, _ Filename:=TempFile, _ Sheet:=TempWB.Sheets(1).Name, _ Source:=TempWB.Sheets(1).UsedRange.Address, _ HtmlType:=xlHtmlStatic) .Publish (True) End With 'Read all data from the htm file into RangetoHTML Set fso = CreateObject("Scripting.FileSystemObject") Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2) RangetoHTML = ts.readall ts.Close RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _ "align=left x:publishsource=") 'Close TempWB TempWB.Close savechanges:=False 'Delete the htm file we used in this function Kill TempFile Set ts = Nothing Set fso = Nothing Set TempWB = Nothing End Function
After added this function, you can paste the following code to your Sub
Sub EmailNegSurveys() Application.DisplayAlerts = False Application.ScreenUpdating = False Dim EApp As Object Set EApp = CreateObject("Outlook.Application") Dim EItem As Object Dim I As Long Dim Rec As String Dim DateStart As Date Dim SurveysWb As Workbook Dim SurveysPath As String SurveysPath = "C:\Users\Yau\Desktop\Example Surveys.xlsx" Set SurveysWb = Workbooks.Open(SurveysPath) Dim SurveysSheet As Worksheet Dim SurveysTable As ListObject Set SurveysSheet = SurveysWb.Sheets(1) Set SurveysTable = SurveysSheet.ListObjects("Table1") Dim MListSheet As Worksheet Dim MListTable As ListObject Set MListSheet = ThisWorkbook.Sheets(1) Set MListTable = MListSheet.ListObjects("Table1") For I = 2 To MListTable.ListRows.Count + 1 Rec = MListTable.Range(I, MListTable.ListColumns("Recipient").Index) DateStart = MListTable.Range(I, MListTable.ListColumns("Date Start").Index) SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Name").Index, Criteria1:=Rec SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Date").Index, Criteria1:=">=" & DateStart, Operator:=xlAnd SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Resolved").Index, Criteria1:=0, Operator:=xlAnd If SurveysTable.Range.SpecialCells(xlCellTypeLastCell).Row > 1 Then Set EItem = EApp.CreateItem(0) With EItem .To = MListTable.Range(I, MListTable.ListColumns("Recipient Email").Index) .CC = MListTable.Range(I, MListTable.ListColumns("Manager Email").Index) .Subject = "Negative Surveys" .HTMLBody = RangetoHTML(SurveysTable.Range.SpecialCells(xlCellTypeVisible)) .Display End With End If SurveysTable.AutoFilter.ShowAllData Next SurveysWb.Close Set EApp = Nothing Set EItem = Nothing Application.DisplayAlerts = True Application.ScreenUpdating = True End Sub
The code run as following logic:
1. Open the Survey Workbook, target the Table1 in Sheets1
2. Loop MList Table from Table1 Row 2 to last row (Since Row 1 is header)
3. Filter Survey Table1 by name, date and resolved
4. If filtered row more than 1 row (Header count as 1), means it have something to notify
5. Copy the table with header to Email
6. Display the email for you
Hope this help 🙂
Regards
Tony
Thanks so much for your answer. I would like each recipient to get all the rows that belong to them, and fulfill the other 2 conditions (Resolved is 0 and Date >= Date Start from the mailing list) listed together as a table in the body of a single email. So for example, Andrew would get row 2, because the other row belonging to him, has Resolved 1.
Joseph would only get row 5, because the other row belonging to him doesn't meet the date condition. However, if Joseph had more than 1 row meeting all conditions, all his rows would be listed together in the email
I would also like the table in each email to contain the headers if possible.
Best,
Gabriel
Here we go Gabriel,
First of all, we need to add an extra function to copy Table to Email body, the function are reference from: https://stackoverflow.com/questions/70921224/macro-to-copy-visible-cells-from-excel-to-email-body
The function code is here, you can paste after End sub of your original code
Function RangetoHTML(rng As Range)
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
After added this function, you can paste the following code to your Sub
Sub EmailNegSurveys()
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Dim EApp As Object
Set EApp = CreateObject("Outlook.Application")
Dim EItem As Object
Dim I As Long
Dim Rec As String
Dim DateStart As Date
Dim SurveysWb As Workbook
Dim SurveysPath As String
SurveysPath = "C:\Users\Yau\Desktop\Example Surveys.xlsx"
Set SurveysWb = Workbooks.Open(SurveysPath)
Dim SurveysSheet As Worksheet
Dim SurveysTable As ListObject
Set SurveysSheet = SurveysWb.Sheets(1)
Set SurveysTable = SurveysSheet.ListObjects("Table1")
Dim MListSheet As Worksheet
Dim MListTable As ListObject
Set MListSheet = ThisWorkbook.Sheets(1)
Set MListTable = MListSheet.ListObjects("Table1")
For I = 2 To MListTable.ListRows.Count + 1
Rec = MListTable.Range(I, MListTable.ListColumns("Recipient").Index)
DateStart = MListTable.Range(I, MListTable.ListColumns("Date Start").Index)
SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Name").Index, Criteria1:=Rec
SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Date").Index, Criteria1:=">=" & DateStart, Operator:=xlAnd
SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Resolved").Index, Criteria1:=0, Operator:=xlAnd
If SurveysTable.Range.SpecialCells(xlCellTypeLastCell).Row > 1 Then
Set EItem = EApp.CreateItem(0)
With EItem
.To = MListTable.Range(I, MListTable.ListColumns("Recipient Email").Index)
.CC = MListTable.Range(I, MListTable.ListColumns("Manager Email").Index)
.Subject = "Negative Surveys"
.HTMLBody = RangetoHTML(SurveysTable.Range.SpecialCells(xlCellTypeVisible))
.Display
End With
End If
SurveysTable.AutoFilter.ShowAllData
Next
SurveysWb.Close
Set EApp = Nothing
Set EItem = Nothing
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
The code run as following logic:
1. Open the Survey Workbook, target the Table1 in Sheets1
2. Loop MList Table from Table1 Row 2 to last row (Since Row 1 is header)
3. Filter Survey Table1 by name, date and resolved
4. If filtered row more than 1 row (Header count as 1), means it have something to notify
5. Copy the table with header to Email
6. Display the email for you
Hope this help 🙂
Regards
Tony
- Ber93Sep 08, 2022Brass Contributor
So just to be sure, my original code is replaced by the sub you sent, which comes after the function, right? So it would be the function first:
Function RangetoHTML(rng As Range) Dim fso As Object Dim ts As Object Dim TempFile As String Dim TempWB As Workbook TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm" 'Copy the range and create a new workbook to past the data in rng.Copy Set TempWB = Workbooks.Add(1) With TempWB.Sheets(1) .Cells(1).PasteSpecial Paste:=8 .Cells(1).PasteSpecial xlPasteValues, , False, False .Cells(1).PasteSpecial xlPasteFormats, , False, False .Cells(1).Select Application.CutCopyMode = False On Error Resume Next .DrawingObjects.Visible = True .DrawingObjects.Delete On Error GoTo 0 End With 'Publish the sheet to a htm file With TempWB.PublishObjects.Add( _ SourceType:=xlSourceRange, _ Filename:=TempFile, _ Sheet:=TempWB.Sheets(1).Name, _ Source:=TempWB.Sheets(1).UsedRange.Address, _ HtmlType:=xlHtmlStatic) .Publish (True) End With 'Read all data from the htm file into RangetoHTML Set fso = CreateObject("Scripting.FileSystemObject") Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2) RangetoHTML = ts.readall ts.Close RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _ "align=left x:publishsource=") 'Close TempWB TempWB.Close savechanges:=False 'Delete the htm file we used in this function Kill TempFile Set ts = Nothing Set fso = Nothing Set TempWB = Nothing End Function
Followed by your modified sub
Sub EmailNegSurveys() Application.DisplayAlerts = False Application.ScreenUpdating = False Dim EApp As Object Set EApp = CreateObject("Outlook.Application") Dim EItem As Object Dim I As Long Dim Rec As String Dim DateStart As Date Dim SurveysWb As Workbook Dim SurveysPath As String SurveysPath = "C:\Users\Yau\Desktop\Example Surveys.xlsx" Set SurveysWb = Workbooks.Open(SurveysPath) Dim SurveysSheet As Worksheet Dim SurveysTable As ListObject Set SurveysSheet = SurveysWb.Sheets(1) Set SurveysTable = SurveysSheet.ListObjects("Table1") Dim MListSheet As Worksheet Dim MListTable As ListObject Set MListSheet = ThisWorkbook.Sheets(1) Set MListTable = MListSheet.ListObjects("Table1") For I = 2 To MListTable.ListRows.Count + 1 Rec = MListTable.Range(I, MListTable.ListColumns("Recipient").Index) DateStart = MListTable.Range(I, MListTable.ListColumns("Date Start").Index) SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Name").Index, Criteria1:=Rec SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Date").Index, Criteria1:=">=" & DateStart, Operator:=xlAnd SurveysTable.Range.AutoFilter Field:=SurveysTable.ListColumns("Resolved").Index, Criteria1:=0, Operator:=xlAnd If SurveysTable.Range.SpecialCells(xlCellTypeLastCell).Row > 1 Then Set EItem = EApp.CreateItem(0) With EItem .To = MListTable.Range(I, MListTable.ListColumns("Recipient Email").Index) .CC = MListTable.Range(I, MListTable.ListColumns("Manager Email").Index) .Subject = "Negative Surveys" .HTMLBody = RangetoHTML(SurveysTable.Range.SpecialCells(xlCellTypeVisible)) .Display End With End If SurveysTable.AutoFilter.ShowAllData Next SurveysWb.Close Set EApp = Nothing Set EItem = Nothing Application.DisplayAlerts = True Application.ScreenUpdating = True End Sub
And that's it, right? Obviously I will replace the file path and that stuff
- gn00588950Sep 08, 2022Copper Contributor
Suggest function placed after sub 🙂 but it shouldn’t matter
and yes , please replace the file path stuff
Regards
Tony
- Ber93Sep 08, 2022Brass Contributor
Hey Tony
I'm modifying the variables referring to the sheets and tables, but I'm uncertain about some details, since I'm a complete VBA noob.
1. When replacing the sheet and workbook references to refer to my real file, should I replace the sheet and table numbers with names? Because my real files have sheet names and table names. I mean lines 21 and 22 from your sub. I'm thinking to change them to the following
Set SurveysSheet = SurveysWb.Data 'Data is the real name of my sheet Set SurveysTable = SurveysSheet.ListObjects("tblRaw") 'Real name of the table
Same goes for lines 26 and 27 of your sub. I'm thinking if I should change them to:
Set MListSheet = ThisWorkbook.Distinct_mailing_list 'real name of my MList sheet. I'm not sure what the (1) in your sub is for Set MListTable = MListSheet.ListObjects("MailingList") '
2. On the other hand, I see line 46 of your sub says to show all data. If I only wanted to display certain columns from my surveys table, is that where I would change that? Because unfortunately my real surveys table has many more fields than the example I sent.