Script moves only a couple of 'Inbox' items on each execution - vba

I have the follwing VBA script for Outlook that should move emails to the Archives folder (that are not categorized in one of the special categories). It both works and not. I mean it moves some emails but skips the others so I have to run it mulitple times until the Inbox is cleaned-up. I don't understand why it behaves this way. It doesn't throw any exceptions it just doesn't do its job for all items. Can you see anything suspicios here?
Option Explicit
Sub CleanUpInbox()
Dim ns As Outlook.NameSpace
Set ns = GetNamespace("MAPI")
Dim inbox As Outlook.Folder: Set inbox = ns.GetDefaultFolder(olFolderInbox)
Dim archive As Outlook.Folder: Set archive = ns.Folders("my#mailbox.abc").Folders("Archives").Folders("2018")
Dim maxDiffInDays As Integer: maxDiffInDays = 14
Dim today As Date: today = DateValue(now())
On Error GoTo bang
Dim mail As Variant ' Outlook.MailItem
For Each mail In inbox.Items
If mail Is Nothing Then
GoTo continue
End If
Dim receivedOn As Date: receivedOn = DateValue(mail.ReceivedTime)
Dim diff As Integer: diff = DateDiff("d", receivedOn, today)
Dim isOld As Boolean: isOld = True ' diff > maxDiffInDays
If isOld Then
'Debug.Print diff
'Debug.Print mail.Subject
'Debug.Print mail.Categories
Dim isPinned As Boolean: isPinned = InStr(mail.Categories, "PINNED")
Dim isTTYL As Boolean: isTTYL = InStr(mail.Categories, "TTYL")
If LinqAll(False, isPinned, isTTYL) Then
Debug.Print mail.Subject
mail.Move archive
End If
End If
GoTo continue
bang:
Debug.Print "bang!"
Debug.Print Err.Description
continue:
Next
End Sub
Function LinqAll(ByVal Expected As Boolean, ParamArray Values() As Variant) As Boolean
Dim x As Variant
For Each x In Values
If x <> Expected Then
LinqAll = False
Exit Function
End If
Next
LinqAll = True
End Function
Function LinqAny(ByVal Expected As Boolean, ParamArray Values() As Variant) As Boolean
Dim x As Variant
For Each x In Values
If x = Expected Then
LinqAny = True
Exit Function
End If
Next
LinqAny = False
End Function

Not sure whether I miss something here, but your code seems to handle any mail as old, for you set isOld to true within the loop. Is there a special reason for declaring isPinedand isTTYLeach loop? Have you tried:
Sub CleanUpInbox()
Dim ns As Outlook.Namespace
Dim inbox As Outlook.Folder: Set inbox = ns.GetDefaultFolder(olFolderInbox)
Dim archive As Outlook.Folder: Set archive = ns.Folders("my#mailbox.abc").Folders("Archives").Folders("2018")
Dim maxDiffInDays As Integer: maxDiffInDays = 14
Dim today As Date: today = DateValue(Now())
Dim mail As Variant ' Outlook.MailItem
Dim receivedOn As Date
Dim diff As Integer
Dim isOld As Boolean
Dim isPinned As Boolean
Dim isTTYL As Boolean
Set ns = GetNamespace("MAPI")
On Error GoTo bang
For Each mail In inbox.Items
If mail Is Nothing Then
GoTo continue
End If
isOld = False
receivedOn = DateValue(mail.ReceivedTime)
diff = DateDiff("d", receivedOn, today)
If diff > maxDiffInDays Then
isOld = True
End If
isPinned = InStr(mail.Categories, "PINNED")
isTTYL = InStr(mail.Categories, "TTYL")
If LinqAll(False, isPinned, isTTYL) Then
Debug.Print mail.Subject
mail.Move archive
End If
GoTo continue
bang:
Debug.Print "bang!"
Debug.Print Err.Description
continue:
Next
End Sub

I've solved it. You must not use Items in a For Each loop and at the samve time .Move its items. It's like modifying the loop collection in C#. The only difference is that C# is throwing a nice exception while VBA just reduces the number of items and then just stops :-o
Instead, I used Do While and two counters. One that counts the processed items and the other that is the current index for Items. Now it processes everything.
Sub CleanUpInbox2()
' ... other variables
Dim processCount As Integer
Dim itemIndex As Integer: itemIndex = 1
Dim itemCount As Integer: itemCount = inbox.Items.Count
Do While processCount < itemCount
processCount = processCount + 1
Set mail = inbox.Items(itemIndex)
' ... body
If LinqAll(False, isPinned, isTTYL) Then
Debug.Print mail.Subject
mail.Move archive
moveCount = moveCount + 1
Else
itemIndex = itemIndex + 1
End If
bang:
Debug.Print "bang!"
Debug.Print Err.Description
continue:
Loop
Debug.Print "Emails processed: " & processCount
Debug.Print "Emails moved: " & moveCount
End Sub
I tried to copy Items first but I didn't succeed with that (apparently there is no new Outlook.Items) so I use indexes.

Related

How to set focus to Inbox?

When you open Outlook, Inbox has the focus. If you go to another folder then run VBA code the focus stays on that folder.
My macro deletes all files in my Deleted folder and Trash folder. I would like it to come back to the Inbox when complete.
Public Sub EmptyFolder()
Dim Items As Outlook.Items
Dim i As Long
Dim Count As Long
Dim Delete As Boolean
' Clear Junk Items
Set ns = Application.GetNamespace("MAPI")
Set Items = Session.GetDefaultFolder(olFolderCalendar).Parent.Folders("Junk").Items
Count = Items.Count
If Count = 0 Then
GoTo DeleteAll
End If
Delete = True
For i = Count To 1 Step -1
Items(i).Delete
Next
DeleteAll:
' Clear Trash Items
Set Items = Session.GetDefaultFolder(olFolderCalendar).Parent.Folders("Trash").Items
Count = Items.Count
If Count = 0 Then
GoTo EndAll
End If
Delete = True
For i = Count To 1 Step -1
Items(i).Delete
Next
EndAll:
End Sub
You could just change the CurrentFolder value to one of your Inbox folders in my example "family" as follow.
Sub ChangeCurrentFolder()
Dim myNamespace As Outlook.NameSpace
Set ns = Application.GetNamespace("MAPI")
Set Application.ActiveExplorer.CurrentFolder = ns.GetDefaultFolder(olFolderInbox)
End Sub
On a related note to what you are trying to achieve (but not directly related to the question), I use a generic function to delete older e-mails from folders and the code is below:
Call the function in Startup
In ThisOutlookSession
Private Sub Application_Startup()
On Error Resume Next
Call DeleteAgedJunkMail
End Sub
In a module
Function DeleteAgedJunkMail() As Boolean
Dim ThisNamespace As Outlook.NameSpace: Set ThisNamespace = Application.GetNamespace("MAPI")
Dim Inbox As Outlook.MAPIFolder: Set Inbox = ThisNamespace.GetDefaultFolder(olFolderInbox)
Dim Junk As Outlook.MAPIFolder: Set Junk = ThisNamespace.GetDefaultFolder(olFolderJunk)
On Error Resume Next
Call DeleteAgedMail(Junk, 7)
Call DeleteAgedMail(Inbox.Folders("Quotes"), 90)
Call DeleteAgedMail(Inbox.Folders("Orders"), 90)
DeleteAgedJunkMail = True
End Function
Function DeleteAgedMail(ByRef Folder As Outlook.MAPIFolder, Optional Days As Long = 7) As Boolean
On Error GoTo ExitFunction
Dim Var As Variant, i As Long, Difference As Long, Items As Long
For i = Folder.Items.Count To 1 Step -1
Set Var = Folder.Items.Item(i)
DoEvents
If Var.Class = olMail Then
Difference = DateDiff("d", Var.SentOn, Now)
If Difference > Days Then
Var.Delete
Items = Items + 1 'Count the # of items deleted
End If
End If
Next
' Record the number of items that were deleted.
Debug.Print "Deleted " & Items & " message(s) from '" & Folder.Name & "'."
DeleteAgedMail = True
ExitFunction:
End Function

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

Server based rule to collate 500+ adresses into ~150 inbox folders

I have a Company Project where ~500 clients send Emails to the my project inbox. Those clients correspond to ~150 offices (I have an Excel-List of the email addresses & according offices).
Each office shall have one Outlook folder, so I can quickly check upon the past correspondence with a specific office.
The Project inbox is looked after and used by several co-workers, hence server- and not client based rules.
How do I set this up?
My simple idea in form of a pseudo code:
for each arriving email
if (from-adress is in "email & office-List")
move that email to outlook folder "according office name"
end if
end for
and the same for outgoing emails:
for each sent email
if (to-adress is in "email & office-List")
move that email to outlook folder "according office name"
end if
end for
Thanks for suggestions!
...and besides, can outlook folders be created programmatically from a list of names?
My solution is a skript i run daily on a manual basis since my employer doesnt allow scripts on arriving messages.
the logic in short is:
fetch list of emails & their corresponding offices (both string lists)
set up folder variables
loop through messages, and move them eventually
the code looks like
Option Compare Text ' makes string comparisons case insensitive
Sub sortEmails()
'sorts the emails into folders
Dim msg As Outlook.MailItem
Dim itm As Object
Dim adress As String
Dim pa As Outlook.PropertyAccessor
Const PR_SMTP_ADDRESS As String = _
"http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
'1) fetch emails
GetEMailsFolders locIDs, emails, n
'1.5) fetch folder objects
'Create an instance of Outlook & inbox reference
Dim Inbox As Outlook.MAPIFolder
Dim outbox As Outlook.MAPIFolder
Set outlookApp = New Outlook.Application
Set NS = outlookApp.GetNamespace("MAPI")
Set objOwner = NS.CreateRecipient("email#host.com")
objOwner.Resolve
'Set inbox = NS.GetSharedDefaultFolder(objOwner, olFolderInbox)
Set Inbox = NS.Folders("email#host.com").Folders("Inbox")
Set outbox = NS.Folders("email#host.com").Folders("Sent Items")
Dim basefolder As Outlook.MAPIFolder
Dim bfName As String
bfName = "Offices" 'name of the folder for the offices
Set basefolder = MkDirConditional(Inbox.Folders("Project folder"), bfName)
'2)loop through inbox & outbox emails
Dim destination As Outlook.MAPIFolder
Dim fold(1 To 2) As Outlook.MAPIFolder
Set fold(1) = Inbox
Set fold(2) = outbox
Dim LocID As String
For Each fol In fold 'loop through inbox & outbox
Debug.Print fol
'reverse fo loop because otherwise moved messages modify indices of following messages
For i = fol.Items.Count To 1 Step -1 'Each itm In fol.Items
Set itm = fol.Items(i)
If TypeName(itm) = "MailItem" Then ' others are AppointmentItem, MeetingItem, or TaskItem
Set msg = itm
'Debug.Print " " & msg.Subject
If fol = Inbox Then
' there are two formats of email adrersses.
If msg.SenderEmailType = "EX" Then 'check two kinds of email adress formats
adress = msg.Sender.GetExchangeUser().PrimarySmtpAddress
ElseIf msg.SenderEmailType = "SMTP" Then 'SMTP case
adress = msg.SenderEmailAddress
Else
Debug.Print " neither EX nor SMTP" & msg.Subject;
End If
pos = Findstring(adress, emails) ' position in the email / standort list
ElseIf fol = outbox Then
For Each rec In msg.Recipients
Set pa = rec.PropertyAccessor
adress = pa.GetProperty(PR_SMTP_ADDRESS)
pos = Findstring(adress, emails)
If pos > 0 Then
Exit For
End If
Next rec
End If
'4.5) if folder doesnt exist, create it
'5) move message
If pos > 0 Then
'Debug.Print " Its a Match!!"
LocID = locIDs(pos)
Set destination = MkDirConditional(basefolder, LocID)
Debug.Print " " & Left(msg.Subject, 20), adress, pos, destination
msg.Move destination
Else
'Debug.Print " not found!"
End If
Else
'Debug.Print " " & "non-mailitem", itm.Subject
End If
Next i
Next fol
End Sub
'// Function - Check folder Exist
Private Function FolderExists(Inbox As Outlook.MAPIFolder, FolderName As String) As Boolean
Dim Sub_Folder As MAPIFolder
On Error GoTo Exit_Err
Set Sub_Folder = Inbox.Folders(FolderName)
FolderExists = True
Exit Function
Exit_Err:
FolderExists = False
End Function
Function MkDirConditional(basefolder As Outlook.MAPIFolder, newfolder As String) As Outlook.MAPIFolder
Debug.Print newfolder & " ";
If FolderExists(basefolder, newfolder) Then
'folder exists, so just skip
Set MkDirConditional = basefolder.Folders(newfolder)
Debug.Print "exists already"
Else
'folder doesnt exist, make it
Set MkDirConditional = basefolder.Folders.Add(newfolder)
Debug.Print "created"
End If
End Function
'function to compare two strings, min the option compare text at the top line
Function Findstring(str As String, arr As Variant) As Integer
'returns -1 if a string is not found, otherwise its index
Findstring = -1
Dim i As Integer
i = 1
For Each Item In arr
'Debug.Print Item
If str = Item Then
Findstring = i
Exit For
End If
i = i + 1
Next
End Function
' function to fetch the lists of emails and offices
Sub GetEMailsFolders(ByRef rng1 As Variant, ByRef rng2 As Variant, ByRef n As Variant)
'declare variables
Dim xExcelFile As String
Dim xExcelApp As Excel.Application
Dim xWb As Excel.Workbook
Dim xWs As Excel.Worksheet
Dim xExcelRange As Excel.Range
Dim TotalRows As Long
'declare SPOC xls file
xExcelFile = "adresses.xlsx"
'open the file
Set xExcelApp = CreateObject("Excel.Application")
Set xWb = xExcelApp.Workbooks.Open(xExcelFile)
Set xWs = xWb.Sheets(1)
'extract LocIDs (column A), emails (column O) and thir number
n = xWs.Range(xWs.Range("A2"), xWs.Range("A2").End(xlDown)).Count ' works
ReDim rng1(1 To n) As Variant
ReDim rng2(1 To n) As Variant
For i = 1 To n
rng1(i) = xWs.Cells(i + 1, 1)
rng2(i) = xWs.Cells(i + 1, 15)
'Debug.Print rng1(i), rng2(i)
Next
Debug.Print "done reading LocIDs & emails"
End Sub

Delete duplicate mails Outlook 2013

Im trying to create a VBA macro that checks if there is a duplicate mail (looks at subject) and then deletes the mail.
This code works but is deleting the oldest duplicates. It's counting in descending order and I can't seem to get the sorting of the items to work.
Basically I need help figuring out how to make sure the "newest" duplicate by received time gets deleted.
Sub RemoveDuplicates()
Dim oFolder As Folder
Dim oEmail As MailItem, oItems As ItemProperties, oItem As ItemProperty
Dim cMail As Collection
Dim i As Long
Set oFolder = Application.ActiveExplorer.CurrentFolder
Set cMail = New Collection
With oFolder
' .Items.Sort "[ReceivedTime]", True
If olMailItem <> .DefaultItemType Then Exit Sub
For i = .Items.Count To 1 Step -1
Set oItems = .Items(i).ItemProperties
Debug.Print oItems("ReceivedTime")
If Not oItems("ReceivedTime") Is Nothing Then
Set oItem = oItems("ReceivedTime")
'// Week old
If oItem >= Date - 7 Then
On Error GoTo ErrHandler
'// Delete Duplicate Subject
cMail.Add oItems("Subject"), oItems("Subject")
On Error GoTo 0
End If
End If
Next i
End With
Exit Sub
ErrHandler:
Debug.Print Err.Number, oItems("Subject"), oItems("ReceivedTime")
oFolder.Items(i).Delete
Resume Next
End Sub
Cache the Items collection before entering the loop (otherwise you get a brand new Items COM object each time), sort it on ReceivedTime (Items.Sort), then loop from Count down to 1.
Expanding on #DmitryStreblechenko's answer:
The following will keep the MailItem with the oldest date and delete more recent ones with the same subject.
For convenience TargetFolder and MinDate are configurable but optional. They default to the currently visible folder and seven days ago.
Sub RemoveDuplicates(Optional TargetFolder As Folder, Optional MinDate As Date)
Dim Items As Items, Email As MailItem
Dim i As Long, Dupes As Object
If MinDate = vbEmpty Then MinDate = Date - 7
If TargetFolder Is Nothing Then Set TargetFolder = ActiveExplorer.CurrentFolder
Set Dupes = CreateObject("Scripting.Dictionary")
Set Items = TargetFolder.Items
Items.Sort "[ReceivedTime]"
Debug.Print "Dedupe <" & TargetFolder.FolderPath & ">, " & Items.Count & " items"
For i = Items.Count To 1 Step -1
If TypeOf Items(i) Is MailItem Then
Set Email = Items(i)
If Email.ReceivedTime >= MinDate Then
If Dupes.Exists(Email.Subject) Then
Debug.Print "DELETE: " & Email.Subject
'Item.Delete
Else
Dupes.Add Email.Subject, 0
End If
End If
End If
Next i
End Sub
This makes use of a Scripting.Dictionary because unlike the Collection object it supports a handy Exists() method.

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.