How do I search between two date ranges in VBA? - excel

I have hit a stumbling block and could just do with some assistance please.
I have been tasked with creating a VBA Macro that looks between two date ranges in a shared inbox sub-folder in Outlook.
If the code finds any Excel documents attached to the emails within that date range it will extract the attachments and will put them into a designated shared drive folder. All of this i had working perfectly, however, I now need to change the code slightly so that on a separate sheet it adds the date the code was last ran (this I have working also) and when the code is next ran it takes the date it last ran as the "Date from" date and searches between that date and whatever the date and time is of the time you are trying to execute the code again - this is where my code isn't working.
Here is my code so far (I am not a VBA expert and some terminology may not be correct so please go easy on my code) - I have had to replace certain sensitive info with "xxxxxx" in the code below.
Sub saveOutlookAttachments()
' For this to work, you need to ensure "Microsoft Office 16.0 Object Library" is ticked
' You can find the object library in Tools -> References
' -- start of initialise all the outlook library details needed
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 at As Outlook.attachment
Dim Folder As MAPIFolder
Dim OutlookMail As Variant
' -- end of initialise all the outlook library details needed
' -- start of Date from and to initialising
Dim DateStart As Date
Dim DateEnd As Date
Dim DateToCheck As String
' Get the date last ran
If Sheet2.Cells(1, 2) <> "" Then
Sheet1.Cells(2, 2) = Sheet2.Cells(1, 2)
Debug.Print "Start Date is: "; Sheet1.Cells(2, 2)
Sheet1.Cells(2, 4) = Now()
Debug.Print "End Date is: "; Sheet1.Cells(2, 4)
Else
Sheet2.Cells(1, 2) = ""
Debug.Print Sheet1.Cells(2, 2)
End If
DateStart = Sheet1.Cells(2, 2) ' Cell B2
DateEnd = Sheet1.Cells(2, 4) ' Cell B4
DateToCheck = "[ReceivedTime] >= """ & DateStart & """ And [ReceivedTime] <= """ & DateEnd & """"
Debug.Print "Date to Check is: "; DateToCheck
' -- end of Date from and to initialising
' -- start of Set ol and Set ns
Set ol = New Outlook.Application
Set ns = ol.GetNamespace("MAPI")
' -- end of Set ol and Set ns
' Which folder should the script be reading from?
Set fol = ns.Folders("~ xxxxxxx ").Folders("Inbox").Folders("xxxxxxxx")
' if there is an error when the code runs then stop the script and go straight to "errordetails" found at the bottom of the screen
On Error GoTo errordetails
' For each email in the folder (Restricted to the date range entered on the Spreadsheet)
For Each i In fol.Items.Restrict(DateToCheck)
' If the email is an Outlook email
If i.Class = OlMail Then
Set mi = i
UserForm1.Show
' If there are more than 0 attachments, ie, if it finds an attachment
If mi.Attachments.Count > 0 Then
For Each at In mi.Attachments
'Debug.Print mi.SenderName & " " & mi.ReceivedTime ' <- uncomment this part if you need to debug (remember to open the "immediate" window also
' Look for attachments that contain ".xls" (this will also pick up ".xlsx" and ".xlsm" etc
If InStr(LCase(at.FileName), ".xls") > 0 Then
' Tell the script where to save the file and what details need to be appeneded to the file name to make it a unique name
at.SaveAsFile "\\xxxx\xxxxx\xxxxx\" & Format(mi.ReceivedTime, "yyyy-mm-dd hh-nn-ss") & at.FileName
Else
' literally do nothing (it's probably not needed but added just in case)
End If
Next at
End If
End If
Next i
UserForm1.Hide
CountNumberOfFiles
Sheet2.Cells(1, 1) = "Date last ran was"
Sheet2.Cells(1, 2) = Now()
errordetails:
Debug.Print "Error number: " & Err.Number _
& " " & Err.Description;
End Sub
Sub CountNumberOfFiles()
Dim FolderPath As String
Dim Path As String
Dim Count As Integer
FolderPath = "\\xxxx\xxxxxxx\xxxxxxxxx"
Path = FolderPath & "\*"
FileName = Dir(Path)
Do While FileName <> ""
Count = Count + 1
FileName = Dir()
Loop
MsgBox "Search completed. There are " & Count & " attachments extracted to the folder \\xxxxxx\xxxxxx\xxxx"
End Sub
In the Immediate window, the Debug.Print for this part does show all the correct dates and times but it says it finds nothing even when there is one item that definitely matches.
' Get the date last ran
If Sheet2.Cells(1, 2) <> "" Then
Sheet1.Cells(2, 2) = Sheet2.Cells(1, 2)
Debug.Print "Start Date is: "; Sheet1.Cells(2, 2)
Sheet1.Cells(2, 4) = Now()
Debug.Print "End Date is: "; Sheet1.Cells(2, 4)
Else
Sheet2.Cells(1, 2) = ""
Debug.Print Sheet1.Cells(2, 2)
End If
DateStart = Sheet1.Cells(2, 2) ' Cell B2
DateEnd = Sheet1.Cells(2, 4) ' Cell B4
DateToCheck = "[ReceivedTime] >= """ & DateStart & """ And [ReceivedTime] <= """ & DateEnd & """"
Debug.Print "Date to Check is: "; DateToCheck
' -- end of Date from and to initialising
Thank you in advance for any assistance provided.

Dates are DateTime, not Text, so create text expressions for the date values:
DateToCheck = "[ReceivedTime] >= #" & Format(DateStart, "yyyy\/mm\/dd") & "# And [ReceivedTime] <= #" & Format(DateEnd, "yyyy\/mm\/dd") & "#"
Addendum:
Looking up the docs (always highly recommended), it appears that criteria must be text:
Although dates and times are typically stored with a Date format, the
Find and Restrict methods require that the date and time be converted
to a string representation. To make sure that the date is formatted as
Outlook expects, use the Format function. The following example
creates a filter to find all contacts that have been modified after
January 15, 1999 at 3:30 P.M.
Example:
sFilter = "[LastModificationTime] > '" & Format("1/15/99 3:30pm", "ddddd h:nn AMPM") & "'"
So, I guess you filter should read:
DateToCheck = "[ReceivedTime] >= '" & Format(DateStart, "ddddd h:nn AMPM") & "' And [ReceivedTime] <= '" & Format(DateEnd, "ddddd h:nn AMPM") & "'"

Related

VBA code to change date in workbook links

In a workbook I have links to another workbook from which it gathers some info. The link contains month number (09,10,11..); month name (Sep,Oct,Nov) and year. I am trying to make a VBA code which would change the dates in the external link to the present values. After it updates link. In case of an error (it couldn't find files using the link) code takes previous dates and loops until the link is working. For example, now it Oct 10 2021, but the code can't find the file it should take Sep 9 2021 and if this link didn't work it takes Aug 8 2021 and etc. Another problem is local of the date. I have to take en-US local for the month name, but I couldn't manage to do this.
I have a code below which is an attempt to do these operations. Thank you for your help in advance!
Sub changeLinks()
Dim link, linkSources, newLink As String
Dim today As Date
Dim monthname As Date
Dim monthnumber As Date
Dim yr As Date
today = Now()
'monthname = Format(Now(), "[$-en-US]MMM;#")
monthnumber = Format(today, "mm")
yr = Format(Now(), "yyyy")
newLink = "https:linklinklink" _
& yr & "/" & monthnumber & "_" & monthname & "/Report" & monthnumber & ".xlsx"
linkSources = ThisWorkbook.linkSources(xlLinkTypeExcelLinks)
If IsArray(linkSources) Then
For Each link In linkSources
'If InStr(link, "test1.xls") Then _'
ThisWorkbook.ChangeLink link, newLink, xlLinkTypeExcelLinks
Next
End If
On Error GoTo pvDate
ThisWorkbook.UpdateLink Name:=ThisWorkbook.linkSources
Exit Sub
pvDate:
monthname = WorksheetFunction.EDate(Format(Now(), "[$-en-US]mmm;#"), 1)
monthnumber = WorksheetFunction.EDate(Format(Now(), "mm"), 1)
yr = WorksheetFunction.EDate(Format(Now(), "yyyy"), 1)
newLink = "https:linklinklink" _
& yr & "/" & monthnumber & "_" & monthname & "/Report" & monthnumber & ".xlsx"
linkSources = ThisWorkbook.linkSources(xlLinkTypeExcelLinks)
If IsArray(linkSources) Then
For Each link In linkSources
'If InStr(link, "test1.xls") Then _'
ThisWorkbook.ChangeLink link, newLink, xlLinkTypeExcelLinks
Next
End If
ThisWorkbook.UpdateLink Name:=ThisWorkbook.linkSources
End Sub
This repeatedly checks if the newlink file can be opened before either giving up or, if successful, proceeding to update the links. I assume you want the month names to be independent of the locale so I have put them in an array.
Option Explicit
Sub changeLinks()
Const URI = "https:linklinklink"
Const MAX_TRY = 5
Dim mthname
mthname = Array("", "Jan", "Feb", "Mar", "Apr", "May", "Jun", _
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
Dim links, link, newlink As String, msg As String
Dim wb As Workbook, wblink As Workbook
Set wb = ThisWorkbook
links = wb.LinkSources(xlLinkTypeExcelLinks)
If Not IsArray(links) Then
MsgBox "No links to update", vbCritical
Exit Sub
End If
' determine latest link
Dim dt As Date, n As Long, m As Integer, bOK As Boolean
dt = Date
Do While Not bOK
m = Month(dt)
newlink = URI & Format(dt, "yyyy/m_") & mthname(m) & "/Report" & m & ".xlsx"
msg = msg & vbCrLf & newlink
On Error Resume Next
Set wblink = Workbooks.Open(newlink, 0, 1)
If wblink Is Nothing Then
' previous month
dt = DateAdd("m", -1, dt)
Else
wblink.Close False
bOK = True
End If
On Error GoTo 0
' limit attempts
n = n + 1
If n > MAX_TRY Then
MsgBox MAX_TRY & " attempts, giving up " & msg, vbExclamation
Exit Sub
End If
Loop
' update links
If bOK Then
n = 0
For Each link In LinkSources
wb.ChangeLink link, newlink, xlLinkTypeExcelLinks
n = n + 1
Next
wb.UpdateLink Name:=wb.LinkSources
MsgBox n & " links updated to " & newlink, vbInformation
End If
End Sub

Excel VBA checking and comparing dates between workbooks

I've got an excel script that that checks and compare dates between cells in different workbooks.
I've got four workbooks, wbk, wbkA, wbkB, wbkC, and wbkD.
Date in last cell in Column A of wbk is used as reference date
Date in Cell A9 of the rest of the workbooks is checked against reference date to check that they are one day after reference date
results are written to a log file
The script should write all results to a log (whether date is ok or not) and should only continue when all dates are ok. Ohterwise it should write to the log file which dates are not ok and exit sub.
If possible I would like to be call comparebooks from other modules instead
My problem:
Date checks work individually but I'm having trouble getting the script to check for multiple dates without running into errors
I cannot get it to work together with the writing to the log
Script to check compare dates
Sub comparebooks()
Dim dateX As Date
Dim dateA As Date
Dim dateB As Date
Dim dateC As Date
Dim dateD As Date
sFilename = ThisWorkbook.Path & "\Logs.txt"
sPath = ThisWorkbook.Path & "\Source\"
sFile = Dir(sPath & "2G Voice*.xlsx")
sFile1 = Dir(sPath & "2G Data*.xlsx")
sFile2 = Dir(sPath & "3G*.xlsx")
sFile3 = Dir(sPath & "4G*.xlsx")
'reference date file 4G
Set wbk = Workbooks.Open(ThisWorkbook.Path & "\2G.xlsx")
Set varSheet = wbk.Worksheets("2G Voice")
dateX = wbk.Worksheets("2G Voice").Range("A" & Rows.Count).End(xlUp)
'file dates to check
Set wbkA = Workbooks.Open(sPath & sFile)
Set varSheetA = wbkA.Worksheets("Sheet1")
dateA = wbkA.Worksheets("Sheet1").Range("A9")
Set wbkB = Workbooks.Open(sPath & sFile1)
Set varSheetB = wbkB.Worksheets("Sheet1")
dateB = wbkB.Worksheets("Sheet1").Range("A9")
Set wbkC = Workbooks.Open(sPath & sFile2)
Set varSheetC = wbkC.Worksheets("Sheet1")
dateC = wbkC.Worksheets("Sheet1").Range("A9")
Set wbkD = Workbooks.Open(sPath & sFile3)
Set varSheetD = wbkD.Worksheets("Sheet1")
dateD = wbkD.Worksheets("Sheet1").Range("A9")
'check 4g date
If dateA = DateAdd("d", 1, dateX) Then
Debug.Print dateA & " 2G Voice is OK"
If dateB = DateAdd("d", 1, dateX) Then
Debug.Print dateB & " 2G Data is OK"
If dateC = DateAdd("d", 1, dateX) Then
Debug.Print dateC & " 3G CS_PS is OK"
If dateD = DateAdd("d", 1, dateX) Then
Debug.Print dateD & " 4G Data is OK"
Else
Debug.Print "Date is not OK"
End If
Exit Sub
End If
End Sub
Logbook script
' Archive file at certain size
If FileLen(sFilename) > 20000 Then
FileCopy sFilename _
, Replace(sFilename, ".txt", Format(Now, "ddmmyyyy hhmmss.txt"))
Kill sFilename
End If
' Open the file to write
Dim filenumber As Variant
filenumber = FreeFile
Open sFilename For Append As #filenumber
Print #filenumber, CStr(Now) & ", " & "Missing source file: " & strType & " is missing " & chknum - i & " file(s)"
Close #filenumber
End If
Can someone please assist?

How to count and export the number, and names of categories in outlook to excel

I have code that allows you to enter a date range, to get the information I want. My problem is in the next step of exporting that data to excel.
I have tried VBA to run from excel, and couldn't get the results I want. Unfortunately I am not familiar with VBA in outlook
Sub CategoriesEmails()
Dim oFolder As MAPIFolder
Dim oDict As Object
Dim sStartDate As String
Dim sEndDate As String
Dim oItems As Outlook.items
Dim sStr As String
Dim sMsg As String
On Error Resume Next
Set oFolder = Application.ActiveExplorer.CurrentFolder
Set oDict = CreateObject("Scripting.Dictionary")
sStartDate = InputBox("Type the start date (format MM/DD/YYYY)")
sEndDate = InputBox("Type the end date (format MM/DD/YYYY)")
Set oItems = oFolder.items.Restrict("[Received] >= '" & sStartDate & "' And [Received] <= '" & sEndDate & "'")
oItems.SetColumns ("Categories")
For Each aitem In oItems
sStr = aitem.Categories
If Not oDict.exists(sStr) Then
oDict(sStr) = 0
End If
oDict(sStr) = CLng(oDict(sStr)) + 1
Next aitem
sMsg = ""
For Each aKey In oDict.keys
sMsg = sMsg & aKey & ": " & oDict(aKey) & vbCrLf
Next
MsgBox sMsg
Set oFolder = Nothing
End Sub
I expect the information to be shown in a message box, which this code accomplishes.
Although dates and times are typically stored with a Date format, the Find and Restrict methods require that the date and time be converted to a string representation. To make sure that the date is formatted as Microsoft Outlook expects, use the Format function.
For example, you may find similar posts there - Outlook Items Restrict with Date issue.

Original "start date" of postponed appointment

Hopefully someone could help :)
I use VBA code that runs through Microsoft Outlook Appointments, and for each appointment - prints a few details - to an Excel table.
If I set the start date of an appointment on 01-April-2019, and then postpone that appointment to 12-April-2019, and then postpone that appointment to 15-April-2019 - I have 3 "Appointment Item" objects in my folder.
I would like to print for each appointment in the folder - the "Start Date" that was set originally.
For example: Print for the first appointment - the first original start date (01-April-2019), for the first postponed appointment - the first postponed start date (12-April-2019), and for the second postponed appointment - the second postponed start date (15-April-2019).
However, when I run my code - the last "Start Date" is printed (15-April-2019, 15-April-2019, 15-April-2019), instead of the original (01-April-2019, 12-April-2019, 15-April-2019) .
I read a lot about different types of "date" objects, but couldn't find the correct one.
Could anyone help me?
Thank you very much!
Sub GetFromOutlook()
'Early Binding: Tools > References > Microsoft Outlook xx.0 Object Library > OK
Dim OutlookApp As Outlook.Application
Dim OutlookNS As Namespace
Dim Folder As MAPIFolder
Dim oApp As Outlook.Application
Dim oG As Outlook.Folder 'Method for IMAP, as used by Gmail.
Dim oM As Outlook.MeetingItem
Dim oAA As Outlook.AppointmentItem
Dim oI As Outlook.RecurrencePattern
Dim sMsg$, sAdd$
Dim i As Long
Dim j As Long
Set OutlookApp = New Outlook.Application
Set OutlookNS = OutlookApp.GetNamespace("MAPI")
Set Folder = OutlookNS.GetDefaultFolder(olFolderInbox).Parent.Folders("CCB Meetings")
Dim icon As String
Set oApp = CreateObject("Outlook.Application")
Set oG = OutlookNS.GetDefaultFolder(olFolderInbox).Parent.Folders("CCB Meetings")
For i = 1 To oG.Items.Count
If TypeName(oG.Items(i)) = "MeetingItem" Then j = j + 1
Next i
If j = 0 Then Exit Sub
' Create titles
Range("A1").Offset(0, 0).Value = "SenderName"
Range("B1").Offset(0, 0).Value = "Subject"
Range("C1").Offset(0, 0).Value = "CreationTime (Scheduled time of the first appointment)"
Range("D1").Offset(0, 0).Value = "ReceivedTime (Scheduled time of the current appointment)"
Range("E1").Offset(0, 0).Value = "Start (start time of the last scheduled appointment)"
Range("F1").Offset(0, 0).Value = "StartTime (doesnt work yet)"
Range("G1").Offset(0, 0).Value = "Location"
Range("H1").Offset(0, 0).Value = "RequiredAttendees"
Range("I1").Offset(0, 0).Value = "OptionalAttendees"
Range("J1").Offset(0, 0).Value = "ResponseStatus"
On Error Resume Next
j = 0
For i = 1 To oG.Items.Count
Set oM = oG.Items(i)
With oG.Items(i).GetAssociatedAppointment(True)
j = j + 1
Range("A1").Offset(j, 0).Value = oM.SenderName
Range("B1").Offset(j, 0).Value = oM.Subject
Range("C1").Offset(j, 0).Value = .CreationTime
Range("D1").Offset(j, 0).Value = oM.ReceivedTime
Range("E1").Offset(j, 0).Value = .Start
Range("F1").Offset(j, 0).Value = oAA.GetRecurrencePattern '??????????????????
Range("G1").Offset(j, 0).Value = .Location
Range("H1").Offset(j, 0).Value = .RequiredAttendees
Range("I1").Offset(j, 0).Value = .OptionalAttendees
Range("J1").Offset(j, 0).Value = .ResponseStatus
End With
Next i
On Error GoTo 0
Set Folder = Nothing
Set OutlookNS = Nothing
Set OutlookApp = Nothing
End Sub
You can create a UserProperties field manually or Add a UserProperties field with VBA code.
UserProperties object
UserProperties.Add method
OlUserPropertyType enumeration
Once you have the field you can enter data manually or with VBA.
With VBA consider ItemAdd. Set custom value when item moved to folder in outlook
I have just noticed Niton's latest comment. I think it is an interesting idea. I suspect you still need my investigative macro and you will still need events to create the custom property so this answer should still be useful.
I would need to invest more time than I have available at the moment to address your requirement. This answer includes what I have to hand in the hope that it will help you.
Your code suggests you do not understand recurring entries, you are not familiar with the different types of calendar items and you have misinterpreted some of the properties. If there is any detailed documentation on calendar items available via the internet, I have failed to find it. There is basic documentation (referenced below): this object has these properties; this property is a long/string/enumeration; one sentence definitions and so on. But none of this basic documentation helped me understand how, for example, exceptions related to master entries.
The code below is an Excel based investigation I conducted some months ago. I have not had the time to move to the next stage but I believe it will give you a start.
Option Explicit
Sub DiagCal()
' Outputs major properties of all calendar items within the default
' calendar for a specified date range. The objective is to better
' understand calendar items and how they link.
' Requires reference to Microsoft Outlook nn.n Library
' where "nn.n" identifies the version of Office you are using.
' 27Dec18 First version coded
' 30Dec18 This version coded
' 18Apr19 Reviewed comments and made some improvements.
' * Together these constants identify the start and length of the report period.
' * The report period starts DateReportStartOffset days before today.
' * DateReportLenType and DateReportLen are used as parameters for function DateAdd
' which is used to calculate the report period end date for the start date. See
' function DateAdd for permitted values for these constants.
' * These constants provided a convenient way of specify the start and end date
' of the report period when this macro was written. Something simpler would
' probably be better now.
Const DateReportLen As Long = 1
Const DateReportLenType As String = "yyyy"
Const DateReportStartOffset As Long = -363
Dim AppointToReport As New Collection
Dim AppOutlook As New Outlook.Application
Dim CalEnt As Object
Dim CalEntClass As Long
Dim DateReportEnd As Date
Dim DateReportStart As Date
Dim FileBody As String
Dim FldrCal As Outlook.Folder
Dim InxAir As Long
Dim InxFC As Long
Dim PathDesktop As String
PathDesktop = CreateObject("WScript.Shell").SpecialFolders("Desktop")
' Identify date range to be reported on
DateReportStart = DateSerial(Year(Now), Month(Now), Day(Now) + DateReportStartOffset)
DateReportEnd = DateAdd(DateReportLenType, DateReportLen, DateReportStart)
' This assumes the calendar of interest is the default calendar.
' Change as necessary
Set FldrCal = AppOutlook.Session.GetDefaultFolder(olFolderCalendar)
' This loop reviews the entire calendar and identifies Calendar Items
' that fall entirely or partially within the report period. All such
' Calendar Items are recorded in collection AppointToReport.
For InxFC = 1 To FldrCal.Items.Count
Set CalEnt = FldrCal.Items(InxFC)
' Occasionally I get syncronisation errors. This code avoids them.
CalEntClass = -1
On Error Resume Next
CalEntClass = CalEnt.Class
On Error GoTo 0
' I have never found anything but appointments in
' Calendar but test just in case
If CalEntClass = olAppointment Then
Call DiagCalRecordEntry(CalEnt, DateReportStart, DateReportEnd, AppointToReport)
End If
Next InxFC
FileBody = "Calendar entries within or partially within " & _
Format(DateReportStart, "d mmm yy") & _
" to " & Format(DateReportEnd, "d mmm yy") & vbLf & _
"Total calendar entries: " & FldrCal.Items.Count & vbLf & _
"Calendar entries within or partially within report period: " & _
AppointToReport.Count
' This loop outputs the major properties of every Calendar Items recorded
' in collection AppointToReport.
For InxAir = 1 To AppointToReport.Count
FileBody = FileBody & vbLf & String(70, "=")
FileBody = FileBody & vbLf & AppointToReport(InxAir)(1)
Next
Call PutTextFileUtf8NoBom(PathDesktop & "\Calendar.txt", FileBody)
End Sub
Sub DiagCalRecordEntry(ByRef CalEnt As Object, _
ByVal DateReportStart As Date, _
ByVal DateReportEnd As Date, _
ByRef AppointToReport As Collection, _
Optional ByVal OriginalDate As Date)
' If calendar entry is within or partially within report range, add
' its details to AppointToReport
Dim AllDayEvent As Boolean
Dim AppointDtls As String
Dim AppointId As String
Dim AppointIdMaster As String
Dim BusyStatus As String
Dim DateRecurrEnd As Date
Dim DateRecurrStart As Date
Dim DateAppointEnd As Date
Dim DateAppointStart As Date
Dim DayOfMonth As Long
Dim DayOfWeekMask As String
Dim DayOfWeekMaskCode As Long
Dim DurationEntry As Long
Dim DurationRecurr As Long
Dim InxE As Long
Dim Instance As Long
Dim Interval As Long
Dim Location As String
Dim MonthOfYear As Long
Dim NoEndDate As Boolean
Dim NumOccurrences As Long
Dim RecurrenceState As String
Dim RecurrenceType As String
Dim RecurrPattern As Outlook.RecurrencePattern
Dim Subject As String
Dim TimeStart As Date
Dim TimeEnd As Date
'Debug.Assert False
' Get values from calendar entry which identify if entry is within
' report range
With CalEnt
DateAppointStart = .Start
DateAppointEnd = .End
Select Case .RecurrenceState
Case olApptNotRecurring
'Debug.Assert False
RecurrenceState = "Non-recurring calendar entry"
Case olApptMaster
'Debug.Assert False
RecurrenceState = "Master calendar entry"
Case olApptException
'Debug.Assert False
RecurrenceState = "Exception to Master calendar entry"
Case olApptOccurrence
Debug.Assert False
' I believe this state can only exist if GetOccurrence() is used
' to get a single occurrence of a Master entery. I do not believe
' it can appear as a calendar entry
RecurrenceState = "Occurrence"
Case Else
Debug.Assert False
RecurrenceState = "Unrecognised (" & .RecurrenceState & ")"
End Select
End With
If RecurrenceState = "Master calendar entry" Then
'Debug.Assert False
Set RecurrPattern = CalEnt.GetRecurrencePattern()
With RecurrPattern
DateRecurrStart = .PatternStartDate
DateRecurrEnd = .PatternEndDate
End With
If DateRecurrStart <= DateReportEnd And _
DateRecurrEnd >= DateReportStart Then
' Some or all occurences of this Master entry are within report range
'Debug.Assert False
Else
' No occurences of this Master entry are within report range
'Debug.Assert False
Exit Sub
End If
Else
' Non recurring or exception appointment
If DateAppointStart <= DateReportEnd And _
DateAppointEnd >= DateReportStart Then
' Entry is within report range
'Debug.Assert False
Else
' Non recurring entry is not within report range
'Debug.Assert False
Exit Sub
End If
End If
' Calendar entry is within or partially within report period
' Get remaining properties from entry
'Debug.Assert False
With CalEnt
AllDayEvent = .AllDayEvent
AppointId = .GlobalAppointmentID
Select Case .BusyStatus
Case olBusy
'Debug.Assert False
BusyStatus = "Busy"
Case olFree
'Debug.Assert False
BusyStatus = "Free"
Case olOutOfOffice
'Debug.Assert False
BusyStatus = "Out of Office"
Case olTentative
Debug.Assert False
BusyStatus = "Tentative appointment"
Case olWorkingElsewhere
'Debug.Assert False
BusyStatus = "Working elsewhere"
Case Else
Debug.Assert False
BusyStatus = "Not recognised (" & .BusyStatus & ")"
End Select
Location = .Location
Subject = .Subject
End With
If RecurrenceState = "Exception to Master calendar entry" Then
RecurrenceState = RecurrenceState & vbLf & _
"Master's Id: " & CalEnt.Parent.GlobalAppointmentID & vbLf & _
"Original Date: " & OriginalDate
End If
AppointDtls = RecurrenceState & vbLf & _
"AllDayEvent: " & AllDayEvent & vbLf & _
"AppointId: " & AppointId & vbLf & _
"BusyStatus: " & BusyStatus & vbLf & _
"DateAppointStart: " & DateAppointStart & vbLf & _
"DateAppointEnd: " & DateAppointEnd & vbLf & _
"DurationEntry: " & DurationEntry & vbLf & _
"Location: " & Location & vbLf & _
"Subject: " & Subject
If RecurrenceState <> "Master calendar entry" Then
' AppointDtls complete for this appointment
Call StoreSingleAppoint(Format(DateAppointStart, "yyyymmddhhmm"), _
AppointDtls, AppointToReport)
Else
'Debug.Assert False
With RecurrPattern
' Not all parameters have a meaningful value for all RecurrenceTypes
' but the value always appears to be of the correct data type.
DateRecurrStart = .PatternStartDate
DateRecurrEnd = .PatternEndDate
DayOfMonth = .DayOfMonth
DayOfWeekMaskCode = .DayOfWeekMask
DayOfWeekMask = ""
If DayOfWeekMaskCode >= olSaturday Then
Debug.Assert False
DayOfWeekMask = "+Saturday"
DayOfWeekMaskCode = DayOfWeekMaskCode - olSaturday
End If
If DayOfWeekMaskCode >= olFriday Then
'Debug.Assert False
DayOfWeekMask = "+Friday" & DayOfWeekMask
DayOfWeekMaskCode = DayOfWeekMaskCode - olFriday
End If
If DayOfWeekMaskCode >= olThursday Then
'Debug.Assert False
DayOfWeekMask = "+Thursday" & DayOfWeekMask
DayOfWeekMaskCode = DayOfWeekMaskCode - olThursday
End If
If DayOfWeekMaskCode >= olWednesday Then
'Debug.Assert False
DayOfWeekMask = "+Wednesday" & DayOfWeekMask
DayOfWeekMaskCode = DayOfWeekMaskCode - olWednesday
End If
If DayOfWeekMaskCode >= olTuesday Then
'Debug.Assert False
DayOfWeekMask = "+Tuesday" & DayOfWeekMask
DayOfWeekMaskCode = DayOfWeekMaskCode - olTuesday
End If
If DayOfWeekMaskCode >= olMonday Then
'Debug.Assert False
DayOfWeekMask = "+Monday" & DayOfWeekMask
DayOfWeekMaskCode = DayOfWeekMaskCode - olMonday
End If
If DayOfWeekMaskCode >= olSunday Then
'Debug.Assert False
DayOfWeekMask = "+Sunday" & DayOfWeekMask
End If
If DayOfWeekMask = "" Then
'Debug.Assert False
DayOfWeekMask = "None"
Else
'Debug.Assert False
DayOfWeekMask = Mid$(DayOfWeekMask, 2) ' Remove leading +
End If
DurationRecurr = .Duration
Instance = .Instance
Interval = .Interval
MonthOfYear = .MonthOfYear
NoEndDate = .NoEndDate
NumOccurrences = .Occurrences
Select Case .RecurrenceType
Case olRecursDaily
'Debug.Assert False
RecurrenceType = "Daily"
Case olRecursMonthly
Debug.Assert False
RecurrenceType = "Monthly"
Case olRecursMonthNth
Debug.Assert False
RecurrenceType = "MonthNth"
Case olRecursWeekly
'Debug.Assert False
RecurrenceType = "Weekly"
Case olRecursYearly
'Debug.Assert False
RecurrenceType = "Yearly"
Case olRecursYearNth
Debug.Assert False
RecurrenceType = "YearNth"
Case Else
Debug.Assert False
RecurrenceType = "Unrecognised Value (" & RecurrenceType & ")"
End Select
TimeStart = .StartTime
TimeEnd = .EndTime
End With
AppointDtls = AppointDtls & vbLf & "DateRecurrStart: " & DateRecurrStart _
& vbLf & "DateRecurrEnd: " & DateRecurrEnd _
& vbLf & "DayOfMonth: " & DayOfMonth _
& vbLf & "DayOfWeekMask: " & DayOfWeekMask _
& vbLf & "DurationRecurr: " & DurationRecurr _
& vbLf & "Instance: " & Instance _
& vbLf & "Interval: " & Interval _
& vbLf & "MonthOfYear: " & MonthOfYear _
& vbLf & "NoEndDate: " & NoEndDate _
& vbLf & "NumOccurrences: " & NumOccurrences _
& vbLf & "RecurrenceType: " & RecurrenceType _
& vbLf & "TimeStart: " & TimeStart & " (" & CDbl(TimeStart) & ")" _
& vbLf & "TimeEnd: " & TimeEnd & " (" & CDbl(TimeEnd) & ")"
For InxE = 1 To RecurrPattern.Exceptions.Count
AppointDtls = AppointDtls & vbLf & "Exception " & InxE & " for occurrence on " & _
RecurrPattern.Exceptions.Item(InxE).OriginalDate
Next
Call StoreSingleAppoint(Format(DateRecurrStart, "yyyymmddhhmm"), _
AppointDtls, AppointToReport)
For InxE = 1 To RecurrPattern.Exceptions.Count
Call DiagCalRecordEntry(RecurrPattern.Exceptions.Item(InxE).AppointmentItem, _
DateReportStart, DateReportEnd, AppointToReport, _
RecurrPattern.Exceptions.Item(InxE).OriginalDate)
Next
End If ' RecurrenceState <> "Master calendar entry"
End Sub
Public Sub PutTextFileUtf8NoBom(ByVal PathFileName As String, ByVal FileBody As String)
' Outputs FileBody as a text file named PathFileName using
' UTF-8 encoding without leading BOM
' Needs reference to "Microsoft ActiveX Data Objects n.n Library"
' Addition to original code says version 2.5. Tested with version 6.1.
' 1Nov16 Copied from http://stackoverflow.com/a/4461250/973283
' but replaced literals with parameters.
' 15Aug17 Discovered routine was adding an LF to the end of the file.
' Added code to discard that LF.
' 11Oct17 Posted to StackOverflow
' 9Aug18 Comment from rellampec suggested removal of adWriteLine from
' WriteTest statement would avoid adding LF.
' 30Sep18 Amended routine to remove adWriteLine from WriteTest statement
' and code to remove LF from file. Successfully tested new version.
' References: http://stackoverflow.com/a/4461250/973283
' https://www.w3schools.com/asp/ado_ref_stream.asp
Dim BinaryStream As Object
Dim UTFStream As Object
Set UTFStream = CreateObject("adodb.stream")
UTFStream.Type = adTypeText
UTFStream.Mode = adModeReadWrite
UTFStream.Charset = "UTF-8"
UTFStream.Open
UTFStream.WriteText FileBody
UTFStream.Position = 3 'skip BOM
Set BinaryStream = CreateObject("adodb.stream")
BinaryStream.Type = adTypeBinary
BinaryStream.Mode = adModeReadWrite
BinaryStream.Open
UTFStream.CopyTo BinaryStream
UTFStream.Flush
UTFStream.Close
Set UTFStream = Nothing
BinaryStream.SaveToFile PathFileName, adSaveCreateOverWrite
BinaryStream.Flush
BinaryStream.Close
Set BinaryStream = Nothing
End Sub
Sub StoreSingleAppoint(ByVal SeqKey As String, _
ByVal AppointDtls As String, _
ByRef AppointToReport As Collection)
' Entries in AppointToReport are of the form:
' VBA.Array(SeqKey, AppointDtls)
' Add new entry to AppointToReport so entries are in ascending order by SeqKey
Dim InxAtr As Long
If AppointToReport.Count = 0 Then
'Debug.Assert False
' first appointment
AppointToReport.Add VBA.Array(SeqKey, AppointDtls)
Else
For InxAtr = AppointToReport.Count To 1 Step -1
If SeqKey >= AppointToReport(InxAtr)(0) Then
' New appointment belongs after this existing entry
'Debug.Assert False
AppointToReport.Add VBA.Array(SeqKey, AppointDtls), , , InxAtr
Exit Sub
End If
Next
' If get here, new appointment belongs before all existing appointments
'Debug.Assert False
AppointToReport.Add VBA.Array(SeqKey, AppointDtls), , 1
End If
End Sub
Create a macro-enabled workbook and copy the above code to a module.
Near the top of the code you will find:
' Identify date range to be reported on
DateReportStart = DateSerial(Year(Now), Month(Now), Day(Now) + DateReportStartOffset)
DateReportEnd = DateAdd(DateReportLenType, DateReportLen, DateReportStart)
I suggest replacing these statements with something simple like:
DateReportStart = #4/15/2019#
DateReportEnd = #4/18/2019#
Warning: VBA date literals use middle-endian format which confuses everyone except civilian Americans.
Macro DiagCal() creates a desktop file named “Calendar.txt” containing details of every calendar item that is entirely or partially within the report period. When I tested it, I created every sort of calendar entry: single appointments; entries recurring by day, week, month, year; weekly patterns; multi-day, all-day and part-day events; exceptions to instances of recurring entries and so on.
Visit https://learn.microsoft.com/en-us/office/vba/api/Outlook.AppointmentItem
On the left is an index with entries for the events, methods and properties of an appointment item. Expand the properties and methods and look for information that did not interest me but might interest you. Look through my code and workout how to add that information. If you cannot see how to add information, report the information you want in a comment and I will add it for you.
Expand the events and study what is available. I have never used appointment item events. I have found events easy enough to use with mail items so I assume appointment items will be similar. It is not immediate clear to me which would be the best events to use. I think you need to know when a new item is added and when an item is changed. I would try some of these events and write code to output a few properties to the Immediate Windows to better understand when these events are triggered and what data is available.
I believe you will have to initialise your workbook with code something like my macro which extracts the interesting properties of existing appointment items. You then need events to output the interesting properties of new or changed events.
I would not use events to update the workbook. (1) If you update the workbook in real time there will probably be a noticeable delay while the event is processed. (2) The update code is likely to be complex and unlikely to be correct on first attempt. If you update the workbook in real time, you will have to cause the events to be triggered again and again until you get the code right.
I would get each event to output a small text file, containing the interesting properties, to a suitable disc folder. Outputting a text file will be take little time and should not be noticeable to the user. These text files can be used again and again to update the workbook until you get the code right.
I hope the above gives you some ideas.

vArray values not clearing out from previous loop in VBA

I have some vArrays which are not clearing out. The purspose of the macro is to work on a raw data tab which has 30+ tabs, each tab holding information for a specific office, 001-New York, etc. The macro is supposed to select x number of tabs (based on a reference file), copy them and save them into a new workbook. The problem is that instead of copying and saving from the raw data file it save the reference file instead. A For...Next loop is used to determine which tabs/offices to select & copy from the raw data file. The varrays are inside the loop and contain the names of the offices. When the code encounters the vArray the varray values are not clearing out when the loop circles back around.
Example:
'For 1' reference a cell with value of "8" so it populates 8 different vArray values (offices in this case). 'For 2' has a reference number of 5 and is supposed to populate 5 vArray values. It does this correctly as I can see the 5 new values in the locals window under vArray (1) thru vArray (5), however, vArray 6 thru 8 are showing values of the previous loop instead of 'Empty'. The vArray values are not clearing out when the macro loops.
sMasterListWBName is the reference file which tells the macro which tabs to copy from the raw data file and where to move the newly created workbook. The sub is also copying, saving, and distributing the reference file instead of the raw data file for some iterations of the loop (secondary issue--I will try to refrain from splitting the thread topic).
Thanks in advance to anyone who tries to answer this question.
Option Explicit
Dim iYear As Integer, iMonth As Integer, iVer As Integer, icount As Integer, iCount2 As Integer
Dim iLetter As String, iReport As String
Dim sMonth As String, sDate As String, sVer As String, sAnswer As String
Dim sFolderName As String, sManagerInitials As String
Dim iManagerNumber As Integer, iManagerStart As Integer, iTabNumber As Integer, iTabStart As Integer
Dim sMasterListWBName As String, sConsolidatedWBName As String, sExists As String
Dim oSheet As Object, oDistList As Object
Dim vArray(300) As Variant
Dim wbDistList As Workbook
Dim wsAgentListSheet As Worksheet, wsMain As Worksheet
Dim rCell As Range, rCell2 As Range, rCellTotal As Range
Public sFINorAgent As String
Sub Agent_Distribute()
On Error Resume Next
iYear = frm_fin_rep_main_distribute.txt_year
iMonth = frm_fin_rep_main_distribute.txt_month
iVer = frm_fin_rep_main_distribute.txt_version
sMonth = Right("0" & iMonth, 2)
sDate = iYear & "." & sMonth
sVer = "V" & iVer
sAnswer = MsgBox("Is the following information correct?" & vbNewLine & vbNewLine & _
"Report - " & frm_fin_rep_main.sLetter & vbNewLine & _
"Year - " & iYear & vbNewLine & _
"Month - " & sMonth & vbNewLine & _
"Name - " & frm_fin_rep_main.sReport & vbNewLine & _
"Version - " & sVer, vbYesNo + vbInformation, "Please verify...")
If sAnswer <> vbYes Then
Exit Sub
End If
Unload frm_fin_rep_main_distribute
frm_agent.Hide
Form_Progress
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
sConsolidatedWBName = ActiveWorkbook.Name
sMasterListWBName = "Dist Master List Final.xls"
If Not IsFileOpen(sMasterListWBName) Then
Workbooks.Open FileName:= _
"W:\Addins\01 GL - Distribution\" & sMasterListWBName, Password:="password"
Workbooks(sConsolidatedWBName).Activate
End If
Set oDistList = Workbooks(sMasterListWBName).Worksheets("Agent")
With oDistList
iManagerNumber = .Range("ManNumber2") 'range value = 66
For iManagerStart = 2 To iManagerNumber '2 to 66
If .Range("A" & iManagerStart) = "x" Then
iTabNumber = .Range("E" & iManagerStart) 'E2 to E66
sFolderName = .Range("F" & iManagerStart) 'F2 to F66
sManagerInitials = .Range("G" & iManagerStart) 'G2 to G66
For iTabStart = 1 To iTabNumber
vArray(iTabStart) = .Range("G" & iManagerStart).Offset(0, iTabStart)
Next iTabStart
If iTabNumber = 1 Then
Sheets(vArray(1)).Select
Else
Sheets(vArray(1)).Select
For iTabStart = 2 To iTabNumber
Sheets(vArray(iTabStart)).Select False
Next iTabStart
End If
ActiveWindow.SelectedSheets.Copy
' *** the following code is optional, remove preceding apostrophes from the following four lines to enable password protection ***
'For Each oSheet In ActiveWorkbook.Sheets
'oSheet.Protect "password"
'oSheet.EnableSelection = xlNoSelection
'Next
ActiveWorkbook.SaveAs FileName:= _
"W:\Financials\" & iYear & "\" & sDate & "\Report to Distribute Electronically\Department Reports\" _
& sFolderName & "\Current Year Financials" & "\" & "Y" & ") " & iYear & "-" & sMonth & " Agent Report Card " & sVer & " - " & sManagerInitials & ".xls"
ActiveWorkbook.Close
End If
iPercent = iManagerStart / iManagerNumber * 95
Task_Progress (iPercent)
Next iManagerStart
End With
Workbooks(sMasterListWBName).Close False
Task_Progress (100)
Unload frm_progress
Set oDistList = Nothing
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
Message_Done
frm_agent.Show (vbModeless)
End Sub
I fixed it. I just added "Workbooks(sWbName).activate" at the end of the loop to make sure the focus is back on the raw data file. Now all files are saving in the correct format and location. Case closed unless someone has anything else to add. Maybe someone knows the reason the macro was losing sight of its active sheet (saving reference file instead of raw data file). Thank you.

Resources