Excel VBA to include body in the forwarded outlook email - excel

I am trying to forward emails based on the subject provided in the A column by looping. Its working perfectly, but I would also like to include the content in the C column to each of the corresponding mail.
Also delete the from and to details from the initial mail.
Request template:
The body content should also use the column value as mentioned below.
Can some one help me remove and include this details in the below..
Option Explicit
Public Sub Example()
Dim olApp As Outlook.Application
Dim olNs As Outlook.Namespace
Dim Inbox As Outlook.MAPIFolder
Dim Item As Variant
Dim MsgFwd As MailItem
Dim Items As Outlook.Items
Dim Email As String
Dim Email1 As String
Dim ItemSubject As String
Dim lngCount As Long
Dim i As Long
Dim RecipTo As Recipient
Dim RecipCC As Recipient
Dim RecipBCC As Recipient
Dim onbehalf As Variant
Set olApp = CreateObject("Outlook.Application")
Set olNs = olApp.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Set Items = Inbox.Items
i = 2 ' i = Row 2
With Worksheets("Sheet1") ' Sheet Name
Do Until IsEmpty(.Cells(i, 1))
ItemSubject = .Cells(i, 1).Value '(i, 1) = (Row 2,Column 1)
Email = .Cells(i, 16).Value '(i, 2) = (Row 2,Column 2)
Email1 = .Cells(i, 2).Value
'// Loop through Inbox Items backwards
For lngCount = Items.Count To 1 Step -1
Set Item = Items.Item(lngCount)
If Item.Subject = ItemSubject Then ' if Subject found then
Set MsgFwd = Item.Forward
Set RecipTo = MsgFwd.Recipients.Add(Email1) ' add To Recipient
Set RecipTo = MsgFwd.Recipients.Add("sen#aa.com")
Set RecipBCC = MsgFwd.Recipients.Add(Email) ' add BCC Recipient
MsgFwd.SentOnBehalfOfName = "doc#aa.com"
RecipTo.Type = olTo
RecipBCC.Type = olBCC
MsgFwd.Display
End If
Next ' exit loop
i = i + 1 ' = Row 2 + 1 = Row 3
Loop
End With
Set olApp = Nothing
Set olNs = Nothing
Set Inbox = Nothing
Set Item = Nothing
Set MsgFwd = Nothing
Set Items = Nothing
MsgBox "Mail sent"
End Sub

Add new variable as string Dim EmailBody As String then assign to column C EmailBody = .Cells(i, 3).Value with in your Do Loop
To remove the following from the Item.Forward body, simply add your Item.Body to your MsgFwd.Body - it should replace the whole forward Email body with Item.Body only
MsgFwd.HTMLBody = EmailBody & "<BR>" & "<BR>" & Item.HTMLBody
Example
Dim EmailBody As String
With Worksheets("Sheet1") ' Sheet Name
Do Until IsEmpty(.Cells(i, 1))
ItemSubject = .Cells(i, 1).Value '(i, 1) = (Row 2,Column 1)
Email = .Cells(i, 16).Value '(i, 2) = (Row 2,Column 2)
Email1 = .Cells(i, 2).Value
EmailBody = .Cells(i, 3).Value
'// Loop through Inbox Items backwards
For lngCount = Items.Count To 1 Step -1
Set Item = Items.Item(lngCount)
If Item.Subject = ItemSubject Then ' if Subject found then
Set MsgFwd = Item.Forward
Set RecipTo = MsgFwd.Recipients.Add(Email1) ' add To Recipient
Set RecipTo = MsgFwd.Recipients.Add("sen#aa.com")
Set RecipBCC = MsgFwd.Recipients.Add(Email) ' add BCC Recipient
MsgFwd.SentOnBehalfOfName = "doc#aa.com"
RecipTo.Type = olTo
RecipBCC.Type = olBCC
Debug.Print Item.Body ' Immediate Window
MsgFwd.HTMLBody = EmailBody & "<BR>" & "<BR>" & Item.HTMLBody
MsgFwd.Display
End If
Next ' exit loop

Related

Is there a function to skip outfiltered rows when running a code in VBA?

I have developed a template with which I can automate invoice reminders by outputting emails. Currently the code loops through the list outputting an email for each individual row (= each individual invoice). I would like to update the code so that it skips outfiltered rows in the excel doc to make it more efficient and easier in use.
My code is as follows:
Sub Send_email_fromtemplate()
Dim edress As String
Dim cc1, cc2, cc3 As String
Dim group As String
Dim number As String
Dim outlookapp As Object
Dim outlookmailitem As Object
Dim r As Long
Dim olInsp As Object
Dim wdDoc As Object
Dim oRng As Object
r = 3
Do While Sheet1.Cells(r, 1) <> ""
Set outlookapp = CreateObject("Outlook.Application")
'call your template
Set outlookmailitem = outlookapp.CreateItemFromTemplate([location])
outlookmailitem.Display
edress = Sheet1.Cells(r, 7)
cc1 = Sheet1.Cells(r, 8)
cc2 = Sheet1.Cells(r, 9)
cc3 = Sheet1.Cells(r, 10)
group = Sheet1.Cells(r, 4)
number = Sheet1.Cells(r, 3).Value
With outlookmailitem
.To = edress
.cc = cc1 & ";" & cc2 & ";" & cc3
.bcc = ""
.Subject = "First invoice reminder " & group
Set olInsp = .GetInspector
Set wdDoc = olInsp.WordEditor
Set oRng = wdDoc.Range
With oRng.Find
Do While .Execute(FindText:="{{Number}}")
oRng.Text = number
Exit Do
Loop
End With
Set xInspect = outlookmailitem.GetInspector
.Display
'.send
End With
'clear your email address
edress = ""
r = r + 1
Loop
'clear your fields
Set outlookapp = Nothing
Set outlookmailitem = Nothing
Set wdDoc = Nothing
Set oRng = Nothing
End Sub
I've tried to solve the problem using a Range function and ActiveSheet.UsedRange.SpecialCells(xlCellTypeVisible), but as the template will be filled with a variable number of rows (each time I send reminders) this is causing troubles, as the range differs each time.
Please, try the next updated code. As I said in my above comment, it is able to calculate the last row on A:A, then create a range from visible rows of the filtered area and iterate between its rows:
Sub Send_email_fromtemplate()
Dim edress As String, cc1 As String, cc2 As String, cc3 As String
Dim group As String, number As String
Dim outlookapp As Object, outlookmailitem As Object, olInsp As Object
Dim wdDoc As Object, xInspect As Object
Dim oRng As Object, lastR As Long, rngVis As Range, r As Range
lastR = Sheet1.Range("A" & Sheet1.rows.count).End(xlUp).row 'last row on A:A
On Error Resume Next 'this is necessary in case of no any filtered visible cell
Set rngVis = Sheet1.Range("A3:J" & lastR).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rngVis Is Nothing Then MsgBox "No visible filtered values in the necessary range...": Exit Sub
Set outlookapp = CreateObject("Outlook.Application") 'you should set it only ones, outside of the iteration...
For Each r In rngVis.rows 'iterate between the discontinuous (visible cells) rows
'call your template
Set outlookmailitem = outlookapp.CreateItemFromTemplate([Location])
outlookmailitem.Display
edress = r.cells(1, 7) 'extract the necessary values from the iterated row
cc1 = r.cells(1, 8)
cc2 = r.cells(1, 9)
cc3 = r.cells(1, 10)
group = r.cells(1, 4)
number = r.cells(1, 3).Value
With outlookmailitem
.To = edress
.cc = cc1 & ";" & cc2 & ";" & cc3
.BCC = ""
.Subject = "First invoice reminder " & group
Set olInsp = .GetInspector
Set wdDoc = olInsp.WordEditor
Set oRng = wdDoc.Range
With oRng.Find
Do While .Execute(FindText:="{{Number}}")
oRng.Text = number
Exit Do
Loop
End With
Set xInspect = outlookmailitem.GetInspector
.Display
'.send
End With
'clear your email address
edress = ""
Next r
'clear memory of used objects:
Set outlookapp = Nothing: Set outlookmailitem = Nothing
Set wdDoc = Nothing: Set oRng = Nothing
End Sub
Please, send some feedback after testing it.

Email Body Loop Values

I am attempting to loop through a column (n=96) in my worksheet, when it comes across a value <10 I would like the macro to open outlook and email offset values (four columns across) from the values it found.
I've generated a working example though it seems to be limited to only one example I've tested. I think I am approaching it from the wrong angle.
Sub SendReminderMail()
Dim p 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)
p = 2
Do Until Trim$(Cells(p, 1).Value) = ""
If Cells(p, 1).Value <= 10 Then
Set OutLookMailItem = OutLookApp.CreateItem(0)
With OutLookMailItem
.To = "Emailaddress etc"
.Subject = "Reminder: " & Cells(1, 7).Value
.Body = Cells(p, 1).Offset(0, 4).Value
.Display
End With
End If
p = p + 1
Loop
End Sub
How do I set it up to loop through all the <10 values and tell it to paste the offset values into the body of the email?
I think that you need to split this into two blocks of code.
First block would iterate through rows, check criteria and, if needed, call the second one, so the mail sending Sub, passing by necessary parameters.
Someting similar to the below code:
Sub SendReminderMail(ByVal MailSubject As String, mailBody As String)
Dim p 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)
Set OutLookMailItem = OutLookApp.CreateItem(0)
With OutLookMailItem
.To = "Emailaddress etc"
.Subject = MailSubject
.Body = mailBody
.Display
End With
End Sub
Sub IterateThroughRows()
Dim p As Integer
Dim Sht As Worksheet
Dim MailSubject As String
Dim mailBody As String
Set Sht = ThisWorkbook.Sheets("SheetName")
p = 2
Do Until Sht.Cells(p, 1).Value = ""
If Cells(p, 1).Value <= 10 Then
mailBody = mailBody + " | " + Sht.Cells(p, 1).Offset(0, 4).Value
End If
p = p + 1
Loop
Call SendReminderMail(MailSubject, mailBody)
MailSubject = "Reminder: " & Sht.Cells(1, 7).Value
End Sub

Create non-duplicate appointments from list in worksheet

I am trying to make appointments from a given date.
To avoid duplicates I tried coloring the cells but that does not seem viable.
Now I am trying to check if an appointment with the same "subject" as the cell exists and if so go to the next line.
I get the error
Object required
Private Sub Workbook_Open()
Set myOutlook = CreateObject("Outlook.Application")
r = 2
Do Until Trim(Cells(r, 8).Value) = ""
If Cells(r, 9).Value = myapt.Subject = Cells(r, 9).Value Then
r = r + 1
Else
Set myapt = myOutlook.createitem(1)
myapt.Subject = Cells(r, 9).Value
myapt.Start = Cells(r, 8).Value
myapt.AllDayEvent = True
myapt.BusyStatus = 5
myapt.ReminderSet = True
'myapt.Body = ""
myapt.Save
Cells(r, 8).Interior.ColorIndex = 4
r = r + 1
End If
Loop
End Sub
To check if an item exists you need to filter the existing items:
Option Explicit
Public Sub CreateItemsIfNotExist()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1") 'define your sheet!
Dim olApp As Object 'create outlook application
Set olApp = CreateObject("Outlook.Application")
Dim olNS As Object 'get namespace
Set olNS = olApp.GetNamespace("MAPI")
'define constants if using late binding
Const olFolderCalendar As Long = 9
Const olAppointmentItem As Long = 1
Dim olRecItems As Object 'get all appointments
Set olRecItems = olNS.GetDefaultFolder(olFolderCalendar)
Dim strFilter As String 'filter for appointments
Dim olFilterRecItems As Object 'filtered appointments
Dim iRow As Long
iRow = 2
Do Until Trim$(ws.Cells(iRow, 8).Value) = vbNullString
'filter appointments for subject
strFilter = "[Subject] = '" & Trim$(ws.Cells(iRow, 9).Value) & "'"
Set olFilterRecItems = olRecItems.Items.Restrict(strFilter)
If olFilterRecItems.Count = 0 Then 'if subject does not exist
With olApp.CreateItem(olAppointmentItem)
.Subject = ws.Cells(iRow, 9).Value
.Start = ws.Cells(iRow, 8).Value
.AllDayEvent = True
.BusyStatus = 5
.ReminderSet = True
.Save
End With
ws.Cells(iRow, 8).Interior.ColorIndex = 4
End If
iRow = iRow + 1
Loop
End Sub
Note that maybe you want to quit outlook in the end olApp.Quit.

Get email subject based on dates

I have a macro that will get all emails that contains "HAPPY", "NEUTRAL" and "SAD" in the subject and copy it to a new sheet of the workbook. I want to add functionality to only display mood based on the date defined by a user.
Also, code below read emails in the inbox. I need it to read all the folders in my mailbox (e.g. Outbox and subfolders).
Sub GetMood()
Dim outlookApp
Dim olNs As Outlook.Namespace
Dim Fldr As Outlook.MAPIFolder
Dim olMail As Variant
Dim myTasks
Dim sir() As String
Dim ws As Worksheet
Dim iRow As Variant
Dim d As Date
x = 2
d = ThisWorkbook.Sheets("Main").Cells(11, 7).Value
Set outlookApp = CreateObject("Outlook.Application")
Set olNs = outlookApp.GetNamespace("MAPI")
Set Fldr = olNs.GetDefaultFolder(olFolderInbox)
Set myTasks = Fldr.Items
For Each olMail In myTasks
If (InStr(1, olMail.Subject, "HAPPY") > 0) Then
ThisWorkbook.Sheets("Report").Cells(1, 1) = "Sender"
ThisWorkbook.Sheets("Report").Cells(1, 2) = "Mood"
ThisWorkbook.Sheets("Report").Cells(1, 3) = "Date"
ThisWorkbook.Sheets("Report").Cells(x, 1) = olMail.SenderName
ThisWorkbook.Sheets("Report").Cells(x, 2) = olMail.Subject
ThisWorkbook.Sheets("Report").Cells(x, 3) = olMail.ReceivedTime
x = x + 1
ElseIf (InStr(1, olMail.Subject, "NEUTRAL") > 0) Then
ThisWorkbook.Sheets("Report").Cells(x, 1) = olMail.SenderName
ThisWorkbook.Sheets("Report").Cells(x, 2) = olMail.Subject
ThisWorkbook.Sheets("Report").Cells(x, 3) = olMail.ReceivedTime
x = x + 1
ElseIf (InStr(1, olMail.Subject, "SAD") > 0) Then
ThisWorkbook.Sheets("Report").Cells(x, 1) = olMail.SenderName
ThisWorkbook.Sheets("Report").Cells(x, 2) = olMail.Subject
ThisWorkbook.Sheets("Report").Cells(x, 3) = olMail.ReceivedTime
x = x + 1
'MsgBox "Report Generated", vbOKOnly
'Else
'olMail.Display
Exit For
End If
Next
End Sub
Private Sub Workbook_Open()
Worksheets("StartSheet").Activate
End Sub
This will look into every folders in Outlook and gather the information in mInfo to create a list in sheet Report.
I've modified the structure so that it'll detect if Outlook is already open, add a column with the detected mood and improve performances! ;)
Sub GetMood()
Dim wS As Excel.Worksheet
Dim outlookApp As Outlook.Application
Dim olNs As Outlook.Namespace
Dim Fldr As Outlook.MAPIFolder
Dim olMail As Outlook.MailItem
'Dim sir() As String
'Dim iRow As Variant
'Dim d As Date
Dim RgPaste As Excel.Range
Dim mSubj As String
Dim mInfo() As Variant
Dim nbInfos As Integer
ReDim mInfo(1 To 1, 1 To 3)
nbInfos = UBound(mInfo, 2)
'd = ThisWorkbook.Sheets("Main").Cells(11, 7).Value
Set wS = ThisWorkbook.Sheets("Report")
With wS
.Cells(1, 1) = "Sender"
.Cells(1, 2) = "Mood"
.Cells(1, 3) = "Date"
Set RgPaste = .Cells(2, 1)
End With 'wS
Set outlookApp = GetObject(, "Outlook.Application")
If outlookApp Is Nothing Then Set outlookApp = CreateObject("Outlook.Application")
Set olNs = outlookApp.GetNamespace("MAPI")
For Each Fldr In olNs.Folders
For Each olMail In Fldr.Items
With olMail
mSubj = .Subject
mInfo(1, 1) = .SenderName
mInfo(1, 2) = mSubj
mInfo(1, 3) = .ReceivedTime
'.Display
End With 'olMail
With RgPaste
If (InStr(1, mSubj, "HAPPY") > 0) Then
.Resize(1, nbInfos).Value = mInfo
.Offset(0, nbInfos) = "HAPPY"
Set RgPaste = .Offset(1, 0)
ElseIf (InStr(1, mSubj, "NEUTRAL") > 0) Then
.Resize(1, nbInfos).Value = mInfo
.Offset(0, nbInfos) = "NEUTRAL"
Set RgPaste = .Offset(1, 0)
ElseIf (InStr(1, mSubj, "SAD") > 0) Then
.Resize(1, nbInfos).Value = mInfo
.Offset(0, nbInfos) = "SAD"
Set RgPaste = .Offset(1, 0)
End If
End With 'RgPaste
Next olMail
Next Fldr
'MsgBox "Report Generated", vbOKOnly
End Sub

bulk emails with excel

Trying to create a bulk email workbook out of Excel using VBA code which includes embedded images. I'm unable to apply a "For i" to the code and can't figure out how to email from an entire list with a ListObject table. For the script below, the Sheet referenced is "Message Generator." I'm trying to send an individual email to everyone in the list until the value in the row in Column B = 0. However, the Integer I set for the loop seems to return the value 0, as though there are no values in the rows and columns at all.
Anyone know how I can send create a workbook to send bulk emails? See below for the script. Thank you!
Dim MainWB As Workbook
Dim olApp As Outlook.Application
Dim olemail As Outlook.MailItem
Dim fso As Scripting.FileSystemObject
Dim ts As Scripting.TextStream
Dim SigPath As String, SigText As String
SigPath = Environ("AppData") & "\Microsoft\Signatures\New.htm"
Set fso = New Scripting.FileSystemObject
Set ts = fso.OpenTextFile(SigPath)
SigText = ts.ReadAll
ts.Close
Set fso = Nothing
Set MainWB = ActiveWorkbook
Dim Subject As String
Dim Body As String
Dim i As Integer
Dim l As Integer
l = NumberOfNonBlankRowsInColumn(2) - 2 'subtract 2 header rows
Set olApp = New Outlook.Application
For i = 0 To l
Set olemail = olApp.CreateItem(olMailItem)
Subject = MainWB.Sheets("Message Generator").Range("B3").Offset(i, 0).Value
Body = MainWB.Sheets("Message Generator").Range("AB3").Offset(i, 0).Value
With olemail
.BodyFormat = olFormatHTML
.To = "UTOAI#outlook.com"
.Subject = Subject
.Body = Body
.Attachments.Add "C:\Users\Jacka\Documents\Test\logo.jpg"
.HTMLBody = "<img src='cid:logo.jpg'" & "width='309.5' height='39.5'><br>" & _vbanewline & .HTMLBody & SigText
.Display
End With
Set olemail = Nothing
Next i
Set olApp = Nothing
End Sub
Function NumberOfNonBlankRowsInColumn(souceCol As Integer) As Integer
Dim NumberOfRowsInColumn As Integer, j As Integer
Dim CurrentRowValue As String
NumberOfRowsInColumn = Cells(Rows.Count, sourceCol).End(xlUp).row
For j = 1 To NumberOfRowsInColumn
CurrentRowValue = Cells(j, sourceCol).Value
If IsEmpty(CurrentRowValue) Or CurrentRowValue = "" Then
Exit For
End If
Next j
NumberOfNonBlankRowsInColumn = (j - 1)
End Function
Try not to make this mistake. The reason it worked sometimes and not others is because I had a value in the row above my column which later deleted. Therefore, I set NonBlankRowsInColumn to 2 (where the values begin) and done. See the edited function below.
Function NumberOfNonBlankRowsInColumn(souceCol As Integer) As Integer
Dim NumberOfRowsInColumn As Integer, j As Integer
Dim CurrentRowValue As String
NumberOfRowsInColumn = Cells(Rows.Count, sourceCol).End(xlUp).row
For j = 2 To NumberOfRowsInColumn
CurrentRowValue = Cells(j, sourceCol).Value
If IsEmpty(CurrentRowValue) Or CurrentRowValue = "" Then
Exit For
End If
Next j
NumberOfNonBlankRowsInColumn = (j - 1)
End Function

Resources