Can't figure out Object required error - vba

The purpose of the code is to send an email to a list of addresses. To determine the start point, a user form will appear asking for the stating Column and Row (with plans that as I improve, I'll add other options in).
I had the code working, however, I made some tweaks and not keep getting an Object required error which I've tried figuring out for hours without luck. Please can you take a look at my code and suggest where I might be going wrong?
NB. I also tried declaring all variables but it didn't fix the issue.
The error is on the Loop Until username.Value = ""
Sub cmdGo_Click()
Application.DisplayAlerts = False
i = cmbRow
If i = "" Then
Exit Sub
End If
username = cmbColumn
If username = "" Then
Exit Sub
End If
Select Case username
Case "A", "a"
username = Cells(i, "a").Value
Case "B", "b"
username = Cells(i, "b").Value
Case "C", "c"
username = Cells(i, "c").Value
Case "D", "d"
username = Cells(i, "d").Value
Case "E", "e"
username = Cells(i, "e").Value
End Select
Do
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
On Error Resume Next
With OutMail
.To = username
.CC = ""
.BCC = ""
.Importance = 1
.Subject = "Hello"
.HTMLBody = "Message"
'display shows each email before sending
.Display
'send sends email automatically
' .Send
End With
On Error GoTo 0
Set OutMail = Nothing
Set OutApp = Nothing
Application.DisplayAlerts = True
i = i + 1
Loop Until username.Value = ""
End Sub

There are few syntax and understanding mistakes in the code. I have listed them below and refactored your code a bit to help to get it work as you desire.
It's always best to use Option Explicit at the top of each module and explicitly declare variables with the desired type
Place the setting of the username inside the loop so it resets each time with a new email based on the i = i+1 counter.
Rest of comments are in code:
Option Explicit
Sub cmdGo_Click()
Application.DisplayAlerts = False
If cmbRow = "" or cmbColumn = "" Then
Exit Sub
End If
Dim i As Long
i = cmbRow
Dim UserNameCol As String 'created a new variable just to get column letter so can be used later in the loop and removed the `Select Case` block.
UserNameCol = cmbColumn
'set outlook outside loop since you only need to call it once, doing it in loop creates unneccesary processing
Dim OutApp As Object
Dim OutMail As Object
Set OutApp = CreateObject("Outlook.Application")
Do Until Len(Cells(i, UserNameCol).Value) = 0 'will stop when blank cell appears
Dim UserName As String
UserName = Cells(i, UserNameCol).Value 'always will user whatever column choosen
Set OutMail = OutApp.CreateItem(0) 'this goes here because a new email is needed each time
On Error Resume Next
With OutMail
.To = UserName
'.CC = "" 'you can remove this lines because you are not putting anything in the field
'.BCC = "" 'you can remove this lines because you are not putting anything in the field
.Importance = 1
.Subject = "Hello"
.HTMLBody = "Message"
'display shows each email before sending
.Display
'send sends email automatically
'.Send
End With
On Error GoTo 0
i = i + 1
Loop
'destroy outlook when finished processing all mails
Set OutMail = Nothing
Set OutApp = Nothing
Application.DisplayAlerts = True
End Sub

Related

VBA Send email when cell changes using Worksheet_Calculate

So my code loops through a range of cells and triggers an email on the condition that, in this example, N150 = F150. This works, the email is sent. But what I'm finding difficult is referencing the changed cell in the email body. You can see within the xMailBody variable I have tried cll.Offset(0, -12) so when N150 = F150 I am given the cell value 12 columns to the left which should be B150. Instead I am getting the value of B145 which is correct in that it's the right column but obviously the incorrect row. My target range is N145:N160 so I think it's only referecing the first row in my range. Any help would be greatly appreciated been trying to figure this out for days!
Dim target As Range
Dim cll As Range
Private Sub Worksheet_Calculate()
Set target = Range("N145:N160")
For Each cll In target
If (Range("N150") = Range("F150"))
Call Mail_small_Text_Outlook(target)
Exit For
End If
Next
End Sub
Sub Mail_small_Text_Outlook()
Dim xOutApp As Object
Dim xOutMail As Object
Dim xMailBody As String
Set xOutApp = CreateObject("Outlook.Application")
Set xOutMail = xOutApp.CreateItem(0)
xMailBody = "Hi there" & vbNewLine & vbNewLine & _
cll.Offset(0, -12) & " has reached its target"
On Error Resume Next
With xOutMail
.To = "email"
.CC = ""
.BCC = ""
.Subject = "Target Reached"
.Body = xMailBody
.Send 'or use .Display
End With
On Error GoTo 0
Set xOutMail = Nothing
Set xOutApp = Nothing
End Sub
You are looping from N145:N160 but only checking if Range("N150") = Range("F150"). If that check is true it will be true on the first iteration when cll is N145 so the email is sent and the loop exits so no other cll is processed.
...
Set target = Range("N145:N160")
For Each cll In target
If cll = cll.offset(0, -12) then
'cll is public, no need to pass it or target across
Mail_small_Text_Outlook
Exit For
End If
Next
...
Instead of using a global variable, pass the value you want in the email as a parameter for your Mail_small_Text_Outlook function.
Dim target As Range
Private Sub Worksheet_Calculate()
Dim FoundCell as String
Set target = Range("N145:N160")
For Each cll In target
If (Range("N150") = Range("F150"))
FoundCell = Cstr(cll.Offset(0, -12).Value2)
Call Mail_small_Text_Outlook(FoundCell)
Exit For
End If
Next
End Sub
Sub Mail_small_Text_Outlook(FoundCell as String)
Dim xOutApp As Object
Dim xOutMail As Object
Dim xMailBody As String
Set xOutApp = CreateObject("Outlook.Application")
Set xOutMail = xOutApp.CreateItem(0)
xMailBody = "Hi there" & vbNewLine & vbNewLine & _
FoundCell & " has reached its target"
On Error Resume Next
With xOutMail
.To = "email"
.CC = ""
.BCC = ""
.Subject = "Target Reached"
.Body = xMailBody
.Send 'or use .Display
End With
On Error GoTo 0
Set xOutMail = Nothing
Set xOutApp = Nothing
End Sub
Now you can watch the value of FoundCell prior to passing it into the function, making your debugging process far easier.

Body missing from first email in list sent using VBA

I'm working on a way to send emails to a list of recipients. All emails should contain the same body, but with unique attachments. The code I'm using successfully retrieves the addresses from column N, and inserts attachments based on paths in corresponding rows in columns O:AZ.
The issue I'm encountering is that the first email created using the code has no body. The recipient and attachments are correct, but the email itself is empty. All other emails created show the body correctly. I have very little experience with VBA, and cannot find what's causing the issue.
Any help regarding the code and possible issues would be appreciated! Please let me know if you need more details regarding the code or data.
Sub create_emails()
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 strobody As String
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
Set sh = Sheets("Sheet2")
Set OutApp = CreateObject("Outlook.Application")
For Each cell In sh.Columns("N").Cells.SpecialCells(xlCellTypeConstants) 'email addresses located in Sheet2, column N
Set rng = sh.Cells(cell.Row, 1).Range("O1:AZ1") 'File paths stored in corresponding rows, columns 0:AZ
If cell.Value Like "?*#?*.?*" And _
Application.WorksheetFunction.CountA(rng) > 0 Then
Set OutMail = OutApp.CreateItem(0)
With OutMail
.SentOnBehalfOfName = "xxx#xxx.xxx"
.To = cell.Value
.Subject = "test subject"
.Body = strbody
strbody = "Test text"
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
.Display 'Or use .Display / .Send
End With
Set OutMail = Nothing
End If
Next cell
Set OutApp = Nothing
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
You're setting strbody after you're using it, so the first time it's used it's empty.
Change:
With OutMail
.SentOnBehalfOfName = "xxx#xxx.xxx"
.To = cell.Value
.Subject = "test subject"
.Body = strbody
strbody = "Test text"
To:
With OutMail
.SentOnBehalfOfName = "xxx#xxx.xxx"
.To = cell.Value
.Subject = "test subject"
strbody = "Test text"
.Body = strbody
And also, if you had Option Explicit set, you'd notice that your declaration for strbody is mistyped to strobody.

Email Multiple Recipients VBA Error

Looking for help with sending emails to a list of people. My code has a simple loop and grabs the value each time through of where to send the email. While testing, the first email will always get sent. After that, the 2nd time through I get error on ".To"
Run-time error - '-2147221238 (8004010a):
The item has been moved or deleted.
This is puzzling to me because the code does accurately grab the next email value?
The emails need to be sent one by one, instead of adding the recipients to a list of bcc. Is this possible with VBA? Thanks in advance!
Sub TestingAgain()
'Setting up the Excel variables.
Dim outApp As Object
Dim outMailItem As Object
Dim iCounter As Integer
Dim sDest As String
Dim sName As String
'Create the Outlook application and the empty email.
Set outApp = CreateObject("Outlook.Application")
Set outMailItem = outApp.CreateItem(0)
With outMailItem
sDest = ""
For i = 2 To WorksheetFunction.CountA(Columns(1))
If i <> "" Then
'Grab first name and email
sDest = Cells(i, 5).Value
sName = Cells(i, 1).Value
'Send each email
.To = sDest
.Subject = "FYI"
.htmlbody = "Some stuff"
.Send
Else
MsgBox ("Error")
End If
Next i
End With
'Clean up the Outlook application.
Set outMailItem = Nothing
Set outApp = Nothing
End Sub
When you send the e-mail, the mailItem instance is done and not available anymore. Refactor your code like :
Sub TestingAgain()
'Setting up the Excel variables.
Dim outApp As Object
Dim outMailItem As Object
Dim iCounter As Integer
Dim sDest As String
Dim sName As String
'Create the Outlook application and the empty email.
Set outApp = CreateObject("Outlook.Application")
sDest = ""
For i = 2 To WorksheetFunction.CountA(Columns(1))
If i <> "" Then
'/ Create the mail item instance.
Set outMailItem = outApp.CreateItem(0)
With outMailItem
'Grab first name and email
sDest = Cells(i, 5).Value
sName = Cells(i, 1).Value
'Send each email
.To = sDest
.Subject = "FYI"
.htmlbody = "Some stuff"
.send
'/ Once sent, mail item is no more available.
End With
Else
MsgBox ("Error")
End If
Next
'Clean up the Outlook application.
Set outMailItem = Nothing
Set outApp = Nothing
End Sub

Creating and assigning tasks in Outlook from Excel

I am attempting to create a service desk tool in Excel, and the way I am thinking of making it easy for the admin to assign them would be to use the tasks in Outlook.
The admin person would receive the email, fill out a line in Excel, then click on a button to open a new task in Outlook, with fields pre-filled out. Then they would drag and drop the email as an attachment.
However, I am trying to get the assigned to email address pre-filled from a value in the spreadsheet. Assign doesn't seem to work, and neither does Recipient. Does anyone have any ideas why not? This is the code that I have so far:
Dim OutApp As Outlook.Application
Dim OutTask As Outlook.TaskItem
Set OutApp = CreateObject("Outlook.Application")
Set OutTask = OutApp.CreateItem(olTaskItem)
With OutTask
.Assign = Cells(2, "G")
.Subject = "Service Request" & Cells(2, "A")
.StartDate = Cells(2, "H")
.DueDate = Cells(2, "H") + Cells(2, "I")
.ReminderTime = .DueDate - 1
.Body = "Please see the attached email for a service request assigned to you."
.Display
End With
Set OutTsk = Nothing
Set OutApp = Nothing
End Sub
Any help would be greatly appreciated!
Have a look at the sample here https://msdn.microsoft.com/en-us/library/office/ff869880(v=office.15).aspx
provided by microsoft
Assign is a method and you are using it like a property.
Receipients are a collection, so you have to add them, again you cant assign them like a property
Also you have misspelt OutTsk at the end. What I would do as a matter of course when programming is turn on Option Explicit this stops misspelt variables from ruining your day.
So try something like this
Sub tasks()
Dim OutApp As Outlook.Application
Dim OutTask As Outlook.TaskItem
Set OutApp = CreateObject("Outlook.Application")
Set OutTask = OutApp.CreateItem(olTaskItem)
Dim myRecipient As Outlook.Recipient
Set myRecipient = OutTask.Recipients.Add(Cells(2, "C"))
myRecipient.Resolve
If myRecipient.Resolved Then
With OutTask
.Subject = "Service Request" & Cells(2, "A")
.StartDate = Cells(2, "H")
.DueDate = Cells(2, "H") + Cells(2, "I")
.ReminderTime = .DueDate - 1
.Body = "Please see the attached email for a service request assigned to you."
.Assign
.Display
End With
End If
Set OutTask = Nothing
Set OutApp = Nothing
End Sub

Can't send multiple Outlook Messages

I can send a single Outlook message using Excel VBA. However, I want to loop through my rows and send an email for each row that meets a certain condition.
Unfortunately, when I put the email code in a for loop only one email gets sent or none at all (depending on how I structure the code).
Is there something about calling Outlook multiple times that I should know?
Private Sub CommandButton1_Click()
Dim OutApp As Object
Dim OutMail As Object
Dim myValue As Variant
Dim contactRange As Range
Dim cell As Range
Dim toAddy As String, nextAddy As String
Dim i As Integer
Set contactRange = Me.Range("ContactYesNo")
myValue = InputBox("Enter body of email message.")
For Each cell In contactRange
If Range(Cells(cell.Row, cell.Column).Address).Value = "Yes" Then
nextAddy = Range(Cells(cell.Row, cell.Column).Address).Offset(0, 5).Value
toAddy = nextAddy & ", " & toAddy
End If
Next cell
If Len(toAddy) > 0 Then
toAddy = Left(toAddy, Len(toAddy) - 2)
End If
For i = 0 To 1 'short loop for testing purposes
Set OutApp = CreateObject("Outlook.Application")
Set OutMail = OutApp.CreateItem(0)
With OutMail
.To = toAddy
.CC = ""
.BCC = ""
.Subject = "test email"
.Body = myValue
.Send
End With
Set OutMail = Nothing
Set OutApp = Nothing
Next i
End Sub
Take the CreateObject line out of the loop:
Set OutApp = CreateObject("Outlook.Application")
For i = 0 To 1 'short loop for testing purposes
Set OutMail = OutApp.CreateItem(0)
...
I've tried to clean up your logic stream but there are many unanswered questions due to the lack of sample data, explicit error messages and output.
Private Sub CommandButton1_Click()
Dim outApp As Object
Dim outMail As Object
Dim myValue As Variant
Dim contactRange As Range
Dim cell As Range
Dim toAddy As String, nextAddy As String
Dim i As Integer
Set outApp = CreateObject("Outlook.Application")
Set contactRange = Me.Range("ContactYesNo")
myValue = InputBox("Enter body of email message.")
With Worksheets(contactRange.Parent.Name) '<~~ surely you know what worksheet you are on..!?!
For Each cell In contactRange
If cell.Value = "Yes" Then 'no need to define a range by the range's address
nextAddy = cell.Offset(0, 5).Value 'again, no need to define a range by the range's address
toAddy = nextAddy & ";" & toAddy 'use a semi-colon to concatenate email addresses
End If
Next cell
End With
If Len(toAddy) > 0 Then
toAddy = Left(toAddy, Len(toAddy) - 2) 'I have no idea why you need to shorten the toAddy by 2
'only send mail where one or more addresses exist
For i = 0 To 1 'short loop for testing purposes
Set outMail = outApp.CreateItem(0)
With outMail
.To = toAddy
.CC = ""
.BCC = ""
.Subject = "test email"
.Body = myValue
.Send
End With
Set outMail = Nothing
Next i
End If
Set outApp = Nothing
End Sub
OK, so I re-wrote the code based on the feedback. I used a loop to send emails one at a time instead of concatenating the addresses together as I wanted to personalize each email. I also needed to create a form to handle the input as inputbox only accepts 256 characters.
A form was pretty much required as I needed to capture the subject line, message body, salutation, path the to the attachment etc.:
Private Sub CommandButton1_Click()
Dim subject As String, msg As String, path As String
subject = TextBox1.Value
msg = TextBox2.Value & vbCrLf & vbCrLf & "Sincerely," & vbCrLf & TextBox4.Value & vbCrLf & TextBox5
path = TextBox3.Value
UserForm1.Hide
Module1.sendEmail subject, msg, path
End Sub
I placed the email code in Module1. Note, be sure to set the .sentOnBehalfOfName attribute or Outlook will simply pick an account which may not be the one you want if you have multiple accounts registered:
Public Sub sendEmail(subject As String, msg As String, path As String)
Dim outApp As Object
Dim outMail As Object
Dim contactRange As Range, cell As Range
Dim toAddy As String, emailMsg As String
Dim count As Integer
Set outApp = CreateObject("Outlook.Application")
Set contactRange = Range("ContactYesNo")
With Worksheets("IT consulting")
For Each cell In contactRange
If cell.Value = "Yes" Then
count = count + 1
toAddy = cell.Offset(0, 6).Value
emailMsg = "Dear " & cell.Offset(0, 2).Value & "," & vbCrLf & vbCrLf & msg
Set outMail = outApp.CreateItem(0)
With outMail
.SentOnBehalfOfName = "me#someemail.com"
.To = toAddy
.CC = ""
.BCC = ""
.subject = subject
.Body = emailMsg
.Attachments.Add path
'.Display
.Send
End With
'log the action
cell.Offset(0, 1).Value = Now & vbCrLf & cell.Offset(0, 1).Value
End If
Set outMail = Nothing
Next cell
End With
Set outApp = Nothing
MsgBox "total emails sent: " & count
End Sub