Call Function To Send Email Without So Much Code In Excel - vba

I have an excel spreadsheet that select pre-defined cells and from this creates and email when a user presses a button. This worked fine when I had about 3 to 4 rows of data but now I have over 500 rows.
What I would like to do is instead of duplicating the code for each row is have one function that gets called on each time. I want the code to work out the row from a link at the end of the Row (which I also need to figure out how to link to the VBA, I know how to do it via a button but a link at the end of each row would be much better). The Link will say send email. If the user presses this link, then it will select the row the link is on and send the email. Hope that makes sense. I just wanted 1 function this could be called from. Instead of having to duplicate the code each time for each row.
Any good ways of doing this? Please see my code and spreadsheet below.
Sub SendEmail()
Dim objOutlook As Outlook.Application
Set objOutlook = New Outlook.Application
Dim objEmail As Outlook.MailItem
Set objEmail = objOutlook.CreateItem(olMailItem)
objEmail.Subject = Cells(2, 1).Text
objEmail.Body = "============" & vbNewLine & Cells(2, 3).Text & vbNewLine & "============" & vbNewLine & Cells(2, 6).Text
objEmail.To = Cells(2, 5).Text
objEmail.SentOnBehalfOfName = "test#test.com"
objEmail.Display
End Sub
I have also attached an example of my spreadsheet. Please note the full on spreadsheet has over 500 records. This is a much condensed version:
>> LINK to sample workbook

You can also try below:
Sub SendEmail(r As Range)
Dim objOutlook As Outlook.Application
Set objOutlook = New Outlook.Application
Dim objEmail As Outlook.MailItem
Set objEmail = objOutlook.CreateItem(olMailItem)
With objEmail
.Subject = r.Value2
.Body = "============" & vbNewLine & r.Offset(0, 2).Value2 & vbNewLine & _
"============" & vbNewLine & r.Offset(0, 5).Value2
.To = r.Offset(0, 4).Value2
.SentOnBehalfOfName = "test#test.com"
.Display
End With
End Sub
Then test it:
Sub Test()
Dim lr As Long, cel As Range
With Sheets("SheetName")
lr = .Range("A" & .Rows.Count).End(xlUp).Row
If lr = 1 Then Msgbox "No email to send": Exit Sub
For Each cel In .Range("A2:A" & lr)
SendEmail cel
Next
End With
End Sub
Edit: To send mail when hyperlink is pressed, you can use a worksheet event.
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
Application.EnableEvents = False
On Error GoTo halt
If Target.Name = "Send Mail" Then '<~~ Check which hyperlink is pressed
'*** This will call the SendEmail routine above and pass
'*** the range where the hyperlink is on
'*** Take note of the Offset(0, -5). I just based it on your screen shot
'*** where your subject is 5 cells from the cell with Send mail
'*** Adjust it to your actual target range
Application.Run SendEmail, Target.Range.Offset(0, -5)
'SendEmail Target.Range.Offset(0, -5)
End If
moveon:
Application.EnableEvents = True
Exit Sub
halt:
MsgBox Err.Description
Resume moveon
End Sub
I used Application.Run so that you don't have to worry whether your SendEmail sub routine is Public or not. If you decide to just make it Public in a Module, you can use the commented line.

Use the row from the selection. Select your row, then get the row from the selected range, and use it in your code for the cells(iRow, 1)
Sub SendEmail()
Dim ActSheet As Worksheet
Dim SelRange As Range
Dim iRow As Integer
Set ActSheet = ActiveSheet
Set SelRange = Selection
iRow = SelRange.Row
Dim objOutlook As Outlook.Application
Set objOutlook = New Outlook.Application
Dim objEmail As Outlook.MailItem
Set objEmail = objOutlook.CreateItem(olMailItem)
objEmail.Subject = Cells(iRow , 1).Text
objEmail.Body = "============" & vbNewLine & Cells(iRow , 3).Text & vbNewLine & "============" & vbNewLine & Cells(iRow , 6).Text
objEmail.To = Cells(iRow , 5).Text
objEmail.SentOnBehalfOfName = "test#test.com"
objEmail.Display
End Sub

Here how you get all the rows and run your sub on all the rows.
Sub sendEmailFromAllRows()
'Getting the last used row
With Sheets("YourSheetName")
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lastrow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Else
lastrow = 1
End If
End With
'Calling your sub to send the mail for each row
For i = 2 To lastrow
SendEmail (i)
Next i
End Sub
Sub SendEmail(iRow As Integer)
Dim objOutlook As Outlook.Application
Set objOutlook = New Outlook.Application
Dim objEmail As Outlook.MailItem
Set objEmail = objOutlook.CreateItem(olMailItem)
objEmail.Subject = Cells(iRow, 1).Text
objEmail.Body = "============" & vbNewLine & Cells(iRow, 3).Text & vbNewLine & "============" & vbNewLine & Cells(iRow, 6).Text
objEmail.To = Cells(iRow, 5).Text
objEmail.SentOnBehalfOfName = "test#test.com"
objEmail.Display
objEmail.Send
End Sub

Related

Pasting Multiple excel ranges as a picture in the same Outlook email

Here is the code I am using to copy over the ranges as well as open a new excel email. I can get both to copy and paste over just fine, but my issue is that when the second picture pastes, it replaces the first picture as opposed to being pasted above it like I need it to. What am I doing wrong?
Private Sub CommandButton4_Click()
'Finds last Row of email report
Dim lRow As Long
Dim lCol As Long
lRow = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).row
'Copy range of interest
Dim r As Range
Set r = Sheets("Email").Range(Cells(8, "E"), Cells(lRow, "N"))
r.Copy
'Open a new mail item
Dim outlookApp As Outlook.Application
Set outlookApp = CreateObject("Outlook.Application")
Dim outMail As Outlook.MailItem
Set outMail = outlookApp.CreateItem(olMailItem)
With outMail
.To = ""
.CC = ""
.BCC = ""
.Subject = shift_txtb2.Text & " " & "Finishing Report" & " " & Format(Now(), "MM/DD/YY")
.HTMLBody = ""
'Attachments.Add
.Display
End With
''Get its Word editor
outMail.Display
Dim wordDoc As Word.Document
Set wordDoc = outMail.GetInspector.WordEditor
''To paste as picture
wordDoc.Range.PasteAndFormat wdChartPicture
Set r = Sheets("Email").Range("P8:T17")
r.Copy
wordDoc.Range.PasteAndFormat wdChartPicture
Unload Me
Sheets(1).Activate
End Sub
Put this before pasting the second one:
wordDoc.Content.InsertParagraphAfter
You can also try:
wordDoc.Content.TypeParagraph

Generate email in Outlook using VBA from directory

I need to generate a series of emails that attach pdf files from specific folders. I am a novice but have some understanding of the code that I'm using. My problem is that I cannot control the number of emails being generated. I want to be able to generate the exact number of emails that there are entries in my directory (rows).
This is the code, any help would be greatly appreciated:
Sub create_email()
'On Error Resume Next
'Dim oMail As Outlook.MailItem`
'Dim num_clients, start_row As Integer`
Sheets("Control").Activate
start_row = Range("start_row").row
num_clients = Range("B100").End(xlUp).row - start_row
For i = 1 To num_clients
Set oMail = Outlook.Application.CreateItem(olMailItem)
'Subject line
oMail.Subject = Range("J9").Offset(i - 1, 0)
'Distribution list
Set RecipTo = oMail.Recipients.Add(Range("K9").Offset(i - 1, 0))
RecipTo.Type = olTo
Set RecipCC = oMail.Recipients.Add(Range("L9").Offset(i - 1, 0))
RecipCC.Type = olCC
oMail.SentOnBehalfOfName = "email#email.com.au"
oMail.Recipients.ResolveAll
'Attachments + message
oMail.Attachments.Add Range("E9").Offset(i - 1, 0) & "\" & Range("F9").Offset(i - 1, 0)
oMail.HTMLBody = "<html><p><font face=""Calibri""><font size=3>Dear Sir/ Madam,</p>" & _
"<html><p><font face=""Calibri"">Kind regards,</p>"
'Displays email pre-send
oMail.Display
Sheets("Control").Activate
Set oMail = Nothing
Next i
End Sub
Is this what you are trying? (Untested)
Sub create_email()
Dim OutApp As Object, oMail As Object
Dim wb As Workbook, ws As Worksheet
Dim i As Long, start_Rows As Long, Last_Row As Long
Set wb = ThisWorkbook
Set ws = wb.Sheets("Control")
With ws
start_Row = .Range("start_row").Row '<~~ Start Row
Last_Row = .Range("B" & .Rows.Count).End(xlUp).Row '<~~ End Row
Set OutApp = CreateObject("Outlook.Application")
For i = start_Row To Last_Row '<~~ Loop from start row to end row
Set oMail = OutApp.CreateItem(0)
With oMail
.Subject = ws.Range("I" & i).Value
.To = ws.Range("J" & i).Value
.Cc = ws.Range("K" & i).Value
.SentOnBehalfOfName = "email#email.com.au"
.Attachments.Add ws.Range("D" & i).Value & "\" & ws.Range("E" & i).Value
.HTMLBody = "<html><p><font face=""Calibri""><font size=3>Dear Sir/ Madam,</p>" & _
"<html><p><font face=""Calibri"">Kind regards,</p>"
.Display
End With
Next i
End With
End Sub

Excel email macro seems to be triggered whenever a cell is populated

I have a excel spreadsheet which tracks important dates, if you have missed an invoice for that date it will automatically send out an email to the relevant person reminding them to do so. The macro functions well and works off a trigger
`
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lr As Long, lr2 As Long, r As Long
lr = Sheets("2015").Cells(Rows.Count, "A").End(xlUp).Row
For r = lr To 2 Step -1
If Range("Q" & r).Value = "Received" Then
Call Macro1
End If
If Range("K" & r).Value = "Overdue" Then
Call email
End If
Next r
End Sub
`
Sub email()
Dim OutApp As Object
Dim OutMail As Object
Dim FName As String
Sheets("2015").Activate
For i = 1 To 100
If Sheets("2015").Range("K" & i).Value = "Overdue" Then
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
FName = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
With OutMail
.To = Sheets("2015").Range("S" & i).Value
.CC = ""
.Subject = Sheets("2015").Range("B" & i).Value
.Body = "You have missed a valuation date for this project!"
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
End If
Next i
End Sub
'
As you can see I am new to this so most of my code is from different examples on the internet. The problem I have is that when a cell in column K meets the requirements "Overdue" the macro will repeatedly email the user when a change in any other cell occurs. That is if K5 = "Overdue" and I go to A6 to enter a new user, the user in A5 will be emailed again. This happens for all cells until I change cell k5 to "sent".
I suspect part of the problem may also be in my e-mail macro...
I give up XD
Your macro's are working fine, I mean at least as you code them. You need to remove your macro's form FOR loops. What you can do is just getting the address of the changed cell and use it to define the mail address and subject. Something like below
** Worksheet macros**
Public mailAddress As String
Public TargetRow As Long
Public mailSubject
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Value = "Overdue" Then
mailAddress = Sheets("2015").Range("S" & Target.Row)
mailSubject = Sheets("2015").Range("B" & Target.Row)
TargetRow = Target.Row
Call email
ElseIf Target.Value = "Received" Then
Call Macro1
End If
End Sub
And your macro should like something like this:
Sub email()
Dim OutApp As Object
Dim OutMail As Object
Dim FName As String
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
FName = ActiveWorkbook.Path & "\" & ActiveWorkbook.Name
With OutMail
.To = mailAddress
.CC = ""
.Subject = mailSubject
.Body = "You have missed a valuation date for this project!"
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
Can you try and post answer here? I dont have excel right now, I could not try it.

Sending multiple emails from Excel

I have a workbook that has 7 worksheets. I have the below vba to send an email once a value is met on a particular sheet.
Each sheet has a different value and a different attachment to be sent. How do I add a code for each sheet so the email is sent?
Thanks in advance
Set as General (Declarations)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
If Not Application.Intersect(Range("M4:M368"), Target) Is Nothing Then
If IsNumeric(Target.Value) And Target.Value < 3500 Then
Call Fuel_LevelW03
End If
End If
End Sub
followed by a module
General Fuel_LevelW03
Sub Fuel_LevelW03()
Dim OutApp As Object
Dim OutMail As Object
Dim strbody As String
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
strbody = "Hi" & vbNewLine & vbNewLine & _
"Please order fuel as attached." & vbNewLine & _
"" & vbNewLine & _
"Kind Regards" & vbNewLine & _
""
On Error Resume Next
With OutMail
.To = "email address"
.CC = "email address"
.BCC = ""
.Subject = "Fuel Order W03"
.Body = strbody
.Attachments.Add ("H:\Fuel Order Sheets\Glen Eden W03 Pump Station.xlsx")
.Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
From what I understand, you try to "tell the method" a bit about what the Target.Value was. Just pass the parameter to the function like this :
If IsNumeric(Target.Value) Then
If Target.Value < 3500 Then
Call Fuel_LevelW03( Sh.Name, Target.Value )
End If
End If
and change the function's name with this one :
Fuel_LevelW03( sheetName as String, targetValue as String )
'Change String to appropriate type
EDIT2 : I changed the code around a bit, if you need any help let me know.
EDIT : Ok, here's how you solve this. Inside the "ThisWorkbook" code object (underneath the sheet code objects, on the left side of the code editor), paste this :
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
If Not Application.Intersect(Range("M4:M368"), Target) Is Nothing Then
If IsNumeric(Target.Value) And Target.Value < 3500 Then
Call Fuel_LevelW03( Sh.Name )
End If
End If
End Sub
Sub Fuel_LevelW03( sheetName as String )
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
If sheetName = "Sheet1" Then 'Replace Sheet1 with the name of your worksheet
strbody = "Hi" & vbNewLine & vbNewLine & _
"Please order fuel as attached." & vbNewLine & _
"" & vbNewLine & _
"Kind Regards" & vbNewLine & _
"STRING BODY1"
With OutMail
.To = "email address"
.CC = "email address"
.BCC = ""
.Subject = "Fuel Order W03"
.Body = strbody
.Attachments.Add ("H:\Fuel Order Sheets\Glen Eden W03 Pump Station.xlsx")
.Send
End With
On Error GoTo 0
ElseIf sheetName = "Sheet2" Then 'Replace Sheet2 with the name of the next sheet and
'Put the same content as the first IF statement, but adapted to "Sheet2"
ElseIf sheetName = "Sheet3" Then 'Replace Sheet3 with the name of the next sheet and
'Put the same content as the first IF statement, but adapted to "Sheet3"
ElseIf sheetName = "Sheet4" Then 'Replace Sheet4 with the name of the next sheet and
'Put the same content as the first IF statement, but adapted to "Sheet4"
'ElseIf ............. (So on, so forth)
End If
Set OutMail = Nothing
Set OutApp = Nothing
End Sub
You can add as many ElseIf's as you want (one for each sheet)
Am pretty sure this is what you need, although am not sure.
If ActiveSheet.Name = "Sheet1" Then
'Do something specific to "Sheet1"
ElseIf ActiveSheet.Name = "Sheet2" Then
'Do something specific to "Sheet2"
'And so on so forth...
End If
You have a button to that macro in each sheet, and depending on the sheet calling the macro, you want a different e-mail to be sent, right? Then this will do it. You can add as many ElseIf's as you want.

Scan Excel column for specific word in VBA

Currently im working on a database Excel spreadsheet and im currently using VBA to implement some automatic functions to the system. Im new to VBA so i need your help :)
My question is this: I have a statues column in which the user needs to select from a drop list 'Complete' or 'In progress'. I need a program which can scan a specific column (example S3) for the word 'Complete'. Once the word is detected, the system will automatically send an email to a specific user tell him that the task has been complete.
Can anyone help me?
Thanks! :)
Update: I have coded the following to search for the word complete and send an email to the user (this is a rough idea)
Sub For_Loop_With_Step()
Dim lCount As Long, lNum As Long
Dim MyCount As Long
MyCount = Application.CountA(Range("S:S"))
For lCount = 1 To MyCount - 1 Step 1
If Cells(lCount + 2, 19) = "Complete" Then
Call Send_Email_Using_VBA
Else
MsgBox "Nothing found"
End If
Next lCount
MsgBox "The For loop made " & lNum & " loop(s). lNum is equal to " & lNum
End Sub
.
Sub Send_Email_Using_VBA()
Dim Email_Subject, Email_Send_From, Email_Send_To, _
Email_Cc, Email_Bcc, Email_Body As String
Dim Mail_Object, Mail_Single As Variant
Email_Subject = "Testing Results"
Email_Send_From = "fromperson#example.com"
Email_Send_To = "toperson#example.com"
'Email_Cc = "someone#example.com"
'Email_Bcc = "someoneelse#example.com"
Email_Body = "Congratulations!!!! You have successfully sent an e-mail using VBA !!!!"
On Error GoTo debugs
Set Mail_Object = CreateObject("Outlook.Application")
Set Mail_Single = Mail_Object.CreateItem(0)
With Mail_Single
.Subject = Email_Subject
.To = Email_Send_To
.cc = Email_Cc
.BCC = Email_Bcc
.Body = Email_Body
.send
End With
debugs:
If Err.Description <> "" Then MsgBox Err.Description
End Sub
Try this (Tried And Tested)
Screenshot:
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim i As Long, lRow As Long
Dim ExitLoop As Boolean
Dim aCell As Range, bCell As Range
'~~> Set this to the relevant sheet
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
'~~> Find the word in the relevant column. 19 is S Column
Set aCell = .Columns(19).Find(What:="Complete", LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
'~~> Update Col T appropriately
'~~> This is required so that mail doesn't go for the same row again
'~~> When you run the macro again
Set bCell = aCell
If Not .Range("T" & aCell.Row).Value = "Mail Sent" Then
If SendEmail = True Then
.Range("T" & aCell.Row).Value = "Mail Sent"
Else
.Range("T" & aCell.Row).Value = "Error: Mail Not Sent"
End If
End If
Do While ExitLoop = False
Set aCell = .Columns(19).FindNext(After:=aCell)
If Not aCell Is Nothing Then
If aCell.Address = bCell.Address Then Exit Do
If Not .Range("T" & aCell.Row).Value = "Mail Sent" Then
If SendEmail = True Then
.Range("T" & aCell.Row).Value = "Mail Sent"
Else
.Range("T" & aCell.Row).Value = "Error: Mail Not Sent"
End If
End If
Else
ExitLoop = True
End If
Loop
End If
End With
End Sub
Function SendEmail() As Boolean
Dim OutApp As Object, OutMail As Object
On Error GoTo Whoa
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = "toperson#example.com"
.Subject = "Testing Results"
.Body = "Your Message Goes Here"
.Display
End With
DoEvents
SendEmail = True
LetsContinue:
On Error Resume Next
Set OutMail = Nothing
Set OutApp = Nothing
On Error GoTo 0
Exit Function
Whoa:
SendEmail = False
Resume LetsContinue
End Function