Search Outlook attachments limited to a weekday before and save attachments - vba

I want search though Outlook folders of now to previous weekday, so will exclude weekends, and if file doesn’t exist, output “this report was not sent on date”.
And for file to save as: following a condition that the title of the heading contains some text at most two. And that the file will be saved with the two found letters in the body of the title.
I want to do this for six different cases.
Sub SaveOutlookAttachments()
Dim ol As Outlook.Application
Dim ns As Outlook.Namespace
Dim ofolder As Outlook.folder
Dim i As Object
Dim mi As Outlook.MailItem
Dim at As Outlook.Attachment
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set ofolder = ns.Folders(1).Folders("Inbox")
For Each i In ofolder.Items
If i.Class = olMail Then
Set mi = i 'This ensure that were looking at an email object rather than any potential item
'I need to find a way to create a case or an if statement that would reference 2 keywords in the title of the email subject in order to download and save the file with those keywords + date at the end.
'The logic is to use the title to distinguish between 4 regional reports for 1 Partner and 3 reports for 3 different partners. These would save files in their names associated with the title of the email. Eg: Comp.ABC Regional Reports 20/10/2019. I should also only search for previous days only within weekdays/working days - Mondays to Fridays.
Debug.Print mi.SenderName, mi.ReceivedTime, mi.Attachments.Count
For Each at In mi.Attachments
at.SaveAsFile "C:\Users\rootname\OneDrive\Desktop\VBATesting" & at.FileName & Format(mi.ReceivedTime, "dd-mm-yyyy") 'Put in a valid folder location to store attachements
Next at
End If
Next i
End Sub

Here's code that first checks the MailItem's ReceivedTime for the Date condition (you can go further and exclude weekends). Then it checks the MailItem's Subject for Keywords from a colKeywords collection you can edit and add to. It also This should get you pretty close to what you want to do. I've also renamed the variables for clarity:
Dim objOutlook As New Outlook.Application
Dim objNamespace As Outlook.NameSpace
Dim objFolder As Outlook.Folder
Dim objItem As Object
Dim objMailItem As Outlook.MailItem
Dim objAttachment As Outlook.Attachment
Dim colKeywords As New Collection
Dim sKeyword As String
Dim iCounter As Integer
Dim iBackdate As Integer
Set objNamespace = objOutlook.GetNamespace("MAPI")
Set objFolder = objNamespace.Folders(1).Folders("Inbox")
' Add your Keywords here
colKeywords.Add "keyword1"
colKeywords.Add "keyword2"
For Each objItem In objFolder.Items
' Check Item Class
If objItem.Class = Outlook.olMail Then
' Set as Mail Item
Set objMailItem = objItem
With objMailItem
Select Case Weekday(Now)
Case 7 ' Saturday: add extra day
iBackdate = 3
Case 1, 2, 3 ' Sunday through Tuesday: add extra 2 days
iBackdate = 4
Case Else ' Other days
iBackdate = 2
End Select
' Check date
If .ReceivedTime > DateAdd("d", -iBackdate, Now) Then
' Loop through all keywords
For iCounter = 1 To colKeywords.Count
' Get keyword
sKeyword = colKeywords.Item(iCounter)
' Check if keyword exists
If InStr(.Subject, sKeyword) > 0 Then
' Save Attachments
For Each objAttachment In .Attachments
objAttachment.SaveAsFile "C:\Users\rootname\OneDrive\Desktop\VBATesting" & objAttachment.FileName & Format(.ReceivedTime, "dd-mm-yyyy") 'Put in a valid folder location to store attachements
Next
End If
Next
End If
End With
End If
Next

Related

Count number of attachments skip pictures in signature (based on objAttachments.Item(s).Size)

I am trying to create a code that will parse Inbox folder in Outlook and organize emails based on several criteria.
If there is a number between brackets. For example (123456)
If there are attachments in email item. Attachment should be more than 10000 to skip Signatures
Logic:
If both criteria match -> Send to Folder1
If one of them does not match (attachments are missing or there is no number between brackets), send to Archive
Criteria 1 is functioning correct, but I have problems adding criteria 2 for attachments.
Here is my current code:
Private Sub olInboxMainItems_ItemAdd(ByVal Item As Object)
'On Error Resume Next
Dim SubjectVar1 As String
Dim openPos1 As Integer
Dim closePos1 As Integer
Dim midBit1 As String
Dim objNamespace1 As Outlook.NameSpace
Dim destinationFolder1 As Outlook.MAPIFolder
Dim ArchiveFolder As Outlook.MAPIFolder
Dim objAttachments As Outlook.Attachments
Dim AttCount As Long
Set objNamespace1 = GetNamespace("MAPI")
Set destinationFolder1 = objNamespace1.Folders("mybox#mail.com").Folders("Inbox").Folders("Folder1")
Set ArchiveFolder = objNamespace1.Folders("mybox#mail.com").Folders("Archive")
Set objAttachments = Item.Attachments
' Check is there a number between brackets
SubjectVar1 = Item.Subject
openPos1 = InStr(SubjectVar1, "(")
closePos1 = InStr(SubjectVar1, ")")
midBit1 = Mid(SubjectVar1, openPos1 + 1, closePos1 - openPos1 - 1)
' Count number of attachments bigger than 10000 bytes
For s = lngCount To 1 Step -1
If objAttachments.Item(s).Size > 10000 Then
' Count attachments.
AttCount = objAttachments.Item(s).Count
End If
Next s
' Perform actions
If midBit1 = "" And AttCount < 1 Then
Item.Move ArchiveFolder
'GoTo EndOfScript
Else
'MsgBox (midBit)
Item.Move destinationFolder1
'GoTo EndOfScript
End If
EndOfScript:
Set destinationFolder1 = Nothing
Set objNamespace1 = Nothing
End Sub
EDIT:
Here is a simple version I am trying to get working for selected email message:
Sub CountAttachmentsinSelectedEmails()
Dim olSel As Selection
Dim oMail As Object
Dim s As Long
Dim AttCount As Long
Dim strMsg As String
Dim nRes
Dim lngCount As Long
Dim objAttachments As Outlook.Attachments
Dim strFile As String
Set olSel = Outlook.Application.ActiveExplorer.Selection
For Each oMail In olSel
For s = lngCount To 1 Step -1
If objAttachments.Item(s).Size > 10000 Then
strFile = objAttachments.Item(s).Count + 1
End If
Next s
Next
MsgBox ("There are " & strFile & " attachments in the ")
End Sub
Result is empty? No numbers at all
EDIT 2:
Sub CountAttachmentsinSelectEmails()
Dim olSel As Selection
Dim oMail As Object
Dim s As Long
Dim objAttachments As Outlook.Attachments
Dim NumFiles As Long
Dim oItem As Object
Set olSel = Outlook.Application.ActiveExplorer.Selection
Set objAttachments = oItem.Attachments
For Each oMail In olSel
For s = objAttachments.Count To 1 Step -1
If objAttachments.Item(s).Size > 10000 Then
NumFiles = NumFiles + 1
End If
Next s
Next
Debug.Print NumFiles
End Sub
Item.Attachments is a collection therefore so is objAttachments.
A collection can have zero or more members. objAttachments.Count is the number of members which you do not check.
You need to loop over the attachments to check their size and extension individually. Signatures, logos and so on count as attachments but I assume you are not interested in them. Could there be more than one interesting attachment? Do you want a total size of 10,000 or any one attachment being more than 10,000 bytes?
When accessing the size you need to specify which attachment you are checking: objAttachments.Item(Index).Size.
The above should you give you some pointers but I can explain in more detail if necessary.
Comments on edit 1
You do not set objAttachments to anything. Add Set objAttachments = oItem.Attachments.
In For s = lngCount To 1 Step -1 you do not set lngCount to a value so it defaults to zero and the for body is never performed. Try For s = objAttachments.Count To 1 Step -1.
strFile is a string but you are using it in a numeric expression. This will work because the interpreter will evaluate the expression and then convert it to a string. However, the value is objAttachments.Item(s).Count + 1. If there are five attachments and any one of them is larger than 10,000 bytes, the answer will be six.
You need something like Dim NumFiles As Long. This will be initialised to 0. Within the If you need NumFiles = NumFiles + 1.
I rarely use MsgBox for diagnostics. I find Debug.Print NumFiles more convenient. If I want to stop execution, I use Debug.Assert False.
Comments on Edit 2
This is the routine I use to test new email handling macros. The relevance is it show how to use Outlook’s Explorer correctly.
Sub TestNewMacro()
' Skeleton for testing a new mail item processing macro using Explorer
' Replace statement marked ##### with call of new macro.
' Add code to create parameters for new test macro and remove any code to
' create parameters for old test macro.
Dim Exp As Explorer
Dim ItemCrnt As MailItem
Dim PathSave As String
' Technique for locating desktop from answer by Kyle:
' http://stackoverflow.com/a/17551579/973283
PathSave = CreateObject("WScript.Shell").SpecialFolders("Desktop")
Set Exp = Outlook.Application.ActiveExplorer
If Exp.Selection.Count = 0 Then
Call MsgBox("Please select one or more emails then try again", vbOKOnly)
Exit Sub
Else
For Each ItemCrnt In Exp.Selection
Call MacroToBeTested(ItemCrnt, PathSave) ' #####
Next
End If
End Sub

Move old email in conversation to subfolder

I am looking for a macro to move earlier email in a conversation (sorted by subject) to a subfolder, except the latest conversation in that subject.
Upon receiving a new mail on the same conversation, then move the older email to subfolder.
I found the base to move emails older than 7 days, but not sure how to move older conversations and leave only the latest mail.
Sub MoveAgedMail()
Dim objOutlook As Outlook.Application
Dim objNamespace As Outlook.NameSpace
Dim objSourceFolder As Outlook.MAPIFolder
Dim objDestFolder As Outlook.MAPIFolder
Dim objVariant As Variant
Dim lngMovedItems As Long
Dim intCount As Integer
Dim intDateDiff As Integer
Dim strDestFolder As String
Set objOutlook = Application
Set objNamespace = objOutlook.GetNamespace("MAPI")
'Set objSourceFolder = objNamespace.GetDefaultFolder(olFolderInbox)
Set objSourceFolder = objNamespace.Folders("Online Archive - OTCGROUP#abc.ssmb.com").Folders("Inbox").Folders("DEST1")
' use a subfolder under Inbox
'Set objDestFolder = objSourceFolder.Folders("DEST")
Set objDestFolder = objNamespace.Folders("Online Archive - OTCGROUP2#abc.ssmb.com").Folders("Inbox").Folders("DEST2")
For intCount = objSourceFolder.Items.Count To 1 Step -1
Set objVariant = objSourceFolder.Items.Item(intCount)
DoEvents
If objVariant.Class = olMail Then
intDateDiff = DateDiff("d", objVariant.SentOn, Now)
' I'm using 7 days, adjust as needed.
If intDateDiff > 7 Then
objVariant.Move objDestFolder
'count the # of items moved
lngMovedItems = lngMovedItems + 1
End If
End If
Next
' Display the number of items that were moved.
MsgBox "Moved " & lngMovedItems & " messages(s)."
Set objDestFolder = Nothing
End Sub
Iterating through all items in the folder is not really a good idea:
For intCount = objSourceFolder.Items.Count To 1 Step -1
Set objVariant = objSourceFolder.Items.Item(intCount)
Use the Find/FindNext or Restrict methods of the Items class instead. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder

How can I embed a mailitem into a word document as a msg file

I have a word document with a table in it, it is a log for Emails. Column 1 is the date of the Email, column 2 is the subject line, and in column 3 I would like to embed the actual Email so it shows as a MSG file.
This VBA is in the word document, and I have opened the mail item from vba in word.
How do I embed the mail item?
Sub create_log()
Dim appOL As Outlook.Application
Dim appFolder As Outlook.Folder
Dim nsOL As Outlook.NameSpace
Dim mailItem As Outlook.mailItem
Dim items As Outlook.items
Set appOL = New Outlook.Application
Set nsOL = appOL.GetNamespace("MAPI")
Set appFolder = Session.GetDefaultFolder(olFolderInbox).Folders("Email_log")
Dim doc As Word.Document
Dim tbl As Word.Table
Dim cell As Word.cell
Dim row As Long
Dim rows_needed As Long
Set doc = Application.ActiveDocument
If doc.Tables.Count > 1 Then
MsgBox "Too many tables, there should only be one"
Exit Sub
End If
Set tbl = doc.Tables(1)
rows_needed = appFolder.items.Count - tbl.Rows.Count
While rows_needed > 0
tbl.Rows.Add
rows_needed = rows_needed - 1
Wend
Set items = appFolder.items
items.Sort "ReceivedTime", False
row = 2
For Each mailItem In items
tbl.cell(row, 1).Range.Text = Left(mailItem.ReceivedTime, 12)
tbl.cell(row, 2).Range.Text = mailItem.Subject
tbl.cell(row, 3).Range. ***how do I add mailItem***
row = row + 1
Next mailItem
End Sub
This is what I want the end result to look like. Note the word document and table are already existing, the macro just fills in the cells in the table.
Save the email to a .msg file, then pass the path to that file in the Range.InsertFile method.

How to Add an Appointment to a Shared Calendar in Outlook?

I am attempting to create an appointment taken from a .CSV file with Subject and Date and place this in someone else's shared calendar.
I have full editor's permissions for this shared calendar. By shared calendar I mean, a regular calendar made in the person's Outlook and clicking "Share" and emailing it to others.
Sub ImportAppointments(full_path As String)
'Initialize variables
Dim exlApp As Excel.Application
Dim exlWkb As Workbook
Dim exlSht As Worksheet
Dim rng As Range
Dim itmAppt As Outlook.AppointmentItem
' Create reference to Excel
Set exlApp = New Excel.Application
' Select file path, currently hardcoded to one directory, change as needed
Dim strFilepath As String
'strFilepath = "P:\Holiday Calendar\Holiday_Calendar_Data.csv"
strFilepath = full_path
' Select workbook (the above .csv file) and select the first worksheet as the data sheet
Set exlSht = Excel.Application.Workbooks.Open(strFilepath).Worksheets(1)
' Initialize variables
Dim iRow As Integer
Dim iCol As Integer
Dim oNs As Namespace
Dim olFldr As Outlook.MAPIFolder
Dim objOwner As Outlook.Recipient
' Allow accessing data stored in the user's mail stores in Outlook
Set oNs = Outlook.GetNamespace("MAPI")
' Set share calender owner
Set objOwner = oNs.CreateRecipient("calvin#xyz.ca")
objOwner.Resolve
If objOwner.Resolved Then
' Set up non-default share folder location
Set olFldr = oNs.GetSharedDefaultFolder(objOwner, olFolderCalendar).Folders("Holiday Calendar")
End If
' Start point
iRow = 2
iCol = 1
' Loop through each calendar entry
While exlSht.Cells(iRow, 1) <> ""
Set itmAppt = Outlook.CreateItem(olAppointmentItem)
' Set appointment Subject, ie (Vacation, Sick Day, Half-Day, etc.)
itmAppt.Subject = exlSht.Cells(iRow, 1)
' Set Date of Event
itmAppt.Start = exlSht.Cells(iRow, 2)
' Force All Day Event
itmAppt.AllDayEvent = True
' Save appointment
itmAppt.Save
' Advance pointer to next row
iRow = iRow + 1
' Transfer appointment into shared calendar folder
itmAppt.Move olFldr
Wend
' Close everything
Excel.Application.Workbooks.Close
exlApp.Quit
Set exlApp = Nothing
Set olFldr = Nothing
Set itmAppt = Nothing
End Sub
My code fails to find the "Holiday Calendar" if I try to insert at someone else's calendar with
Set olFldr = oNs.GetSharedDefaultFolder(objOwner, olFolderCalendar).Folders("Holiday Calendar")
Instead of calling Application.CreateItem / AppointmentItem.Move, create the item directly using olFldr.Items.Add.
This line of code is slightly off if the calendar you are writing to is at the same folder level as the default calendar:
Set olFldr = oNs.GetSharedDefaultFolder(objOwner, olFolderCalendar).Folders("Holiday Calendar")
Instead, you need to specify .Parent before the .Folders property
Set olFldr = oNs.GetSharedDefaultFolder(objOwner, olFolderCalendar).Parent.Folders("Holiday Calendar")
I derived this answer from: https://www.slipstick.com/developer/working-vba-nondefault-outlook-folders/
Sharing this as it took me ages to come up with a solution to add a calendar meeting request from an Exchange Shared Mailbox.
This code creates, displays and pre-fills an appointment which will be saved in the Shared Mailbox, and if sent to other recipients will appear to the recipient as being sent from the shared mailbox account!
Sub SendEmailFromSharedMailbox()
Dim olApp As Outlook.Application
Set olApp = Outlook.Application
Dim olNS As Outlook.Namespace
Dim objOwner As Outlook.Recipient
Set olNS = olApp.GetNamespace("MAPI")
Set objOwner = olNS.CreateRecipient("Shared Mailbox Name")
objOwner.Resolve
If objOwner.Resolved Then
MsgBox objOwner.Name
Set newCalFolder = olNS.GetSharedDefaultFolder(objOwner, olFolderCalendar)
'Now create the email
Set olAppt = newCalFolder.Items.Add(olAppointmentItem)
With olAppt
'Define calendar item properties
.Start = "19/9/2019 2:00 PM"
.End = "19/9/2019 2:30 PM"
.Subject = "Appointment Subject Here"
.Recipients.Add ("someone#email.com")
'Add more variables as required, eg reminder, importance, etc
.Display
End With
End If
End Sub

How do I create a macro to move the oldest 20 emails from the bottom of my inbox to another folder in outlook?

I'm trying to move the bottom 20 emails to another folder in Outlook to another folder where the macro runs. I'm able to move then when selected but I don't want to have to select 20 from the bottom (oldest) first. I'd like to automate this bit too.
Any help would be appreciated.
Here's what I have so far but it moves the most recent mail only, regardless of how the inbox is sorted:
Public Sub Move_Inbox_Emails()
Dim outApp As Object
Dim outNS As Object
Dim inboxFolder As Object
Dim destFolder As Object
Dim outEmail As Object
Dim inboxItems As Object
Dim i As Integer
Dim inputNumber As String
Dim numberToMove As Integer
inputNumber = InputBox("Enter number of emails to move")
On Error Resume Next
numberToMove = CInt(inputNumber)
On Error GoTo 0
If numberToMove < 1 Then Exit Sub
Set outApp = CreateObject("Outlook.Application")
Set outNS = outApp.GetNamespace("MAPI")
Set inboxFolder = outNS.GetDefaultFolder(olFolderInbox)
Set destFolder = inboxFolder.Parent.Folders("Mobus") 'Test folder at same level as Inbox
'Sort Inbox items by Received Time
Set itemsCol = inboxFolder.Items
itemsCol.Sort "[ReceivedTime]", False
'Loop through sorted items for the number entered by the user, up to the number of items in the Inbox
For i = inboxFolder.Items.Count To inboxFolder.Items.Count - numberToMove + 1 Step -1
If inboxFolder.Items(i).Class = OlObjectClass.olMail Then
Set outEmail = inboxFolder.Items(i)
'Debug.Print outEmail.ReceivedTime, outEmail.subject
outEmail.Move destFolder
End If
Next
End Sub
I've solved this now with some ideas from the commentors, thanks very much. This code now prompts for how many to move and takes them from the oldest first:
Public Sub Move_Inbox_Emails_From_Excel()
Dim outApp As Object
Dim outNS As Object
Dim inboxFolder As Object
Dim destFolder As Object
Dim outEmail As Object
Dim inboxItems As Object
Dim i As Integer
Dim inputNumber As String
Dim numberToMove As Integer
inputNumber = InputBox("Enter number of emails to move")
On Error Resume Next
numberToMove = CInt(inputNumber)
On Error GoTo 0
If numberToMove < 1 Then Exit Sub
Set outApp = CreateObject("Outlook.Application")
Set outNS = outApp.GetNamespace("MAPI")
Set inboxFolder = outNS.GetDefaultFolder(olFolderInbox)
Set destFolder = inboxFolder.Parent.Folders("Mobus") 'Test folder at same level as Inbox
'Sort Inbox items by Received Time
Set inboxItems = inboxFolder.Items
'inboxItems.Sort "[ReceivedTime]", False 'ascending order (oldest first)
inboxItems.Sort "[ReceivedTime]", True 'descending order (newest first)
'Loop through sorted items for the number entered by the user, up to the number of items in the Inbox
For i = inboxFolder.Items.Count To inboxFolder.Items.Count - numberToMove + 1 Step -1
Set outEmail = inboxItems(i)
'Debug.Print i, outEmail.Subject
outEmail.Move destFolder
Next
End Sub
Sort the Items collection by ReceivedTime property, loop though the last 20 items (use a down loop - step -1) and move the items.