Custom Subject line for Outlook - vba

I'm working on automating an Excel database so by clicking a button, the macro will automatically send an email using the email, subject, and body for that specific row entry.
For example, I want to press the button and the macro automatically sends out emails to the cells filled in Red to their respective emails with the customized subject.
I found some code online that, once pressed, will send out an automatic email. However, the subject line isn't customized.
Here's the code I'm working on right now:
Sub SendReminderMail()
Dim OutLookApp As Object
Dim OutLookMailItem As Object
Dim iCounter As Integer
Dim MailDest As String
Dim SUBJECT As String
Set OutLookApp = CreateObject("Outlook.application")
Set OutLookMailItem = OutLookApp.CreateItem(0)
With OutLookMailItem
.SUBJECT = ""
For iCounter = 1 To WorksheetFunction.CountA(Columns(4))
If SUBJECT = "" And Cells(iCounter, 4).Offset(0, -1) = "Send Reminder" Then
.SUBJECT = Cells(iCounter, 6).Value
ElseIf SUBJECT <> "" And Cells(iCounter, 4).Offset(0, -1) = "Send Reminder" Then
SUBJECT = SUBJECT & ";" & Cells(iCounter, 6).Value
End If
Next iCounter
MailDest = ""
For iCounter = 1 To WorksheetFunction.CountA(Columns(4))
If MailDest = "" And Cells(iCounter, 4).Offset(0, -1) = "Send Reminder" Then
MailDest = Cells(iCounter, 4).Value
ElseIf MailDest <> "" And Cells(iCounter, 4).Offset(0, -1) = "Send Reminder" Then
MailDest = MailDest & ";" & Cells(iCounter, 4).Value
End If
Next iCounter
.BCC = MailDest
.Body = "Reminder: Time to contact this firm"
.Send
End With
Set OutLookMailItem = Nothing
Set OutLookApp = Nothing
End Sub
Current problems I'm facing:
The email sends out to the correct email addresses but the subject is ALWAYS the subject in the 6th row - "Reminder to email Andrew". It doesn't change for other contacts. I need the subject to change for every email to each different contact.
I noticed if I have different contact names but they're listed under the same e-mail address, then the macro will only e-mail to the first entry with the same e-mail, but not the second one.
Any help is appreciated. Thanks

So tempted to leave out the comment but there's still a possibility you will miss it XD
Sub SendReminderMail()
Dim OutLookApp As Object
Dim OutLookMailItem As Object
Dim lastRow As Long
Dim iCounter As Long
Dim MailDest As String
Dim subj As String
lastRow = ThisWorkbook.WorkSheets("Sheet6").Cells(Rows.Count, "D").End(xlUp).Row 'change worksheet
For iCounter = 2 To lastRow
Set OutLookApp = CreateObject("Outlook.application")
Set OutLookMailItem = OutLookApp.CreateItem(0)
With OutLookMailItem
subj = ""
MailDest = ""
If Cells(iCounter, 3) = "Send Reminder" Then
subj = Cells(iCounter, 6).Value
MailDest = Cells(iCounter, 4).Value
.BCC = MailDest
.SUBJECT = subj
.Body = "Reminder: Time to contact this firm"
.Send
End If
End With
Next iCounter
End Sub

I believe you're looking for everything in between this line:
With OutLookMailItem
and this line
End With
to run ONCE for each row in your spreadsheet, am I right? For each individual row, if you have the "Send Reminder" text in Cells(iCounter,4), you want an email to be sent out to that person.
If that's the case - that's never going to happen because you iterate through this code from 1 all the way to 6
For iCounter = 1 To WorksheetFunction.CountA(Columns(4))
If SUBJECT = "" And Cells(iCounter, 4).Offset(0, -1) = "Send Reminder" Then
.SUBJECT = Cells(iCounter, 6).Value
ElseIf SUBJECT <> "" And Cells(iCounter, 4).Offset(0, -1) = "Send Reminder" Then
SUBJECT = SUBJECT & ";" & Cells(iCounter, 6).Value
End If
prior to ever executing this code:
.Send
This is why your .SUBJECT is always "Reminder to email Andrew." It was "Reminder to email Ner," but that was overwritten by "Reminder to email Roo", which was overwritten by "Reminder to email Andrew."
I copied your code into VBA and ran it for myself and I'm not sure how it's working for you because I can't get it to run.
If my suggestion above is not what you're looking to do, then your problem is probably lying in the fact that you are not using a . before SUBJECT in your ElseIf statement:
For iCounter = 1 To WorksheetFunction.CountA(Columns(4))
If SUBJECT = "" And Cells(iCounter, 4).Offset(0, -1) = "Send Reminder" Then
.SUBJECT = Cells(iCounter, 6).Value
ElseIf SUBJECT <> "" And Cells(iCounter, 4).Offset(0, -1) = "Send Reminder" Then
SUBJECT = SUBJECT & ";" & Cells(iCounter, 6).Value
'^ period here
End If
Next iCounter
If you're actually looking to build out the .SUBJECT property of OutlookMailItem, you need to put a period before it, as you want to reference .SUBJECT - the property of OutlookMailItem, not the variable SUBJECT (which is very confusing btw).
Some other notes:
your For loop doesn't need to start at 1, because that's your header row. It might be easier in the future to do something like this:
lastRow = Range("D" & Rows.Count).End(xlUp).Row
For iCounter = 2 to lastRow step 1
'/ VBA will iterate through each row until it hits the end
'/ Assuming column D has data in it to your actual "last row"
Next iCounter

Use F8 to step through your code. Turn on your locals window and watch what is happening with your variables at each step. Hover your cursor over the yellow line or any previous line to see what the variables / functions are at that point or when previous lines finished executing.
Pay particular attention to your subject variable.
This bit is also probably not doing what you want it to be doing:
For iCounter = 1 To WorksheetFunction.CountA(Columns(4))
If I put that into a spreadsheet with a bunch of values in columns A through J and run this:
Sub testing()
Dim X
X = WorksheetFunction.CountA(Columns(4))
End Sub
X is 0
I suspect you want the last row with a value in column D.
Sub lastrow()
Dim X
X = Worksheets("Sheet1").Cells(Rows.Count, "D").End(xlUp).Row
End Sub
That gives me 19. The last cell in column D with a value. You don't want to count or counta because it won't count blanks. So you might have 20 rows of data but if only 5 have a value in column D, your loop will go through 5 times, since you based it off the cells with values, not the rows.
Mainly, if you are going to be doing these regularly, play with F8 and your View/Locals window while doing this and you will start to see where things go wrong.
Then make a very simple sub like that one to play with the individual piece of code until you understand it, before you try to modify a larger complex chunk. That will help you learn to fish.
I don't have outlook so I can't test the rest of your code, but that block will give your trouble even if everything else is perfect.

Related

How to attach a variable number of attachments?

The below code is an auto email sender. It works only if there are attachments.
Some emails have 1 or more attachments. If the path is blank it will not work.
Each email has a variable number of attachments and some paths is empty. Is there any way to make the pdfadd1 to pdfadd5 ignore adding attachment if the path is empty?
Sub send_mass_email_display_only()
Dim i As Integer
Dim name As String, email As String, body As String, subject As String, copy As String, pdfadd1 As String, pdfadd2 As String, pdfadd3 As String, pdfadd4 As String, pdfadd5 As String
Dim OutApp As Object
Dim OutMail As Object
body = ActiveSheet.TextBoxes("TextBox 1").Text
For i = 2 To 3
'Specific rows
name = Split(Cells(i, 1).Value, " ")(0)
'name = Cells(i, 1).Value
email = Cells(i, 2).Value
subject = Cells(i, 3).Value
copy = Cells(i, 4).Value
pdfadd1 = Cells(i, 5).Value
pdfadd2 = Cells(i, 6).Value
'pdfadd3 = Cells(i, 7).Value
'pdfadd4 = Cells(i, 8).Value
'pdfadd5 = Cells(i, 9).Value
body = Replace(body, "C1", name)
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.SentOnBehalfOfName = Cells(7, 17).Value
.To = email
.cc = copy
.subject = subject
.body = body
.Attachments.Add (pdfadd1)
.Attachments.Add (pdfadd2)
'.Attachments.Add (pdfadd3)
'.Attachments.Add (pdfadd4)
'.Attachments.Add (pdfadd5)
.display
'.Send
End With
body = ActiveSheet.TextBoxes("TextBox 1").Text 'reset body text
Next i
Set OutMail = Nothing
Set OutApp = Nothing
'MsgBox "Email(s) Sent!"
End Sub
Here is the relevant part. I just added an If statement to each one to make sure the length is greater than zero.
.body = body
If Len(pdfadd1) > 0 Then .Attachments.Add pdfadd1
If Len(pdfadd2) > 0 Then .Attachments.Add pdfadd2
If Len(pdfadd3) > 0 Then .Attachments.Add pdfadd3
If Len(pdfadd4) > 0 Then .Attachments.Add pdfadd4
If Len(pdfadd5) > 0 Then .Attachments.Add pdfadd5
.display
Also, you do not need the parenthesis around the argument for .Add in this case as it's not returning anything.

Mails with attachments through vba macro

I am running a macro for sending mails to multiple recipients via Outlook with one or more attachments through vba excel. I am not well versed in macros and hence took some inputs from various sources and came upon the below final code.
However I have mentioned max. limit of 3 file attachments which is constant for all recipients but have to disable by commenting whenever I have to attach only 1 or 2 files accordingly like e.g in the below code I have disabled the 2nd and 3rd attachment columns for attaching 1 file across.
Is there any way where the macro would automatically take the inputs according to the values entered and left blank e.g If one recipient has 1 attachment and the next recipient has 2 or 3 attachments
Sub SendMail()
Dim objOutlook As Object
Dim objMail As Object
Dim ws As Worksheet
Set objOutlook = CreateObject("Outlook.Application")
Set ws = ActiveSheet
For Each cell In ws.Range("A2:A1000")
Set objMail = objOutlook.CreateItem(0)
With objMail
.To = cell.Value
.Cc = cell.Offset(0, 1).Value
.Bcc = cell.Offset(0, 2).Value
.Subject = cell.Offset(0, 3).Value
.Body = cell.Offset(0, 4).Value
.Attachments.Add cell.Offset(0, 5).Value
'.Attachments.Add cell.Offset(0, 6).Value
'.Attachments.Add cell.Offset(0, 7).Value
.Send
End With
Set objMail = Nothing
Next cell
Set ws = Nothing
Set objOutlook = Nothing
End Sub
Dim i As Long, c As Range
'....
With objMail
.To = cell.Value
.Cc = cell.Offset(0, 1).Value
.Bcc = cell.Offset(0, 2).Value
.Subject = cell.Offset(0, 3).Value
.Body = cell.Offset(0, 4).Value
For i = 5 to 6
Set c = cell.Offset(0, i)
If c.Value <> "" Then .Attachments.Add c.Value
Next i
.Send
End With
'....

Send Automatic Email for loop

I have a code:
Sub sendEmail()
Dim OutlookApp As Object
Dim OutlookItem As Object
Dim i As Integer
Dim Address As String
Set OutlookApp = CreateObject("Outlook.Application")
Set OutlookItem = OutlookApp.CreateItem(0)
With OutlookItem
For i = 4 To 15
If Cells(i, 18) <= Cells(i, 6) Then
Address = Cells(i, 14).Value
Set OutlookApp = CreateObject("Outlook.application")
Set OutlookItem = OutlookApp.CreateItem(0)
.To = Address
.Subject = "Calibration Due Soon !!!"
.Body = "Reminder: Calibration of " & Cells(i, 4) & "is due on " & Cells(i, 9)
.Send
Set OutlookItem = Nothing
Set OutlookApp = Nothing
'Application.Wait (Now + #12:00:08 AM#)
ElseIf Cells(i, 18) > Cells(i, 15) Then
Exit Sub
ElseIf Cells(i, 18) = "" And Cells(i, 15) = "" Then
Exit Sub
End If
Next i
End With
End Sub
It does send only first email then it prompts me with run-time error saying :
The item has been moved or deleted
And debugger highlights the " .To = Address" line.
When I use .Display instead of send it works.
Any ideas?
Re-creating the Outlook application object in every loop iteration does not make sense. It's equivalent to killing Outlook and re-starting it for every mail you send. Let's not do that.
First, set a reference to the "Microsoft Outlook 15.0 Object Library" in your Excel VBA project (or whatever version you happen to have installed).
Now you can create an Outlook object directly with New and it also enables the autocompletion and all the Outlook-specific constants, like olMailItem.
Now your code can be condensed to something like this:
Sub sendEmail()
Dim OutlookApp As New Outlook.Application
Dim r As Range
For Each r In ActiveSheet.Range("4:15").Rows
If r.Cells(18) <= r.Cells(6) And r.Cells(18) > "" And r.Cells(15) > "" Then
With OutlookApp.CreateItem(olMailItem)
.To = r.Cells(14)
.Subject = "Calibration Due Soon !!!"
.Body = "Reminder: Calibration of " & r.Cells(4) & " is due on " & r.Cells(9)
.Send
End With
End If
Next r
OutlookApp.Quit
Set OutlookApp = Nothing
End Sub

VBA send email from excel (depending on condition)

I am an absolute beginner and try to facilitate a few tasks in my colleagues daily work. I want to create a code that sends reminder mails with information from an excel file. The idea is that Excel should check every relevant row from row 12 on and check if there is an "x" written in a column that marks for which row I would like to send a reminder.
With the code below I can generate all the emails, but I have difficulties where and how to include the check for if ('If Cells(s, 6).Value = "x" Then') so that that Excel continues through all the rows that are filled out.
Many thanks for your help!
Sub SendReminderMail()
Dim s As Long
Dim OutLookApp As Object
Dim OutLookMailItem As Object
Dim iCounter As Integer
Dim MailDest As String
If MsgBox("Are you sure?", vbYesNo) = vbNo Then Exit Sub
Set OutLookApp = CreateObject("Outlook.application")
Set OutLookMailItem = OutLookApp.CreateItem(0)
s = 12
Do Until Trim$(Cells(s, 1).Value) = ""
Set OutLookMailItem = OutLookApp.CreateItem(0)
With OutLookMailItem
.To = Cells(s, 5).Value
.Subject = "Reminder: " & Cells(1, 7).Value
.Body = "Text, " & vbCrLf & vbCrLf & "Text'" & Cells(s, 2).Value
s = s + 1
.Display
End With
Loop
End Sub
Since you are checking every row with the Do...Loop then you need to check the if inside that loop. I've moved the increment to s outside the If so that it happens whether or not you create a mail item. Otherwise you'd only change rows if there was a mail item to send, and that means you'd be stuck looping on a row where there was no "x".
Sub SendReminderMail()
Dim s As Long
Dim OutLookApp As Object
Dim OutLookMailItem As Object
Dim iCounter As Integer
Dim MailDest As String
If MsgBox("Are you sure?", vbYesNo) = vbNo Then Exit Sub
Set OutLookApp = CreateObject("Outlook.application")
Set OutLookMailItem = OutLookApp.CreateItem(0)
s = 12
Do Until Trim$(Cells(s, 1).Value) = ""
If Cells(s,6).Value = "x" Then
Set OutLookMailItem = OutLookApp.CreateItem(0)
With OutLookMailItem
.To = Cells(s, 5).Value
.Subject = "Reminder: " & Cells(1, 7).Value
.Body = "Text, " & vbCrLf & vbCrLf & "Text'" & Cells(s, 2).Value
.Display
End With
End If
s = s + 1
Loop
End Sub

How to send email reminder from code

Sub SendReminderMail()
Dim OutlookApp As Object
Dim OutLookMailItem As Object
Dim iCounter As Integer
Dim MailDest As String
Set OutlookApp = CreateObject("Outlook.application")
Set OutLookMailItem = OutlookApp.CreateItem(0)
With OutLookMailItem
MailDest = ""
For iCounter = 1 To WorksheetFunction.CountA(Columns(34))
If MailDest = "" And Cells(iCounter, 34).Offset(0, -1) = "Send Reminder" Then
MailDest = Cells(iCounter, 34).Value
ElseIf MailDest <> "" And Cells(iCounter, 34).Offset(0, -1) = "Send Reminder" Then
MailDest = MailDest & ";" & Cells(iCounter, 34).Value
End If
Next iCounter
.BCC = MailDest
.Subject = "ECR Notification"
.HTMLBody = "Reminder: This is a test for an automatic ECR email notification. Please complete your tasks for ECR#"
.Send
End With
Set OutLookMailItem = Nothing
Set OutlookApp = Nothing
End Sub
Need code to email the values in columns AE with the "set reminder" text
GD mjac,
You are still shy with your information...?
Your presented code collects all addresses and subsequently sends a single message ? I would expect, based on your example sheet/data that you would want to send an email to each recipient for each ECR code that is 'open' ?
So assuming the following:
You want to send an email for every line where the "Send reminder" is
true
The email addresses in columns "AH" will differ for every line ?
In your code you use the Outlook.Application objects Set OutlookApp = CreateObject("Outlook.application"), be careful with opening application type objects and be sure to ensure they will be closed in the event the code finishes or when an error is triggered, otherwise you could potentially end up with a number of Outlook instances that are 'running' using valuable reqources. The below code has some basic error handling to ensure the OutlookApp object is closed if no longer required.
Setup your Workbook as follows:
In VB Editor under Tools|References find 'Microsoft Outlook xx.x Object Library', where xx.x represents the version of Outlook that you are working with. (see also: https://msdn.microsoft.com/en-us/library/office/ff865816.aspx) This will make for easier coding as you get intellisense suggestions for your objects.
Declare OutlookApp as public, above all other subs/functions etc. etc.
(i.e. at the top of your 'coding' window)
Public OutlookApp As Outlook.Application
your sendReminderMail() sub
Sub SendReminderMail()
Dim iCounter As Integer
Dim MailDest As String
Dim ecr As Long
On Error GoTo doOutlookErr:
Set OutlookApp = New Outlook.Application
For iCounter = 1 To WorksheetFunction.CountA(Columns(34))
MailDest = Cells(iCounter, 34).Value
ecr = Cells(iCounter, 34).Offset(0, -3).Value
If Not MailDest = vbNullString And Cells(iCounter, 34).Offset(0, -1) = "Send Reminder" Then
sendMail MailDest, ecr
MailDest = vbNullString
End If
Next iCounter
'basic errorhandling to prevent Outlook instances to remain open in case of an error.
doOutlookErrExit:
If Not OutlookApp Is Nothing Then
OutlookApp.Quit
End If
Exit Sub
doOutlookErr:
MsgBox Err.Description, vbOKOnly, Err.Source & ":" & Err.Number
Resume doOutlookErrExit
End Sub
added sendMail Function:
Function sendMail(sendAddress As String, ecr As Long) As Boolean
'Initiate function return value
sendMail = False
On Error GoTo doEmailErr:
'Initiate variables
Dim OutLookMailItem As Outlook.MailItem
Dim htmlBody As String
'Create the mail item
Set OutLookMailItem = OutlookApp.CreateItem(olMailItem)
'Create the concatenated body of the mail
htmlBody = "<html><body>Reminder: This is a test for an automatic ECR email notification.<br>" & _
"Please complete your tasks for ECR#" & CStr(ecr) & "</body></html>"
'Chuck 'm together and send
With OutLookMailItem
.BCC = sendAddress
.Subject = "ECR Notification"
.HTMLBody = htmlBody
.Send
End With
sendMail = True
doEmailErrExit:
Exit Function
doEmailErr:
MsgBox Err.Description, vbOKOnly, Err.Source & ":" & Err.Number
Resume doEmailErrExit
End Function