How to make a single, non-duplicating, Outlook Calendar Entry? - excel

I'm trying to run some VBA code in Excel. When the file is saved I'd like to add a calendar entry.
I have the basics working using the following code:
Private Sub CreateAppointment()
Set olOutlook = CreateObject("Outlook.Application")
Set Namespace = olOutlook.GetNamespace("MAPI")
Set oloFolder = Namespace.GetDefaultFolder(9)
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 3 To LastRow
Description = Cells(i, 6).Value
StartDate = Cells(i, 5).Value
Set Appointment = oloFolder.Items.Add
With Appointment
.Start = StartDate
.Subject = Description
.Save
End With
Next i
End Sub
Each time the sheet is saved it creates a duplicate entry.
How do I adjust this code so only one instance is added as a calendar entry in Outlook?

Indicate in the Excel document that the row was processed.
Update a cell in an unused column the row.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
' If desperate declare as Variant
Private Sub CreateAppointment_VerifyCellInRow()
Dim oOutlook As Object
Dim oNamespace As Object
Dim oFolder As Object
Dim oAppointment As Object
Dim LastRow As Long
Dim i As Long
Dim startDate 'As Date
Dim Description As String
Dim createIndicator As String
Set oOutlook = CreateObject("Outlook.Application")
Set oNamespace = oOutlook.GetNamespace("MAPI")
Set oFolder = oNamespace.GetDefaultFolder(9)
createIndicator = "Added"
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 3 To LastRow
If Cells(i, 7).Value <> createIndicator Then
startDate = Cells(i, 5).Value
'StartDate = Format(Date + 1, "ddddd ") & "03:00 PM"
Debug.Print startDate
Description = Cells(i, 6).Value
'Description = "Test"
Debug.Print Description
Set oAppointment = oFolder.Items.Add
With oAppointment
.Start = startDate
.Subject = Description
.Save
Cells(i, 7).Value = createIndicator
End With
End If
Next i
End Sub

If creating appointments where the source document cannot be updated to indicate that the appointment was processed.
Option Explicit ' Consider this mandatory
' Tools | Options | Editor tab
' Require Variable Declaration
' If desperate declare as Variant
Private Sub CreateAppointment_SearchCalendar()
Dim oOutlook As Object
Dim oNamespace As Object
Dim oFolder As Object
Dim oAppt As Object
Dim Description As String
Dim startDate As Date
Dim LastRow As Long
Dim i As Long
Set oOutlook = CreateObject("Outlook.Application")
Set oNamespace = oOutlook.GetNamespace("MAPI")
Set oFolder = oNamespace.GetDefaultFolder(9)
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
For i = 3 To LastRow
startDate = Cells(i, 5).Value
'startDate = Format(Date + 1, "ddddd ") & "03:00 PM"
Debug.Print startDate
Description = Cells(i, 6).Value
'Description = "Test"
Debug.Print Description
If Calendar_ApptExists(oFolder, startDate, Description) = False Then
Set oAppt = oFolder.Items.Add
With oAppt
.Start = startDate
.Subject = Description
.Save
Debug.Print "Appointment created."
End With
Else
Debug.Print "Existing appointment at " & startDate & " with subject: " & Description
End If
Next
End Sub
Function Calendar_ApptExists(oCalendar As Object, appt_Start As Date, appt_subject As String) As Boolean
Dim oCalendarItems As Object
Dim oAppt As Object
Dim strFilter As String
Dim strFilter2 As String
Dim oCalendarItemsDate As Object
Dim oCalendarItemsDateSubject As Object
Set oCalendarItems = oCalendar.Items
' Items in calendar
Debug.Print "Items in Calendar: " & oCalendarItems.Count
' Appointments with appt_Start
strFilter = "[Start] = " & Chr(34) & Format(appt_Start, "ddddd hhmm AM/PM") & Chr(34)
Debug.Print " " & strFilter
Set oCalendarItemsDate = oCalendarItems.Restrict(strFilter)
Debug.Print " Appointments starting at " & appt_Start & ": " & oCalendarItemsDate.Count
strFilter2 = strFilter & " and " & "[Subject] = '" & appt_subject & "'"
Debug.Print " " & strFilter2
Set oCalendarItemsDateSubject = oCalendarItems.Restrict(strFilter2)
Debug.Print " strFilter2 appointments: " & oCalendarItemsDateSubject.Count
If oCalendarItemsDate.Count > 0 Then
Debug.Print " Existing appointment at same time."
If oCalendarItemsDateSubject.Count > 0 Then
Debug.Print " Existing appointment with same time and subject."
Calendar_ApptExists = True
End If
End If
End Function

After calling Save, read the EntryID property and save it. Next time (if it is set), you can call Namespace.GetItemFromID instead of Folder.Items.Add.

Related

How to extract email data based on Date & subject using VBA?

I have a code that is extracting the emails based on subject. But I wanted to extract the mails based on date as well. So it should be the intersection of Date & subject, only if both condition satisfies I should get the extracted data. Just with the subject condition the code works fine, but when I am adding the date condition, it's not picking up correctly.
For eg: I want to extract yesterday's email with subject line as "Volume data". what am I doing wrong in the code? Can someone help please?
Option Explicit
Sub FinalMacro()
Application.DisplayAlerts = False
Dim wkb As Workbook
Set wkb = ThisWorkbook
Sheets("Sheet1").Cells.Clear
' point to the desired email
Const strMail As String = "emailaddress"
Dim oApp As Outlook.Application
Dim oMapi As Outlook.MAPIFolder
Dim oItem As Object
On Error Resume Next
Set oApp = GetObject(, "OUTLOOK.APPLICATION")
If (oApp Is Nothing) Then Set oApp = CreateObject("OUTLOOK.APPLICATION")
On Error GoTo 0
Set oMapi = oApp.GetNamespace("MAPI").Folders(strMail).Folders("inbox") 'Folders("Others")
For Each oItem In oMapi.Items
If oItem.Subject = "Volume data" & oItem.ReceivedTime = Date Then
'If oItem.ReceivedTime = Date Then
Dim HTMLdoc As MSHTML.HTMLDocument
Dim tables As MSHTML.IHTMLElementCollection
Dim table As MSHTML.HTMLTable
Set HTMLdoc = New MSHTML.HTMLDocument
With HTMLdoc
.Body.innerHTML = oItem.HTMLBody
Set tables = .getElementsByTagName("table")
End With
Dim t As Long, r As Long, c As Long
Dim eRow As Long
For t = 0 To tables.Length - 1
eRow = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
For r = 0 To (tables(t).Rows.Length - 1)
For c = 0 To (tables(t).Rows(r).Cells.Length - 1)
Range("A" & eRow).Offset(r, c).Value = tables(t).Rows(r).Cells(c).innerText
Next c
Next r
eRow = Sheets(1).Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
Next t
Cells(eRow, 1) = "Date & Time of Receipt:" & " " & oItem.ReceivedTime
Cells(eRow, 1).Interior.Color = vbRed
Cells(eRow, 1).Font.Color = vbWhite
Cells(eRow, 1).Columns.AutoFit
Set oApp = Nothing
Set oMapi = Nothing
Set HTMLdoc = Nothing
Set tables = Nothing
'End If
End If
Next oItem
wkb.Save
Application.DisplayAlerts = True
End Sub
Please, test the next adapted code:
Sub FinalMacro()
Dim wkb As Workbook: Set wkb = ThisWorkbook
'Sheets("Sheet1").cells.Clear 'uncomment if you need to start from the first row...
' point to the desired email
Const strMail As String = "emailaddress"
Dim oApp As Outlook.Application, oMapi As Outlook.MAPIFolder, oItem As Outlook.MailItem
Dim destCell As Range, i As Long
With ActiveSheet
Set destCell = .cells(rows.count, "A").End(xlUp) 'last cell where from to extract the last date
End With
On Error Resume Next
Set oApp = GetObject(, "OUTLOOK.APPLICATION")
If (oApp Is Nothing) Then Set oApp = CreateObject("OUTLOOK.APPLICATION")
On Error GoTo 0
Dim HTMLdoc As MSHTML.HTMLDocument, tables As MSHTML.IHTMLElementCollection
Dim table As MSHTML.HTMLTable
Dim t As Long, r As Long, c As Long, eRow As Long
Set oMapi = oApp.GetNamespace("MAPI").Folders(strMail).Folders("inbox") 'Folders("Others")
'the necessary elements to extract only the necessary mails:_______________________________________________
Dim startDate As String, endDate As String, flt As String
startDate = CStr(Date) & " " & "00:00" 'Date can be replaced with any string Date
endDate = CStr(Date + 1) & " " & "00:00" 'the same, it should be the previous Date +1
flt = "[Subject] = 'Volume data' and [ReceivedTime] >= '" & startDate & "' and [ReceivedTime] < '" & endDate & "'"
Dim myItems As Outlook.items
Set myItems = oMapi.items.Restrict(flt) '____________________________________________________________
Application.DisplayAlerts = False
For Each oItem In myItems
Set HTMLdoc = New MSHTML.HTMLDocument
With HTMLdoc
.body.innerHTML = oItem.HtmlBody
Set tables = .getElementsByTagName("table")
End With
For t = 0 To tables.Length - 1
eRow = ActiveSheet.cells(rows.count, 1).End(xlUp).row + 1
For r = 0 To (tables(t).rows.Length - 1)
For c = 0 To (tables(t).rows(r).cells.Length - 1)
Range("A" & eRow).Offset(r, c).value = tables(t).rows(r).cells(c).innerText
Next c
Next r
eRow = ActiveSheet.cells(rows.count, 1).End(xlUp).Offset(1, 0).row
Next t
cells(eRow, 1) = "Date & Time of Receipt:" & " " & oItem.ReceivedTime
cells(eRow, 1).Interior.color = vbRed
cells(eRow, 1).Font.color = vbWhite
cells(eRow, 1).Columns.AutoFit
Next oItem
wkb.Save
Application.DisplayAlerts = True
End Sub
endDate is necessary only if you choose for filtering a Date in the past.
Not tested, of course, I do not have the necessary data, but this should be the idea. I tested only the filtering part and it worked as needed.
Edited:
Now, there are some variants in order to build the necessary start/end date, in order to fulfill different cases:
To process mails received from 12th of October 2021 till the end of the month, use the next definitions:
startDate = CStr(DateSerial(2021, 10, 12)) & " " & "00:00"
endDate = CStr(DateSerial(2021, 11, 1)) & " " & "00:00"
To process mails received today after 12 o'clock, use the next definitions:
startDate = CStr(Date) & " " & "12:00"
endDate = CStr(Date + 1) & " " & "00:00"
In such a case the filter (flt) string definition may miss the endDate part, which does not matter too much in such a context...
Since your code records oItem.ReceivedTime as cells(eRow, 1) = "Date & Time of Receipt:" & " " & oItem.ReceivedTime, the last recorded time can be extracted and process all mails received after that specific time:
'1. comment the next existing code line:
'Sheets("Sheet1").Cells.Clear
`2. declare the next new (necessary) variables:
Dim destCell As Range, lastOne As String, arrD, arrS
Set DestCell = ActiveSheet.cells(rows.count, "A").End(xlUp)
arrD = Split(destCell.value, " "): arrS = Split(arrD(6), ":")
lastOne = arrD(5) & " " & arrS(0) & ":" & arrS(1)
`3. Change the filter string:
flt = "[Subject] = 'Volume data' and [ReceivedTime] > '" & lastOne & "'"
If something not clear enough, do not hesitate to ask for clarifications. But after you tried understanding how it works and deduce where a mistake could appear and why...
Our mission here is not to supply free code samples, it is to make as many as possible users LEARN...

Search Inbox by Subject, Sender and Date for each email address in Excel range

I search the inbox folder for given subject, sender from column 1 and date.
Based on the result it should populate rows in column 2 with Yes or No. But it populates all rows as No. I'm sure I should see at least one Yes.
The value of variable i is always nothing. Looks like it is a problem with filterstring variable.
Sub searchemailsreceived()
Application.ScreenUpdating = False
ThisWorkbook.Activate
Dim ol As outlook.Application
Dim ns As outlook.Namespace
Dim fol As outlook.Folder
Dim i As Object
Dim mi As outlook.MailItem
Dim filterstring As String
Dim dmi As outlook.MailItem
Dim lstRow As Long
Dim rng As Range
ThisWorkbook.Sheets("Sheet1").Activate
lstRow = Cells(Rows.Count, 2).End(xlUp).Row
Set rng = Range("A2:A" & lstRow)
Set ol = New outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.GetDefaultFolder(olFolderInbox)
Set dmi = ol.CreateItem(olMailItem)
For Each cell In rng
filterstring = "#SQL=(""urn:schemas:httpmail:fromemail"" LIKE '%" & Range(cell.Address).Offset(0, 0).Value2 & "%' AND ""urn:schemas:httpmail:subject"" LIKE '%This is my subject%' AND ""urn:schemas:httpmail:datereceived"" >= '4/1/2021 12:00 AM')"
For Each i In fol.Items.Restrict(filterstring)
If i.Class = olMail Then
Range(cell.Address).Offset(0, 1).Value2 = "Yes"
GoTo landhere
End If
Next i
Range(cell.Address).Offset(0, 1).Value2 = "No"
landhere:
Next cell
Set mi = Nothing
Set dmi = Nothing
Set ol = Nothing
Application.ScreenUpdating = False
End Sub
Try the following cleaned function (untested):
Sub SearchEmailsReceived()
Application.ScreenUpdating = False
Dim ol As Outlook.Application: Set ol = New Outlook.Application
Dim fol As Outlook.MAPIFolder: Set fol = ol.GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim lstRow As Long: lstRow = ws.Cells(Rows.Count, 2).End(xlUp).Row
Dim rng As Range: Set rng = ws.Range("A2:A" & lstRow)
Dim i As Object, filterstring As String, Cell As Range
Dim dmi As Outlook.MailItem: Set dmi = ol.CreateItem(olMailItem)
For Each Cell In rng
filterstring = "#SQL=urn:schemas:httpmail:fromemail LIKE '%" & Cell.Value2 & "%' AND urn:schemas:httpmail:subject LIKE '%This is my subject%' AND urn:schemas:httpmail:datereceived >= '4/1/2021 12:00 AM'"
Cell.Offset(0, 1) = "No"
For Each i In fol.Items.Restrict(filterstring)
If i.Class = olMail Then Cell.Offset(0, 1) = "Yes"
Next i
Next Cell
Set dmi = Nothing
Set ol = Nothing
Application.ScreenUpdating = False
End Sub
The answer linked by #niton shows the SQL=urn... doesn't contain quote marks so they have been removed. You may want to cut down the filterstring and test whether each additional AND statement causes issues though. Maybe comment out subject and date to test whether it finds any e-mails from the recipients first then work them back in the further requirements once you know the base works ok
the fromemail schema didn't work for me. what worked for me is ""http://schemas.microsoft.com/mapi/proptag/0x0065001f"" CI_STARTSWITH '%" & Cell.Value2 & "%'
Thank you for helping.
Demonstration with "urn:schemas:httpmail:fromemail" and "proptag/0x0065001f".
Option Explicit
Sub searchemailsreceived_Demo()
'Application.ScreenUpdating = False
Dim ol As Outlook.Application
Dim ns As Outlook.Namespace
Dim fol As Outlook.Folder
Dim folItems As Outlook.Items
Dim folItemsSQL As Outlook.Items
Dim folItems1SQL As Outlook.Items
Dim folItems2SQL As Outlook.Items
Dim folItems3SQL As Outlook.Items
Dim i As Long
Dim filterString1 As String
Dim filterString2 As String
Dim filterString3 As String
Dim filterStringSQL As String
Dim filterString1SQL As String
Dim filterString2SQL As String
Dim filterString3SQL As String
Dim lastRowColA As Long
Dim rng As Range
Dim cell As Object
Dim foundFlag As Boolean
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Sheets("Sheet1")
lastRowColA = Cells(Rows.Count, 1).End(xlUp).Row
Set rng = Range("A2:A" & lastRowColA)
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
Set fol = ns.GetDefaultFolder(olFolderInbox)
Set folItems = fol.Items
Debug.Print "folItems.Count..: " & folItems.Count
For Each cell In rng
'filterString1 = """http://schemas.microsoft.com/mapi/proptag/0x0065001f"" CI_STARTSWITH '%" & cell.Value2 & "%'"
' or
' Based on sample data, a filter without wildcards may be preferable.
' Col A = email addresses starting in row 2
Debug.Print
filterString1 = """urn:schemas:httpmail:fromemail"" LIKE '" & cell.Value2 & "'"
Debug.Print "filterString1 ..: " & filterString1
filterString1SQL = "#SQL=(" & filterString1 & ")"
Debug.Print "filterString1SQL: " & filterString1SQL
Set folItems1SQL = folItems.Restrict(filterString1SQL)
Debug.Print "folItems1SQL.Count.: " & folItems1SQL.Count
' Condition 1
foundFlag = False
For i = 1 To folItems1SQL.Count
If folItems1SQL(i).Class = olMail Then
Debug.Print "i = " & i
Debug.Print " - folItems1SQL(i).SenderEmailAddress: " & folItems1SQL(i).SenderEmailAddress
cell.Offset(0, 2).Value2 = "Yes"
foundFlag = True
Exit For
End If
Next
If foundFlag = False Then
cell.Offset(0, 2).Value2 = "No"
End If
' Condition 2
Dim strSubject As String
strSubject = "test"
Debug.Print
filterString2 = """urn:schemas:httpmail:subject"" LIKE '%" & strSubject & "%'"
Debug.Print "filterString2 ..: " & filterString2
filterString2SQL = "#SQL=(" & filterString2 & ")"
Debug.Print "filterString2SQL: " & filterString2SQL
Set folItems2SQL = folItems.Restrict(filterString2SQL)
Debug.Print "folItems2SQL.Count.: " & folItems2SQL.Count
foundFlag = False
For i = 1 To folItems2SQL.Count
If folItems2SQL(i).Class = olMail Then
Debug.Print "i = " & i
Debug.Print " - folItems2SQL(i).Subject: " & folItems2SQL(i).Subject
cell.Offset(0, 3).Value2 = "Yes"
foundFlag = True
Exit For
End If
Next
If foundFlag = False Then
cell.Offset(0, 3).Value2 = "No"
End If
' Condition 3
Dim strDate As String
strDate = "2021/04/01 12:00 AM"
Debug.Print
filterString3 = """urn:schemas:httpmail:datereceived"" >= '" & strDate & "'"
Debug.Print "filterString3: " & filterString3
filterString3SQL = "#SQL=(" & filterString3 & ")"
Debug.Print "filterString3SQL: " & filterString3SQL
Set folItems3SQL = folItems.Restrict(filterString3SQL)
Debug.Print "folItems3SQL.Count : " & folItems3SQL.Count
foundFlag = False
For i = 1 To folItems3SQL.Count
If folItems3SQL(i).Class = olMail Then
Debug.Print "i = " & i
Debug.Print " - folItems3SQL(i).ReceivedTime: " & folItems3SQL(i).ReceivedTime
cell.Offset(0, 4).Value2 = "Yes"
foundFlag = True
Exit For
End If
Next
If foundFlag = False Then
cell.Offset(0, 4).Value2 = "No"
End If
' Condition 1 AND Condition 2 AND Condition 3
Debug.Print
Debug.Print filterString1
Debug.Print filterString2
Debug.Print filterString3
filterStringSQL = "#SQL=(" & filterString1 & " AND " & filterString2 & " AND " & filterString3 & ")"
Debug.Print "filterStringSQL: " & filterStringSQL
Set folItemsSQL = folItems.Restrict(filterStringSQL)
Debug.Print "folItemsSQL.Count : " & folItemsSQL.Count
foundFlag = False
For i = 1 To folItemsSQL.Count
If folItemsSQL(i).Class = olMail Then
Debug.Print "i = " & i
Debug.Print " - folItemsSQL(i).SenderEmailAddress: " & folItemsSQL(i).SenderEmailAddress
Debug.Print " - folItemsSQL(i).Subject...........: " & folItemsSQL(i).Subject
Debug.Print " - folItemsSQL(i).ReceivedTime......: " & folItemsSQL(i).ReceivedTime
Debug.Print
cell.Offset(0, 1).Value2 = "Yes"
foundFlag = True
Exit For
End If
Next
If foundFlag = False Then
cell.Offset(0, 1).Value2 = "No"
End If
Next cell
Application.ScreenUpdating = True
End Sub
Actually I tried a smaller and it worked but thank you.
Sub searchemailsreceived()
Application.ScreenUpdating = False
ThisWorkbook.Activate
Dim ol As Outlook.Application: Set ol = New Outlook.Application
Dim ns As Outlook.Namespace: Set ns = ol.GetNamespace("MAPI")
Dim fol As Outlook.Folder: Set fol = ns.GetDefaultFolder(olFolderInbox)
Dim filterstring As String
Dim lstRow As Long: lstRow = Cells(Rows.Count, 2).End(xlUp).Row
Dim rng As Range: Set rng = Range("A2:A" & lstRow)
ThisWorkbook.Sheets("Sheet1").Activate
For Each Cell In rng
filterstring = "#SQL=(""http://schemas.microsoft.com/mapi/proptag/0x0065001f"" CI_STARTSWITH '%" & Cell.Value2 & "%' AND ""urn:schemas:httpmail:subject"" LIKE '%This is a subject%' AND ""urn:schemas:httpmail:datereceived"" >= '1/1/2000 12:00 AM')"
Range(Cell.Address).Offset(0, 2).Value2 = fol.Items.Restrict(filterstring).Count
filterstring = ""
Next Cell
Set ol = Nothing
Application.ScreenUpdating = False
End Sub

Shared Calendar with Outlook loaded from Citrix

I have the following function in Excel to access shared calendar folders in Outlook and list all certain appointments (identified from its subject) within specified date range.
The code seems doesn't work as expected as Outlook is loaded from Citrix server.
I'm not so sure about this and need somebody's help on how to solve this.
Option Explicit
Function GetColleagueAppointments(dtStartAppt As Date, dtEndAppt As Date, strUserName As String) 'As String
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
' Purpose: List down all colleague's client meetings between date range
'
' Inputs: dtStartAppt Start date to search
' dtEndAppt End date to search
' strUserName Colleague calendars to search
'
' Assumptions: * User must have access to the appropriate shared calendars in
' Outlook
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dim objOL As New Outlook.Application ' Outlook
Dim objNS As NameSpace ' Namespace
Dim OLFldr As Outlook.MAPIFolder ' Calendar folder
Dim OLAppt As Object ' Single appointment
Dim OLRecip As Outlook.Recipient ' Outlook user name
Dim OLAppts As Outlook.Items ' Appointment collection
Dim oFinalItems As Outlook.Items
Dim strRestriction As String ' Day for appointment
Dim strList() As String ' List of all available timeslots
Dim dtmNext As Date ' Next available time
Dim intDuration As Integer ' Duration of free timeslot
Dim i As Integer ' Counter
Dim lr As Long, r As Long
Dim wb As Workbook
Dim ws As Worksheet
'FastWB True
Set wb = ThisWorkbook
Set ws = wb.Sheets("Meeting List")
Const C_Procedure = "GetColleagueAppointments" ' Procedure name
'This is an enumeration value in context of getDefaultSharedFolder
Const olFolderCalendar As Byte = 9
strRestriction = "[Start] >= '" & _
Format$(dtStartAppt, "mm/dd/yyyy hh:mm AMPM") _
& "' AND [End] <= '" & _
Format$(dtEndAppt, "mm/dd/yyyy hh:mm AMPM") & "'"
' loop through shared Calendar for all Employees in array
Set objNS = objOL.GetNamespace("MAPI")
With ws
On Error Resume Next
Set OLRecip = objNS.CreateRecipient(strUserName)
OLRecip.Resolve
'If OLRecip.Resolved Then
'Set olFolder = olNS.GetSharedDefaultFolder(objOwner, olFolderCalendar)
Set OLFldr = objNS.GetSharedDefaultFolder(OLRecip, olFolderCalendar)
'End If
' calendar not shared
If Err.Number <> 0 Then
'# Employee Date Start End Client Agenda Location
r = Last(1, .Columns("G")) + 1
.Range("F" & r).Value = r - 1 '#
.Range("G" & r).Value = strUserName 'Employee
.Range("H" & r).Value = "Calendar not shared" 'Format(dtStartAppt, "d-mmm-yyyy") 'Date
.Range("I" & r).Value = "Calendar not shared" 'Start
.Range("J" & r).Value = "Calendar not shared" 'End
.Range("K" & r).Value = "Calendar not shared" 'Client
.Range("L" & r).Value = "Calendar not shared" 'Agenda
.Range("M" & r).Value = "Calendar not shared" 'Location
GoTo ExitHere
End If
'On Error GoTo ErrHandler
Set OLAppts = OLFldr.Items
' Sort the collection (required by IncludeRecurrences)
OLAppts.Sort "[Start]"
' Make sure recurring appointments are included
OLAppts.IncludeRecurrences = True
' Filter the collection to include only the day's appointments
Set OLAppts = OLAppts.Restrict(strRestriction)
'Construct filter for Subject containing 'Client'
Const PropTag As String = "http://schemas.microsoft.com/mapi/proptag/"
strRestriction = "#SQL=" & Chr(34) & PropTag _
& "0x0037001E" & Chr(34) & " like '%Client%'"
' Filter the collection to include only the day's appointments
Set OLAppts = OLAppts.Restrict(strRestriction)
' Sort it again to put recurring appointments in correct order
OLAppts.Sort "[Start]"
With OLAppts
' capture subject, start time and duration of each item
Set OLAppt = .GetFirst
Do While TypeName(OLAppt) <> "Nothing"
r = Last(1, .Columns("G")) + 1
'- Client - HSBC - Trade Reporting
'# Employee Date Start End Client Agenda Location
If InStr(LCase(OLAppt.Subject), "client") > 0 Then
strList = Split(OLAppt.Subject, "-")
.Range("F" & r).Value = r - 1
.Range("G" & r).Value = strUserName
.Range("H" & r).Value = Format(dtStartAppt, "d-mmm-yyyy")
.Range("I" & r).Value = OLAppt.Start
.Range("J" & r).Value = OLAppt.End
.Range("K" & r).Value = Trim(CStr(strList(1)))
.Range("L" & r).Value = Trim(CStr(strList(2)))
.Range("J" & r).Value = OLAppt.Location
End If
Set OLAppt = .GetNext
Loop
End With
End With
ExitHere:
On Error Resume Next
Set OLAppt = Nothing
Set OLAppts = Nothing
Set objNS = Nothing
Set objOL = Nothing
Exit Function
ErrHandler:
MsgBox Err.Number & ": " & C_Procedure & vbCrLf & Err.Description
Resume ExitHere
End Function
Remove the first On Error Resume Next (the one below With ws) and post the line where the code is failing.
Also, you mentioned Outlook is run on a Citrix server. I hope you're running this script in the same instance where Outlook is running otherwise I'm not sure how you expect communication with the Outlook instance would happen.
I also hope you do have access to the shared calendar, something you didn't explicitly mention.

How to resolve vba runtime error 91, when trying to close a PPT

I wrote code to send automated birthday emails using Outlook and PPT. My code was working fine for a while and was getting the result as expected. All of the sudden, I started getting error 91 and debugging tool points to the line, where the PPT closes.
myDOBPPT.Close
I have declared the PPT and assigned a destination path for my template.
Any clues or solution on why this is occurring all of a sudden?
Option Explicit
Private Sub Btn_SendEmail_Click()
'Declaring Outlook
Dim OutlookApp As Outlook.Application
Dim OutlookMail As Outlook.MailItem
'Declaring Sender Outlook
Dim SenderOutlookApp As Outlook.Application
Dim SenderOutlookMail As Outlook.MailItem
'Declaring PPT
Dim objPPT As PowerPoint.Application
Dim myDOBPPT As PowerPoint.Presentation
Dim DestinationPPT As String
'Assigning Path of files
DestinationPPT = "C:\Users\charles.hill\Desktop\BirthdayAutomation\Birthday_Automation.pptx"
'Declaring and assigning values for varibales
Dim i As Long
i = 2
Dim randomslidenumber As Integer
Dim FirstSlide As Double
Dim LastSlide As Double
Dim Mydate As Date
Mydate = Date
'Declaring the Logo Image
Dim LogoImage As String
'Assigning Path of files
LogoImage = "C:\Users\charles.hill\Pictures\Saved Pictures\TIGA Logo.jpg"
'Worksheets("Emp_Details").Range("A2:A" & Range("A2").End(xlDown).Row).ClearContents
Application.ScreenUpdating = False
For i = 2 To Cells(Rows.Count, 1).End(xlUp).Row
Set objPPT = CreateObject("PowerPoint.Application")
objPPT.Visible = True
Set myDOBPPT = objPPT.Presentations.Open(DestinationPPT) 'PPT with birthday images opens
If Mydate = DateSerial(Year(Date), Month(Cells(i, 4).Value), Day(Cells(i, 4).Value)) Then
'Jump to Random Slide
With myDOBPPT
FirstSlide = 1
LastSlide = myDOBPPT.Slides.Count
Randomize
randomslidenumber = Int(((LastSlide - FirstSlide) * Rnd() + FirstSlide))
End With
With myDOBPPT.Slides(randomslidenumber)
.Shapes("NameOval").TextEffect.Text = WorksheetFunction.Proper(Sheet1.Cells(i, "B").Value) 'Employee's Name
.Shapes("DOB").TextEffect.Text = VBA.Format(Sheet1.Cells(i, "D").Value, "DD Mmm") 'Employee's DOB
.Export (ActiveWorkbook.Path & "\slide") & ".gif", "gif"
End With
Set OutlookApp = New Outlook.Application
Set OutlookMail = OutlookApp.CreateItem(olMailItem)
OutlookMail.To = Cells(i, 5).Value
OutlookMail.CC = Cells(i, 6).Value
OutlookMail.BCC = ""
OutlookMail.Subject = "Happy Birthday " & Cells(i, 2).Value & "!!"
OutlookMail.Attachments.Add (ActiveWorkbook.Path & "\slide.gif")
OutlookMail.HTMLBody = "Good Morning All" & "<br> <br>" & _
"Please join TIGA in wishing " & Cells(i, 2).Value & " " & Cells(i, 3).Value & " a Happy Birthday! Hope you have a fantastic day" & "<br> <br>" & _
"<center><img src='cid:slide.gif' height='576' width='768'/></center>" & "<br> <br>" & _
"Best Wishes and Regards," & "<br>" & "HR Team" & "<br> <br>" & _
"<img src='C:\Users\charles.hill\Pictures\Saved Pictures\TIGA Logo.jpg'/>"
OutlookMail.Display
OutlookMail.Send
'Updates Email Sent column to Yes
With Worksheets("Emp_Details").Cells(i, 7)
.Value = "Yes"
End With
End If
Next i
myDOBPPT.Close
Set myDOBPPT = Nothing
objPPT.Quit
Set objPPT = Nothing
Set OutlookMail = Nothing
Set OutlookApp = Nothing
On Error Resume Next
VBA.Kill (ActiveWorkbook.Path & "\slide.gif")
ActiveWorkbook.Save
MsgBox "Processing Done", vbInformation
MsgBox "Records Updated and Workbook saved", vbInformation
'Declaring variables for updating Email sent column and send birthday wishes log.
Dim RowNum As Integer
RowNum = 2
Dim CurrentDate As Date
CurrentDate = Date
Dim Last_Row
Dim xInspect As Object
Dim PageEditor As Object
Const wdFormatPlainText = 0
'Worksheets("Sheet1").Range("G2:G500").ClearContents
'For RowNum = 2 To Cells(Rows.Count, 1).End(xlUp).Row
' If Worksheets("Sheet1").Cells(RowNum, 4).Value = CurrentDate Then
' Worksheets("Sheet1").Cells(RowNum, 7).Value = "Yes"
'End If
'Next RowNum
'ActiveWorkbook.Save
'MsgBox "Records Updated and Workbook saved", vbInformation
Set SenderOutlookApp = New Outlook.Application
Set SenderOutlookMail = SenderOutlookApp.CreateItem(olMailItem)
Set xInspect = SenderOutlookMail.GetInspector
Set PageEditor = xInspect.WordEditor
Last_Row = Worksheets("Emp_Details").Range("A" & Rows.Count).End(xlUp).Row
Worksheets("Log").Range("A2:I500").ClearContents
For RowNum = 2 To Last_Row
If Worksheets("Emp_Details").Cells(RowNum, "G").Value = "Yes" Then
Worksheets("Emp_Details").Rows(RowNum).Copy Destination:=Sheets("Log").Range("A" & Rows.Count).End(xlUp).Offset(1, 0)
End If
Next RowNum
Worksheets("Log").UsedRange.Copy
With SenderOutlookMail
.To = "sreenandini.jayaram#tiga.us"
.CC = ""
.BCC = ""
.Subject = "Birthday Wishes Log" & " " & Date
.Body = "Birthday wishes were sent out to the following Employees" & vbCrLf
.Display
PageEditor.Application.Selection.Start = Len(.Body)
PageEditor.Application.Selection.End = PageEditor.Application.Selection.Start
PageEditor.Application.Selection.PasteAndFormat Type:=wdFormatPlainText
.Display
.Send
Set PageEditor = Nothing
Set xInspect = Nothing
End With
Set SenderOutlookMail = Nothing
Set SenderOutlookApp = Nothing
Application.ScreenUpdating = True
End Sub 'Ending Button Click Sub-routine
You are getting that error because you are initializing the object inside the loop and trying to close it outside the loop. If the code doesn't enter the loop then myDOBPPT will be Nothing
For i = 2 To Cells(Rows.Count, 1).End(xlUp).Row
'
'
'
Set myDOBPPT = objPPT.Presentations.Open(DestinationPPT)
'
'
'
Next i
myDOBPPT.Close
You can also test it by changing myDOBPPT.Close to the below.
For i = 2 To Cells(Rows.Count, 1).End(xlUp).Row
'
'
'
Set myDOBPPT = objPPT.Presentations.Open(DestinationPPT)
'
'
'
Next i
If myDOBPPT Is Nothing Then
MsgBox "myDOBPPT is nothing"
End If
Move it inside the loop
For i = 2 To Cells(Rows.Count, 1).End(xlUp).Row
'
'
'
Set myDOBPPT = objPPT.Presentations.Open(DestinationPPT)
'
'
'
myDOBPPT.Close
Next i

Choose different email body based on cell value

There are 3 body contents to be picked based on the value in D column.
1) if "D" column value is "High" then bodycontent1 should be selected
2) if "D" column value is "Medium" then bodycontent2 should be selected
3) if "D" column value is "Low" then bodycontent3 should be selected
The below code just picks the bodycontent1 for any criteria.
Code:
Option Explicit
Public Sub Example()
Dim olApp As Outlook.Application
Dim olNs As Outlook.Namespace
Dim Inbox As Outlook.MAPIFolder
Dim Item As Variant
Dim MsgFwd As MailItem
Dim Items As Outlook.Items
Dim Email As String
Dim Email1 As String
Dim ItemSubject As String
Dim lngCount As Long
Dim i As Long
Dim RecipTo As Recipient
Dim RecipCC As Recipient
Dim RecipBCC As Recipient
Dim onbehalf As Variant
Dim EmailBody As String
Dim BodyName As String
Dim Bodycontent1 As String
Dim Bodycontent2 As String
Dim Bodycontent3 As String
Dim Criteria1 As String
Set olApp = CreateObject("Outlook.Application")
Set olNs = olApp.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Set Items = Inbox.Items
i = 2 ' i = Row 2
With Worksheets("Sheet1") ' Sheet Name
Do Until IsEmpty(.Cells(i, 1))
ItemSubject = .Cells(i, 1).Value '(i, 1) = (Row 2,Column 1)
Email = .Cells(i, 16).Value '(i, 2) = (Row 2,Column 2)
Email1 = .Cells(i, 2).Value
Criteria1 = .Cells(i, 4).Value
Bodycontent1 = "Hello this is for testing purpose1" & "<BR>" & _
"Regards," & "<BR>" & _
"Kelvin"
Bodycontent2 = "Hello this is for testing purpose2" & "<BR>" & _
"Regards," & "<BR>" & _
"Kelvin"
Bodycontent3 = "Hello this is for testing purpose3" & "<BR>" & _
"Regards," & "<BR>" & _
"Kelvin"
'// Loop through Inbox Items backwards
For lngCount = Items.Count To 1 Step -1
Set Item = Items.Item(lngCount)
If Item.Subject = ItemSubject Then ' if Subject found then
Set MsgFwd = Item.Forward
Set RecipTo = MsgFwd.Recipients.Add(Email1)
Set RecipTo = MsgFwd.Recipients.Add("secnww#hp.com")
Set RecipBCC = MsgFwd.Recipients.Add(Email)
MsgFwd.SentOnBehalfOfName = "doc#hp.com"
BodyName = .Cells(i, 3).Value
RecipTo.Type = olTo
RecipBCC.Type = olBCC
Debug.Print Item.Body
If Criteria1 = "high" Then
MsgFwd.HTMLBody = Bodycontent1 & Item.HTMLBody
ElseIf Criteria1 = "medium" Then
MsgFwd.HTMLBody = Bodycontent2 & Item.HTMLBody
Else 'If Criteria1 = "Low" Then
MsgFwd.HTMLBody = Bodycontent3 & Item.HTMLBody
MsgFwd.Display
End If
End If
Next ' exit loop
i = i + 1 ' = Row 2 + 1 = Row 3
Loop
End With
Set olApp = Nothing
Set olNs = Nothing
Set Inbox = Nothing
Set Item = Nothing
Set MsgFwd = Nothing
Set Items = Nothing
MsgBox "Mail sent"
End Sub
You should use Select Case rather than If/ElseIf
See the part about LastRow which is clear than Loop+i=i+1
I've added an Exit For (commented), in case you want to gain time, and only forward the 1st message with the subject you're looking for!
Final code :
Option Explicit
Public Sub Example()
Dim olApp As Outlook.Application
Dim olNs As Outlook.NameSpace
Dim Inbox As Outlook.MAPIFolder
Dim Item As Variant
Dim MsgFwd As MailItem
Dim wS As Worksheet
Dim Items As Outlook.Items
Dim Email As String
Dim Email1 As String
Dim ItemSubject As String
Dim lngCount As Long
Dim LastRow As Long
Dim i As Long
Dim BodyName As String
Dim Bodycontent1 As String
Dim Bodycontent2 As String
Dim Bodycontent3 As String
Dim Criteria1 As String
Set olApp = CreateObject("Outlook.Application")
Set olNs = olApp.GetNamespace("MAPI")
Set Inbox = olNs.GetDefaultFolder(olFolderInbox)
Set Items = Inbox.Items
Bodycontent1 = "Hello this is for testing purpose1" & "<BR>" & _
"Regards," & "<BR>" & _
"Kelvin"
Bodycontent2 = "Hello this is for testing purpose2" & "<BR>" & _
"Regards," & "<BR>" & _
"Kelvin"
Bodycontent3 = "Hello this is for testing purpose3" & "<BR>" & _
"Regards," & "<BR>" & _
"Kelvin"
Set wS = thisworkbook.Worksheets("Sheet1") ' Sheet Name
With wS
LastRow = .Range("A" & .rows.Count).End(xlup).Row
For i = 2 To LastRow
ItemSubject = .Cells(i, 1).value
Email = .Cells(i, 16).value
Email1 = .Cells(i, 2).value
Criteria1 = .Cells(i, 4).value
BodyName = .Cells(i, 3).value
'// Loop through Inbox Items backwards
For lngCount = Items.Count To 1 Step -1
Set Item = Items.Item(lngCount)
If Item.Subject <> ItemSubject Then
Else
'If Subject found then
Set MsgFwd = Item.Forward
With MsgFwd
.To = Email1 & " ; secnww#hp.com"
.BCC = Email
.SentOnBehalfOfName = "doc#hp.com"
Select Case LCase(Criteria1)
Case Is = "high"
.HTMLBody = Bodycontent1 & Item.HTMLBody
Case Is = "medium"
.HTMLBody = Bodycontent2 & Item.HTMLBody
Case Is = "low"
.HTMLBody = Bodycontent3 & Item.HTMLBody
Case Else
MsgBox "Criteria : " & Criteria1 & " not recognised!", _
vbCritical + vbOKOnly, "Case not handled"
End Select
.Display
'Exit For
End With 'MsgFwd
End If
Next lngCount
Next i
End With 'wS
Set olApp = Nothing
Set olNs = Nothing
Set Inbox = Nothing
Set Item = Nothing
Set MsgFwd = Nothing
Set Items = Nothing
MsgBox "Mail sent"
End Sub

Resources