Forum Discussion

Ber93's avatar
Ber93
Brass Contributor
Sep 08, 2022
Solved

VBA to email table range based on conditions

I'm completely new to VBA, but I need to take care of automating something at work. I've followed a great Youtube tutorial by Chester Tugwell called "VBA to Automate Sending Email From Excel Table with Attachment from Folder", but instead of attaching an existing file, I need to add an Excel table as part of the email's body. The contents of the table need to be selected from an existing table in a separate file, based on a couple of conditions.

I have two files (attaching both). One is "Example MList" which is a mailing list, and the other is "Example Surveys", which contains a table with the content that needs to be in the email body. The trick is that each recipient should only receive the rows that are relevant to them. I need the body of each email to contain rows from the Surveys file, where the name of the case owner is equal to the recipient, the date of the case is >= than the date listed in the MList, and the Resolved field =0.

In the MList file, I've saved the VBA code I have from the Youtube tutorial, but the body part is what I'm having trouble with.

 

I'm attaching both example files too. This is what they look like

 

Mailing list

Table that needs to go in the email body based on recipient's conditions

 

 

This is the code I have so far

Sub EmailNegSurveys()
Dim EApp As Object
Set EApp = CreateObject("Outlook.Application")
Dim EItem As Object
Dim path As String
path = "C:" 'the file path
Dim RList As Range
Set RList = Range("A2", Range("a2").End(xlDown))
Dim R As Range
For Each R In RList
    Set EItem = EApp.CreateItem(0)
        With EItem
        .To = R.Offset(0, 3)
        .CC = R.Offset(0, 4)
        .Subject = "Negative Surveys"
        .Body = "" 'This should be a table from the file "Example Surveys", retrieving the rows where the Name matches the Recipient, the Date is >= "Date Start", and "Resolved"=0
        .Display
End With
Next R
Set EApp = Nothing
Set EItem = Nothing
End Sub

 

Any help is much appreciated

  • Ber93 

     

    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

     

13 Replies

  • gn00588950's avatar
    gn00588950
    Copper Contributor

    Hi Ber93 

     

    In order to prepare your email body, you need to access the Table in Surveys workbook.

    You can do it via below code:

     

    Dim SurveysWb As Workbook
    Dim SurveysPath As String
    SurveysPath = "C:\Example Surveys.xlsx"
    Set SurveysWb = Workbooks.Open(SurveysPath)
    
    'Get access to Surverys Workbook's Table 1
    Dim SurveysSheet As Worksheet
    Dim SurveysTable As ListObject
    Set SurveysSheet = SurveysWb.Sheets(1)
    Set SurveysTable = SurveysSheet.ListObjects("Table1")
    
    'Loop all rows in SurveysTable 
    For I = 2 To SurveysTable.ListRows.Count + 1
        'Do your stuff here..
    Next

     

    After you are able to access the table, I would like to ask a question:

    How you would like to prepare the content from Surveys table?

    Say for example, you have Andrew & Joseph in MList

    When you prepare email for Andrew, will you get all rows that belongs to Andrew and combine them in single email? 

    Or send it out with 2 emails?

    Once you get this direction, we can work on how to prepare the email content.

     

    Regards

    Tony

    • Ber93's avatar
      Ber93
      Brass Contributor
      Hi 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
      • gn00588950's avatar
        gn00588950
        Copper Contributor

        Ber93 

         

        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

         

Resources