Excel Macro to Save Outlook 2010 attachment, oldest email to newest email - excel

Need to save excel attachments in Outlook emails from oldest email to newest email and mark email as read. The newer attachments will overwrite the older if there is more than one unread email.
I receive an number of emails daily that need to be saved to run a report. However, if one report is missed, it is ignored and I go to the next dataset. The following works but does not always save the oldest first...it jumps around.
I have tried a number of options to save oldest first, with no luck. Any help on how I could make this consistently take the oldest email first. Thanks
Sub Save_Attachments()
Dim olApp As Outlook.Application, olNameSpace As Outlook.Namespace
Dim olFolder As Outlook.MAPIFolder, olMail As Outlook.MailItem
Dim olAttachment As Outlook.Attachment, lngAttachmentCounter As Long
Dim i As String
On Error GoTo Oooops
Set olApp = New Outlook.Application
Set olNameSpace = olApp.GetNamespace("MAPI")
Set olFolder = olNameSpace.GetDefaultFolder(olFolderInbox).Folders("sub_folder")
If olFolder Is Nothing Then Exit Sub
For Each olMail In olFolder.Items
If olMail.UnRead = True Then
For Each olAttachment In olMail.Attachments
lngAttachmentCounter = lngAttachmentCounter + 1
olAttachment.SaveAsFile ThisWorkbook.Path & "\zzzzz.xls"
Next olAttachment
End If
If olMail.UnRead Then
olMail.UnRead = False
End If
Next olMail
Exit Sub
Oooops:
MsgBox Err.Description, vbExclamation, "An error occurred"
End Sub

Since you did not state the options you tried maybe you did not try
For j = olFolder.Items.count To 1 Step -1
Something like this.
Option Explicit
Sub Save_Attachments_ReverseOrder()
Dim olApp As Outlook.Application, olNameSpace As Outlook.Namespace
Dim olFolder As Outlook.MAPIFolder
Dim olMail As Object ' <-- olMail is not necessarily a mailitem
Dim olAttachment As Outlook.attachment, lngAttachmentCounter As Long
Dim j As Long
On Error GoTo Oooops
Set olApp = New Outlook.Application
Set olNameSpace = olApp.GetNamespace("MAPI")
Set olFolder = olNameSpace.GetDefaultFolder(olFolderInbox).Folders("sub_folder")
If olFolder Is Nothing Then Exit Sub
For j = olFolder.Items.count To 1 Step -1
Set olMail = olFolder.Items(j)
If TypeOf olMail Is mailitem Then
If olMail.UnRead = True Then
Debug.Print olMail.subject & " - " & olMail.ReceivedTime
'For Each olAttachment In olMail.Attachments
' lngAttachmentCounter = lngAttachmentCounter + 1
' olAttachment.SaveAsFile ThisWorkbook.Path & "\zzzzz.xls"
'Next olAttachment
olMail.UnRead = False
Else
Debug.Print vbCr & olMail.subject & " - " & olMail.ReceivedTime & " was previously read"
End If
Else
Debug.Print vbCr & "Current item is not a mailitem."
End If
Next j
Exit Sub
Oooops:
MsgBox Err.Description, vbExclamation, "An error occurred"
End Sub

Related

VBA: My Email .body doesn't concatenate with itself: application-defined or object-defined error

I have a script that searches a group inbox subfolder and replies to the first email with a matching subject. It then replies to all. When I populate the email I cannot add my text to the rest of the email. Only either or.
I've seen many responses to similar problems that show .HTMLBody = "test" & .HTMLBody as a solution but when the debug reaches this line, the second .HTMLBody is shown as 'application-defined or object-defined error'.
Any insight into whats causing the problem or where else I can get the info from previous emails in the chain to input it that way would be greatly appreciated.
Thanks,
Sub Find_Email()
Dim olMail As Outlook.MailItem
Dim olReply As Outlook.MailItem
Dim olItems As Outlook.Items
Dim olNS As Namespace
Dim olMailbox As Folder
Dim olFolder As Folder
Dim subFolder As Folder
Dim BodyText As String
Set olNS = GetNamespace("MAPI")
Set olMailbox = olNS.Folders("Group_Inbox")
Set olFolder = olMailbox.Folders("test_Folder")
Set subFolder = olFolder.Folders("test_subFolder")
Set olItems = subFolder.Items
TheDate = Format(Date, "DD-MM-YYYY")
TheDate1 = Format(Date, "YYYY-MM")
TheDate2 = Format(Date, "YYYYMMDD")
TheDate3 = Format(Date, "YYYY")
'Find most recent email and populate
olItems.Sort "ReceivedTime", True
For i = 1 To olItems.Count
Set olMail = olItems(i)
If InStr(olMail.Subject, "Desired_Subject " & TheDate) > 0 Then
Set olReply = olMail.ReplyAll
With olReply
.Display
.To = "Recipients#gmail.com"
.CC = ""
.Subject = "Test_Subject"
'.Attachments.Add "Document_destination"
.BodyFormat = olFormatHTML
.HTMLBody = "This is a test email sending in Excel" & .HTMLBody ' This is the problem line.
Exit Sub
End With
End If
Next i
End Sub
'I understand that it might be the fact it is in a group inbox, which means that it could work for you but 'still may not work for me.
'Thanks again,
Try this (i can't test it, just a thought )
'Somewehere declare this string variable
Dim incomingHTMLBody as string
olItems.Sort "ReceivedTime", True
For i = 1 To olItems.Count
Set olMail = olItems(i)
If InStr(olMail.Subject, "Desired_Subject " & TheDate) > 0 Then
incomingHTMLBody = olMail.HTMLBody
Set olReply = olMail.ReplyAll
With olReply
.Display
.To = "Recipients#gmail.com"
.CC = ""
.Subject = "Test_Subject"
'.Attachments.Add "Document_destination"
.BodyFormat = olFormatHTML
.HTMLBody = "This is a test email sending in Excel" & incomingHTMLBody
Exit Sub
End With
End If
Next i
End Sub
You may need a bit more care referencing Outlook objects in your environment.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
' If desperate declare as Variant
Sub Find_Email()
Dim objApp As Outlook.Application
Set objApp = CreateObject("outlook.application")
Dim objNS As Namespace
Set objNS = objApp.GetNamespace("MAPI")
Dim objMailbox As Outlook.Folder
Set objMailbox = objNS.Folders("Group_Inbox")
Dim objFolder As Outlook.Folder
Set objFolder = objMailbox.Folders("test_Folder")
Dim subFolder As Outlook.Folder
Set subFolder = objFolder.Folders("test_subFolder")
Dim objItems As Outlook.Items
Set objItems = subFolder.Items
Dim TheDate As Date
TheDate = Format(Date, "DD-MM-YYYY")
'Find most recent email and populate
objItems.Sort "ReceivedTime", True
Dim i As Long
Dim objMail As Outlook.MailItem ' olMail is not a good variable name
Dim objReply As Outlook.MailItem
Debug.Print objItems.Count
For i = 1 To objItems.Count
Debug.Print objItems(i).Subject
If objItems(i).Class = olMail Then ' verify item is a mailitem
Set objMail = objItems(i)
If InStr(objMail.Subject, "Desired_Subject " & TheDate) > 0 Then
Set objReply = objMail.ReplyAll
With objReply
.Display
.To = "Recipients#gmail.com"
.CC = ""
.Subject = "Test_Subject"
'.Attachments.Add "Document_destination"
.BodyFormat = olFormatHTML
Debug.Print .htmlbody ' verify property is available
.htmlbody = "This is a test email sending in Excel" & .htmlbody ' This is the problem line.
Exit For
End With
End If
End If
Next i
End Sub

VBA to search and reply in Outlook with specific criteria

I am Using windows 10, Excel 2013 and Outlook 2013
I am new to Macro. I need macro to perform below Task:
1) From Excel I want to open Outlook if Outlook is closed and move Point.2, If outlook is already open then move to Point.2
2) Search for a specific email in outlook in all folders and sub folders with criteria “A” and “B”
a) Latest dated received or sent email.
b) With specific Subject contains “Approved”, this to be taken from active cell.
3) Open the found latest mail as per above criteria do “Reply all”.
4) Write a comment and display the mail or send.
Below code was my start, but it has the following issues:
The code search for the exact name, while i need to search for any email contain the word which in active cell.
The code search only in sent emails, while i need to search in both inbox and sent.
The code just open the email, i need to write template comment as well.
Many thanks in advance.
Sub ReplyMail_No_Movements()
' Outlook's constant
Const olFolderSentMail = 5
' Variables
Dim OutlookApp As Object
Dim IsOutlookCreated As Boolean
Dim sFilter As String, sSubject As String
' Get/create outlook object
On Error Resume Next
Set OutlookApp = GetObject(, "Outlook.Application")
If Err Then
Set OutlookApp = CreateObject("Outlook.Application")
IsOutlookCreated = True
End If
On Error GoTo 0
' Restrict items
sSubject = ActiveCell.Value
sFilter = "[Subject] = '" & sSubject & "'"
' Main
With OutlookApp.Session.GetDefaultFolder(olFolderSentMail).Items.Restrict(sFilter)
If .Count > 0 Then
.Sort "ReceivedTime", True
With .Item(1).replyall
.Display
'.Send
End With
Else
MsgBox "No emails found with Subject:" & vbLf & "'" & sSubject & "'"
End If
End With
' Quit Outlook instance if it was created by this code
If IsOutlookCreated Then
OutlookApp.Quit
Set OutlookApp = Nothing
End If
End Sub
It seems work now:
Sub ReplyAllLastEmailFromInboxAndSent()
Dim olApp As Outlook.Application
Dim olNs As Namespace
Dim Fldr As MAPIFolder
Dim objMail As Object
Dim objReplyToThisMail As MailItem
Dim lngCount As Long
Dim objConversation As Conversation
Dim objTable As Table
Dim objVar As Variant
Dim strBody As String
Dim searchFolderName As String
Set olApp = Session.Application
Set olNs = olApp.GetNamespace("MAPI")
Set Fldr = olNs.GetDefaultFolder(olFolderSentMail)
searchFolderName = "'" & Outlook.Session.GetDefaultFolder(olFolderInbox).FolderPath & "','" & Outlook.Session.GetDefaultFolder(olFolderSentMail).FolderPath & "'"
lngCount = 1
For Each objMail In Fldr.Items
If TypeName(objMail) = "MailItem" Then
If InStr(objMail.Subject, ActiveCell.Value) <> 0 Then
Set objConversation = objMail.GetConversation
Set objTable = objConversation.GetTable
objVar = objTable.GetArray(objTable.GetRowCount)
Set objReplyToThisMail = olApp.Session.GetItemFromID(objVar(UBound(objVar), 0))
With objReplyToThisMail.replyall
strBody = "Dear " & "<br>" & _
"<p>Following up with the below. May you please advise?" & _
"<p>Thank you," & vbCrLf
.HTMLBody = strBody & .HTMLBody
.Display
End With
Exit For
End If
End If
Next objMail
Set olApp = Nothing
Set olNs = Nothing
Set Fldr = Nothing
Set objMail = Nothing
Set objReplyToThisMail = Nothing
lngCount = Empty
Set objConversation = Nothing
Set objTable = Nothing
If IsArray(objVar) Then Erase objVar
End Sub

Have VBA loop through all inboxes in Outlook including shared inboxes

My goal with this code is to reply to a specific email in the user's outlook depending on the subject(B8). Essentially have the code loop through all the user's inboxes including shared inboxes to find the email.
The first code I have will go into the user's outlook but only their main inbox and pull the email to reply to. This works without error.
Sub Display()
Dim Fldr As Outlook.Folder
Dim olfolder As Outlook.MAPIFolder
Dim olMail As Outlook.MailItem
Dim olReply As Outlook.MailItem
Dim olitems As Outlook.Items
Dim i As Long
Dim signature As String
Dim olitem As Object
Set Fldr = Session.GetDefaultFolder(olFolderInbox)
Set olitems = Fldr.Items
olitems.Sort "[Received]", True
For i = 1 To olitems.Count
Set olitem = olitems(i)
If Not TypeOf olitem Is Outlook.MailItem Then GoTo SkipToNext
Set olMail = olitem
signature = Environ("appdata") & "\Microsoft\Signatures\"
If Dir(signature, vbDirectory) <> vbNullString Then
signature = signature & Dir$(signature & "*.htm")
Else:
signature = ""
End If
signature = CreateObject("Scripting.FileSystemObject").GetFile(signature).OpenAsTextStream(1, -2).ReadAll
Set olMail = olitems(i)
If InStr(olMail.Subject, Worksheets("Checklist Form").Range("B8")) <> 0 Then
If Not olMail.Categories = "Executed" Then
Set olReply = olMail.ReplyAll
With olReply
.HTMLBody = "<p style='font-family:calibri;font-size:14.5'>" & "Hi Everyone," & "Regards," & "</p><br>" & signature & .HTMLBody
.Display
.Subject
End With
Exit For
olMail.Categories = "Executed"
Exit For
End If
End If
SkipToNext:
Next i
End Sub
This second section of code is my trial and error as well as the use of other resources attempt to have the code loop through all the inboxes of the user. The thing is it doesn't do anything anymore.
I did have working code for this scenario, then I mistakenly saved over it and I have not been successful in getting it back working. Below is as close as I have been able to get.
Any suggestions would be greatly appreciated.
The second script seems to be skipping from "Set olitems = Fldr.Items" to the bottom End If.
I thought maybe to move the End if right below "If not storeinbox Is Nothing Then" but the error "Object variable or With block variable not set" occurs.
When I do change the code line (While making the change above also) "Set Fldr = Storeinbox" to "Set Fldr = Session.GetDefaultFolder(olFolderInbox)" emails will populate, but only in the user's specific inbox(Does not pick up subject text, just most recent email).
I have added additional code to the second script
Set olitem = olitems(i)
If Not TypeOf olitem Is Outlook.MailItem Then GoTo SkipToNext
Set olMail = olitem
Which was missing. This will populate the email for the user's specific email address by the subject. If I type in a subject from another inbox then nothing will happen but it will go through the code with no errors.
Getting closer, but still nothing for the shared inboxes.
Sub Display()
Dim Fldr As Outlook.Folder
Dim olfolder As Outlook.MAPIFolder
Dim olMail As Outlook.MailItem
Dim olReply As Outlook.MailItem
Dim olItems As Outlook.Items
Dim i As Integer
Dim signature As String
Dim allStores As Stores
Dim storeInbox As Folder
Dim j As Long
Set allStores = Session.Stores
For j = 1 To allStores.Count
On Error Resume Next
Debug.Print j & " DisplayName - " & allStores(j).DisplayName
On Error GoTo 0
Set storeInbox = Nothing
On Error Resume Next
Set storeInbox = allStores(j).GetDefaultFolder(olFolderInbox)
On Error GoTo 0
If Not storeInbox Is Nothing Then
Set Fldr = storeinbox
Set olItems = Fldr.Items
olItems.Sort "[Received]", True
For i = 1 To olItems.Count
Set olitem = olitems(i)
If Not TypeOf olitem Is Outlook.MailItem Then GoTo SkipToNext
Set olMail = olitem
signature = Environ("appdata") & "\Microsoft\Signatures\"
If Dir(signature, vbDirectory) <> vbNullString Then
signature = signature & Dir$(signature & "*.htm")
Else
signature = ""
End If
signature = CreateObject("Scripting.FileSystemObject").GetFile(signature).OpenAsTextStream(1, -2).ReadAll
Set olMail = olItems(i)
If InStr(olMail.Subject, Worksheets("Checklist Form").Range("B8")) <> 0 Then
If Not olMail.Categories = "Executed" Then
Set olReply = olMail.ReplyAll
With olReply
.HTMLBody = "<p style='font-family:calibri;font-size:14.5'>" & "Hi Everyone," &
"Regards," & "</p><br>" & signature & .HTMLBody
.Display
.Subject
End With
Exit For
olMail.Categories = "Executed"
End If
End If
Next
End If
ExitRoutine:
Set allStores = Nothing
Set storeInbox = Nothing
SkipToNext:
Next j
End Sub
If you Set allStores = Nothing inside the j loop it will only be something in the first iteration.
Option Explicit
' Think of Option Explicit as being mandatory
' Tools | Options
' Editor tab
' Checkbox "Require Variable Declaration"
' Option Explict will generate automatically on new modules
' You may type it in at the top of an existing module
' This as well points out possible spelling errors in the variables
Sub Display()
'In Excel set reference to Outlook Object Library
Dim Fldr As Outlook.Folder
Dim olMail As Outlook.MailItem
Dim olItem As Object
Dim olReply As Outlook.MailItem
Dim olItems As Outlook.Items
Dim signature As String
Dim i As Long
Dim j As Long
Dim allStores As Stores
Dim storeInbox As Folder
signature = Environ("appdata") & "\Microsoft\Signatures\"
If Dir(signature, vbDirectory) <> vbNullString Then
signature = signature & Dir$(signature & "*.htm")
signature = CreateObject("Scripting.FileSystemObject").GetFile(signature).OpenAsTextStream(1, -2).ReadAll
Else
signature = ""
End If
' Usually works with Outlook open.
' If this proves to be unreliable,
' you may need a CreateObject("Outlook.Application")
Set allStores = Session.Stores
For j = 1 To allStores.Count
' No need to bypass wrong index error here
' The error has been fixed by using j not i
Debug.Print j & " DisplayName - " & allStores(j).DisplayName
' Reset storeInbox to nothing or it will remain the previous
' when there is an error on the current store
' This is one example of why to be careful with On Error Resume Next
Set storeInbox = Nothing
On Error Resume Next
' bypass error if store does not have an inbox
Set storeInbox = allStores(j).GetDefaultFolder(olFolderInbox)
On Error GoTo 0
If Not storeInbox Is Nothing Then
Set Fldr = storeInbox
Set olItems = Fldr.Items
' Not needed?
'olItems.Sort "[Received]", True
For i = 1 To olItems.Count
Set olItem = olItems(i)
If TypeOf olItem Is Outlook.MailItem Then
Set olMail = olItem
If InStr(olMail.Subject, Worksheets("Checklist Form").Range("B8")) <> 0 Then
If Not olMail.Categories = "Executed" Then
Set olReply = olMail.ReplyAll
With olReply
.HTMLBody = "<p style='font-family:calibri;font-size:14.5'>" & "Hi Everyone," & _
"Regards," & "</p><br>" & signature & .HTMLBody
.Display
' Generates a compile error. Appears not needed.
'.Subject
End With
olMail.Categories = "Executed"
olMail.Display 'olMail.Save
End If
End If
End If
Next
End If
Next j
ExitRoutine:
Set allStores = Nothing
Set storeInbox = Nothing
End Sub

Move a specific number of emails from shared Outlook folder

Every few days I manually move a specified number of emails from a shared network mailbox to subfolders of team managers. They want them moved from oldest to newest. Both the managers and the number can change each time.
I wrote a script for moving a small number of emails with a specific subject line in the folder to a subfolder to be worked by a certain group.
I have tried to adapt this to my current task.
Sub Moverdaily()
On Error GoTo errHandler
Dim olApp As Outlook.Application
Dim objNS As Outlook.Namespace
Dim olFolder As Outlook.MAPIFolder
Dim msg As Outlook.MailItem
Dim manager= As Outlook.MAPIFolder
Dim cell,start,finish,rng As Range
Dim countE,countM As Integer
Dim emcount, casecount, movedcount
Set rng = Range(Range("A2"), Range("A2").End(xlDown))
Set olApp = Outlook.Application
Set objNS = olApp.GetNamespace("MAPI")
Set olFolder = objNS.Folders("Documents").Folders("Inbox")
Set manager = objNS.Folders("Document").Folders("Inbox").Folders("Manager")
Set finish = ThisWorkbook.Sheets("Mover").Range("I11")
Set start = ThisWorkbook.Sheets("Mover").Range("I10")
start.Value = Format(Now, "hh:mm:ss")
Set emcount = Range("I12")
Set casecount = Range("I13")
Set movedcount = Range("I14")
countM = 0
countE = 0
For i = olFolder.Items.count To 1 Step -1
For Each cell In rng
If (cell.Text = (onlyDigits(msg.Subject))) Then
msg.move manager
countM = 1 + countM
cell.Offset(0, 1).Value = "Moved"
End If
Next
countE = 1 + countE
Next
finish.Value = Format(Now, "hh:mm:ss")
emcount.Value = countE
casecount.Value = rng.count
movedcount.Value = countM
errHandler:
MsgBox ("Error " & Err.Number & ": " & Err.Description)
Exit Sub
End Sub
Firstly, do not use "for each" with a collection that you change - MailItem.Mpve removes an itemn from that collection. Use a for i = Items.Count to 1 step -1 instead.
Secondly, do not loop through all item - if you already know the entry ids (rngarry), simply call Namespace.GetItemfromID.

Reply to Outlook mail from Excel

I am trying to "replytoall" with a given format in the Body.
I use the following code to search for and display the mails.
Sub Test()
Dim olApp As Outlook.Application
Dim olNs As Namespace
Dim Fldr As MAPIFolder
Dim olMail As Variant
Dim i As Integer
Set olApp = New Outlook.Application
Set olNs = olApp.GetNamespace("MAPI")
Set Fldr = olNs.GetDefaultFolder(olFolderInbox)
i = 1
For Each olMail In Fldr.Items
If InStr(olMail.Subject, "Application for Privilege Leave - Leave ID - Dev-PL-45252-4") <> 0 Then
olMail.Display
i = i + 1
End If
Next olMail
End Sub
I need to Replyall with the same subject and a prescribed body and signature.
It is similar to when we open up a mail in Outlook and click on the Reply to All button.
I want it triggered from Excel.
Since you are using Early Binding, Change
Dim olMail As Variant
to
Dim olMail As Outlook.MailItem
And then you will be able to access all the properties of the olMail item. One of which is .ReplyAll
ScreenShot
If InStr(olMail.Subject, "Blah Blah") <> 0 Then
olMail.Display
olMail.ReplyAll
DoEvents
'
'~~> Rest of the code
'
i = i + 1
End If
There is a ReplyAll method which returns a mail object. See here.
So if you are iterating through some mails, then this should work:
For Each oMail in Fldr.Items
If InStr(olMail.Subject, "mysubject") <> 0 Then
With oMail.ReplyAll
.Subject = oMail.Subject '~~> this is optional
.Body = "your Body"
'~~> all other stuff you need your mail to have
.Display '~~> change to .Send if it is already ok
End With
End If
Next
Not tested but should be close.
Try this one:
olMail.ReplyAll
olMail.ReplyAll.body = bodyMail & vbLF & .body

Resources