Impossible Excel-VBA Email Loop - excel

If someone could help me from going insane, my mother would appreciate it.
I have a long list of email addresses (many repeats) with associated Audit Locations. Basically I need to create one email for each email address and populate said email body with a list of all the associated Audit Locations.
e.g.
Column One (Email Address) | Column 2 (Audit Location)
Yoda1#lightside.org | Coruscant
Yoda1#lightside.org | Death Star
Yoda1#lightside.org | Tatooine
Vader#Darkside.org | Death Star
Vader#Darkside.org | Coruscant
Jarjar#terrible.org | Yavin
So far I have created a CommandButton Controlled vba that takes Column One and makes it unique in a new worksheet.
Then I have another sub that creates an email for each unique email address. But I am stuck on the "If...Then" statement. Essentially, I want to add the information in Column 2 (Audit Location) if the Recipient of the email is the email address in Column One and then continue to append to the email body until the email address no longer equals the recipient email address. Any guidance would be huge.
Private Sub CommandButton1_Click()
Call MakeUnique
Call EmailOut
End Sub
Sub MakeUnique()
Dim vaData As Variant
Dim colUnique As Collection
Dim aOutput() As Variant
Dim i As Long
'Put the data in an array
vaData = Sheet1.Range("A:A").Value
'Create a new collection
Set colUnique = New Collection
'Loop through the data
For i = LBound(vaData, 1) To UBound(vaData, 1)
'Collections can't have duplicate keys, so try to
'add each item to the collection ignoring errors.
'Only unique items will be added
On Error Resume Next
colUnique.Add vaData(i, 1), CStr(vaData(i, 1))
On Error GoTo 0
Next i
'size an array to write out to the sheet
ReDim aOutput(1 To colUnique.Count, 1 To 1)
'Loop through the collection and fill the output array
For i = 1 To colUnique.Count
aOutput(i, 1) = colUnique.Item(i)
Next i
'Write the unique values to column B
Sheets.Add.Name = "Unique"
ActiveSheet.Range("A1").Resize(UBound(aOutput, 1), UBound(aOutput, 2)).Value = aOutput
End Sub
Sub EmailOut()
Dim xOutApp As Object
Dim xOutMail As Object
Dim xMailBody As String
On Error Resume Next
Dim cell As Range
For Each cell In Worksheets("Unique").Columns("a").Cells.SpecialCells(xlCellTypeConstants)
recip = cell.Value
Set xOutApp = CreateObject("Outlook.Application")
Set xOutMail = xOutApp.CreateItem(0)
For Each org In Columns("b").Cells.SpecialCells(xlCellTypeConstants)
If org.Value Like recip Then
xMailBody = "Body content" & vbNewLine & vbNewLine & _
"This is line 1" & " " & cell.Offset(0, 3).Value & vbNewLine & _
[B5] & vbNewLine & _
"This is line 2"
End If
Next org
On Error Resume Next
With xOutMail
.To = recip
.CC = ""
.BCC = ""
.Subject = cell.Offset(0, 2).Value & " " & cell.Offset(0, 3).Value & " " & "Remittance Advice"
.Body = xMailBody
.Display 'or use .Send
End With
On Error GoTo 0
Set xOutMail = Nothing
Set xOutApp = Nothing
Next
End Sub

Based on your example I quickly wrote the following:
Option Explicit
Public Sub SendEmails()
Dim dictEmailData As Object
Dim CurrentWorkBook As Workbook
Dim WrkSht As Worksheet
Dim rngToLookUp As Range
Dim lngLastRow As Long, i As Long
Dim arryEmailData As Variant
Dim objOutlookApp As Object, objOutlookEmail As Object
Dim varKey As Variant
Application.ScreenUpdating = False
Set CurrentWorkBook = Workbooks("SomeWBName")
Set WrkSht = CurrentWorkBook.Worksheets("SomeWSName")
lngLastRow = WrkSht.Cells(WrkSht.Rows.Count, "A").End(xlUp).Row 'Find last row with data
Set rngToLookUp = WrkSht.Range("A2:B" & lngLastRow) 'set range for last row of data
arryEmailData = rngToLookUp.Value2 'Get the email data from the sheet into an array
Set dictEmailData = CreateObject("Scripting.Dictionary") 'set the dicitonary object
On Error GoTo CleanFail
For i = LBound(arryEmailData, 1) To UBound(arryEmailData, 1)
varKey = UCase(Trim(arryEmailData(i, 1)))
If Not dictEmailData.Exists(varKey) Then
dictEmailData(varKey) = vbNewLine & vbNewLine & Trim(arryEmailData(i, 2))
Else
dictEmailData(varKey) = dictEmailData(varKey) & vbNewLine & Trim(arryEmailData(i, 2))
End If
varKey = Empty
Next i
'for each unique key in the dicitonary
'get the corresponding item
'created in the loop above
Set objOutlookApp = CreateObject("Outlook.Application") 'set the outlook object
Dim Msg As String, MailBody As String
For Each varKey In dictEmailData.Keys
Msg = dictEmailData.Item(varKey)
Set objOutlookEmail = objOutlookApp.CreateItem(0)
MailBody = "Dear Colleague," & Msg
With objOutlookEmail
.To = varKey
.Subject = "Remittance Advice"
.Body = MailBody
.Send
End With
Set objOutlookEmail = Nothing
Msg = Empty: MailBody = Empty
Next
MsgBox "All Emails have been sent", vbInformation
CleanExit:
Set objOutlookApp = Nothing
Application.ScreenUpdating = True
Exit Sub
CleanFail:
Resume CleanExit
End Sub
Add the first occurrence of a varKey = email address to the dictionary dictEmailData along with its corresponding item dictEmailData(varKey) = Email body. On the next occurrence of the email address, append to the Email body. Once the dictionary is built, loop through it and send the emails
Printing to the immediate window yields:

Related

Searching for unique value and call sub, if not go to next cell

I am trying to create automatic call for a sub based on unique values.
Column E
The order is in column E
Sub FindDate()
Dim Cell As Range
'For Each Cell In ActiveSheet.Range("A1:A50")
' If Cell.Value = [Today()] Then
' Cell.Select
'ActiveCell.Offset(0, 4).Select
' End If
'Exit For
'Next
For Each Cell In ActiveSheet.Range("E2:E100")
If ActiveCell.Value = "" Then
End If
Exit For
Next
For Each Cell In ActiveSheet.Range("E2:E100")
If ActiveCell.Value = ActiveCell.Offset(-1, 0) Then
ActiveCell.Offset(1, 0).Select
Call EmailOrder
' ElseIf ActiveCell.Value <> ActiveCell.Offset(-1, 0) Then Call EmailOrder
'ElseIf ActiveCell.Value = "" Then Exit Sub
End If
Next Cell
End Sub
At the moment with this code (I know it is a really messy but I am just a VBA beginner) when I select the second PAU21001316 (from the picture) then it is calling my EmailOrder sub for PAU21001316 and PAU21001318 but not for the PAU21001319 and PAU21001320.
The code should do : If I select a cell, let's say PAU21001309 to look if the cell above ( or below) is the same value, if it's the same to move one cell below if not to run Call EmailOrder and after to move to the next cell and to do the same. Then If a cell is empty to stop.
The point is to run every unique value at the same time.
The other thing that I was trying to do (the first code as comment) was to go to the today's date and move 4 columns which will go to the first Order number. It's moving the active cell but after that do nothing, just looping.
If anyone can help me to finish my code I will be grateful.
Sub EmailOrder(c As Range)
Dim ActiveC As Variant
Dim DirFile As String
Dim OutApp As Outlook.Application
Dim OutMail As Outlook.MailItem
Dim strbody As String
Dim SigString As String
Dim Signature As String
Dim xOutMsg As String
Dim Timenow As String
Dim signImageFolderName As String
Dim completeFolderPath As String
Dim colFiles As New Collection
'GetFiles "C:\xxx\", ActiveC & ".pdf", True, colFiles
'If colFiles.Count > 0 Then
' 'work with found files
'End If
If Time < TimeValue("12:00:00") Then
Timenow = "Good Morning"
ElseIf Time > TimeValue("12:00:00") And Time < TimeValue("17:00:00") Then
Timenow = "Good Afternoon"
Else
Timenow = "Good Evening"
End If
xOutMsg = Timenow & ", <br> <br> xxx<br/>"
ActiveC = Application.ActiveCell.Value
Dim sRes As String
Dim po As Range
Dim rg As Range
Dim b2 As Range
Set po = ActiveCell.Offset(0, 3)
Set rg = Sheets("Email").Range("B1:D200")
Set b2 = po
sRes = Application.VLookup(b2, rg, 3, True)
'Optimize Code
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
DirFile = "C:\xxx\" & ActiveC & ".pdf"
If Dir(DirFile) = "" Then
MsgBox "File does not exist", vbCritical
End If
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(olMailItem)
'Change only Mysig.htm to the name of your signature
SigString = Environ("appdata") & _
"\Microsoft\Signatures\xxx.htm"
signImageFolderName = "xxxfiles"
completeFolderPath = Environ("appdata") & "\Microsoft\Signatures\" & signImageFolderName
If Dir(SigString) <> "" Then
Signature = GetBoiler(SigString)
Signature = VBA.Replace(Signature, signImageFolderName, completeFolderPath)
Else
Signature = ""
End If
'Create Outlook email with attachment
On Error Resume Next
With OutMail
.To = sRes
.CC = ""
.BCC = ""
.Subject = "xxx " & ActiveC
.HTMLBody = xOutMsg & "<br>" & Signature
.Attachments.Add "C:xxx\" & ActiveC & ".pdf"
.Display
End With
Call FindDate
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function GetBoiler(ByVal sFile As String) As String
'Dick Kusleika
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
This is the main code, made form different codes. The main purpose is to get the value of the active cell and look in to the folder (I couldn't do to look in to the sub folders) for the file name.pdf and the to attach it to the email. The other part is to look for the supplier name in column H and VLOOKUP to another sheet "Email" for the supplier email and add it to the "To" section. The other code is for the signature and the body of the email.
The code is working but only when I select the specific cell. But it will be faster if is doing every PO for the day automatically.
Try this:
Sub Tester()
Dim f As Range, c As Range
Set f = Range("A1:A50").Find(Date) 'Look for today's date
If f Is Nothing Then Exit Sub 'Today not found....
Set c = f.Offset(0, 4) 'move over to Col E
Do While Len(c.Value) > 0
If c.Offset(1, 0).Value <> c.Value Then
EmailOrder c 'pass cell directly to your called sub
End If
Set c = c.Offset(1, 0) 'move down one row
Loop
End Sub
Sub EmailOrder(c As Range)
Const FLDR As String = "C:\xxx\" 'start search here
Dim ActiveC As Variant
Dim OutApp As Outlook.Application
Dim OutMail As Outlook.MailItem
Dim strbody As String
Dim SigString As String
Dim Signature As String
Dim sRes As String
Dim po
Dim rg As Range, b2 As Range
Dim signImageFolderName As String, completeFolderPath As String
Dim colFiles As Collection
ActiveC = c.Value
po = c.Offset(0, 3).Value
Set rg = Sheets("Email").Range("B1:D200")
sRes = Application.VLookup(po, rg, 3, True) 'False?
Set colFiles = GetMatches(FLDR, ActiveC & ".pdf") 'find any matches
If colFiles.Count = 0 Then
MsgBox "File '" & ActiveC & ".pdf' does not exist", vbCritical
Exit Sub
End If
'what to do if >1 files found?
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(olMailItem)
'Change only Mysig.htm to the name of your signature
SigString = Environ("appdata") & "\Microsoft\Signatures\xxx.htm"
signImageFolderName = "xxxfiles"
completeFolderPath = Environ("appdata") & "\Microsoft\Signatures\" & signImageFolderName
If Dir(SigString) <> "" Then
Signature = VBA.Replace(GetBoiler(SigString), signImageFolderName, completeFolderPath)
End If
With OutMail
.To = sRes
.CC = ""
.BCC = ""
.Subject = "xxx " & ActiveC
.HTMLBody = TimeGreeting & ", <br> <br> xxx<br/>" & Signature
.Attachments.Add colFiles(1).Path 'assuming you only want the first match if >1
.Display
End With
Call FindDate
End Sub
Function TimeGreeting() As String
If Time < TimeValue("12:00:00") Then
TimeGreeting = "Good Morning"
ElseIf Time > TimeValue("12:00:00") And Time < TimeValue("17:00:00") Then
TimeGreeting = "Good Afternoon"
Else
TimeGreeting = "Good Evening"
End If
End Function
Function for file searching:
'Return a collection of file objects given a starting folder and a file pattern
' e.g. "*.txt"
'Pass False for last parameter if don't want to check subfolders
Function GetMatches(startFolder As String, filePattern As String, _
Optional subFolders As Boolean = True) As Collection
Dim fso, fldr, f, subFldr
Dim colFiles As New Collection
Dim colSub As New Collection
Set fso = CreateObject("scripting.filesystemobject")
colSub.Add startFolder '<< start with the top-level folder
Do While colSub.Count > 0
Set fldr = fso.getfolder(colSub(1))
colSub.Remove 1 '<< remove from queue
For Each f In fldr.Files 'check all files
If UCase(f.Name) Like UCase(filePattern) Then colFiles.Add f
Next f
If subFolders Then 'add subfolders to queue for listing
For Each subFldr In fldr.subFolders
colSub.Add subFldr.Path
Next subFldr
End If
Loop
Set GetMatches = colFiles
End Function

How to send specific row data to the respective users from an Excel sheet?

I have an Excel sheet with data of users as shown below in table. Here I need to send the email to user their specific details containing in column A,B,C.
Using this code, I can only send the multiple row data in multiple email, but I need to send the multiple row data in single mail to respective user.
Sub BulkMail()
Application.ScreenUpdating = False ThisWorkbook.Activate Dim outMail As Outlook.MailItem
'Creating variable to hold values of different items of mail
Dim sendTo, subj, msg, Name, Company, Time As String
Dim lstRow As Long
'My data is on sheet "Exceltip.com" you can have any sheet name.
ThisWorkbook.Sheets("Sheet2").Activate
'Getting last row of containing email id in column 1.
lstRow = Cells(Rows.Count, 1).End(xlUp).Row
'Variable to hold all email ids
Dim rng As Range
Set rng = Range("A2:A" & 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 rng
sendTo = Range(cell.Address).Offset(0, 0).Value2
subj = Range(cell.Address).Offset(0, 1).Value2
msg = Range(cell.Address).Offset(0, 2).Value2
Name = Range(cell.Address).Offset(0, 3).Value2
Company = Range(cell.Address).Offset(0, 4).Value2
Time = Range(cell.Address).Offset(0, 5).Value2
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
.Subject = subj
strbody = msg & vbNewLine & Name & " " & Company & " " & Time
.Body = strbody
.Send 'this send mail without any notification. If you want see mail
'before send, use .Display method.
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 Sub
I have added two columns. In column F there are the users' name (without duplicate.I have added the last user. He doesn't have messages). In column G there are how many message there are for the user in column F.
Here an example
I have got a problem with foreach cell... and then I used the for loop (classic).
In this example I used another for. The first for check the user's name and with the second for check how many messages there are for the user. I have put one or more messages in the strbody variable. When the second for is finished, I insert the number of messages for the user (COLUMN G) and then send the email.
My Code:
Sub bulkMail()
Const COLUMN_F As Byte = 6
Const COLUMN_G As Byte = 7
Application.ScreenUpdating = False
ThisWorkbook.Activate
Dim outMail As Outlook.MailItem
'Creating variable to hold values of different items of mail
Dim sendTo, subj, msg, Name, Company, Time As String
Dim lstRow As Long
'add variable
Dim numberUsers, i,j, numberMsg As Integer
'My data is on sheet "Exceltip.com" you can have any sheet name.
ThisWorkbook.Sheets("Sheet2").Activate
'Getting last row of containing email id in column 1.
lstRow = Cells(Rows.Count, 1).End(xlUp).Row
'count number users in Column F -> 6. Here there are the users without duplicate name.
numberUsers = Cells(Rows.Count, COLUMN_F).End(xlUp).Row
'Variable to hold all email ids
'i didn't use range because i had problems with foreach (i don't know why)
'Dim rng As Range
'Set rng = Range("A2:A" & 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 i = 2 To numberUsers
numberMsg = 0
strbody = ""
'For Each cell In rng
For j = 2 To lstRow
'Name = Range(cell.Address).Offset(0, 3).Value2
Name = Cells(j, 1) ' I get the name column A
If (Cells(i, COLUMN_F) = Name) Then
numberMsg = numberMsg + 1 ' count the number of messages
sendTo = Cells(j, 1) 'Range(cell.Address).Offset(0, 0).Value2 - COLUMN A
subj = Cells(j, 5) 'Range(cell.Address).Offset(0, 1).Value2 - COLUMN E
msg = Cells(j, 4) 'Range(cell.Address).Offset(0, 2).Value2 - COLUMN D
'Name = cells(j,1)Range(cell.Address).Offset(0, 3).Value2
Company = Cells(j, 2) 'Range(cell.Address).Offset(0, 4).Value2 - COLUMN B
Time = Cells(j, 3) 'Range(cell.Address).Offset(0, 5).Value2 - COLUMN C
strbody = strbody & msg & vbNewLine & Name & " " & Company & " " & Time & vbNewLine
'Debug.Print (strbody)
End If
Next j 'loop ends
Cells(i, COLUMN_G) = numberMsg ' get in COLUMN G the number of message for the user in COLUMN F
On Error Resume Next 'to hand any error during creation of below object
'check if there is almost a message for a user
If (numberMsg <> 0) Then
Set outMail = outApp.CreateItem(0)
'Writing and sending mail in new mail
With outMail
.To = sendTo
.Subject = subj
'strbody = msg & vbNewLine & Name & " " & Company & " " & Time
.Body = strbody
.Send 'this send mail without any notification. If you want see mail
'before send, use .Display method.
End With
On Error GoTo 0 'To clean any error captured earlier
Set outMail = Nothing 'nullifying outmail object for next mail
End If
'Next cell 'loop ends
Next i
cleanup: 'freeing all objects created
Set outApp = Nothing
Application.ScreenUpdating = True
Application.ScreenUpdating = True
End Sub
before trying my code check if my cells match yours.

How can I send multiple e-mails, to multiple recipients using VBA?

Sub SendEmail()
Dim OutlookApp As Object
Dim MItem As Object
Dim cell As Range
Dim email_ As String
Dim cc_ As String
Dim subject_ As String
Dim body_ As String
Dim attach_ As String
Dim StrBody As String
'Create Outlook object
Set OutlookApp = CreateObject("Outlook.Application")
'Loop through the rows
For Each cell In Sheets("publico").Range("H2:H2000").Cells.SpecialCells(xlCellTypeConstants)
If cell.Row <> 1 Then
If cell.Value <> "" Then ' to check email address cell is empty or not
email_ = cell.Value ' email address mention in the F column
Else
email_ = cell.Offset(0, 1).Value 'alternative email address
End If
subject_ = Sheets("CAPA").Range("D1").Value 'as of now i mentioned column B as subject, change the value accordingly
' body_ = Sheets("CAPA").Range("D2").Value 'please change the offset value based on the body content cell
StrBody = Sheets("CAPA").Range("D2").Value & "<br><br>" & _
Sheets("CAPA").Range("D3").Value & "<br><br>" & _
Sheets("CAPA").Range("F7").Value & "<br><br><br>"
**Sheets("publico").Range**
' cc_ = cell.Offset(0, 3).Value ' remove comments if you are going to use the cc and also change the offset value according to cc mail address cell value.
' attach_ = cell.Offset(0, 4).Value ' remove comments "'" if you going attache any file and change the offset value based on the attachment value position.
'Create Mail Item and send it
Set MItem = OutlookApp.CreateItem(0)
With MItem
.To = email_
' .CC = cc_
.Subject = subject_
.HTMLBody = StrBody
'.Attachments.Add attach_
'.Display
End With
MItem.Send
Sheets("publico").Range("J2").Value = "enviado"
End If
Next
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
So, for branch 100, manager 15, I will send line 1 and 2 of the sheet, to the manager mail only.
In the case of no manager assigned, the email will be directed to the head (email2).
After sending an e-mail, the F column must generate a log "OK".
EDIT:
I edited the code provided and the e-mail is going to the proper recipients.
Please try the below code.
Sub SendEmail()
Dim OutlookApp As Object
Dim MItem As Object
Dim cell As Range
Dim email_ As String
Dim cc_ As String
Dim subject_ As String
Dim body_ As String
Dim attach_ As String
'Create Outlook object
Set OutlookApp = CreateObject("Outlook.Application")
'Loop through the rows
For Each cell In Columns("f").Cells.SpecialCells(xlCellTypeConstants)
If cell.Row <> 1 Then
If cell.Value <> "" Then ' to check email address cell is empty or not
email_ = cell.Value 'email address mention in the F column
Else
email_ = cell.Offset(0, 1).Value 'alternative email address
End If
subject_ = cell.Offset(0, -4).Value 'as of now i mentioned column B as subject, change the value accordingly
body_ = cell.Offset(0, 2).Value 'please change the offset value based on the body content cell
' cc_ = cell.Offset(0, 3).Value ' remove comments if you are going to use the cc and also change the offset value according to cc mail address cell value.
' attach_ = cell.Offset(0, 4).Value ' remove comments "'" if you going attache any file and change the offset value based on the attachment value position.
'Create Mail Item and send it
Set MItem = OutlookApp.CreateItem(0)
With MItem
.To = email_
' .CC = cc_
.Subject = subject_
.Body = body_
'.Attachments.Add attach_
'.Display
End With
MItem.Send
cell.Value = "ok"
End If
Next
End Sub
Thanks,
Arun

Email people based on a range VBA

I have the following data set
I have the following code to send an email to each row. How do I make it group the rows and send them all as 1 email like in the picture
Here is an example of the email I am looking to build
At the moment the code steps through each row and builds and email off of that. I want it to check the A Column for a code and find all other columns with the same code and build one email using information from all of their columns
Sub SendIntransitEmail()
Dim Mail_Object, OutApp As Variant
Dim eRng1, eRng2, eRng3, rng1, rng2, rng3, rng4, cl As Range
Dim sTo, sCC, sLoc, sFile1, sFile2, sHeader, sBody As String
Set rng4 = ThisWorkbook.Worksheets("sheet1").Range("B4")
Dim intNum As Integer
intNum = ThisWorkbook.Worksheets("sheet1").Range("B1")
For i = 5 To intNum
On Error Resume Next
Set Mail_Object = CreateObject("Outlook.Application")
Set OutApp = Mail_Object.CreateItem(0)
Set rng1 = ThisWorkbook.Worksheets("sheet1").Range("A" & i)
Set eRng1 = ThisWorkbook.Worksheets("sheet1").Range(Cells(i, 5), Cells(i, 8))
Set eRng2 = ThisWorkbook.Worksheets("sheet1").Range(Cells(i, 9), Cells(i, 40))
Set eRng3 = ThisWorkbook.Worksheets("sheet1").Range(Cells(4, 2), Cells(4, 4))
Set eRng4 = ThisWorkbook.Worksheets("sheet1").Range(Cells(i, 2), Cells(d, 2))
For Each cl In eRng1
sTo = sTo & ";" & cl.Value
Next
sTo = Mid(sTo, 2)
For Each cl In eRng2
sCC = sCC & ";" & cl.Value
Next
For Each cl In eRng3
sDelivery = sDelivery & cl.Value
Next
For Each cl In eRng4
sTrailer = sTrailer & cl.Value
Next
For Each cl In eRng5
sShipper = sShipper & cl.Value
Next
sCC = Mid(sCC, 2)
Set OApp = CreateObject("Outlook.Application")
Set OMail = OApp.CreateItem(0)
With OutApp
.To = sTo
.CC = sCC
.Subject = "Location " & rng1
.BodyFormat = olFormatHTML
.HTMLBody = "<p> Hello, </p><p>Your delivery information is below: </p><p>
Deliver Number: " & sDelivery & "<p/> <p> Trailer Number: " & sTrailer & "
<p/><p>Shipper ID: " & sShipper & "<p/><p>Best Regards </p>"
.display
End With
Set OMail = Nothing
Set OApp = Nothing
Set eRng1 = Nothing
Set eRng2 = Nothing
sTo = ""
sCC = ""
Next i
End Sub
I see what you're trying to do now. You should shift your loop from running on each column, to rather doing by row. Something like... if row doesn't have member above, collect all appropriate members in the row's column, then run a loop through remaining rows, testing to see if they MATCH and then appending them to the email.
At the moment, I'm too lazy to write this out but here's a custom formula that might help you that will only test if the member in the respective row exists above.
Sub SendIntransitEmail()
Dim Mail_Object, OutApp As Variant
Dim eRng1, eRng2, eRng3, rng1, rng2, rng3, rng4, cl As Range
Dim sTo, sCC, sLoc, sFile1, sFile2, sHeader, sBody As String
Set rng4 = ThisWorkbook.Worksheets("sheet1").Range("B4")
Dim intNum As Integer
intNum = ThisWorkbook.Worksheets("sheet1").Range("B1")
Set Mail_Object = CreateObject("Outlook.Application")
For i = 5 To intNum
On Error Resume Next 'I wouldn't use this...
'test if first instance of plant
If New_Plant_Test(ThisWorkbook.Worksheets("sheet1").Cells(i, 1)) = True Then
'run a loop from this row all the way down to populate the respective emails,
'example:
For Each rcell In Range(ThisWorkbook.Worksheets("sheet1").Cells(i, 1), ThisWorkbook.Worksheets("sheet1").Cells(intNum, 1)).Cells
'apply respective values to variables in that row.
'this should probably be a separate private macro.
Next rcell
'send email and clear variables and clear variables
Else
'skips as plant already existed
End If
Next i 'continue loop by each row
End Sub
Private Function New_Plant_Test(rng As Range) As Boolean
Dim tRow As Long, ws As Worksheet
tRow = rng.Row
Set ws = Sheets(rng.Parent.Name)
On Error GoTo NewMember
tRow = Application.WorksheetFunction.Match(ws.Cells(tRow, 1), Range(ws.Cells(1, 1), ws.Cells(tRow - 1, 1)), False)
On Error GoTo 0
Exit Function
NewMember:
New_Plant_Test = True
End Function

I have a list of email addresses in excel that i need to send emails to. The subject and body are in cells besides the email address

As mentioned in the subject of this post, I am attempting to send emails automatically by running a macro so that if cell J2 has the words "Send Reminder" in it, then the email address in cell K2 should be sent an email with the subject title in cell L2 and Body in Cell M. I have a list of emails ranging from cells K2:K59
Currently I have the following code:
Sub SendEm()
Dim i As Integer, Mail_Object, Email_Subject, o As Variant, lr As Long
lr = Cells(Rows.Count, "K").End(xlUp).Row
Set Mail_Object = CreateObject("Outlook.Application")
For i = 2 To lr
With Mail_Object.CreateItem(o)
.Subject = Range("L2").Value
.To = Range("K" & i).Value
.Body = Range("M2").Value
.Send
End With
Next i
MsgBox "E-mail successfully sent", 64
Application.DisplayAlerts = False
Set Mail_Object = Nothing
End Sub
I already have outlook open with references for Microsoft Outlook 14.0 Object Library selected amongst others, and I get an error saying " Run-time error '287' Application-definer or object-defined error, if i try to debug it, it highlights .Send in my code.
Can anyone help point out what I am doing wrong? I have tried various types of code to send emails based on different youtube videos etc. but seem to run into this error each time!
Thanks for your help ahead of time!
Edit1: I updated the code to the following based on suggestions and now a different issue:
Private Sub CommandButton21_Click()
'~~> Excel Objects/Variables
Dim ws As Worksheet
Dim lRow As Long, i As Long
'~~> Outlook Objects/Variables
Dim OutApp As Object
Dim OutMail As Object
Dim emailRange As Range, cl As Range
Dim sTo As String
Dim subjectRange As Range, c2 As Range
Dim sSubject As String
Dim bodyRange As Range, c3 As Range
Dim sBody As String
'~~> Set your worksheet here
Set ws = ThisWorkbook.Sheets("Sheet11")
'~~> Open Outlook
Set OutApp = CreateObject("Outlook.Application")
With ws
'~~> Get last row from Col J as that is what we
'~~> are going to check for the condition
lRow = .Range("J" & .Rows.Count).End(xlUp).Row
'~~> Loop through the rows
For i = 2 To lRow
If .Range("J" & i).Value = "Send Reminder" Then
'~~> Create new email
Set emailRange = Worksheets("Sheet11").Range("K2:K59")
For Each cl In emailRange
sTo = sTo & ";" & cl.Value
Next
sTo = Mid(sTo, 2)
Set subjectRange = Worksheets("Sheet11").Range("L2:L59")
For Each c2 In subjectRange
sSubject = sSubject & ";" & c2.Value
Next
sSubject = Mid(sSubject, 2)
Set bodyRange = Worksheets("Sheet11").Range("M2:M59")
For Each c3 In bodyRange
sBody = sBody & ":" & c3.Value
Next
sBody = Mid(sBody, 2)
Set OutMail = OutApp.CreateItem(0)
'On Error Resume Next
With OutMail
'~~> Customize your email
.To = ""
.CC = sTo
.BCC = ""
.Subject = "typed subject1" & sSubject
.Body = ""
.Display '<~~ Change to .Send to actually send it
End With
End If
Next i
End With
End Sub
This code opens up multiple windows in outlook with all the emails listed in K2:K59. For example, if three cells in J2:J59 have send reminder, i open 3 email windows with all the emails listed in the cc box, instead of either multiple windows with individual emails or one window with all the emails. I think I have to close the loop somehow but am not certain how! Thanks for your help.
Mail_Object.CreateItem(o)
Shouldn't that be
Mail_Object.CreateItem(0)
0 and not o
In the below code, you are not required to set a reference to MS Outlook Object Library. I am using Late Binding with MS Outlook.
Try this (Untested)
I have commented the code so you shall not have a problem understanding the code but if you do then simply post back :)
Option Explicit
Sub Sample()
'~~> Excel Objects/Variables
Dim ws As Worksheet
Dim lRow As Long, i As Long
'~~> Outlook Objects/Variables
Dim OutApp As Object
Dim OutMail As Object
'~~> Set your worksheet here
Set ws = ThisWorkbook.Sheets("Sheet1")
'~~> Open Outlook
Set OutApp = CreateObject("Outlook.Application")
With ws
'~~> Get last row from Col J as that is what we
'~~> are going to check for the condition
lRow = .Range("J" & .Rows.Count).End(xlUp).Row
'~~> Loop through the rows
For i = 2 To lRow
If .Range("J" & i).Value = "Send Reminder" Then
'~~> Create new email
Set OutMail = OutApp.CreateItem(0)
With OutMail
'~~> Customize your email
.To = ws.Range("K" & i).Value
.Subject = ws.Range("L" & i).Value
.Body = ws.Range("M" & i).Value
.Display '<~~ Change to .Send to actually send it
End With
End If
Next i
End With
End Sub
Since you have Outlook open you do not have to do anything complicated.
Set Mail_Object = GetObject(, "Outlook.Application")
I did something similar yesterday, here is the code I used, hope it helps you out.
Sub EmailCopy()
Dim oApp, oMail As Object, X As Long, MyBody As String
Application.ScreenUpdating = False
On Error Resume Next
Set oApp = CreateObject("Outlook.Application")
For X = 2 To Range("A" & Rows.Count).End(xlUp).Row
MyBody = Replace(Join(Application.Transpose(Range("E5:E" & Range("D" & Rows.Count).End(xlUp).Row - 1).Value), vbLf & vbLf), "<FirstName>", Range("B" & X).Text)
MyBody = MyBody & vbLf & vbLf & Join(Application.Transpose(Range("E" & Range("D" & Rows.Count).End(xlUp).Row & ":E" & Range("E" & Rows.Count).End(xlUp).Row)), vbLf)
Set oMail = oApp.CreateItem(0)
With oMail
.To = Range("A" & X).Text
.cc = Range("E1").Text
.Subject = Range("E2").Text
.Body = MyBody
.Attachments.Add Range("E3").Text
.Display
If UCase(Range("E4").Text) = "SEND" Then
.Send
ElseIf UCase(Range("E4").Text) = "DRAFT" Then
.Save
.Close False
Else
MsgBox "You need to choose Draft or Send in cell E4"
End
End If
End With
Application.ScreenUpdating = True
Set oMail = Nothing
Next
Set oApp = Nothing
End Sub
Recipients go in Column A and First Name goes in column B, Any CC's go in E1, Subject goes in E2, Any attachment links go in E3, E4 is either Draft or Send to create a draft or do a send.
Then the message body goes in E5 down as far as you want, each line will be separated by a double return. Anywhere you use FirstName wrapped in greater than and less than signs the code will replace it with the person's First Name from column B.
Straight after that put the signature you want and put "Signature" in column D next to the start of it, this will be separated by single returns.

Resources