The aim is to check individual worksheets for a list of dates in a range, and then send an email listing these dates to an email address located in the sheet.
The current code concatenates the dates in the current sheet as well as the dates on the previous sheet, instead of just the dates found on this sheet.
I'm struggling to make it uni-vocal, tried putting "ws." before each aCell instruction but get compile error.
Sub Mail_Outlook()
Dim ws As Worksheet
Dim wsName As Variant
Dim OutApp As Object
Dim OutMail As Object
Dim cell As Range
Dim strbody As String
Dim string1 As String
Dim aCell As Range
Dim i As Integer
i = 0
For Each wsName In Array("sheet1", "sheet2", "sheet3")
Set ws = Worksheets(wsName)
'retrieve all missing dates
For Each aCell In ws.Range("Aa1:Aa1000")
If aCell.Value <> "" Then
i = i + 1
If i <> 1 Then
string1 = string1 & ", " & aCell.Value
Else
string1 = aCell.Value
End If
End If
Next
'send email
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
strbody = "Good day " & ws.Range("E3").Cells & vbNewLine & vbNewLine & _
"" & vbNewLine & vbNewLine & _
"" & vbNewLine & vbNewLine & _
string1 & vbNewLine & vbNewLine & vbNewLine & vbNewLine & _
"(This is an automated message)" & vbNewLine & vbNewLine & _
"Best regards" & vbNewLine & vbNewLine & _
On Error Resume Next
With OutMail
.To = ws.Range("E5").Text
.CC = ""
.BCC = ""
.Subject = ""
.Body = strbody
'You can add a file like this
'.Attachments.Add ("C:\test.txt")
.Display 'or use .Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
Next
End Sub
From the OP's comments:
code as is produces no errors, but e.g. the second email contains the string for the first sheet and second sheet, rather than just the second sheet.
Zero the string before going into the second iteration of the loop.
For Each wsName In Array("sheet1", "sheet2", "sheet3")
Set ws = Worksheets(wsName)
string1 = vbNullString 'reset string1 to a zero-length string for each ws
'retrieve all missing dates
For Each aCell In ws.Range("Aa1:Aa1000")
'all the rest of the concatenation code
next aCell
'all the rest of the email code
Next wsName
Related
I'm trying to automate an emailing process with outlook.
So far my code enables to:
Send different attachments to different recipients
Send the same range of the excel sheet (ex: A1:B3) as an image in the email body to all the recipients
Personalized message
What I would like is to send different ranges to different recipients (like the attachments) for example:
Email 1: Range A1 B3
Email 2: Range A4:B7
Email 3: Range A8:B11
etc...
Is it possible to make it on loop or sth?
Sub Send_Files()
Dim OutApp As Object
Dim OutMail As Object
Dim sh As Worksheet
Dim cell As Range
Dim FileCell As Range
Dim rng As Range
Dim MakeJPG As String
Dim PictureRange As Range
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set sh = Sheets("Sheet1")
Set OutApp = CreateObject("Outlook.Application")
For Each cell In sh.Columns("B").Cells.SpecialCells(xlCellTypeConstants)
'Enter the path/file names in the C:Z column in each row
Set rng = sh.Cells(cell.Row, 1).Range("C1:Z1")
MakeJPG = CopyRangeToJPG("Sheet1", "F31: J37")
If MakeJPG = "" Then
MsgBox "Something go wrong, we can't create the mail"
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Exit Sub
End If
On Error Resume Next
If cell.Value Like "?*#?*.?*" And _
Application.WorksheetFunction.CountA(rng) > 0 Then
Set OutMail = OutApp.CreateItem(0)
With OutMail
.Display
.To = cell.Value
.Subject = Range("B11") & Range("H13") & " - " & cell.Offset(0, 2)
.Attachments.Add MakeJPG, 1, 0
.HTMLBody = "Bonjour " & cell.Offset(0, -1).Value & "," & "<br/>" & "<br/>" & Range("B15") & " " & Range("C15") & " " & Range("D15") & "<p>" & Range("B16") & "<p>" & "<\p>" & "</p><img src=""cid:NamePicture.jpg"" width=550 height=150></html>" & "<p>" & "<\p>" & Range("B17") & .HTMLBody
For Each FileCell In rng.SpecialCells(xlCellTypeConstants)
If Trim(FileCell) <> "" Then
If Dir(FileCell.Value) <> "" Then
.Attachments.Add FileCell.Value
End If
End If
Next FileCell
End With
Set OutMail = Nothing
End If
Next cell
Set OutApp = Nothing
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
Function CopyRangeToJPG(NameWorksheet As String, RangeAddress As String) As String
Dim PictureRange As Range
With ActiveWorkbook
On Error Resume Next
.Worksheets(NameWorksheet).Activate
Set PictureRange = .Worksheets(NameWorksheet).Range(RangeAddress)
If PictureRange Is Nothing Then
MsgBox "Sorry this is not a correct range"
On Error GoTo 0
Exit Function
End If
PictureRange.CopyPicture
With .Worksheets(NameWorksheet).ChartObjects.Add(PictureRange.Left, PictureRange.Top, PictureRange.Width, PictureRange.Height)
.Activate
.Chart.Paste
.Chart.Export Environ$("temp") & Application.PathSeparator & "NamePicture.jpg", "JPG"
End With
.Worksheets(NameWorksheet).ChartObjects(.Worksheets(NameWorksheet).ChartObjects.Count).Delete
End With
CopyRangeToJPG = Environ$("temp") & Application.PathSeparator & "NamePicture.jpg"
Set PictureRange = Nothing
End Function
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
End Sub
Set the initial image range and then offset it after each email sent .
Set rngImage = sh.Range("F27:J28")
Set rngImage = rngImage.Offset(rngImage.Rows.Count)
With the constant TEST = True this code should run without sending emails. If correct set TEST = False.
Option Explicit
Sub Send_Files()
Dim OutApp As Object, OutMail As Object
Dim ws As Worksheet, cell As Range, cellA, rngA As Range
Dim jpgFilename As String, filename As String, html As String
Dim rngImage As Range, sImage As String, n As Long
Const TEST = True ' set to False to use Outlook
Const IMG_NAME = "Image_"
Const IMG_RANGE = "F27:J28" ' first email
If Not TEST Then
Set OutApp = CreateObject("Outlook.Application")
End If
Set ws = Sheets("Sheet1")
Set rngImage = ws.Range(IMG_RANGE) ' first image
' scan column B for valid email addresses
For Each cell In ws.Columns("B").Cells.SpecialCells(xlCellTypeConstants)
'Enter the path/file names in the C:Z column in each row
Set rngA = cell.Offset(,1).Resize(, 24) ' C:Z attachments
If Application.WorksheetFunction.CountA(rngA) = 0 Then
' no attachments - do nothing
ElseIf cell.Value Like "?*#?*.?*" Then
sImage = IMG_NAME & rngImage.Row & ".jpg" ' unique image name for each email
jpgFilename = CopyRangeToJPG(rngImage, sImage)
' email body
html = "Bonjour " & cell.Offset(0, -1).Value & "," & "<br/><br/>" _
& ws.Range("B15") & " " & ws.Range("C15") & " " & ws.Range("D15") & _
"<p>" & ws.Range("B16") & "</p><br/>" & _
"<img src=""cid:" & sImage & """ width=550 height=150>" & _
"<br/>" & ws.Range("B17")
If TEST Then
MsgBox "Image: " & jpgFilename & vbLf & html, vbInformation, "To: " & cell.Value
'Debug.Print html
Else
Set OutMail = OutApp.CreateItem(0)
With OutMail
.Display
.To = cell.Value
.Subject = Range("B11") & Range("H13") & " - " & cell.Offset(0, 2)
.Attachments.Add jpgFilename, 1, 0
.HTMLBody = html & .HTMLBody
' add attachments
For Each cellA In rngA.SpecialCells(xlCellTypeConstants)
filename = Trim(cellA.Value)
If filename <> "" Then
If Dir(filename) <> "" Then ' check file exists
.Attachments.Add filename
Else
MsgBox "Could not attach : " & filename, vbExclamation, cell.Value
End If
End If
Next
End With
Set OutMail = Nothing
End If
' next image
Set rngImage = rngImage.Offset(rngImage.Rows.Count)
n = n + 1
End If
Next
Set OutApp = Nothing
MsgBox n & " emails sent", vbInformation
End Sub
Function CopyRangeToJPG(rngImage As Range, filename As String) As String
Dim ws As Worksheet, folder As String
Set ws = rngImage.Parent ' sheet
' check range
If rngImage Is Nothing Then
MsgBox "Sorry this is not a correct range"
CopyRangeToJPG = ""
Exit Function
End If
' create image file
folder = Environ$("temp") & Application.PathSeparator
rngImage.CopyPicture
With ws.ChartObjects.Add(rngImage.Left, rngImage.Top, rngImage.Width, rngImage.Height)
.Activate
.Chart.Paste
.Chart.Export folder & filename, "JPG"
End With
ws.ChartObjects(ws.ChartObjects.Count).Delete
' return status
CopyRangeToJPG = folder & filename
End Function
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
I have the following piece of code which sends emails in bulk.
Sub Sengrd_Files()
Dim OutApp As Object
Dim OutMail As Object
Dim sh As Worksheet
Dim cell As Range
Dim FileCell As Range
Dim rng As Range
para2 = ""
para3 = ""
para232 = Range("AA2").Value
With Application
.EnableEvents = False
.ScreenUpdating = True
End With
Set sh = Sheets("Sheet1")
Set OutApp = CreateObject("Outlook.Application")
For Each cell In sh.Columns("B").Cells.SpecialCells(xlCellTypeConstants)
'Enter the path/file names in the C:Z column in each row
Set rng = sh.Cells(cell.Row, 1).Range("C1:Z1")
If cell.Value Like "?*#?*.?*" And _
Application.WorksheetFunction.CountA(rng) > 0 Then
Set OutMail = OutApp.CreateItem(0)
With OutMail
.to = cell.Value
.Subject = "Circle Profitability Report for the period ended 30-NOV-2017"
.Body = "Dear Sir/Madam," _
& vbNewLine _
& para232 & vbNewLine _
& vbNewLine & para2 & vbNewLine _
& Remark & vbNewLine & vbNewLine _
& para3 & vbNewLine & vbNewLine
For Each FileCell In rng.SpecialCells(xlCellTypeConstants)
If Trim(FileCell) <> "" Then
If Dir(FileCell.Value) <> "" Then
.Attachments.Add FileCell.Value
End If
End If
Next FileCell
.Send 'Or use .Display
End With
Set OutMail = Nothing
End If
Next cell
Set OutApp = Nothing
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
7 different mails will be sent to different people mentioned in Column B with Attachment defined in Col C.
The Macro by default sends mails for ALL line items probably because of this line in code
**For Each cell In sh.Columns("B").Cells.SpecialCells(xlCellTypeConstants)**
I cannot define a variable i and change the above line to
**For Each i =1 to 5 sh.Columns("B").Cells.SpecialCells(xlCellTypeConstants)**
due to syntax error. Can anyone help me in syntax in replacing "For each cell in" to a finite range.
This is how to make the bulk-mail-sender send only to a given range (in this case B2 - B5):
For Each cell In sh.Range("B2:B5")
And do not forget - spam is bad.
I have to send reports to over 400 email addresses (on column B). The filepaths for each report are on columns C, D and E.
With this post: How to add default signature in Outlook the signature is added when the .display method is used.
The signature I want to show is for user number 1. I've selected the corresponding signature as a default signature for new messages.
This signature contains a picture, but this doesn't seem to cause any problems.
I wouldn't want the macro to show the mail every time it sends the mail, because I want to avoid the constant blinking on the screen.
I tried to look for something like "hide" method from here but didn't find anything useful (.display would run in the background, and it would stay hidden from the user). Other idea was to add application.screenupdating = false and correspondingly true in the end, but this didn't have any impact.
How could I display the email in the background without showing it every time to the user?
Sub sendFiles_weeklyReports()
Dim OutApp As Object
Dim OutMail As Object
Dim sh As Worksheet
Dim EmailCell As Range
Dim FileCell As Range
Dim rng As Range
Dim lastRow As Long
Dim timestampColumn As Long
Dim fileLogColumn As Long
Dim i As Long
Dim strbody As String
Dim receiverName As String
Dim myMessage As String
Dim reportNameRange As String
Dim answerConfirmation As Variant
Application.ScreenUpdating = False
Set sh = Sheets("Report sender")
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.createitem(0)
lastRow = sh.Cells(Rows.Count, "B").End(xlUp).Row
i = 0
reportNameRange = "C1:E1"
timestampColumn = 17 'based on offset on EmailCell (column B)!
fileLogColumn = 18 'based on offset on EmailCell (column B)!
myMessage = "Are you sure you want to send weekly reports?" & vbNewLine & "'" & _
sh.Range("C2").Value & "', " & vbNewLine & "'" & sh.Range("D2").Value & "' and " & vbNewLine & _
"'" & sh.Range("E2").Value & "'?"
answerConfirmation = MsgBox(myMessage, vbYesNo, "Send emails")
If answerConfirmation = vbYes Then
GoTo Start
End If
If answerConfirmation = vbNo Then
GoTo Quit
End If
Start:
For Each EmailCell In sh.Range("B3:B" & lastRow)
EmailCell.Offset(0, fileLogColumn).ClearContents
EmailCell.Offset(0, timestampColumn).ClearContents
Set rng = sh.Cells(EmailCell.Row, 1).Range(reportNameRange)
If EmailCell.Value Like "?*#?*.?*" And Application.WorksheetFunction.CountA(rng) > 0 Then
With OutMail
For Each FileCell In rng
If Trim(FileCell) <> "" Then
If Dir(FileCell.Value) <> "" Then 'checks if there's a file path in the cell
.Attachments.Add FileCell.Value
EmailCell.Offset(0, fileLogColumn).Value = EmailCell.Offset(0, fileLogColumn).Value & ", " & _
Dir(FileCell.Value)
i = i + 1
End If
End If
Next FileCell
receiverName = EmailCell.Offset(0, -1).Value
strbody = "<BODY style=font-size:11pt;font-family:Calibri><p>Dear " & receiverName & ",</p>" & _
"<p>Please find attached the weekly reports.</p>" & _
"<p>Kind regards,</p></BODY>"
.SendUsingAccount = OutApp.Session.Accounts.Item(1)
.To = EmailCell.Value
.Subject = "Weekly Reporting – " & UCase("w") & "eek " & Format(Date, "ww") _
& " " & UCase(Left(Format(Date, "mmmm"), 1)) & Right(Format(Date, "mmmm"), _
Len(Format(Date, "mmmm")) - 1) & " " & Year(Now)
.display
.HTMLBody = strbody & .HTMLBody
.Send
EmailCell.Offset(0, timestampColumn).Value = Now
SkipEmail:
End With
Set OutMail = Nothing
End If
Next EmailCell
Set OutApp = Nothing
Application.ScreenUpdating = True
Call MsgBox("Weekly reports have been sent.", vbInformation, "Emails sent")
Quit:
End Sub
Appears .GetInspector has the same functionality of .Display except the "display".
Sub generateDefaultSignature_WithoutDisplay()
Dim OutApp As Object ' If initiated outside of Outlook
Dim OutMail As Object
Dim strbody As String
Dim receiverName As String
receiverName = const_meFirstLast ' My name
strbody = "<BODY style=font-size:11pt;font-family:Calibri><p>Dear " & receiverName & ",</p>" & _
"<p>Please find attached the weekly reports.</p>" & _
"<p>Kind regards,</p></BODY>"
Set OutApp = CreateObject("Outlook.Application") ' If initiated outside of Outlook
Set OutMail = OutApp.CreateItem(0)
With OutMail
.SendUsingAccount = OutApp.Session.Accounts.Item(1)
.To = const_emAddress ' My email address
.Subject = "Weekly Reporting – " & UCase("w") & "eek " & Format(Date, "ww") _
& " " & UCase(Left(Format(Date, "mmmm"), 1)) & Right(Format(Date, "mmmm"), _
Len(Format(Date, "mmmm")) - 1) & " " & Year(Now)
' Default Signature
' Outlook 2013
' There is a report that .GetInspector is insufficient
' to generate the signature in Outlook 2016
'.GetInspector ' rather than .Display
' Appears mailitem.GetInspector was not supposed to be valid as is
' .GetInspector is described here
' https://learn.microsoft.com/en-us/office/vba/api/outlook.mailitem.getinspector
Dim objInspector As Inspector
Set objInspector = .GetInspector
.HTMLBody = strbody & .HTMLBody
.Send
End With
ExitRoutine:
Set OutApp = Nothing
Set OutMail = Nothing
End Sub
How do I display a range from Excel in Outlook's new email window?
How do I fill the To: and CC: with email addresses from specific cells within the Excel file?
I have the code below which is for object creation. (office 13)
Option Explicit
Private Sub CommandButton1_Click()
On Error GoTo ErrHandler
Dim objOutlook As Object
Set objOutlook = CreateObject("Outlook.Application")
Dim objEmail As Object
Set objEmail = objOutlook.CreateItem(olMailItem)
With objEmail
.to = ""
.Subject = ""
.Body = ""
.Display
End With
Set objEmail = Nothing: Set objOutlook = Nothing
ErrHandler:
'
End Sub
If you have multiple email addresses in a range, for example in "A1:A3", you can create a list and set the .To property equal to this list (same approach for .CC):
Dim ws As Worksheet, rng As Range, sTo As String
Set ws = ThisWorkbook.Worksheets("Email Addresses")
For Each rng In ws.Range("A1:A3")
sTo = sTo & rng & ", " 'creates To: list
Next
sTo = Left(sTo, Len(sTo) - 2) 'removes last ", "
With objEmail
.To = sTo
.subject = ""
.Body = ""
.Display
End With
Just add the cell after the TO: and it will get the value in it. Let say your email address is in B1, the code would look like this:
.To = Cells(1, 2).Value
To add a range, you need to convert it to a string before. You need to add this part before your email part:
Dim myCell As Range, myString As String
For Each myCell In Range("A1:A2") 'Change range to suit your needs
myString = myString & "," & myCell.Value
Next myCell
'Remove extra comma
myString = Right(myString, Len(myString) - 1)
With this piece of code, you'll convert your range to a string with comma between each cells. You can change "," with vbLf to get the next cell on a different line instead of a comma.
Here is an example of code with the range as a string:
.Body = "Hi " & Cells(1, 3).Value & "," & vbLf & vbLf _
& "Here is the main text of my email" & vbLf & myString & vbLf & vbLf_
& Application.UserName & vbLf & vbLf
In this example, it use the value in C1 as the name of the person you're writing to, your predefined text and the name of the user at the end.