Populating recipient name in Outlook email - excel

I created a vb macro to send emails to listed people in an excel file with their corresponding data table.
Everything is working fine just one problem! After many efforts I could not get/ write a code to get Name of the recipient after Hello in strbody.
Here is the sample file Click here
Link to RangetoHTML function Click here (it needs to be pasted after end sub in below code)
Below is has been fixed and working now. refer to the sample filefor column arrangement
Sub Credit_Auto()
Dim test1 As Long, test2 As Long
test1 = Timer
Application.ScreenUpdating = False
'This code populates emails to outlook as per the Credit analysts.
Dim OutApp As Object
Dim OutMail As Object
Dim rng As Range
Dim Ash As Worksheet
Dim Cws As Worksheet
Dim Rcount As Long
Dim Rnum As Long
Dim FilterRange As Range
Dim FieldNum As Integer
Dim SigString As String
Dim Signature As String
Dim name_rg As Range
Dim name As String
Set OutApp = CreateObject("Outlook.Application")
'You may want to change the signature file path below to get your signature properly
'C:\Users\<UserName>\AppData\Roaming\Microsoft\Signatures
SigString = Environ("appdata") & _
"\Microsoft\Signatures\Pratik Kumar2.htm"
If Dir(SigString) <> "" Then
Signature = GetBoiler(SigString)
Else
Signature = ""
End If
On Error Resume Next
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
'Set filter sheet, you can also use Sheets("MySheet")
Set Ash = ActiveSheet
'Set filter range and filter column (column with e-mail addresses)
Set FilterRange = Ash.Range("A1:G" & Ash.Rows.Count)
FieldNum = 7
'Add a worksheet for the unique list and copy the unique list in A1
Set Cws = Worksheets.Add
FilterRange.Columns(FieldNum).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=Cws.Range("A1"), _
CriteriaRange:="", Unique:=True
'Count of the unique values + the header cell
Rcount = Application.WorksheetFunction.CountA(Cws.Columns(1))
'If there are unique values start the loop
If Rcount >= 2 Then
For Rnum = 2 To Rcount
'Filter the FilterRange on the FieldNum column
FilterRange.AutoFilter Field:=FieldNum, _
Criteria1:=Cws.Cells(Rnum, 1).Value
'If the unique value is a mail address create a mail
If Cws.Cells(Rnum, 1).Value Like "?*#?*.?*" Then
With Ash.AutoFilter.Range
On Error Resume Next
Set rng = .SpecialCells(xlCellTypeVisible)
On Error GoTo 0
End With
Set OutMail = OutApp.CreateItem(0)
'Search email address from Cws into Ash ~
Set name_rg = Ash.Columns(7).Find(Cws.Cells(Rnum, 1))
If Not name_rg Is Nothing Then
'input the row index of <name_rg>
'returns the name from col 6 ~
name = Ash.Cells(name_rg.Row, 6)
Else
name = "email not found in Ash"
End If
Set name_rg = Nothing
strbody = "Hello " & name & "," & "<br>" & "<br>" & _
"Please allocate the below account(s) to it's appropriate parent account." & "<br>"
On Error GoTo Cleanup
On Error Resume Next
With OutMail
.to = Cws.Cells(Rnum, 1).Value
.Subject = "Unallocated Credit Account"
.HTMLBody = strbody & RangetoHTML(rng) & "<br>" & Signature
.Send
End With
Set Ws = Nothing
On Error GoTo 0
Set OutMail = Nothing
End If
'Close AutoFilter
Ash.AutoFilterMode = False
Next Rnum
End If
Cleanup:
Set OutApp = Nothing
Application.DisplayAlerts = False
Cws.Delete
Application.DisplayAlerts = True
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
test2 = Timer
MsgBox "All the Credit Analysts have been notified and the entire process took " & Format((test2 - test1) / 86400, "hh:mm:ss") & " Seconds."
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2010
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
Columns("E:G").Select
Selection.Delete Shift:=xlToLeft
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
Function GetBoiler(ByVal sFile As String) As String
Dim fso As Object
Dim ts As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(sFile).OpenAsTextStream(1, -2)
GetBoiler = ts.readall
ts.Close
End Function

You could use the Range.Find Method.
Returns a Range object that represents the first cell where that information
is found. https://msdn.microsoft.com/en-us/vba/excel-vba/articles/range-find-method-excel
In your case this code below should do the trick .
Then you could do a loop though all the recipients emails
dim name_rg as range
dim name as string
{...}
' ~ Search email address from Cws into Ash ~
set name_rg = Ash.columns(7).Find(Cws.Cells(Rnum, 1))
If Not name_rg Is Nothing then
' ~ input the row index of <name_rg>
' returns the name from col 6 ~
name = Ash.cells(name_rg.row, 6)
Else
name = "email not found in Ash"
End If
{...}
set name_rg = Nothing

Related

Filter on To address and send email to one person with two or more rows in Excel VBA

From the table below I want to:
1-Filter per column B for unique values.
2-Once filtered if its only "1" row, then put each cell on that row into a variable.
3-Once filtered if the result is more than 1 record, meaning the same email address has two or more records then grab all the range from A to E (range to HTML).
4-Paste the information in an email.
5-Loop until column B hits a blank cell, which means its the end.
Table Example:
Record ID Email Data Data Data
Record1 test1#test.com 1 1 1
Record2 test2#test.com 2 2 2
Record3 test1#test.com 3 3 3
The following emails should be sent or displayed:
1- One email with two rows with all columns from A to E to test1#test.com in a range to Html.
2- One email with one row with all columns from A to E to test2#test.com in variable then paste them into HTML.
'*** You must have a Outlook email configured in outlook application on your system ***
'*** add reference to outook object library from references in tools ***
Sub BulkMail()
Application.ScreenUpdating = False
Dim WB As String
Dim WB1 As String
Dim WS As Worksheet
Dim Path As String
Dim LastRow As Long
Dim LastRow1 As Long
Dim ALastRow As Long
Dim lRow As Long
Dim lCol As Long
WB = CreateObject("WScript.Shell").specialfolders("Desktop")
WB1 = "CCE Allocation Email Source\Email Source file.xlsx"
Path = WB & "\" & WB1
Workbooks.Open Filename:=Path
'Creating references to Application and MailItem Objects of Outlook
Dim outApp As Outlook.Application
Dim outMail As Outlook.MailItem
'Creating variable to hold values of different items of mail
Dim sendTo, subj, atchmnt, msg, ccTo, bccTo As String
Dim name As String
Dim lstRow As Long
'My data is on sheet "Exceltip.com" you can have any sheet name.
Set WS = ActiveWorkbook.Sheets("Sheet1")
With WS
'Getting last row of containing email id in column 3.
lstRow = Cells(Rows.Count, 3).End(xlUp).Row
'Variable to hold all email ids
Dim rng As Range
Set rng = Range("A1:H" & lstRow)
Dim rng1 As Range
Set rng1 = Range("H2:H" & lstRow)
'initializing outlook object to access its features
Set outApp = New Outlook.Application
'On Error GoTo cleanup 'to handle any error during creation of object.
'Loop to iterate through each row, hold data in of email in variables and send
'mail to each email id.
For Each cell In rng1
sendTo = Range(cell.Address).Offset(0, 0).Value2
name = Split(cell, ".")(0)
strHtml = "<html>" & "<body>" & "Hi " & name & ", <br><br> Here is the information to report your time in PSA for the week of March 21 to March 25, 2022" & "<br>" & "</br>" & "</body>" & "</html>"
strHtml1 = "<html>" & "<body>" & "<font face='Arial'> <p style=font-size:10pt>" & "<br><br><b>Thanks & Regards</font><br><br> " & " <font face='Cambria' color='blue'> <style=font-size:11pt><i>Padmini Chandrashekar</i></b><br></font>" & _
"<font face='Arial'><style=font-size:10pt><b>PCO,CMU</b></font><br><font face='Calibri' color='blue'><font style=font-size:10pt>ITIL-V4 Foundation Certified<br></font></font><font face='Arial'><font style=font-size:8pt>India Global Delivery Center|<font color='red'>CGI</font><br>E-City Tower II , Electronic City Phase 1,<br>Bangalore, India - 560100.<br>|<font color='blue'>M-9739012740</font>|</font><br><br><font color='red'><b>Vacation Alert : Nil</b></font></p>" & "</body>" & "</html>"
On Error Resume Next 'to hand any error during creation of below object
Set outMail = outApp.CreateItem(0)
'Writing and sending mail in new mail
With outMail
.To = sendTo
.cc = ""
.Subject = "PSA for the week of March 18 to March 21"
.HTMLBody = strHtml & RangetoHTML(Union(rng.Rows(1), Application.Intersect(rng, cell.EntireRow))) & strHtml1
'.Attachments.Add atchmnt
'.Send 'this send mail without any notification. If you want see mail
.Display
End With
On Error GoTo 0 'To clean any error captured earlier
Set outMail = Nothing 'nullifying outmail object for next mail
Next cell 'loop ends
cleanup: 'freeing all objects created
Set outApp = Nothing
Application.ScreenUpdating = True
Application.ScreenUpdating = True
End With
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2016
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
Unique email addresses are saved to a dictionary.
Data is filtered once for each dictionary entry then visible data is passed to RangetoHTML.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
' If desperate declare as Variant
Sub BulkMail()
'Application.ScreenUpdating = False
Dim wBPathRoot As String
Dim wB1 As String
Dim Path As String
Dim wbDataSource As Workbook
Dim wS As Worksheet
Dim LastRow As Long
Dim emailAddress As String
Dim objDictionary As Object
Dim arrKey As Variant
' To store unique email addresses
Set objDictionary = CreateObject("Scripting.Dictionary")
wBPathRoot = CreateObject("WScript.Shell").specialfolders("Desktop")
Debug.Print wBPathRoot
WB1 = "CCE Allocation Email Source\Email Source file.xlsx"
Path = wBPathRoot & "\" & wB1
Debug.Print Path
Set wbDataSource = Workbooks.Open(Filename:=Path)
' Early binding requires reference to Microsoft Outlook XX.X Object Library
' Application and MailItem Objects of Outlook
Dim OutApp As Outlook.Application
Dim OutMail As Outlook.MailItem
' Variables to hold values of different items of mail
Dim sendTo As String
Dim subj As String
Dim strHtml As String
Set OutApp = New Outlook.Application
Set wS = wbDataSource.Sheets("Sheet1")
With wS
'Getting last row containing emailAddress in column 2.
LastRow = .Range("B" & .Rows.Count).End(xlUp).Row
Debug.Print "LastRow: " & LastRow
Dim i As Long
For i = 2 To LastRow
Debug.Print "B" & i
emailAddress = .Range("B" & i)
Debug.Print " emailAddress: " & emailAddress
If Not objDictionary.Exists(emailAddress) Then
objDictionary.Add emailAddress, True
Debug.Print " Added: " & emailAddress
End If
Next
End With
arrKey = objDictionary.Keys
'For i = LBound(arrKey) To UBound(arrKey)
' Debug.Print " Key " & i & " - " & arrKey(i)
'Next
For i = LBound(arrKey) To UBound(arrKey)
Debug.Print " Key " & i & " - " & arrKey(i)
emailAddress = arrKey(i)
Set OutMail = OutApp.CreateItem(olMailItem)
With wS
wS.Range("A1:E" & LastRow).AutoFilter 2, "=" & emailAddress
Dim visRange As Range
Set visRange = wS.Range("A1:E" & LastRow).Rows.SpecialCells(xlCellTypeVisible)
sendTo = emailAddress
'Writing and sending new mail
With OutMail
.To = sendTo
.Subject = "PSA for the week of March 18 to March 21"
strHtml = "<html>" & "<body>" & "Hi " & "</body>"
.HTMLBody = strHtml & RangetoHTML(visRange)
.Display
End With
Set OutMail = Nothing 'nullifying OutMail object for next mail
End With
Next
cleanup:
'freeing objects created
Set OutApp = Nothing
If wS.AutoFilterMode Then wS.ShowAllData
Application.ScreenUpdating = True
Debug.Print "Done"
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2016
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

Send table from Excel to specific people in Outlook based on content in Excel table

The ask: to extract information from Excel, send the info to respective email addresses on Outlook based on the names in a column.
Eg: if cell contains "Mary", copy the entire table and send to Mary's email address.
I keep the list of names and their respective email addresses in a separate sheet.
This is the information, assuming range is A1:D5
This is the list of email addresses, in a separate sheet
Updated: Long data
Desired output for Mary
The idea: In the data table,
i) i want to look up row 2 (names) for every name,
ii) copy the information about that person into Outlook.
Eg:
For Mary, copy A1: C5 and send to Mary's email address.
For Tom, copy headers (A1:A5) and Tom's data (D1:D5), excluding Mary's data, and send to Tom's email address.
I am stuck on how to do a loop search. I only have the basic macro to extract data and send to Outlook with no name reference:
Sub Mail_Selection_Range_Outlook_Body()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Nothing
' Only send the visible cells in the selection.
Set rng = Sheets("Sheet1").Range("A1:D5").SpecialCells(xlCellTypeVisible)
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected. " & _
vbNewLine & "Please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = "mary#123.com"
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.HTMLBody = RangetoHTML(rng)
.Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
' By Ron de Bruin.
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
You could loop through the columns (from right to left) and hide the unwanted ones.
update1 - restrict to 5 rows
update2 - don't send if no column visible
update3 - reformat added
Option Explicit
Sub Mail_Selection_Range_Outlook_Body()
Dim wb As Workbook
Dim wsUser As Worksheet, wsTable As Worksheet, wsEmail As Worksheet
Dim rng As Range, n As Long
Dim lastcol As Long, lastrow As Long, i As Long, c As Long
Dim sName As String, sAddr As String
Set wb = ThisWorkbook
Set wsTable = wb.Sheets("Sheet1")
Set wsEmail = wb.Sheets("Email")
With wsTable
lastcol = .Cells(1, Columns.Count).End(xlToLeft).Column
End With
Dim OutApp As Object, OutMail As Object, bSend As Boolean
Set OutApp = CreateObject("Outlook.Application")
' for each user
Set wsUser = wb.Sheets("Sheet2")
With wsUser
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To lastrow
sName = .Cells(i, "A")
sAddr = .Cells(i, "B")
bSend = False
' hide columns for not name
wsTable.Columns.Hidden = False
For c = lastcol To 2 Step -1
If wsTable.Cells(2, c).Value2 <> sName _
Or wsTable.Cells(3, c).Value2 = "Beef" Then
wsTable.Columns(c).Hidden = True
Else
bSend = True
End If
Next
' send email
If bSend Then
' visible
' copy to email sheet
Set rng = wsTable.UsedRange.Rows("1:5").SpecialCells(xlCellTypeVisible)
wsEmail.Cells.Clear
rng.Copy wsEmail.Range("A1")
Set rng = Reformat(wsEmail)
Set OutMail = OutApp.CreateItem(0)
With OutMail
.to = sAddr
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.HTMLBody = RangetoHTML(rng)
.Display
End With
wsEmail.Cells.Clear
n = n + 1
End If
Next
End With
wsTable.Columns.Hidden = False
Set OutMail = Nothing
Set OutApp = Nothing
MsgBox n & " emails sent", vbInformation
End Sub
Function Reformat(ws) As Range
Const MAX = 4
Dim lastrow As Long, lastcol As Long
Dim r As Long, c As Long
With ws
lastrow = .Cells(.Rows.Count, 1).End(xlUp).Row
lastcol = .Cells(1, .Columns.Count).End(xlToLeft).Column
r = lastrow + 2
c = MAX + 2
Do While c <= lastcol
.Cells(1, 1).Resize(lastrow).Copy .Cells(r, 1)
.Cells(1, c).Resize(lastrow, MAX).Copy .Cells(r, 2)
c = c + MAX
r = r + lastrow + 2
Loop
.Columns(MAX + 2).Resize(, lastcol).Delete
End With
Set Reformat = ws.UsedRange
End Function

Send Email to List with Tables/Ranges based on Each User in List

I am trying to:
Send an email to each user in a list.
(MailInfo - sheet1 - has two columns. A = Users, B = Email addresses)
Attach rows from four sheets with rows relevant to them (Columns A:H)
(Users are listed in column H in the other 4 sheets. Currently just 4 sheets that have ranges)
I want to loop through the agent list in Sheet 1 and then add the tables into the body of the email with just the rows that are relevant to them.
The code below will open an email for each user with complete tables.
I was able to get the following (from Ron de Bruin's documentation), to open emails for each row in EmailList 1 (I renamed it to MailInfo) and had to add column B to add the mail addresses.
I need to figure out the filtering of the values in the ranges to each user in column A in MailInfo.
Sub Send_Row_Or_Rows_1()
'For Tips see: http://www.rondebruin.nl/win/winmail/Outlook/tips.htm
'Don't forget to copy the function RangetoHTML in the module.
'Working in Excel 2000-2016
Dim OutApp As Object
Dim OutMail As Object
Dim rng As Range
Dim rng1 As Range
Dim Ash As Worksheet
Dim Cws As Worksheet
Dim Rcount As Long
Dim Rnum As Long
Dim FilterRange As Range
Dim FieldNum As Integer
Dim mailAddress As String
On Error GoTo cleanup
Set OutApp = CreateObject("Outlook.Application")
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
'Set filter sheet, you can also use Sheets("MySheet")
Set Ash = ActiveSheet
'Set filter range and filter column (Column with names)
Set FilterRange = Ash.Range("A1:H" & Ash.Rows.Count)
FieldNum = 1 'Filter column = A because the filter range start in A
'Add a worksheet for the unique list and copy the unique list in A1
Set Cws = Worksheets.Add
FilterRange.Columns(FieldNum).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=Cws.Range("A1"), _
CriteriaRange:="", Unique:=True
'Count of the unique values + the header cell
Rcount = Application.WorksheetFunction.CountA(Cws.Columns(1))
'If there are unique values start the loop
If Rcount >= 2 Then
For Rnum = 2 To Rcount
'Filter the FilterRange on the FieldNum column
FilterRange.AutoFilter Field:=FieldNum, _
Criteria1:=Cws.Cells(Rnum, 1).Value
'Look for the mail address in the MailInfo worksheet
mailAddress = ""
On Error Resume Next
mailAddress = Application.WorksheetFunction. _
VLookup(Cws.Cells(Rnum, 1).Value, _
Worksheets("Mailinfo").Range("A1:B" & _
Worksheets("Mailinfo").Rows.Count), 2, False)
On Error GoTo 0
If mailAddress <> "" Then
With Ash.AutoFilter.Range
On Error Resume Next
Set rng = Sheets("SampleTable1").Range("A1:H10").SpecialCells(xlCellTypeVisible)
Set rng1 = Sheets("SampleTable2").Range("A1:H10").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
End With
Set OutMail = OutApp.createitem(0)
On Error Resume Next
With OutMail
.To = mailAddress
.Subject = "Test mail"
.HTMLBody = RangetoHTML(rng) & "<br>" & RangetoHTML(rng1)
.display 'Or use Send
End With
On Error GoTo 0
Set OutMail = Nothing
End If
'Close AutoFilter
Ash.AutoFilterMode = False
Next Rnum
End If
cleanup:
Set OutApp = Nothing
Application.DisplayAlerts = False
Cws.Delete
Application.DisplayAlerts = True
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2016
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
For each user, consolidate the filtered data from the 4 sheets onto 1 temporary sheet and then use RangeToHtml.
Option Explicit
Sub Send_Row_Or_Rows_1()
Dim wb As Workbook
Dim wsInfo As Worksheet, ws As Worksheet, wsTmp As Worksheet
Dim i As Long, lastrow As Long
Set wb = ThisWorkbook
' sheets to copy
Dim data(3) As Worksheet
Set data(0) = wb.Sheets("SampleTable1")
Set data(1) = wb.Sheets("SampleTable2")
Set data(2) = wb.Sheets("SampleTable3")
Set data(3) = wb.Sheets("SampleTable4")
' add a temporary sheet
Application.DisplayAlerts = False
For Each ws In Sheets
If ws.name = "~tmp" Then ws.Delete
Next
Set wsTmp = Sheets.Add
wsTmp.name = "~tmp"
Application.DisplayAlerts = True
Dim rngCopy As Range
Dim sName As String, sAddr As String
Dim n As Long, k As Long, r As Long
' outlook
Dim appOut As Object, OutMail As Object
Set appOut = CreateObject("Outlook.Application")
' scan users
Set wsInfo = wb.Sheets("Mail Info")
With wsInfo
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
' for each user
For i = 2 To lastrow
sName = Trim(.Cells(i, "A"))
sAddr = Trim(.Cells(i, "B"))
r = 1
wsTmp.Cells.Clear
' consolidate each sheet on tmp sheet
For k = 0 To UBound(data)
Set ws = data(k)
' filter on name in col H 8
With ws.UsedRange
.AutoFilter
.AutoFilter 8, sName ' col H
Set rngCopy = .SpecialCells(xlCellTypeVisible)
rngCopy.Copy wsTmp.Cells(r, 1)
If r > 1 Then wsTmp.Rows(r).Delete ' leave 1 header
r = wsTmp.Cells(ws.Rows.Count, "A").End(xlUp).Row + 2 ' leave blank line
.AutoFilter
End With
Next
' email sheet
If r > 1 Then
Set OutMail = appOut.createitem(0)
With OutMail
.To = sAddr
.Subject = "Test Mail to " & sName
.HTMLBody = RangetoHTML(wsTmp.UsedRange)
.display 'Or use Send
End With
Set OutMail = Nothing
n = n + 1
End If
Next
End With
Application.DisplayAlerts = False
'ws.Sheets("~tmp").Delete
Application.DisplayAlerts = True
MsgBox n & " emails sent", vbInformation
End Sub

Email table range to same addresses

So currently my code works almost to what I like it too.
It currently groups the same emails together and emails that range to the person.
But in the email I'm trying to not include column A which is their email.
For Each cell In WS.Columns("A").Cells.SpecialCells(xlCellTypeConstants)
If cell.Value Like "?*#?*.?*" Then
'check if this email address has been used to generate an outlook email or not
If dict.exists(cell.Value) = False Then
dict.Add cell.Value, "" 'add the new email address
Set OutMail = OutApp.CreateItem(0)
Set rng = WS.UsedRange.Rows(1)
'find all of the rows with the same email and add it to the range
For Each cell2 In WS.UsedRange.Columns(1).Cells
If cell2.Value = cell.Value Then
Set rng = Application.Union(rng, WS.UsedRange.Rows(cell2.Row))
End If
Next cell2
I tried modifying the above code but can't seem to work it out.. Can anyone help me out?
full code:
Option Explicit
Sub Test1()
Dim OutApp As Object
Dim OutMail As Object
Dim dict As Object 'keep the unique list of emails
Dim cell As Range
Dim cell2 As Range
Dim rng As Range
Dim i As Long
Dim WS As Worksheet
Dim Signature As String
Application.ScreenUpdating = False
Set OutApp = CreateObject("Outlook.Application")
Set dict = CreateObject("scripting.dictionary")
Set WS = ThisWorkbook.Sheets("Sheet1") 'Current worksheet name
On Error GoTo cleanup
For Each cell In WS.Columns("A").Cells.SpecialCells(xlCellTypeConstants)
If cell.Value Like "?*#?*.?*" Then
'check if this email address has been used to generate an outlook email or not
If dict.exists(cell.Value) = False Then
dict.Add cell.Value, "" 'add the new email address
Set OutMail = OutApp.CreateItem(0)
Set rng = WS.UsedRange.Rows(1)
'find all of the rows with the same email and add it to the range
For Each cell2 In WS.UsedRange.Columns(1).Cells
If cell2.Value = cell.Value Then
Set rng = Application.Union(rng, WS.UsedRange.Rows(cell2.Row))
End If
Next cell2
On Error Resume Next
With OutMail
.SentOnBehalfOfName = ""
.GetInspector ' ## This inserts default signature
Signature = .HTMLBody ' ## Capture the signature HTML
.To = cell.Value
.CC = ""
.Subject = "Reminder"
.HTMLBody = "<BODY style=font-size:12pt;font-family:Calibri><font color=#000000>Hi " & WorksheetFunction.Proper(RemoveNumbers(Left((cell.Value), InStr((cell.Value), ".") - 1))) & ", " & "<br><br>" & "Please see your trip numbers and estimated cost below:" & vbNewLine & vbNewLine & RangetoHTML(rng) & Signature & "</font></BODY>"
.Display
End With
On Error GoTo 0
Set OutMail = Nothing
End If
End If
Next cell
cleanup:
Set OutApp = Nothing
Application.ScreenUpdating = True
End Sub
Function RemoveNumbers(Txt As String) As String
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "[0-9]"
RemoveNumbers = .Replace(Txt, "")
End With
End Function
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
One option is to use Intersect and Resize.
After the loop that creates rng but before passing rng to RangetoHTML:
With WS.UsedRange
Set rng = Intersect(rng, .Columns(2).Resize(,.Columns.Count - 1))
End With

Error when selected cell range to paste is hidden

I've code to paste specified ranges in different sheets into Outlook email.
I've separate code to hide cells. I only want to paste if there is data in the table.
When the last row of the specific range is hidden, the code fails. If I leave the last row unhidden, it will result in empty rows in the email.
How do I run the code even if the last row within the range is hidden?
Sub Trigger_Email()
'add rng as you add tabs. Remember to add rng under (i) Set rng and also
(ii) With OutMail
Dim rng As Range 'For TAB01 Tab
Dim rng2 As Range 'For TAB02 Tab
Dim rng3 As Range 'For TAB03 Tab
Dim rng4 As Range 'For TAB04 Tab
Dim rng5 As Range 'For TAB05 Tab
Dim OutApp As Object
Dim OutMail As Object
Dim StrBody As String
'Shows what appears at top of email
StrBody = "Hi XXX," & "<br>" & "<br>" & _
"The company provides" & "<br>" & "<br>" & _
"For your consideration& views." & "<br>" & "<br>"
Set rng = Nothing
On Error Resume Next
'This determines range to be printed into email.
'ADD rng(n+1) hear if you increase cover type. Determine range here as well.
Set rng =
Sheets("TAB01").Range("A5:G22").Rows.SpecialCells(xlCellTypeVisible)
Set rng2 =
Sheets("TAB02").Range("A1:F39").Rows.SpecialCells(xlCellTypeVisible)
Set rng3 =
Sheets("TAB03").Range("A1:F50").Rows.SpecialCells(xlCellTypeVisible)
Set rng4 =
Sheets("TAB04").Range("A1:F50").Rows.SpecialCells(xlCellTypeVisible)
Set rng5 =
Sheets("TAB05").Range("A1:F50").Rows.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected" & _
vbNewLine & "please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
'add more rng tabs below to display in body of email.
With OutMail
.To = "TEST#HOTMAIL.COM"
.CC = ""
.BCC = ""
.Subject = "TEST 01" & Cells(5, 1)
.HTMLBody = StrBody & rangetoHTML(rng) & rangetoHTML(rng3) &
rangetoHTML(rng4) & rangetoHTML(rng5) & rangetoHTML(rng2)
.Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
'Ignore this section. It prints excel format into HTML format in email.
Function rangetoHTML(rng As Range)
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TemTAB05B 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 TemTAB05B = Workbooks.Add(1)
With TemTAB05B.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 TemTAB05B.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TemTAB05B.Sheets(1).Name, _
Source:=TemTAB05B.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 TemTAB05B
TemTAB05B.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TemTAB05B = Nothing
End Function

Resources