Intermittent failure of worksheet macro to populate outlook "To" field - excel

I have a workbook with a summary tab, with one row per client, each row containing variables that populate an email to that client. Each row is mapped to a separate tab using excel formulas (which also refers to a lookups tab, where I can update client information such as emails and names). Each tab contains a worksheet macro to pull information into an outlook email (code below). This works fine most of the time, but often the final emails to generate fail to populate the email "To" field. This happens most after making changes to the workbook. If I save, close and reopen the workbook, most (if not all) of the email "To" fields populate correctly. The first emails to generate are always fine, but at some point while generating they stop populating "to", then every email to generate after that point does not have the "to" field. It ranges from 10% to 100% of the emails populating "to" correctly.
The code below is on each of the tabs (usually about 50 clients/tabs/emails run). Any ideas on why this could be, and how to make it always work 100%? It seems like I might just be asking too much of excel, having them all run at once, maybe they need to run in sequence, I'm not sure. Any help appreciated!
Dim xRg As Range
'Update by Extendoffice 2018/3/7
Public Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Target.Cells.Count > 1 Then Exit Sub
Set xRg = Intersect(Range("AA1"), Target)
If xRg Is Nothing Then Exit Sub
If IsNumeric(Target.Value) And Target.Value > 0 Then
Call Mail_small_Text_Outlook
End If
End Sub
Sub Mail_small_Text_Outlook()
Application.EnableEvents = False
On Error GoTo Handler
ListObjects("Table6").Range.AutoFilter Field:=1, Criteria1:= _
"<>"
Dim xOutApp As Object
Dim xOutMail As Object
Dim xMailBody As String
Set xOutApp = CreateObject("Outlook.Application")
Set xOutMail = xOutApp.CreateItem(0)
On Error Resume Next
With xOutMail
.To = Range("H2")
.CC = "billing#example.com; name#example.com"
.BCC = ""
.Subject = Range("C2") & " - (ID: " & Range("I2") & ") - " & Range("B2") & " Lightning Docs Usage & Billing"
.HTMLBody = "<font size=-0> Hello " & Range("G2") & ",<br/><br/>" & vbNewLine & vbNewLine & _
"Please review the following list of loan documents that were produced through our online system from " & Range("B2") & ". <br/>Your total bill for this month's documents is " & FormatCurrency(Range("F2")) & " (" & Range("E2") & " x " & FormatCurrency(Range("D2")) & "):</font>" & vbNewLine & vbNewLine & _
RangetoHTML(Range("Table6")) & vbNewLine & vbNewLine & _
"<br/><font size=-0>Please let us know within two business days whether your records match ours. If we do not get a response within this time frame, we will invoice you shortly thereafter. If your credit card is on file and we have a pre-existing authorization, we will charge your card on file and provide you with a copy of your paid invoice." & vbNewLine & vbNewLine & _
"<br/><br/>Thank you!</font><br/>" & vbNewLine
.Display 'or use .Send
End With
On Error GoTo 0
Set xOutMail = Nothing
Set xOutApp = Nothing
Handler:
Application.EnableEvents = True
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2016
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , SkipBlanks:=True, Transpose:=False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Private Sub Worksheet_Calculate()
Dim xI As Integer
Dim xRg As Range
Set xRg = Range("AA1")
On Error GoTo Err01
xI = Int(xRg.Value)
If xI = 1 Then
Call Mail_small_Text_Outlook
End If
Err01:
End Sub

First of all, you need to find what value the Range("H2") code returns. You can use Debug.Print statements to track what values are assigned to the To property.
Second, I'd suggest using the Recipients.Add method instead of relying to the To property.
Set myItem = Application.CreateItem(olMailItem)
Set myRecipient = myItem.Recipients.Add("Eugene Astafiev")
myRecipient.Resolve()
Read more about that in the How To: Fill TO,CC and BCC fields in Outlook programmatically article.

Related

Only one message with HTML code in a loop is sent

Following Ron de Bruin's indications here, I created a VBA script to send each of my students an e-mail with their qualification. Each message contains text and a range with two rows and is composed in HTML format.
The routine seems to work using the .Display method of the Outmail object created in the script.
When I change .Display to .Send only the first message in the list is sent and, during tests, several times I had to close Outlook from the Task Manager because it is hung without closing. If I do this and run the script again, then the messages are sent and each receiver receives the message twice.
Here's a sample of the code:
Option Explicit
Sub GetLblAddress()
Dim wb As Workbook
Dim ws As Worksheet
Dim oLblRg As Range
On Error Resume Next
' Range C2:I2 contains labels of points earned in each exercise
Set oLblRg = Application.InputBox(Prompt:="Select labels in worksheet", _
Title:="SEND NOTES", _
Default:="C2:I2", _
Type:=8)
'Missing error trap yet!
Set ws = oLblRg.Parent
Set wb = ws.Parent
SendNotes wb, ws, oLblRg.Address
End Sub
Sub SendNotes(wb As Workbook, ws As Worksheet, sIniAd As String)
Const sSIGN As String = "<br><br>" & "Saludos" & "<br><br>" & "myname here"
Dim wsList As Worksheet
Dim rng As Range
Dim OutApp As Outlook.Application
Dim OutMail As Outlook.mailitem
Dim OutAccount As Outlook.Account
Dim mailAd As String
Dim rw, lstRw, nCol, numSend As Long
Dim sAd, s As String
Dim sTo, sSubj, sBody As String
Dim bSend As Boolean
On Error GoTo CleanUp:
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
'worksheet "Listado" contains e-mail addresses in column H
Set wsList = wb.Worksheets("Listado")
With ws
If .Range(sIniAd).Rows.Count <> 1 Or Left(.Range(sIniAd)(1, 1), 1) <> "P" Then
Err.Raise 1
End If
End With
lstRw = ws.Cells(Rows.Count, 1).End(xlUp).Row
'Here begins loop that runs through the range of qualifications
'and send the corresponding row to each student present in the exam
numSend = 0
Application.StatusBar = "Creando instancia de Outlook." 'Just to keep me informed
Set OutApp = CreateObject("Outlook.Application")
Set OutAccount = OutApp.Session.Accounts("MyAddress#gmail.com")
For rw = 3 To lstRw
bSend = False
With ws
nCol = .Range(sIniAd).Columns.Count + 2
sAd = sIniAd & "," & .Cells(rw, 3).Address & ":" & .Cells(rw, nCol).Address
'Range rng contains two rows: labels and marks
Set rng = .Range(sAd)
sTo = wsList.Cells(rw, 8) 'Mail address of the student
sSubj = "Notas del Examen"
sBody = "Hola." & "<br><br>" & "Tu calificación en el examen es:" & "<br><br>"
'Set boolean variable bSend to know wether send a message
bSend = IsNumeric(.Cells(rw, 3)) And UCase(.Cells(rw, 10).Value) = "NO" And sTo <> vbNullString
End With
'Here's the "meat"
If bSend Then
Set OutMail = OutApp.CreateItem(olmailitem)
With OutMail
.To = sTo
.Subject = sSubj
.HTMLBody = sBody & RangetoHTML(rng) & sSIGN
.SendUsingAccount = OutAccount
.Display 'or use .SEND
End With
numSend = numSend + 1
'Sets the "Send" state to Yes
ws.Cells(rw, 10) = "SI"
End If
'Report advance of script into the status bar
Application.StatusBar = "Procesando: " & rw - 2 & "/" & lstRw - 2 & " (" & Format((rw - 2) / (lstRw - 2), "0%") & ")."
Next rw
CleanUp:
Application.StatusBar = False
Application.CutCopyMode = False
Set OutMail = Nothing
Set OutApp = Nothing
Set ws = Nothing
Set wsList = Nothing
If Err.Number <> 0 Then
If Err.Number = 1 Then
MsgBox "Debe seleccionar sólo la fila de ETIQUETAS", vbExclamation, "SEND NOTES"
ElseIf rng Is Nothing Then
MsgBox "No hay un rango seleccionado o la hoja está protegida," & _
vbNewLine & "corregir e intentar nuevamente.", vbExclamation, "SEND NOTES"
Else
MsgBox Err.Description, vbExclamation, "SEND NOTES"
End If
ElseIf numSend = 0 Then
MsgBox "No se han enviado mensajes.", vbInformation, "SEND NOTES"
ElseIf numSend = 1 Then
MsgBox "Se ha enviado 1 mensaje.", vbInformation, "SEND NOTES"
Else
MsgBox "Se han enviado " & numSend & " mensajes.", vbInformation, "SEND NOTES"
End If
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
Function RangetoHTML(rng As Range)
'Extracted from http://www.rondebruin.nl/win/s1/outlook/bmail2.htm
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).CurrentRegion.Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Alternative question: Is it possible to include HTML code in a message without using Outlook (something like what is suggested here)?

Copying Excel table with gradient filled cells to Outlook mail

I have a table in Excel that I want to send to a distribution list in Outlook with the table in the email body.
Using MVP Ron de Bruin's examples and a few others on here I've got code that keeps some of the table formatting but doesn't copy the cells colour if it is a gradient (please use the images as reference).
Sub DisplayEmailButton_Click()
Mail_Selection_Range_Outlook_Body
End Sub
Sub Mail_Selection_Range_Outlook_Body()
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Nothing
On Error Resume Next
Set rng = Sheets("Sheet1").Range("C2:Q18").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected" & _
vbNewLine & "please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = "Team01"
.CC = ""
.BCC = ""
.Subject = "Daily Statistics"
.HTMLBody = "Please see attached daily statistics." & vbCrLf &
RangetoHTML(rng)
.Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
Dim TempFile As String, ddo As Long
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
' Temporary publish the rng range to a htm file
ddo = ActiveWorkbook.DisplayDrawingObjects
ActiveWorkbook.DisplayDrawingObjects = xlHide
With ActiveWorkbook.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=ActiveSheet.Name, _
Source:=Union(rng, rng).Address, _
HtmlType:=xlHtmlStatic)
.Publish True
.Delete
End With
ActiveWorkbook.DisplayDrawingObjects = ddo
'Read all data from the htm file into RangetoHTML
With
CreateObject("Scripting.FileSystemObject").GetFile(TempFile)
.OpenAsTextStream(1, -2)
RangetoHTML = Replace(.ReadAll, "align=center x:publishsource=", "align=left
x:publishsource=")
.Close
End With
'Delete the htm file we used in this function
Kill TempFile
End Function
As Tim suggested I was expecting way too much from that procedure (Thank you, Tim, for the advice!) so I looked into a workaround. If the range is saved as a picture then it keeps all the formatting and the picture can then easily be attached to an email or displayed in the body of the email.
To save as a picture:
Dim Wb As ThisWorkbook
Dim Ws As Worksheet
Dim Ch As Chart
Set Rng = Ws.Range("A1:G18")
Set Ch = Charts.Add
Ch.Location xlLocationAsObject, "Sheet2"
Set Ch = ActiveChart
ActiveChart.Parent.Name = "StatsTemp"
ActiveSheet.ChartObjects("StatsTemp").Height = Rng.Height
ActiveSheet.ChartObjects("StatsTemp").Width = Rng.Width
Rng.CopyPicture xlScreen, xlBitmap
Ch.Paste
Ch.Export Environ("UserProfile") & "\Desktop" & "\" & Format("TempImage") & ".jpg"
Worksheets("Sheet2").ChartObjects("StatsTemp").Delete
Worksheets("Sheet1").Activate
The above code saves the range as an image "TempImage.JPG" to the users desktop by creating a new chart on sheet 2, pasting the range to the chart then saves the chart as an image and deletes the chart.
To attach the picture to an email in the email body:
Dim StrBody As String
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
StrBody = "Some text here." & "<br>"
On Error Resume Next
With OutMail
.to = "email address"
.CC = ""
.BCC = ""
.Subject = "Email Subject"
.HTMLBody = StrBody & "<img src = '" & Environ("userProfile") &
"\desktop\TempImage.jpg'>"
.Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
The above code creates an email using Microsoft Outlook which contains the saved image file in the email body and displays the email.
The image can be deleted after using:
Kill Environ("UserProfile") & "\Desktop" & "\TempImage.jpg"
Hopefully, this will be of some use to someone!
Credit to Ron de Bruin Microsoft Office MVP for his WinTips!

Attach multiple files to Outlook email from a filtered list and loop [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have a list of customers with their invoice data (one customer may have one or more than one rows of data). I have assembled a macro script from multiple codes to filter out the customer (basis on email address) and send them a dunning letter with their account statement.
The code is working fine with email creation, except I am not able to attach their invoice copies listed in column 2 (In TempoWB workbook).
I think the problem is with Loop The code is jumping from Do while directly to .HTMLBody.It's skipping the previous codes to search and attach files. How can I fix it?
Here is the Zip file with all required data and files. In case you want to give it a try. Just copy the 'Renamed' invoice folder to C:\Invoices.
(customer names and other data has been altered for compliance reason)
Option Explicit
Sub Dunning_3_Populate_Emails_TempWB()
Application.ScreenUpdating = False
'This code populates emails to outlook as per the Credit analysts.
Dim OutApp As Object
Dim OutMail As Object
Dim rng As Range
Dim Ash As Worksheet
Dim Cws As Worksheet
Dim Rcount As Long
Dim Rnum As Long
Dim name_rg As Range
Dim name As String
Dim Subj As String
Dim irow As Integer
Dim dpath As String
Dim pfile As String
Dim strbody As String
Dim TempoWB As Workbook
'Folder location for Invoice copies
dpath = "C:\Invoices\Renamed"
'Column number to pick the invoices
irow = 2
Set OutApp = CreateObject("Outlook.Application")
name = Ash.Cells(name_rg.Row, 16)
Subj = Ash.Cells(name_rg.Row, 15)
Else
name = "email not found in Ash"
End If
------------------------------------------------------------------------------
'This portion has codes to filter the required data based on the unique email address
-----------------------------------------------------------------------------
'Create a new workbook with selected/ filtered data
rng.Copy
Set TempoWB = Workbooks.Add(1)
With TempoWB.Sheets(1)
.Cells(1).PasteSpecial
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
Columns("O:Q").Select
Selection.Delete Shift:=xlToLeft
On Error GoTo 0
'Location to save the temporary workbook
Application.DisplayAlerts = False
TempoWB.SaveAs Filename:="C:\Invoices\TempoWB.xlsx"
End With
'E-mail body for the dunning letters
strbody = "Hello " & name & "," & "<br>" & "<br>" & _
"<b>Below is the summary of your account and attached are the invoices:</b>" & "<br>" & "<br>"
On Error GoTo Cleanup
On Error Resume Next
With OutMail
.Display
.To = Cws.Cells(Rnum, 1).Value
.Subject = subj
Workbooks("TempoWB.xlsx").Activate
For irow = 2 To Lastrow
.Attachments.Add ("C:\Dunning Temp\" & Cells(irow, 2).Value & ".pdf")
Next
.HTMLBody = strbody & RangetoHTML(rng) & .HTMLBody
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
End If
'Close TempoWB
Application.DisplayAlerts = False
Workbooks("TempoWB.xlsx").Close SaveChanges:=False
On Error Resume Next
'Close AutoFilter
Ash.AutoFilterMode = False
Next Rnum
End If
End Sub
My initial suspicion of the row counter was just flat wrong. The problem turned out to be several small errors that simply caused the code to look for the attachment in places it could never find it.
Two things you should know:
1) The code currently in your question didn't feel right so I tossed it and went with the version you originally posted.
2) You need to update the path/directory strings and clear some comment blocks I've made. Nothing too difficult.
Option Explicit 'PO - Option Explicit, use it !
Sub Dunning_3_Populate_Emails()
Dim test1 As Long, test2 As Long
test1 = Timer
Application.ScreenUpdating = False
'This code populates emails to outlook.
Dim OutApp As Object
Dim OutMail As Object
Dim rng As Range
Dim Ash As Worksheet
Dim Cws As Worksheet
Dim Rcount As Long
Dim Rnum As Long
Dim FilterRange As Range
Dim FieldNum As Integer
Dim SigString As String
Dim Signature As String
Dim name_rg As Range
Dim name As String
Dim Subj As String
Dim irow As Integer
Dim dpath As String
Dim pfile As String
Dim strbody As String
dpath = Environ("appdata") & "\VBA\Stack Overflow\Attachments" 'PO - my environment only, delete
' dpath = "C:\Invoices\Renamed" 'PO - original code, use if it is correct or modify
irow = 2
'looping through all the files and sending an mail
Set OutApp = CreateObject("Outlook.Application")
'C:\Users\<UserName>\AppData\Roaming\Microsoft\Signatures 'PO - not my edit, guessing it is here for reference
'----------------------------------------------------------------
'PO - blocked this off because it wasn't related to the problem
' should be perfectly ok to unblock
'----------------------------------------------------------------
' SigString = Environ("appdata") & _
' "\Microsoft\Signatures\My Signature.htm"
'
' If Dir(SigString) <> "" Then
' Signature = GetBoiler(SigString)
' Else
' Signature = ""
' End If
'
' On Error Resume Next
'
' With Application
' .EnableEvents = False
' .ScreenUpdating = False
' End With
'----------------------------------------------------------------
'Set filter sheet, you can also use Sheets("MySheet")
Set Ash = ActiveSheet
'Set filter range and filter column (column with e-mail addresses)
Set FilterRange = Ash.Range("A1:Q" & Ash.Rows.Count)
FieldNum = 17 'Filter column = B because the filter range start in column A
'Add a worksheet for the unique list and copy the unique list in A1
Set Cws = Worksheets.Add
FilterRange.Columns(FieldNum).AdvancedFilter _
Action:=xlFilterCopy, _
CopyToRange:=Cws.Range("A1"), _
CriteriaRange:="", Unique:=True
'Count of the unique values + the header cell
Rcount = Application.WorksheetFunction.CountA(Cws.Columns(1))
'If there are unique values start the loop
If Rcount >= 2 Then
For Rnum = 2 To Rcount
'Filter the FilterRange on the FieldNum column
FilterRange.AutoFilter Field:=FieldNum, _
Criteria1:=Cws.Cells(Rnum, 1).Value
'If the unique value is a mail addres create a mail
If Cws.Cells(Rnum, 1).Value Like "?*#?*.?*" Then
With Ash.AutoFilter.Range
On Error Resume Next
Set rng = .SpecialCells(xlCellTypeVisible)
On Error GoTo 0
End With
Set OutMail = OutApp.CreateItem(0)
' ~ Search email address from Cws into Ash ~
Set name_rg = Ash.Columns(17).Find(Cws.Cells(Rnum, 1))
If Not name_rg Is Nothing Then
name = Ash.Cells(name_rg.Row, 16)
Subj = Ash.Cells(name_rg.Row, 15)
Else
name = "email not found in Ash"
End If
Set name_rg = Nothing
strbody = "Hello " & name & "," & "<br>" & "<br>" & _
"Hope you are fine!" & "<br>" & "<br>" & _
"I am writing to share the list of open invoice(s) on your account with <b>Keysight Technologies Inc.</b>" & "<br>" & "<br>" & _
"Please refer to th account statement below and let me know if you show any discrepancy on any of the open invoice(s), so that the required help can be arranged asap to get that resolved." & "<br>" & "<br>" & _
"Also, if the invoice(s) has been paid already, kindly share the payment details" & "<br>" & "<br>" & _
"<mark><i>** Please let me know if you have not recieved invoice copy so that I can arrange the invoice copy for you.</i></mark>" & "<br>" & "<br>" & _
"<b>Below is the summary of your account:</b>" & "<br>" & "<br>"
On Error GoTo Cleanup
On Error Resume Next
With OutMail
.To = Cws.Cells(Rnum, 1).Value
.Subject = Subj
'----------------------------------------------------------------
'PO - ranges and objects should be qualified to avoid bugs
' It is very likely Cells() was reading from the last active sheet (Cws)
'----------------------------------------------------------------
'Do While Cells(irow, 2) <> Empty 'PO - unqualified, dangerous
Do While Ash.Cells(irow, 2) <> Empty
'pikcing up file name from column B
'pfile = Dir(dpath & "\*" & Cells(irow, 2) & "*") 'PO - unqualified, dangerous
pfile = Dir(dpath & "\*" & Ash.Cells(irow, 2) & "*")
'checking for file exist in a folder and if its a pdf file
'If pfile <> "" And Right(pfile, 2) = "pdf" Then 'PO - a 2 letter string cannot equal a 3 letter string
If pfile <> "" And Right(pfile, 2) = "xt" Then 'PO - be sure to modify this
.Attachments.Add (dpath & "\" & pfile)
End If
'go to next file listed on the C column
irow = irow + 1
Loop
.HTMLBody = strbody & RangetoHTML(rng) & "<br>" & Signature
.Send
End With
' Set ws = Nothing 'PO - "ws" is undefied, probably "Cws"
On Error GoTo 0
Set OutMail = Nothing
End If
'Close AutoFilter
Ash.AutoFilterMode = False
Next Rnum
End If
Cleanup:
Set OutApp = Nothing
Application.DisplayAlerts = False
Cws.Delete
Application.DisplayAlerts = True
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
test2 = Timer
MsgBox "All the Collection Letters have been sent and it took only " & Format((test2 - test1) / 86400, "hh:mm:ss") & " Seconds."
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2010
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
' TempFile = Environ$("temp") & "/" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm" 'PO forward slash is wrong syntax
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
Columns("O:Q").Select
Selection.Delete Shift:=xlToLeft
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
FileName:=TempFile, _
Sheet:=TempWB.Sheets(1).name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
Function GetBoiler(ByVal sFile As String) As String
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
And lastly, the nested while loops are the reason you started getting stuck in a loop. Reducing your code to illustrate the point, it looked something ike this:
Do While Cells(irow, 2) <> Empty
Do While Cells(irow, 2) = Empty
Loop
Loop
Both conditions will almost always be met so you get stuck on the inside loop if the cell is empty and you get stuck on the outside loop if the cell is populated.

Transfer Hyperlinks in Excel Range to Outlook Email

I am trying to create an email from excel ranges (rng 1 through 6) that have hyperlinks for each cell in Columns A and D. Here is an example of the code that creates the hyperlinks for these ranges. That all works just fine.
ActiveSheet.Hyperlinks.Add Anchor:=ActiveWorkbook.Sheets("Overdue").Range("A" & D2), _
Address:="some address" & ActiveWorkbook.Sheets("Overdue").Range("A" & D2).Value
ActiveSheet.Hyperlinks.Add Anchor:=ActiveWorkbook.Sheets("Overdue").Range("D" & D2), _
Address:="some other address" & ActiveWorkbook.Sheets("Overdue").Range("A" & D2).Value
I then have the below code that creates an email from the excel ranges (rng1 through 6). When the email is created the hyperlinks do not transfer to Outlook. The text is underlined as if there is a hyperlink but it is not clickable.
Sub Mail_Body()
Dim rng1 As Range
Dim OutApp As Object
Dim OutMail As Object
Dim wb2 As Workbook
Dim MyDate, Weeknr, MyFileName, MyTime, MyMonth
Dim Mail1 As String
Dim Mail2 As String
Dim Subject As String
Dim Warr As String
Dim rng2 As Range
Dim rng3 As Range
Dim rng4 As Range
Dim rng5 As Range
Dim rng6 As Range
Dim Subject_email As String
Application.ScreenUpdating = False
Application.EnableEvents = False
nPath = Environ("temp") & "\" & ThisWorkbook.Sheets("Lists").Range("AA1").Value
Set wb2 = Workbooks.Open(nPath)
D2 = Sheets("Critical").Range("A1").Offset(Sheets("Critical").Rows.Count - 1, 0).End(xlUp).Row
D3 = Sheets("High").Range("A1").Offset(Sheets("High").Rows.Count - 1, 0).End(xlUp).Row
D4 = Sheets("Low").Range("A1").Offset(Sheets("Low").Rows.Count - 1, 0).End(xlUp).Row
D5 = Sheets("Other").Range("A1").Offset(Sheets("Other").Rows.Count - 1, 0).End(xlUp).Row
D6 = Sheets("Overdue").Range("A1").Offset(Sheets("Overdue").Rows.Count - 1, 0).End(xlUp).Row
Set rng = Nothing
Set rng1 = Nothing
Set rng2 = Nothing
Set rng3 = Nothing
Set rng4 = Nothing
Set rng5 = Nothing
Set rng6 = Nothing
Set rng2 = Sheets("Critical").Range("A1:J" & D2).SpecialCells(xlCellTypeVisible)
Set rng3 = Sheets("High").Range("A1:J" & D3).SpecialCells(xlCellTypeVisible)
Set rng4 = Sheets("Low").Range("A1:J" & D4).SpecialCells(xlCellTypeVisible)
Set rng5 = Sheets("Other").Range("A1:J" & D5).SpecialCells(xlCellTypeVisible)
Set rng6 = Sheets("Overdue").Range("A1:L" & D6).SpecialCells(xlCellTypeVisible)
Set OutMail = Nothing
Set OutApp = Nothing
On Error Resume Next
Set OutApp = GetObject(, "Outlook.Application")
If OutApp Is Nothing Then Set OutApp = CreateObject("Outlook.Application")
On Error GoTo 0
'MsgBox OutApp
Set OutMail = OutApp.CreateItem(0)
Dim Session As Object
Set Session = OutApp.GetNamespace("MAPI")
Session.Logon
Create email
With OutMail
.To = Mail1
.CC = Mail2
.BCC = ""
.Subject = Subject_email
.HTMLBody = "Overview:" & "<br>" & RangetoHTML(rng1) _
& "<br>" & "<u>Critical</u>" & "<br>" & RangetoHTML(rng2) & "<br>" & "<u>High</u>" _
& "<br>" & RangetoHTML(rng3) & "<br>" & "<u>Low</u>" & "<br>" & RangetoHTML(rng4) _
& "<br>" & "<u>Other</u>" & "<br>" & RangetoHTML(rng5) _
& "<br>" & "<u>Overdue</u>" & "<br>" & RangetoHTML(rng6)
.Attachments.Add nPath '.FullName
.Recipients.ResolveAll
.Display '.Send
End With
I'm unable to share the output of this code, but what happens, as explained above, is the hyperlinks from the Excel sheet do not transfer to the Outlook email. They are blue and underlined but there is no hyperlink.
How do I carry over the active hyperlinks from excel to outlook? I've been unable to find a pre-existing solution that fits my specific needs.
I found a solution to the issue: https://www.mrexcel.com/forum/excel-questions/560111-retain-hyperlinks-after-rangetohtml-paste-outlook.html
In the RangetoHTML function, change the .pastevalues to .pasteall and the hyperlinks will be copied over.
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2010
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
Dim r As Long
TempFile = Environ$("temp") & "/" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteAll, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).PasteSpecial xlPasteRowHeights
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.ReadAll
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function

VBA - Copy Cell from Sheet into Outlook email

I am new to VBA and have created the following code that sends an email.
My question is how do i copy some cells from my excel sheet that I am currently using to be pasted inside the excel file?
Thanks,
Sub CIR_Save_Email()
Dim objoutlook As Object
Set objoutlook = CreateObject("outlook.application")
Dim objemail As Object
Set objemail = objoutlook.createitem(olmailitem)
Const olFormatHTML As Long = 2
emailbodymessage = "<HTML><BODY>Hi Team," & _
"<br><br>Attached is the Display's CIR for today<br><br>" & _
"<b>Brief overview of CIR</b><br><br>" & _
"<b>Purpose:</b> To get a snapshot of what your current inventory levels by SKU are every day." & _
"<ul style=""list-style-type:circle"">" & _
"<li><b>Unrestricted QTY</b> The total inventory at that DC (i.e.Deliveries Created + Available Qty)</li>" & _
"<li><b>Deliveries Created:</b> Orders that are being processing at that DC (i.e. they will NOT be included in Available Inventory)</li>" & _
"<li><b>Available:</b> How many cases are available to use at that DC </li>" & _
"<li><b>Avail DOS:</b> How many DOS the available cases equate to</li>" & _
"<li><b>IT QTY:</b> How man cases are in transit</li>" & _
"<li><b>Avail +IT DOS:</b> How many DOS the available cases equate to</li>" & _
"</ul> </body> </html>"
emailbodymessage2 = "<html><body><ul style=""list-style-type:circle"">" & _
"<li><b>Future Available:</b> The total DOS of cases Avail + IT</li>" & _
"<li><b>QI QTY:</b> How many cases are on Qualitiy (ie Non-Conformance)</li>" & _
"<li><b>Blocked QTY:</b> How many cases are blocked from ordering due to damages, short dating, expired, etc." & _
"<li><b>CM- months:</b> The forecasts of the months past (CM-1=July)</li>" & _
"<li><b>% to Fcst:</b> How much of your projected forecast has shipped this month</li>" & _
"<li><b>Current SNAP Fcst:</b> This month's projected forecast</li>" & _
"<li><b>CM+ months:</b> The forecasts of the months moving forward (CM+1= September)</li>" & _
"</ul> </body></html>"
With objemail
.To = emaillist
.cc = ""
.Subject = "Display's CIR " & Date
.BodyFormat = olFormatHTML '// 2
.HTMLBody = emailbodymessage & emailbodymessage2
.display
End With
End Sub
You can use the following function (internally uses exporting range into HTML) to convert excel range into html. Then resultant HTML should be included into your generated HTML body.
The function is exporting Range into HTML temporary created file and then strips content to only div (without surrounding HTML tags).
However, I'm not sure if formatting and other details will fit your requirements. Other solution is to construct HTML from cells manually, but it is much more work.
Usage: str = GetHtml("Sheet1","D4:E6")
Public Function GetHtml(ByVal sheetName As String, ByVal rangeName As String) As String
Dim fso As FileSystemObject
Dim fileName As String
Dim txtStream As TextStream
Dim html As String
Dim line As String
Dim readLines As Boolean
Set fso = New FileSystemObject
Dim rng As range
fileName = fso.GetSpecialFolder(2) & "\" & Replace(fso.GetTempName, ".tmp", ".html")
If fso.FileExists(fileName) Then
fso.DeleteFile fileName
End If
Set rng = Sheets(sheetName).range(rangeName)
ActiveWorkbook.PublishObjects.Add(SourceType:=xlSourceRange, fileName:=fileName, Sheet:=rng.Worksheet.Name, Source:=rng.Address, HtmlType:=xlHtmlStatic).Publish
Set txtStream = fso.OpenTextFile(fileName, ForReading, False)
readLines = False
html = ""
Do While Not txtStream.AtEndOfStream
line = txtStream.ReadLine
If InStr(line, "<!--START OF OUTPUT FROM EXCEL PUBLISH AS WEB PAGE WIZARD") > 0 Then
readLines = True
End If
If readLines Then
html = html & vbCrLf & line
End If
If readLines And InStr(line, "<!--END OF OUTPUT FROM EXCEL PUBLISH AS WEB PAGE WIZARD") > 0 Then
readLines = False
End If
Loop
txtStream.Close
Set txtStream = Nothing
If fso.FileExists(fileName) Then
fso.DeleteFile fileName
End If
Set fso = Nothing
GetHtml = html
End Function
You said you want to 'copy some cells from my excel sheet that I am currently using to be pasted inside the excel file'. I think you man copy from Excel and paste into the body of an Email, right.
Sub Mail_Selection_Range_Outlook_Body()
'For Tips see: http://www.rondebruin.nl/win/winmail/Outlook/tips.htm
'Don't forget to copy the function RangetoHTML in the module.
'Working in Excel 2000-2016
Dim rng As Range
Dim OutApp As Object
Dim OutMail As Object
Set rng = Nothing
On Error Resume Next
'Only the visible cells in the selection
Set rng = Selection.SpecialCells(xlCellTypeVisible)
'You can also use a fixed range if you want
'Set rng = Sheets("YourSheet").Range("D4:D12").SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "The selection is not a range or the sheet is protected" & _
vbNewLine & "please correct and try again.", vbOKOnly
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = "ron#debruin.nl"
.CC = ""
.BCC = ""
.Subject = "This is the Subject line"
.HTMLBody = RangetoHTML(rng)
.Send 'or use .Display
End With
On Error GoTo 0
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Function RangetoHTML(rng As Range)
' Changed by Ron de Bruin 28-Oct-2006
' Working in Office 2000-2016
Dim fso As Object
Dim ts As Object
Dim TempFile As String
Dim TempWB As Workbook
TempFile = Environ$("temp") & "\" & Format(Now, "dd-mm-yy h-mm-ss") & ".htm"
'Copy the range and create a new workbook to past the data in
rng.Copy
Set TempWB = Workbooks.Add(1)
With TempWB.Sheets(1)
.Cells(1).PasteSpecial Paste:=8
.Cells(1).PasteSpecial xlPasteValues, , False, False
.Cells(1).PasteSpecial xlPasteFormats, , False, False
.Cells(1).Select
Application.CutCopyMode = False
On Error Resume Next
.DrawingObjects.Visible = True
.DrawingObjects.Delete
On Error GoTo 0
End With
'Publish the sheet to a htm file
With TempWB.PublishObjects.Add( _
SourceType:=xlSourceRange, _
Filename:=TempFile, _
Sheet:=TempWB.Sheets(1).Name, _
Source:=TempWB.Sheets(1).UsedRange.Address, _
HtmlType:=xlHtmlStatic)
.Publish (True)
End With
'Read all data from the htm file into RangetoHTML
Set fso = CreateObject("Scripting.FileSystemObject")
Set ts = fso.GetFile(TempFile).OpenAsTextStream(1, -2)
RangetoHTML = ts.readall
ts.Close
RangetoHTML = Replace(RangetoHTML, "align=center x:publishsource=", _
"align=left x:publishsource=")
'Close TempWB
TempWB.Close savechanges:=False
'Delete the htm file we used in this function
Kill TempFile
Set ts = Nothing
Set fso = Nothing
Set TempWB = Nothing
End Function
https://www.rondebruin.nl/win/s1/outlook/bmail2.htm

Resources