If statements - msgbox - vba

I'm now trying to write an IF statement to say something to the effect of: If file is more than 5 days old, do not run macro. If more than 5 days old, run macro.
I would like to this to be a yes or no dialogue box. Here is my code. Please help. I'm still trying to learn this vba code.
Sub LastModifiedFile()
'Function FileLastModified(strFullFileName As String)
Dim fs As Object, f As Object, s As String, dtmodpath As String
dtmodpath = "\\jdshare\pdcmaterials\5_Tools\FTP\Cancelled_Report.txt"
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFile(dtmodpath)
's = UCase(strFullFileName) & vbCrLf
s = f.DateLastModified
FileLastModified = s
Set fs = Nothing: Set f = Nothing
Range("E5").Value = FileLastModified
'If FileExists(strFullName) Then
'MsgBox FileLastModified(strFullName)
'Else
'MsgBox "File Older than 5 Years : " & vbNewLine & strFullName
'End If
'End Function
End Sub

Congrats for using correctly the .DateLastModified property!
Instead of the MsgBox-es call a function. The DateAdd() returns date, which is 5 days before the current date, thus it is easy to compare. This shows a MsgBox() which informs whether the file has more or less than 5 days from the last modification:
Option Explicit
Sub LastModifiedFile()
Dim fileObject As Object
Dim file As Object
Dim modPath As String
modPath = "\\jdshare\pdcmaterials\5_Tools\FTP\Cancelled_Report.txt"
Set fileObject = CreateObject("Scripting.FileSystemObject")
Set file = fileObject.GetFile(modPath)
If DateAdd("d", -5, Now) < file.DateLastModified Then
MsgBox "Less than 5 days."
Else
MsgBox "More than 5 days."
End If
End Sub
If you want to put a MsgBox in the whole story with Yes and No, then this should be ok:
Sub LastModifiedFile()
Dim fileObject As Object
Dim file As Object
Dim modPath As String
modPath = "\\jdshare\pdcmaterials\5_Tools\FTP\Cancelled_Report.txt"
Set fileObject = CreateObject("Scripting.FileSystemObject")
Set file = fileObject.GetFile(modPath)
Dim msgBoxStatement As String
If DateAdd("d", -5, Now) < file.DateLastModified Then
msgBoxStatement = "This file is NOT older than 5 days!" & vbCrLf & _
"Should it be deleted?"
Else
msgBoxStatement = "This file is older than 5 days!" & vbCrLf & _
"Should it be deleted?"
End If
Select Case MsgBox(msgBoxStatement, vbYesNo Or vbQuestion, "Delete?")
Case vbYes
'run the for deletion
Case vbNo
'do not run the code for deletion
End Select
End Sub

Use DateDiff function to compute your number of days.
Its not totally clear what you want to do with your Yes/No message box, here's an attempt :
Sub LastModifiedFile()
Dim fs As Object, f As Object, s As String, dtmodpath As String
Dim dtLastMod As Date
Dim intDays As Long
dtmodpath = "\\jdshare\pdcmaterials\5_Tools\FTP\Cancelled_Report.txt"
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFile(dtmodpath)
dtLastMod = f.DateLastModified
' Here you compute the number of days between the file last mod date, and the current date
intDays = DateDiff("d", dtLastMod, Now)
Set fs = Nothing: Set f = Nothing
Range("E5").Value = dtLastMod
If intDays > 5 Then
If MsgBox("File is " & intDays & " days old, proceed with macro ?", vbYesNo, "Continue?") = vbYes Then
' RUN MACRO GOES HERE
End If
Else
MsgBox "File is " & intDays & " days old, cancelling"
End If
End Sub

Related

Excel VBA code only works in debug mode

I've already searched in a bunch of topics and no solution seemed to work for me.
I've an Excel macro file that sometimes works fine, but sometimes only works in stepping mode.
This is a sub inside a main sub that passes a value (message) to a spreadsheet from an Outlook Calendar by category (key). (for this code I adapted from Script to total hours by calendar category in Outlook) .The value goes into the row with the same name as the category and the week value in the column. I've tried the DoEvents and I thought it had worked, but when I tried to run it in a different computer it failed again.
Any ideas?
Option Explicit
Public keyArray
Sub totalCategories()
Dim app As New Outlook.Application
Dim namespace As Outlook.namespace
Dim calendar As Outlook.Folder
Dim appt As Outlook.AppointmentItem
Dim apptList As Outlook.Items
Dim apptListFiltered As Outlook.Items
Dim startDate As String
Dim endDate As String
Dim category As String
Dim duration As Integer
Dim outMsg As String
Dim firstDayOfTheYear As Date
'Going to be used to get start and end date
firstDayOfTheYear = Date
firstDayOfTheYear = "01/01/" & Right(firstDayOfTheYear, 4)
' Access appointment list
Set namespace = app.GetNamespace("MAPI")
Set calendar = namespace.GetDefaultFolder(olFolderCalendar)
Set apptList = calendar.Items
' Include recurring appointments and sort the list
apptList.IncludeRecurrences = True
apptList.Sort "[Start]"
' Get selected date
startDate = firstDayOfTheYear + 7 * (CInt(SelectWeek.week) - 1)
endDate = firstDayOfTheYear + 7 * (CInt(SelectWeek.week) - 1) + 6
startDate = Format(startDate, "dd/MM/yyyy") & " 00:01"
endDate = Format(endDate, "dd/MM/yyyy") & " 11:59 PM"
' Filter the appointment list
Dim strFilter As String
strFilter = "[Start] >= '" & startDate & "'" & " AND [End] <= '" & endDate & "'"
Set apptListFiltered = apptList.Restrict(strFilter)
' Loop through the appointments and total for each category
Dim catHours
Set catHours = CreateObject("Scripting.Dictionary")
For Each appt In apptListFiltered
category = appt.Categories
duration = appt.duration
If catHours.Exists(category) Then
catHours(category) = catHours(category) + duration
Else
catHours.Add category, duration
End If
Next
' Loop through the categories
Dim key
keyArray = catHours.Keys
DoEvents 'prevents a bug from happening --> in some cases the total hours weren't divided by categories
For Each key In keyArray
outMsg = catHours(key) / 60
'Print in Realizado sheet --> activities must be in range (name manager) as "atividades"
writeReport SelectWeek.week, outMsg, key
Next
' Clean up objects
Set app = Nothing
Set namespace = Nothing
Set calendar = Nothing
Set appt = Nothing
Set apptList = Nothing
Set apptListFiltered = Nothing
End Sub
Sub writeReport(week, message As String, key)
Dim ws As Worksheet
Dim i As Integer
Dim Activities, nActivities As Integer
Set ws = Sheets("5")
Activities = Range("activities")
nActivities = UBound(Activities)
DoEvents
For i = 1 To nActivities
DoEvents
If key = Cells(i + 8, 2).Value Then
ws.Cells(i + 8, week + 3).Value = CDbl(message)
Exit For
End If
Next i
End Sub
You need to handle errors explicitly so you know exactly what is going on. Trust me that this will save you HEAPS of time troubleshooting your own code, especially in VBA.
Common practice is something like "try, catch, finally".
Dim position as string
Sub foo()
position = "sub function short description"
On Error GoTo catch
Err.Clear
'do stuff
finally:
On Error Resume Next
'do cleanup stuff
Exit Sub
catch:
Debug.Print Right(ThisWorkbook.FullName, Len(ThisWorkbook.FullName) - 3) & ", _
Position: " & errorPosition & " , Error Code: [ " & Hex(Err.number) & "], _
Description: " & Err.Description & ""
Resume finally
End Sub
Problem solved!
From this:
If key = Cells(i + 8, 2).Value Then
ws.Cells(i + 8, week + 3).Value = CDbl(message)
Exit For
End If
To this:
If key = Activities(i, 1) Then
ws.Cells(i + 8, week + 3).Value = CDbl(message)
Exit For
End If

Adding a Delimiter to a memo field in Access

I am trying to delimit a memo field. The field has within it multiple notes that I need to parse out into different columns. Each note however follows the same logic, starts with mm/dd/yyyy then the note itself followed by a space.
So an example for one memo field would be
01/25/2000 worked on Rack-ID 03/03/2010 contracted Rack-ID 05/15/2014 updated Rack-ID
I need each note parsed out into a different column in Access.
I was working on the Split function in VBA, they had originally had "|" as a delimiter but removed it, now I have to pull on the dates
Note "tbl_example" is the table in my test access database
"Tx_example" is the name of the column holding the data to be delimited in my test access database
Sub Example()
On Error GoTo err_Handler
Dim rsD As DAO.Recordset
Set rsD = CurrentDb.OpenRecordset("tbl_Example")
Do While Not rsD.EOF
rsD.Edit
rsD!F1 = Trim(Split(rsD!Tx_Example, "|")(0))
rsD!F2 = Trim(Split(rsD!Tx_Example, "|")(1))
rsD!F3 = Trim(Split(rsD!Tx_Example, "|")(2))
rsD.Update
rsD.MoveNext
Loop
sub_Exit:
rsD.Close
Set rsD = Nothing
Exit Sub
err_Handler:
If Err.Number = 9 Then
Resume Next
Else
MsgBox "Err: " & Err.Number & vbNewLine & Err.Description
End If
End Sub
Not certain how I replace the "|" with a date search.
Also I'm not against doing a replace and inserting "|" right before each date so I would have my delimiter back.
Problem is I'm not sure how to find the date in a text memo field, otherwise I would be able to use the replace function, or an update query.
Any help is appreciated, thanks.
Public Function AddPipesBeforeDates(ByVal strText As String) As String
Dim regex As RegExp
Dim matches As Object
Set regex = CreateObject("VBScript.RegExp")
regex.Global = True
regex.Pattern = "\d{2}/\d{2}/\d{4}"
Set matches = regex.Execute(strText)
For Each m In matches
strText = Replace(strText, m, "|" & m)
Next
AddPipesBeforeDates = strText
Set matches = Nothing
Set regex = Nothing
End Function
Sub Example()
On Error GoTo err_Handler
Dim rsD As DAO.Recordset
Dim results as variant
Set rsD = CurrentDb.OpenRecordset("tbl_Example")
Do While Not rsD.EOF
results = split(AddPipesBeforeDates(rsD!Tx_Example), "|")
rsD.Edit
rsD!F1 = Trim(results(1))
rsD!F2 = Trim(results(2))
rsD!F3 = Trim(results(3))
rsD.Update
rsD.MoveNext
Loop
sub_Exit:
rsD.Close
Set rsD = Nothing
Exit Sub
err_Handler:
If Err.Number = 9 Then
Resume Next
Else
MsgBox "Err: " & Err.Number & vbNewLine & Err.Description
End If
End Sub
I wrote something like this recently to pull dated notes out of a memo field. This code may not work exactly as is, but will get you closer and give you some ideas.
Some initial notes: Don't expect the routine to do the full job on the first sweep. Create a temp table to write your results to, and each time you run the routine, clear the temp table first. After each run, tweak your code to catch everything it missed in the previous run. This way, you will refine the routine until you capture all your data accurately. You may also have to hand edit some notes to get them to work
+----------+------------+
| Field | DataType |
+----------+------------+
| NoteId | AutoNumber |
| ParentId | Long |
| NoteDate | Date/Time |
| NoteNext | Memo |
+----------+------------+
Also, I highly recommend not using multiple note fields in the same table. Your Notes table should look like my temp_Notes table above. After your routine is successful, its a simple matter of running against your Notes table rather than temp_Notes.
Public Sub MigrateNotes()
Dim db As DAO.Database
Dim rsDest As DAO.Recordset
Dim rsSource As DAO.Recordset
Dim nId As Long
Dim aNotes() As String
Dim n As Long
Dim dtDate As Date
Dim sNote As String
On Error GoTo EH
DoCmd.Hourglass True
Set db = CurrentDb
db.Execute "DELETE * FROM temp_Notes"
Set rsDest = db.OpenRecordset("SELECT * FROM temp_Notes")
Set rsSource = db.OpenRecordset("SELECT RowId,Diary FROM SourceNotes")
With rsSource
Do Until .EOF
nId = .Fields("RowId").Value
aNotes = parseNotes(.Fields("Diary").Value)
For n = 0 To UBound(aNotes)
dtDate = CDate(Left(aNotes(n), 10))
sNote = Right(aNotes(n), Len(aNotes(n)) - 11)
With rsDest
.AddNew
.Fields("RowId").Value = nId
.Fields("NoteDate").Value = dtDate
.Fields("NoteText").Value = sNote
.Update
End With
Next 'n
.MoveNext
Loop
End With
MsgBox "Complete"
GoTo FINISH
EH:
With Err
MsgBox .Number & vbCrLf & .Source & vbCrLf & .Description
.Clear
End With
'for debugging purposes
Debug.Assert 0
GoTo FINISH
Resume
FINISH:
DoCmd.Hourglass False
On Error Resume Next
'release resources
If Not rsDest Is Nothing Then
rsDest.Close
Set rsDest = Nothing
End If
If Not rsSource Is Nothing Then
rsSource.Close
Set rsSource = Nothing
End If
End Sub
Private Function parseNotes(ByVal sRaw As String) As String()
Dim nYear As Long
Dim sYearToken As String
Dim nIndex As Long
Dim nSkip As Long
For nYear = 1999 To Year(Date)
sYearToken = "/" & nYear & " "
'use 12 to skip past first date ("mm/dd/yyyy " = 11)
nSkip = 12
Do
'find the end of the date
nIndex = InStr(nSkip, sRaw, sYearToken)
If nIndex > 0 Then
'find the start of the date
nIndex = nIndex - 6
sRaw = Left(sRaw, nIndex) & vbCrLf & Right(sRaw, Len(sRaw) - nIndex)
'use 12 to skip past next date
nSkip = nIndex + 12
End If
Loop Until nIndex <= 0
Next 'n
parseNotes = Split(sRaw, vbCrLf)
End Function

Read and copy text files by date into active worksheet

I'm trying to create a macro that reads each *.txt file from a folder, and if the modification date matches the current one, copy the contents into a worksheet of the *.xls file. I've been checking a lot of the codes you have been sharing here, but I just can't make it work.
When debbuging, at the 8th line, I get an error:
438: Object doesn't support this property or method
Sub GetSAPfiles()
Dim Cont As Integer
Dim RootDir As String
RootDir = "\HOME\SAP\dir\"
SAPfile = Dir(RootDir)
Set SAPfile = CreateObject("Scripting.FileSystemObject")
Set SF = SAPfile.GetFile(RootDir + SAPfile)
Do While SAPfile <> ""
Dim ObjDate, CurrDate
CurrDate = Format(Now(), "MM/DD/YYYY")
ObjDate = Format(file.DateLastModified, "MM/DD/YYYY")
If CurrDate = ObjDate Then
Cont = Cont + 1
Dim TxtFl, Txt
Set TxtFl = SAPfile.OpenTextFile(RootDir + SAPfile)
Txt = TxtFl.ReadLine
ActiveSheet.Cells(Cont, "A").Value = Txt
ArchTxt.Close
End If
SAPfile = Dir(RootDir)
Loop
End Sub
Try something like this instead, use the command prompt to get an array of files and loop through them using the FSO to check the modified date and read the text into the next blank cell in column A:
Sub SO()
RootDir$ = "\HOME\SAP\dir\"
For Each x In Filter(Split(CreateObject("WScript.Shell").Exec("CMD /C DIR " & RootDir & "*.* /B /A:-D").StdOut.ReadAll, vbCrLf), ".")
With CreateObject("Scripting.FileSystemObject")
If DateValue(.GetFile(RootDir & x).DateLastModified) = Date Then _
Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = .OpenTextFile(RootDir & x, 1).ReadAll
End With
Next x
End Sub

Multi Macro Communication

I am currently attempting to self-teach myself the great world of macro coding in VBA but have come across a stumbling block when trying to process 3 macros that I would ideally like to process as 1 but the code seems to be far too complicated for me at this stage.
What I need is to convert data from US date format mm/dd/yyyy into UK date format dd.mm.yyyy and changing the / to . at the same time ideally overwriting the original data.
This is currently what I have in separate Modules:
Sub FixFormat()
'display a message with an option if US date formats are
'included in the data
MsgBox "US Date Formats Included", vbQuestion + vbYesNo, "Addresses"
If Response = Yes Then MsgBox "Delimit Process Needed", vbOKOnly, "Addresses"
If Response = No Then MsgBox "End", vbOKOnly
End
End Sub
and
Sub FixDates()
Dim cell As Range
Dim lastRow As Long
lastRow = Range("A" & Rows.Count).End(xlUp).Row
For Each cell In Range("A1:A" & lastRow)
If InStr(cell.Value, ".") <> 0 Then
cell.Value = RegexReplace(cell.Value, _
"(\d{2})\.(\d{2})\.(\d{4})", "$3.$2.$1")
End If
If InStr(cell.Value, "/") <> 0 Then
cell.Value = RegexReplace(cell.Value, _
"(\d{2})/(\d{2})/(\d{4})", "$3.$1.$2")
End If
cell.NumberFormat = "yyyy-mm-d;#"
Next
End Sub
Function RegexReplace(ByVal text As String, _
ByVal replace_what As String, _
ByVal replace_with As String) As String
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
RE.Pattern = replace_what
RE.Global = True
RegexReplace = RE.Replace(text, replace_with)
End Function
Is there any way to do this without having to run 2 separate macros?
Yes, you can Call the subroutine you want to run as a result of the message box.
Sub FixFormat()
'display a message with an option if US date formats are
'included in the data
If MsgBox("US Date Formats Included", vbQuestion + vbYesNo, "Addresses") = 6 Then
MsgBox "Delimit Process Needed", vbOKOnly, "Addresses"
Call FixDates
Else
MsgBox "End", vbOKOnly
End If
End Sub
See this link for more information on the MsgBox function: http://msdn.microsoft.com/en-us/library/139z2azd(v=vs.90).aspx

Providing status updates for macro that goes into not responding state until completion

I have a VBA Macro to search through email archives.
When searching through tens of thousands of emails, (or even just a couple hundred on my test machine) it displays the status for a few seconds, then enters a Not Responding state while running through the rest of the emails.
This has led impatient users to close out of the task prematurely, and I would like to rectify this by providing status updates.
I have coded the following solution, and believe that the problem lies in the way the GarbageCollector functions in VBA during the Loop.
Public Sub searchAndMove()
UserForm1.Show
' Send a message to the user indicating
' the program has completed successfully,
' and displaying the number of messages sent during the run.
End Sub
Private Sub UserForm_Activate()
Me.Width = 240
Me.Height = 60
Me.Label1.Width = 230
Me.Label1.Height = 50
Dim oSelectTarget As Outlook.Folder
Dim oMoveTarget As Outlook.Folder
Dim oSearchCriteria As String
' Select the target folder to search and then the folder to
' which the files should be moved
Set oSelectTarget = Application.Session.PickFolder
Set oMoveTarget = Application.Session.PickFolder
oSearchCriteria = InputBox("Input search string: ")
Dim selectedItems As Outlook.Items
Set selectedItems = oSelectTarget.Items
Dim selectedEmail As Outlook.MailItem
Dim StatusBarMsg As String
StatusBarMsg = ""
Dim initialCount As Long
initialCount = selectedItems.count
Dim movedCounter As Long
movedCounter = 0
Dim x As Long
Dim exists As Long
' Function Loop, stepping backwards
' to prevent errors derived from modifying the collection
For x = selectedItems.count To 1 Step -1
Set selectedEmail = selectedItems.Item(x)
' Test to determine if the subject contains the search string
exists = InStr(selectedEmail.Subject, oSearchCriteria)
If Len(selectedEmail.Subject) > 999 Then
selectedEmail.Move oMoveTarget
Else:
If exists <> 0 Then
selectedEmail.Move oMoveTarget
movedCounter = (movedCounter + 1)
Else: End If
End If
Set selectedEmail = Nothing
StatusBarMsg = "Processing " & x & " out of " & initialCount & " messages."
UserForm1.Label1.Caption = StatusBarMsg
UserForm1.Repaint
Next x
Dim Msg As String
Dim Response
Msg = "SearchAndMove has detected and moved " & movedCounter & _
" messages since last run."
Response = MsgBox(Msg, vbOKOnly)
' Close the References to prevent a reference leak
Set oSelectTarget = Nothing
Set oMoveTarget = Nothing
Set selectedItems = Nothing
Set selectedEmail = Nothing
Unload Me
End Sub
Change the line
UserForm1.Repaint
to
DoEvents
Yes this will increase the execution time but in case there are thousands of emails then you don't have much of an option.
TIP:
Also you might want to change
StatusBarMsg = "Processing " & x & " out of " & initialCount & " messages."
to
StatusBarMsg = "Please do not interrupt. Processing " & x & " out of " & initialCount & " messages."
Also it is advisable to inform your user at the beginning of the process that it might take time and hence they can run the process when they are sure they do not want to work on that pc?
Something like this
Sub Sample()
Dim strWarning As String
Dim Ret
strWarning = "This process may take sometime. It is advisable to run this " & _
"when you don't intend to use the pc for sometime. Would you like to Continue?"
Ret = MsgBox(strWarning, vbYesNo, "Information")
If Ret <> vbYes Then Exit Sub
For x = SelectedItems.Count To 1 Step -1
'~~> Rest of the code
End Sub
HTH
Sid