Reading cell value - excel

So I have created a macro which is sending an email via outlook. I am stuck at some point. I need to include new cell values and corresponding cell values on top of the column in the body of an email. So basically I need my macro to read those two values.
This is my module:
Sub SendEmail()
Dim olApp As Outlook.Application
Set olApp = CreateObject("Outlook.Application")
Dim olMail As Outlook.MailItem
Set olMail = olApp.CreateItem(olMailItem)
'So I want to send an email with cell new value and top of the column
'value corresponding to that cell
'Example: I wanna change cell C3 from A to X and I want to include
'that change in body of my email automatically
'So it reads "New cell value is X on 3-06"
olMail.To = "*****#*****.com"
olMail.Subject = "Look what has been changed"
olMail.Body = "Hi" & vbNewLine & vbNewLine & _
"New cell value is <Here is new cell value> on <Top of the column
of that cell value> " & vbNewLine & vbNewLine & _
"BR"
olMail.Send
End Sub
And this is my macro:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("A3:AP3")) Is Nothing Then SendEmail
End Sub
And this is a part of the worksheet I am working on:

One of the problems you are facing is that the Target can be multiple cells and one Worksheet_Change change can be over multiple cells at the same time, not just once cell. You could check if the Target is one cell size, and do nothing if more than 1, 1 size, but then you would loose that change or parts of it at least.
An approach would be to hold a history of the changes, and send that email even if the changes are over multiple cells at once.
With that in mind, you should create an additional sheet that holds last changes, say sheet History Sheet.
In the sheet your working on, place in the code part of the sheet:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("A3:AP3")) Is Nothing Then
checkHistory Target.Worksheet.Range("A3:AP3")
End If
End Sub
In any module in the same workbook, add this:
Sub checkHistory(rng As Range)
Dim wsHistory As Worksheet: Set wsHistory = ThisWorkbook.Sheets("History Sheet")
Dim arrData As Variant, arrHistory As Variant
Dim R As Long, C As Long
Dim bChanges As Boolean
arrData = rng.Offset(-2).Resize(3)
arrHistory = wsHistory.Range(rng.Offset(-2).Resize(3).Address)
Dim arrChanges() As String: ReDim arrChanges(LBound(arrData) To UBound(arrData), LBound(arrData, 2) To UBound(arrData, 2))
For C = LBound(arrData, 2) To UBound(arrData, 2)
If arrData(3, C) <> arrHistory(3, C) Then
arrChanges(3, C) = arrData(3, C)
If Not bChanges Then bChanges = True
End If
Next C
If bChanges Then
Dim strNewVal As String, strHeading As String
wsHistory.Range(rng.Offset(-2).Resize(3).Address) = arrData
For C = LBound(arrChanges, 2) To UBound(arrChanges, 2)
If arrChanges(3, C) <> "" Then
strNewVal = strNewVal & ", " & arrChanges(3, C) 'new values
strHeading = strHeading & ", " & arrData(1, C) 'heading
End If
Next C
strNewVal = Right(strNewVal, Len(strNewVal) - 2)
strHeading = Right(strHeading, Len(strHeading) - 2)
SendEmail strNewVal, strHeading
End If
End Sub
Sub SendEmail(strNewVal As String, strHeading As String)
Dim olApp As Outlook.Application
Set olApp = CreateObject("Outlook.Application")
Dim olMail As Outlook.MailItem
Set olMail = olApp.CreateItem(olMailItem)
olMail.To = "*****#*****.com"
olMail.Subject = "Look what has been changed"
olMail.Body = "Hi" & vbNewLine & vbNewLine & _
"New cell value is " & strNewVal & " on " & strHeading & vbNewLine & vbNewLine & _
"BR"
olMail.Send
End Sub
With the above, you would send that email regardless if the change is over 1 cell, or more, as comma separated values.

Some small changes:
Sub SendEmail(rng As Range)
Dim olApp As Outlook.Application, c As Range, bdy
If rng Is Nothing Then Exit Sub '<< nothing to report
Set olApp = CreateObject("Outlook.Application")
Dim olMail As Outlook.MailItem
Set olMail = olApp.CreateItem(olMailItem)
olMail.To = "*****#*****.com"
olMail.Subject = "Look what has been changed"
bdy = "Hi" & vbNewLine & vbNewLine
'check each changed cell
For Each c in rng.Cells
bdy = bdy & "New cell value is '" & c.Value & _
"' on " & c.EntireColumn.Cells(1).Value & _
vbNewLine & vbNewLine
Next c
olMail.Body = bdy & vbNewLine & vbNewLine & "BR"
olMail.Send
End Sub
Event handler:
Private Sub Worksheet_Change(ByVal Target As Range)
SendEmail Application.Intersect(Target, Me.Range("A3:AP3"))
End Sub

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 create emails from Excel table?

I have a table in Excel. It is built as follows:
|Information on food|
|date: April 28th, 2021|
|Person|Email|Apples|Bananas|Bread|
|------|-----|------|-------|-----|
|Person_A|person_A#mailme.com|3|8|9|
|Person_B|person_B#mailme.com|10|59|11|
|Person _C|person_C#maime.com|98|12|20|
There is also a date field in the table. For a test, this could be set to todays date.
Based on this information, I am looking for a VBA code which prepares an email to each of the listed persons and is telling them what they have eaten on the specific date.
I need to access several fields in the table, and at the same time loop through the email addresses. Then I would like VBA to open Outlook and prepare the emails. Ideally not send them so I can take a final look before I send the mails.
It would be fine to access certain cells specifically via ranges etc. I am using Excel/Outlook 2016.
How can this be achieved in VBA?
Assuming the data is a named table and title/date are above the corner of the table as shown in your example. Also all the rows of the table have valid data. The emails are prepared and shown but not sent (unless you change the code where shown).
Option Explicit
Sub EmailMenu()
Const TBL_NAME = "Table1"
Const CSS = "body{font:12px Verdana};h1{font:14px Verdana Bold};"
Dim emails As Object, k
Set emails = CreateObject("Scripting.Dictionary")
Dim ws As Worksheet, rng As Range
Dim sName As String, sAddress As String
Dim r As Long, c As Integer, s As String, msg As String
Dim sTitle As String, sDate As String
Set ws = ThisWorkbook.Sheets("Sheet1")
Set rng = ws.ListObjects(TBL_NAME).Range
sTitle = rng.Cells(-1, 1)
sDate = rng.Cells(0, 1)
' prepare emails
For r = 2 To rng.Rows.Count
sName = rng.Cells(r, 1)
sAddress = rng.Cells(r, 2)
If InStr(sAddress, "#") = 0 Then
MsgBox "Invalid Email: '" & sAddress & "'", vbCritical, "Error Row " & r
Exit Sub
End If
s = "<style>" & CSS & "</style><h1>" & sDate & "<br>" & sName & "</h1>"
s = s & "<table border=""1"" cellspacing=""0"" cellpadding=""5"">" & _
"<tr bgcolor=""#ddddff""><th>Item</th><th>Qu.</th></tr>"
For c = 3 To rng.Columns.Count
s = s & "<tr><td>" & rng.Cells(1, c) & _
"</td><td>" & rng.Cells(r, c) & _
"</td></tr>" & vbCrLf
Next
s = s & "</table>"
' add to dictonary
emails.Add sAddress, Array(sName, sDate, s)
Next
' confirm
msg = "Do you want to send " & emails.Count & " emails ?"
If MsgBox(msg, vbYesNo) = vbNo Then Exit Sub
' send emails
Dim oApp As Object, oMail As Object, ar
Set oApp = CreateObject("Outlook.Application")
For Each k In emails.keys
ar = emails(k)
Set oMail = oApp.CreateItem(0)
With oMail
.To = CStr(k)
'.CC = "email#test.com"
.Subject = sTitle
.HTMLBody = ar(2)
.display ' or .send
End With
Next
oApp.Quit
End Sub

How to keep the hyperlinks, in table column, clickable when sending to body of mail?

I am trying to send an email via Outlook using VBA.
I have a column filled with hyperlinks. When the email is constructed, the hyperlinks turns into plain text and are not clickable.
I reference the column using Cells(row_num,1) because all the hyperlinks are unique.
How to make them show up as hyperlinks?
Sub SendEmail()
Dim olook As Outlook.Application
Dim omailitem As Outlook.MailItem
Dim i As Byte, row_num As Byte
row_num = 2
Set olook = New Outlook.Application
For i = 1 To 15
Set omailitem = olook.CreateItem(0)
With omailitem
.To = Sheets(1).Cells(row_num, 2)
.Subject = "Tool Notification"
.Body = "Hello!" & vbNewLine & vbNewLine & _
"Below are the link(s) to the task(s) that you have due on: " & _
Cells(row_num, 4).Value & _
vbNewLine & vbNewLine & "Link: " & Cells(row_num, 1).Value & _
vbNewLine & vbNewLine & "Thank you," & _
vbNewLine & vbNewLine & "Tool"
.Display
End With
row_num = row_num + 1
Next
End Sub
Sample Data
https://i.stack.imgur.com/m9Stx.png
Check the code's comments and adjust it to fit your needs.
This should be pasted in a standard module.
EDIT: Adjusted to accumulate links by sender
Code:
Option Explicit
Sub SendEmail()
Dim olApp As Outlook.Application
Dim olMail As Outlook.MailItem
Dim targetSheet As Worksheet
Dim targetRange As Range
Dim cell As Range
Dim lastRow As Long
Dim recipientAddr As String
Dim bodyContent As String
Dim duedateFormat As String
Dim linkFormat As String
' Set reference to target Sheet (replace 1 with the sheet's name or codename)
Set targetSheet = ThisWorkbook.Worksheets(1)
' Find last cell in column b
lastRow = targetSheet.Cells(targetSheet.Rows.Count, 2).End(xlUp).Row
' Set target range
Set targetRange = targetSheet.Range("B2:B" & lastRow)
' Start new outlook instance
Set olApp = New Outlook.Application
' Loop through each cell in column B
For Each cell In targetRange.Cells
' If cell has data
If cell.Value <> vbNullString Then
' Check if is the same recipient as next
If cell.Value = cell.Offset(1, 0).Value Then
linkFormat = linkFormat & "" & cell.Offset(0, -1) & "<br>"
Else
linkFormat = linkFormat & "" & cell.Offset(0, -1) & ""
' Collect email data from cells
recipientAddr = cell.Value
duedateFormat = Format(cell.Offset(0, 2).Value, "mm-dd-yyyy")
' Build the link string
bodyContent = "Hello!<br><br>" & _
"Below are the link(s) to the task(s) that you have due on: " & duedateFormat & "<br><br>" & _
"Link(s): <br>" & _
linkFormat & "<br><br>" & _
"Thank you,<br><br>" & _
"Tool"
' Create the mail item and display it
Set olMail = olApp.CreateItem(olMailItem)
With olMail
.To = cell.Value
.Subject = "Tool Notification"
.HTMLBody = bodyContent
.Display
End With
' Reset the link
linkFormat = vbNullString
End If
End If
Next cell
End Sub
Let me know if it works

Impossible Excel-VBA Email Loop

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:

How to assign Thisworkbook macro to Form control

I have a workbook to send out email reminders based on the due date. I would like to change it such that the macro will run when I click a button instead of running automatically when it is opened.
ThisWorkbook:
Sub Workbook_Open()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.Cells(1, 1).Value = "Feedback Report (FR) Log" Then CheckDates ws:=ws
Next ws
End Sub
Module1:
Option Explicit
Dim Bcell As Range
Dim iTo, iSubject, iBody As String
Dim ImportanceLevel As String
Public Sub CheckDates(ws As Worksheet)
For Each Bcell In Range("a4", Range("a" & Rows.Count).End(xlUp))
' if email column is not empty then command continues
If Bcell.Offset(0, 15) <> Empty Then
' mail will not be sent if current time is within 23.7 hours
' from time of mail last sent.
If Now() - Bcell.Offset(0, 49) > 0.9875 Then
If Bcell.Offset(0, 25) = Empty Then
If DateDiff("d", Now(), Bcell.Offset(0, 13)) = 7 Then
iTo = Bcell.Offset(0, 15)
iSubject = Bcell & " Due"
iBody = "<font face=""Calibri"" size=""3"">" & "Dear all,<br/><br/>" & _
"<u>FR No. " & Bcell & "</u><br/><br/>" & "Please be reminded that " & Bcell & " will be due by <b><FONT COLOR=#ff0000>" & _
Bcell.Offset(0, 13) & "</font></b>." & _
" Kindly ensure that the FR is closed by the due date and provide the draft FR report with preliminary investigation (Section B & D filled) to Quality.<br/><br/>" _
& "Thank you<br/><br/>" & "Best Regards,<br/>" & "Quality Department<br/><br/>" _
& "company Pte Ltd.<br/>" & "</font>"
SendEmail
Bcell.Offset(0, 49) = Now()
End If
End If
End If
End If
iTo = Empty
iSubject = Empty
iBody = Empty
Next Bcell
End Sub
Private Sub SendEmail()
Dim OutApp As Object
Dim OutMail As Object
Dim strbody As String
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = iTo
.CC = "email#email.com"
.BCC = ""
.Subject = iSubject
.HTMLBody = iBody
.Importance = ImportanceLevel
'You can add a file like this
'.Attachments.Add ("C:\test.txt")
.send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
You have two ways to accomplish this depending on the type of button you want to use to run the macro:
a) If the button is a simply Shape (Insert > Shapes), you need to move the contents of Workbook_Open to a new sub within your Module1 (let's call it "trigger") and right-click the shape > Assign macro > "trigger".
Sub trigger()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.Cells(1, 1).Value = "Feedback Report (FR) Log" Then CheckDates ws:=ws
End If
Next ws
End Sub
b) If the button is not a shape but a form button, you need to double-click it while on the design view and move the contents of "trigger" to its own click sub (CommandButton1_click()).
c) Finally, remember to remove the contents of Workbook_Open() sub.

Resources