Copy recurring appointment in outlook with VBA macro - vba

How can I copy a recurring appointment in outlook 2013 with VBA? I've tried copying the RecurrencePattern Object from the source item to the destination item (cAppt), but this sets the start date to the next immediate calendar interval (e.g. if it is 4:12 now, the recurring series is set to start at today at 4:30) instead of the actual start date of the original item. Any hints on how to do this?
Private Sub curCal_ItemAdd(ByVal Item As Object)
Dim cAppt As AppointmentItem
Dim oPatt As RecurrencePattern
Dim cPatt As RecurrencePattern
Dim moveCal As AppointmentItem
' On Error Resume Next
'only copy items not marked as private
If Item.Sensitivity <> olPrivate Then
Item.Body = Item.Body & "[" & GetGUID & "]"
Item.Save
Set cAppt = Application.CreateItem(olAppointmentItem)
If Item.IsRecurring Then
Set cPatt = cAppt.GetRecurrencePattern
cPatt = Item.GetRecurrencePattern
End If
With cAppt
.Subject = Item.Subject
.Start = Item.Start
.Duration = Item.Duration
.Location = Item.Location
.Body = Item.Body
End With
' set the category after it's moved to force EAS to sync changes
Set moveCal = cAppt.Move(newCalFolder)
moveCal.Categories = "moved"
moveCal.Save
End If
End Sub

Try to use AppointmentItem.Copy instead of Application.CreateItem.

I know this is a very old post, but I wanted to share my findings on why OP's original VBScript didn't work.
AppointmentItem.Copy can work, but depending on when it's used it can cause VBScript to break (e.g. copying an appointment to a shared calendar automatically when its added to your personal). Application.CreateItem does not have this drawback.
After doing some testing, I can confirm (in Outlook 2016 anyway) that GetRecurrencePattern method captures all relevant attributes except the StartTime property. As a result, the start time is set to a default of the next immediate time frame on your calendar.
To fix this, you can change the script as follows:
Private Sub curCal_ItemAdd(ByVal Item As Object)
Dim cAppt As AppointmentItem
'Dim oPatt As RecurrencePattern --unnecessary declaration, can delete.
Dim cPatt As RecurrencePattern
Dim moveCal As AppointmentItem
' On Error Resume Next
'only copy items not marked as private
If Item.Sensitivity <> olPrivate Then
Item.Body = Item.Body & "[" & GetGUID & "]"
Item.Save
Set cAppt = Application.CreateItem(olAppointmentItem)
If Item.IsRecurring Then
Set cPatt = cAppt.GetRecurrencePattern
cPatt = Item.GetRecurrencePattern
cPatt.StartTime = Item.Start 'Add appointment time as StartTime.
cPatt.Duration = Item.Duration 'need to define Duration (or EndTime) after changing StartTime.
End If
With cAppt
.Subject = Item.Subject
.Start = Item.Start
.Duration = Item.Duration
.Location = Item.Location
.Body = Item.Body
End With
' set the category after it's moved to force EAS to sync changes
Set moveCal = cAppt.Move(newCalFolder)
moveCal.Categories = "moved"
moveCal.Save
End If
End Sub
Also, not sure if OP needed to give credit, but credit where credit is due the code is mostly copypasta from http://www.slipstick.com/developer/copy-new-appointments-to-another-calendar-using-vba/

Related

430 Error on Date - itm.ReceivedTime in a subfolder

I get a 430 error running code on a subfolder of a shared inbox.
Sub GetEmails()
'Add Tools->References->"Microsoft Outlook nn.n Object Library"
'nn.n varies as per our Outlook Installation
Const NUM_DAYS As Long = 34
Dim OutlookApp As Outlook.Application
Dim i As Long
Dim Folder As Outlook.MAPIFolder
Dim itm As Object
Dim iRow As Long, oRow As Long, ws As Worksheet, sBody As String
Dim mailboxName As String, inboxName As String, subfolderName As String
mailboxName = "mailboxname"
inboxName = "Inbox"
subfolderName = "subfoldername"
Set OutlookApp = New Outlook.Application
On Error Resume Next
Set Folder = OutlookApp.Session.Folders(mailboxName) _
.Folders(inboxName).Folders(subfolderName)
On Error GoTo 0
If Folder Is Nothing Then
MsgBox "Source folder not found!", vbExclamation, _
"Problem with export"
Exit Sub
End If
Set ws = ThisWorkbook.Worksheets(1)
'add headers
ws.Range("A1").Resize(1, 4).Value = Array("Sender", "Subject", "Date", "Body")
iRow = 2
Folder.Items.Sort "Received"
For Each itm In Folder.Items
If TypeOf itm Is Outlook.MailItem Then 'check it's a mail item (not appointment, etc)
If Date - itm.ReceivedTime <= NUM_DAYS Then
sBody = Left(Trim(itm.Body), 150) 'first 150 chars of Body
sBody = Replace(sBody, vbCrLf, "; ") 'remove newlines
sBody = Replace(sBody, vbLf, "; ")
ws.Cells(iRow, 1).Resize(1, 4).Value = _
Array(itm.SenderName, itm.Subject, itm.ReceivedTime, sBody)
iRow = iRow + 1
End If
End If
Next itm
MsgBox "Outlook Mails Extracted to Excel"
End Sub
I tried changing "itm" to "item". It works on the regular inbox. The issue happens when I try to pull from a subfolder.
I tried Debug Print. I don't know if I'm putting it in the right place.
The 430 error happens on the line:
If Date - itm.ReceivedTime <= NUM_DAYS Then
If I try to pull 30 days worth of data, it will only pull like the last seven days. So it works but it is limited.
First of all, the Sort method deals with non-existsing property:
Folder.Items.Sort "Received"
You need to use the ReceivedTime property instead.
Second, the sorted collection is lost and you continue dealing with unsorted one.
Folder.Items.Sort "Received"
For Each itm In Folder.Items
Asking each time the Items property returns a new Items instance. So, you need to get an instance once and then re-use in the code. Only by following this way you will preserve the sorting order.
The 430 error happens on the line:
If Date - itm.ReceivedTime <= NUM_DAYS Then
The error code indicates that Class doesn't support Automation (Error 430) which don't tell us anything meaningful.
Anyway, calculating dates that way to get items for specific dates in Outlook is not the best and proper way. Instead, you need to consider using the Find/FindNext or Restrict methods of the Items class which allows getting/dealing with items that correspond to your conditions only. Read more about these methods in the articles I wrote for the technical blog:
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
For example, you could use the following search criteria to get items for a specific timeframe:
'This filter uses urn:schemas:httpmail namespace
strFilter = AddQuotes("urn:schemas:httpmail:datereceived") _
& " > '" & datStartUTC & "' AND " _
& AddQuotes("urn:schemas:httpmail:datereceived") _
& " < '" & datEndUTC & "'"
See Filtering Items Using a Date-time Comparison for more information.

How to mark and move unread messages older than three days?

I want to mark all unanswered emails, older than three days, with a flag and move them into a folder named "mini". The source is "Posteingang" (German for inbox).
Sub Mails_verschieben()
Set myaccount = Application.GetNamespace("MAPI").DefaultStore
Set mynamespace = Application.GetNamespace("MAPI")
Dim ursprung As MAPIFolder
Dim ziel As MAPIFolder
Set ursprung = Session.Folders(myaccount.DisplayName).Folders("Posteingang")
Set ziel = Session.Folders(myaccount.DisplayName).Folders("mini")
For i = ursprung.Items.Count To 1 Step -1 'alle emails im Postfach durchgehen
With ursprung.Items(i)
If .ReceivedTime < Date - 3 And ursprung.Items(i) = .LastModificationTime Then
.FlagIcon = 5
.FlagStatus = olFlagMarked
.Save
ursprung.Items(i).Move ziel 'in Ordner verschieben
End If
End With
Next i
End Sub
I get
Object Doesn't Support this Property or Method
at
If .ReceivedTime < Date - 3 And ursprung.Items(i) = .LastModificationTime Then
I want also to run this script automatically but found nothing.
I modified my code:
Sub Mails_verschieben()
Set myaccount = Application.GetNamespace("MAPI").DefaultStore
Set mynamespace = Application.GetNamespace("MAPI")
Dim ursprung As MAPIFolder
Dim ziel As MAPIFolder
Dim MailX As MailItem
Set ursprung = mynamespace.GetDefaultFolder(olFolderInbox)
Set ziel = Session.Folders(myaccount.DisplayName).Folders("mini")
For i = ursprung.Items.Count To 1 Step -1 'alle emails im Postfach durchgehen
For Each MailX In ursprung.Items(i)
If MailX.ReceivedTime < Date - 3 And ursprung.Items(i) = MailX.LastModificationTime Then
MailX.FlagIcon = 5
MailX.FlagStatus = olFlagMarked
MailX.Save
ursprung.Items(i).Move ziel 'in Ordner verschieben
End If
Next
Next i
End Sub
Also getting error.
First, please remember that an Outlook folder may contain different item types - mails, appointments, documents and etc. Check the item type at runtime to make sure you deal with mail item before accessing their properties. For example:
For x = 1 To Items.Count
If Items.Item(x).Class = OlObjectClass.olMail Then
' For mail item, use the SenderName property.
End If
Next
Second, to get the standard/default Inbox folder you don't need to use the following code:
Set ursprung = Session.Folders(myaccount.DisplayName).Folders("Posteingang")
Instead, use the NameSpace.GetDefaultFolder method which returns a Folder object that represents the default folder of the requested type for the current profile.
Set ursprung = mynamespace.GetDefaultFolder(olFolderInbox)
Third, instead of iterating over all items in a folder:
For i = ursprung.Items.Count To 1 Step -1 'alle emails im Postfach durchgehen
With ursprung.Items(i)
You need to use the Find/FindNext or Restrict methods of the Items class. 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
There appears to be an unneeded condition in
If MailX.ReceivedTime < Date - 3 And ursprung.Items(i) = MailX.LastModificationTime Then
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
' If desperate declare as Variant
Sub Mails_verschieben2()
Dim ursprung As Folder
Dim ziel As Folder
Dim ursprungItems As Items
Dim i As Long
' Not usual
Dim myDefaultStore As Store
Set myDefaultStore = Session.defaultStore
Set ursprung = Session.Folders(myDefaultStore.DisplayName).Folders("Posteingang")
'Set ursprung = Session.Folders(myDefaultStore.DisplayName).Folders("Inbox")
Debug.Print ursprung.name
' Standard
Set ursprung = Session.GetDefaultFolder(olFolderInbox)
Debug.Print ursprung.name
'Folder at same level as Inbox
Set ziel = ursprung.Parent.Folders("mini")
Debug.Print ziel.name
Set ursprungItems = ursprung.Items
ursprungItems.Sort "[ReceivedTime]", True ' newest first
' You could use .Restrict but in normal sized inboxes
' the time saved may not be noticeable.
For i = ursprungItems.count To 1 Step -1 'alle emails im Postfach durchgehen
' Verify that the item is a mailitem
' before attempting to return mailitem properties
If TypeOf ursprungItems(i) Is mailItem Then
With ursprungItems(i)
If .ReceivedTime < Date - 3 Then
'.FlagIcon = 5
'.FlagStatus = olFlagMarked
'.Save
'.Move ziel 'in Ordner verschieben
Debug.Print "Older mail."
Debug.Print " Subject: " & .Subject
Debug.Print " ReceivedTime: " & .ReceivedTime
Else
Debug.Print "Newer mail."
Debug.Print " Subject: " & .Subject
Debug.Print " ReceivedTime: " & .ReceivedTime
Exit For ' Stop when newer mail encountered.
End If
End With
Else
Debug.Print "Non-mailitem ignored."
End If
Next i
Debug.Print "Done."
End Sub

Make outlook 2003 macro work when word is the editor?

What I have, is a similar piece of code & i made it work with the outlook editor (hard enough) and I am trying to get it to now work with Word acting as the outlook editor. (Users are used to word mail) I tried: To move the code directly into word under this document and it did nothing. To follow code i saw on: creating an objword objdoc and then pairing it with the outlook class type of deal, with no luck. Here is a sample of code:
Sub SetCategory()
Dim olMessage As Outlook.MailItem
Set olMessage = Application.ActiveInspector.CurrentItem
If olMessage.SenderName = donations Then
olMessage.Categories = "donations"
ElseIf olMessage.SenderName = "Donations" Then
olMessage.Categories = "donations"
End If
With olMessage
.Send
End With
End Sub
When using "word mail" you are not using Outlook. This describes how to invoke Outlook from Word. Once Outlook is open you can use Outlook VBA.
http://www.howto-outlook.com/howto/senddocasmail.htm
Untested, and you will have to remove the parts you do not need.
Sub SendDocAsMail()
Dim oOutlookApp As Outlook.Application
Dim oItem As Outlook.MailItem
On Error Resume Next
'Start Outlook if it isn't running
Set oOutlookApp = GetObject(, "Outlook.Application")
If Err <> 0 Then
Set oOutlookApp = CreateObject("Outlook.Application")
End If
On Error GoTo 0 ' <=== Important to see errors now if there are any
'Create a new message
Set oItem = oOutlookApp.CreateItem(olMailItem)
' --------------------------
'Set oItem = oOutlookApp.ActiveInspector.CurrentItem
If oItem.SenderName = donations Then
oItem.Categories = "donations"
ElseIf oItem.SenderName = "Donations" Then
oItem.Categories = "donations"
End If
' --------------------------
'Allow the user to write a short intro and put it at the top of the body
Dim msgIntro As String
msgIntro = InputBox("Write a short intro to put above your default " & _
"signature and current document." & vbCrLf & vbCrLf & _
"Press Cancel to create the mail without intro and " & _
"signature.", "Intro")
'Copy the open document
Selection.WholeStory
Selection.Copy
Selection.End = True
'Set the WordEditor
Dim objInsp As Outlook.Inspector
Dim wdEditor As Word.Document
Set objInsp = oItem.GetInspector
Set wdEditor = objInsp.WordEditor
'Write the intro if specified
Dim i As Integer
If msgIntro = IsNothing Then
i = 1
'Comment the next line to leave your default signature below the document
wdEditor.Content.Delete
Else
'Write the intro above the signature
wdEditor.Characters(1).InsertBefore (msgIntro)
i = wdEditor.Characters.Count
wdEditor.Characters(i).InlineShapes.AddHorizontalLineStandard
wdEditor.Characters(i + 1).InsertParagraph
i = i + 2
End If
'Place the current document under the intro and signature
wdEditor.Characters(i).PasteAndFormat (wdFormatOriginalFormatting)
'Display the message
oItem.Display
'Clean up
Set oItem = Nothing
Set oOutlookApp = Nothing
Set objInsp = Nothing
Set wdEditor = Nothing
End Sub
Edit: Added, based on comment. This is a step that beginners trip on.
"Since this macro also uses Outlook functionality to create the mail we must add the reference to the project. To do this choose Tools-> References… and select Microsoft Outlook 12.0 Object Library (or 14.0 when using Outlook 2010). After this press OK."
Latest Outlook versions use Word as an email editor by default. There is no need to check out the editor type. The WordEditor property of the Inspector class returns the Microsoft Word Document Object Model of the message being displayed. You can read more about that in the Chapter 17: Working with Item Bodies .
Also you may find the How to automate Outlook and Word by using Visual C# .NET to create a pre-populated e-mail message that can be edited article helpful.

Excel VBA Email Rows to a Single Recipient

I have a worksheet that tracks invoices and I am trying to generate an auto-emailer that if a cell in column 12 contains AUTOEMAIL it will combine all of the rows with a similar email address which I've generated using a TRIM function. It will pull all of the like rows (Email Addresses based on column 15) into a LotusNotes Email. Ron De Bruin has some fantastic examples on his site. I attempted to write a loop which attempts to loop through and copy all rows based on an email address. When I go to run, the code does nothing but no errors are presented. There are instances online of this done in Outlook, but they don't apply to LotusNotes as the issue is late vs early binding. I'm newer to VBA automation as well.
Sub Send_Data()
Dim noSession As Object, noDatabase As Object, noDocument As Object
Dim vaRecipient As Variant
Dim rnBody As Range
Dim Data As DataObject
Const stSubject As String = "TEST"
Const stMsg As String = "TEST"
Const stPrompt As String = "Please select the range:"
lastrow = Range("N" & Rows.Count).End(xlUp).row
For Each Cell In Range("N8:N" & lastrow)
If WorksheetFunction.CountIf(Range("N8:N" & Cell.row), Cell) = 1 Then
If Cells(Cell.row, 11) = "AUTOEMAIL" Then
rnBody = "Hello" & vbNewLine & vbNewLine & _
ActiveCell.EntireRow.Select
On Error Resume Next
'The user canceled the operation.
If rnBody Is Nothing Then Exit Sub
On Error GoTo 0
'Instantiate Lotus Notes COM's objects.
Set noSession = CreateObject("Notes.NotesSession")
Set noDatabase = noSession.GETDATABASE("", "")
'Make sure Lotus Notes is open and available.
If noDatabase.IsOpen = False Then noDatabase.OPENMAIL
'Create the document for the e-mail.
Set noDocument = noDatabase.CreateDocument
'Copy the selected range into memory.
rnBody.Copy
'Retrieve the data from then copied range.
Set Data = New DataObject
Data.GetFromClipboard
'Add data to the mainproperties of the e-mail's document.
With noDocument
.Form = "Memo"
.SendTo = vaRecipient
.Subject = stSubject
'Retrieve the data from the clipboard.
.Body = stMsg & " " & Data.GetText
.SaveMessageOnSend = True
End With
' SEND EMAIL
With noDocument
.PostedDate = Now()
.Send 0, vaRecipient
End With
' REMOVE FROM MEMORY
Set noDocument = Nothing
Set noDatabase = Nothing
Set noSession = Nothing
'SWITCH BACK TO EXCEL
AppActivate "Microsoft Excel"
'EMPTY COPY-PAST CLIPBOARD
Application.CutCopyMode = False
' DISPLAYS TO USER IF SUCCESSFUL
MsgBox "Complete!", vbInformation
End If
End If
Next Cell
End Sub
I set the email body range as a Prompt Box where the user could highlight the cells and then another prompt box in which it asked for the email that was created using a TRIM() function. I realized that the way the code was set-up would not allow for what I wanted to do. The new method works quite well
Treevar

Outlook Forms: Importing / VLOOKUP Data from Excel?

I am a bit new to Outlook forms, but not to VBA overall - nor HTML/Web design of forms. However, my problem is finding a way to combine the two.
I am trying to design a form for users to fill out, and based on what they fill out in drop-down box's, it will then tell them what we want them to attach in the email. Currently we have this done in Excel, based on dropbox's it then VLOOKUPS to the 2nd Spreadsheet that contains the forms required.
Is there anyway I can bring in the Excel with the VLOOKUP behind the scenes in my VBA Outlook Form so that it can look-up what attachments we want the user to do? Otherwise, it would be a TON of SELECT CASE statements in VBA =/
This seems to the do the trick for me.
Some of it I have cobbled together from sites like this, the rest has been created by myself from scratch.
When I click my button:
An input box appears, which is the value that will be looked up in the spreadsheet.
it looks in the range (specified in the code), for a match
returns the value, two columns to the left of it.
when it finds a match it puts it in the Subject line in Outlook.
Dim jobno As String
Dim Proj As String
Sub Test()
jobno = InputBox("Job Number?", "Test")
GetNameFromXL
If jobno <> "" Then
Set myItem = Application.CreateItem(0)
If Proj <> "" Then
myItem.Subject = jobno & " - " & Proj & " - " & Format(Date, "dd.mm.yy")
Else
myItem.Subject = jobno & " - " & Format(Date, "dd.mm.yy")
End If
myItem.Display
Else
Exit Sub
End If
End Sub
Sub GetNameFromXL()
'Late binding. No reference to Excel Object required.
Dim xlApp As Object
Dim xlWB As Object
Dim xlWS As Object
Set xlApp = CreateObject("Excel.Application")
'Open the spreadsheet to get data
Set xlWB = xlApp.Workbooks.Open("X:\...\FILENAME.xlsx") ' <-- Put your file path and name here
Set xlWS = xlWB.Worksheets(1) ' <-- Looks in the 1st Worksheet
Debug.Print "-----Start of 'For Each' loop"
For Each c In xlWS.Range("A6:A100") 'Change range to value you want to 'VLookUp'
Proj = c.Offset(0, 2).Value 'This looks at the 2nd column after the range above
Debug.Print c & Proj
If jobno = c Then
Debug.Print "-----Match Found: " & jobno & " = " & Proj
GoTo lbl_Exit
Else
End If
Next c
Debug.Print "-----End of For Each loop"
MsgBox jobno & " not found in WorkBook."
'Clean up
Set xlWS = Nothing
Set xlWB = Nothing
Set c = Nothing
Proj = ""
xlApp.Quit
Set xlApp = Nothing
lbl_Exit:
Exit Sub
End Sub