Do I understand the order of execution?
1- Sub CreatePayrollLog(JobID As Integer) which calls
2 - OpenPayrollLogExcelFile
No clue as to when the function at the end is called - probably not important for now. I have all caps in my code notes so as to see them easier in Notepad and I would like to copy and paste from there, so I'm not yelling, OK? Some of these comments may pinpoint the problem so read all the code before acting on anything. I divided the code via **** so I could compartmentalize it.
After wading through the code, consider the concept of Activeworkbook (or sheet, or whatever). If for any reason the workbook being controlled by this code isn't the active object, you should expect problems. First, sometimes users can interfere, making it not the active object. Or your code can do this (because you didn't close it for example). Or sometimes an object isn't "active" if it hasn't been "activated" via code. Elsewhere in this forum in a recent Excel question, it was pointed out that each necessary object should be explicitly created. You're not doing this, which may be causing you to not reference the hierarchy of the object model for Excel (pointed out in code comments, I think). Very important take away; activeworkbook or activesheet or range are not children of the application object AFAIK. I also agree that declaring as Object isn't good enough. Should be Excel.Application - see https://www.accessforums.net/showthread.php?t=69106 posts 3 and 7. Apparently what I pointed out re individual objects was covered by the link in post 3, which I didn't visit. Obviously, correct hierarchy is important regardless.
Lastly, I am not a big fan of so much code nested in a With block. You have the most I've ever seen. I recommend trying to minimize (not eliminate) a whole lot of If's, Do's, For's and more With's like you have. Makes it very hard to follow. When you get this working, months from now if you have to look at this again, it won't be so fresh. I spent about 2 hours sifting through the maze. Not really any corrections by me; mostly comments. Now for the code...
Code:
Option Compare Database
Option Explicit
Dim XL As Object
Dim WB As Object
Dim FileToOpen As String
'***************************************************************************************************************************
Sub CreatePayrollLog(JobID As Integer) 'WHAT PROVIDES JOBID?
Dim HighlightYellow As Boolean
Dim Answer As Variant
HighlightYellow = False
Answer = MsgBox("Would you like any existing entries on the log to be highlighted in yellow?", vbYesNo + vbQuestion + vbDefaultButton2, "Highlight Existing Entries")
'If Answer = vbNo Then
' HighlightYellow = False
'ElseIf Answer = vbYes Then
'HighlightYellow = True
'End If
'IF YOU HAD 3 CHOICES, 2 CHECKS WOULD BE REQ'D. YOU ONLY HAVE 2, SO ONLY 1 CHECK IS REQ'D IN THIS CASE (IF NOT YES, THEN HAS TO BE NO)
If Answer = vbYes Then HighlightYellow = True
Call OpenPayrollLogExcelFile 'CALL KEYWORD NOT REQ'D BUT IS OK
Call AddPayrollData(JobID, HighlightYellow)
DoCmd.Beep
MsgBox "The file is open and the payroll data has been added. Don't forget to save the file." 'HOW DO YOU KNOW THIS FOR SURE?
WB.Close
'SHOULD YOU BE CLOSING THIS INSTANCE OF THE APPLICATION AS WELL?
Set WB = Nothing
Set XL = Nothing
End Sub
'************************************************************************************************************************************
Sub AddPayrollData(JobID As Integer, Optional HighlightYellow As Boolean) 'HIGHLIGHT DOESN'T LOOK OPTIONAL. SEEMS IT WOULD ALWAYS BE T OR F
Dim rstQ_PayrollLog As Recordset 'SHOULD DECLARE THE RS TYPE: DAO?
Dim FoundEmptyRow As Boolean, FoundMatchingEntry As Boolean
Dim RowNumber As Integer, EmptyRow As Integer, LCounter As Integer
Dim PositionAndOCC As String
FoundEmptyRow = False
FoundMatchingEntry = False
RowNumber = 8
EmptyRow = 0
Set rstQ_PayrollLog = CurrentDb.OpenRecordset(Name:="Q_PayrollLog", Type:=RecordsetTypeEnum.dbOpenSnapshot)
'SET Set rstQ_PayrollLog = CURRENTDB.OPENRECORDSET ("Q_PayrollLog", dbOpenSnapshot) IS SHORTER
'IF NO RECORDS, YOU WILL GENERATE AN ERROR. FIRST USE: IF NOT (rstQ_PayrollLog.BOF AND rstQ_PayrollLog.EOF) THEN...
'OR TEST RS COUNT: IF rstQ_PayrollLog.RECORDCOUNT <> 0 THEN... (DON'T USE >0)
'IF NO RECORDS, THEN EXIT
rstQ_PayrollLog.MoveLast
rstQ_PayrollLog.MoveFirst
With XL 'XL IS THE APPLICATION. YOU ARE REFERENCING EXCEL OBJECTS WHICH ARE NOT A CHILD OF THE APPLICATION OBJECT. THE HIERARCHY IS
'APPLICATION>WORKBOOK>WORKSHEET>RANGE. SEE ALSO ??? BELOW.
.Sheets("PayrollLog").Activate
Do While Not rstQ_PayrollLog.EOF
FoundMatchingEntry = False
If rstQ_PayrollLog.Fields("JobID") = JobID Then
If .Range("A1") = "[company name here]" Then .Range("A1") = rstQ_PayrollLog.Fields("CompanyName")
If .Range("A2") = "[job title here]" Then .Range("A2") = rstQ_PayrollLog.Fields("JobTitle")
Do While FoundEmptyRow = False
If IsEmpty(.Range("A" & RowNumber)) Then
'ISEMPTY IS AN ACCESS and EXCEL FUNCTION. COULD BE A PROBLEM HERE AS THEY MEAN DIFFERENT THINGS TO EACH.
'IN ACCESS, A VARIABLE NOT INITIALIZED (MADE = TO SOMETHING) WOULD RETURN TRUE. XL.RANGE ISN'T A VARIABLE.
'NOT CERTAIN WHICH WOULD TAKE PRECEDENCE.
FoundEmptyRow = True
Else
If HighlightYellow = True Then .Range("A" & RowNumber & ":M" & RowNumber).Interior.ColorIndex = 6 'Yellow
RowNumber = RowNumber + 1
End If
Loop
If EmptyRow = 0 Then EmptyRow = RowNumber
''''now i need it to scroll through each record in the query and check to see if this record is already on the log.
''''if it's not, it should write it to next empty row.
'this next section goes through the existing items on the log (if there are any) to see if it's
'the same timecard record as the current one in the Recordset.
If RowNumber > 8 Then
For LCounter = 8 To (RowNumber - 1)
If rstQ_PayrollLog.Fields("TimecardID") = .Range("N" & LCounter) Then
FoundMatchingEntry = True
Exit For
End If
Next
End If
If FoundMatchingEntry = True Then
If IsNull(rstQ_PayrollLog.Fields("OCCCode")) Or rstQ_PayrollLog.Fields("OCCCode") = "" Then
PositionAndOCC = rstQ_PayrollLog.Fields("PositionTitle")
Else
PositionAndOCC = rstQ_PayrollLog.Fields("PositionTitle") & " / " & rstQ_PayrollLog.Fields("OCCCode")
End If
.Range("A" & LCounter) = rstQ_PayrollLog.Fields("FullName")
.Range("B" & LCounter) = PositionAndOCC
.Range("C" & LCounter) = Val(rstQ_PayrollLog.Fields("PositionAcctCode"))
.Range("D" & LCounter) = rstQ_PayrollLog.Fields("WeekEnding")
.Range("E" & LCounter) = rstQ_PayrollLog.Fields("NumberDaysWorked")
.Range("F" & LCounter) = rstQ_PayrollLog.Fields("Rate")
.Range("G" & LCounter) = rstQ_PayrollLog.Fields("1XTotalAmt")
.Range("H" & LCounter) = rstQ_PayrollLog.Fields("1point5XTotalAmt")
.Range("I" & LCounter) = rstQ_PayrollLog.Fields("2X3X")
.Range("J" & LCounter) = rstQ_PayrollLog.Fields("MPTotalAmt")
.Range("K" & LCounter) = rstQ_PayrollLog.Fields("TOTALAMT")
.Range("L" & LCounter) = rstQ_PayrollLog.Fields("BoxRentalTotalAmt")
.Range("M" & LCounter) = rstQ_PayrollLog.Fields("MileageTotalAmt")
.Range("N" & LCounter) = rstQ_PayrollLog.Fields("TimecardID")
.Range("A" & LCounter & ":M" & LCounter).ShrinkToFit = True
If LCounter > 8 Then .Range("A" & LCounter & ":M" & LCounter).Borders.LineStyle = xlContinuous
.Range("C" & LCounter).HorizontalAlignment = xlLeft
.Range("D" & LCounter).NumberFormat = "mm/dd/yy"
.Range("D" & LCounter).HorizontalAlignment = xlLeft
.Range("E" & LCounter).HorizontalAlignment = xlCenter
.Range("F" & LCounter).NumberFormat = "0.0000"
.Range("F" & LCounter).HorizontalAlignment = xlCenter
If .Range("I" & LCounter) = "" Or IsEmpty(.Range("I" & LCounter)) Then
.Range("I" & LCounter) = .Range("I" & LCounter)
Else
.Range("I" & LCounter) = RemoveFirstChar(.Range("I" & LCounter))
End If
.Range("G" & LCounter & ":M" & LCounter).NumberFormat = "_($* #,##0.00_);_($* (#,##0.00);_($* ""-""??_);_(@_)"
.Range("K" & LCounter).Font.Bold = True
.Range("K" & LCounter).Font.Size = 12
Else
If IsNull(rstQ_PayrollLog.Fields("OCCCode")) Or rstQ_PayrollLog.Fields("OCCCode") = "" Then
PositionAndOCC = rstQ_PayrollLog.Fields("PositionTitle")
Else
PositionAndOCC = rstQ_PayrollLog.Fields("PositionTitle") & " / " & rstQ_PayrollLog.Fields("OCCCode")
End If
.Range("A" & EmptyRow) = rstQ_PayrollLog.Fields("FullName")
.Range("B" & EmptyRow) = PositionAndOCC
.Range("C" & EmptyRow) = Val(rstQ_PayrollLog.Fields("PositionAcctCode"))
.Range("D" & EmptyRow) = rstQ_PayrollLog.Fields("WeekEnding")
.Range("E" & EmptyRow) = rstQ_PayrollLog.Fields("NumberDaysWorked")
.Range("F" & EmptyRow) = rstQ_PayrollLog.Fields("Rate")
.Range("G" & EmptyRow) = rstQ_PayrollLog.Fields("1XTotalAmt")
.Range("H" & EmptyRow) = rstQ_PayrollLog.Fields("1point5XTotalAmt")
.Range("I" & EmptyRow) = rstQ_PayrollLog.Fields("2X3X")
.Range("J" & EmptyRow) = rstQ_PayrollLog.Fields("MPTotalAmt")
.Range("K" & EmptyRow) = rstQ_PayrollLog.Fields("TOTALAMT")
.Range("L" & EmptyRow) = rstQ_PayrollLog.Fields("BoxRentalTotalAmt")
.Range("M" & EmptyRow) = rstQ_PayrollLog.Fields("MileageTotalAmt")
.Range("N" & EmptyRow) = rstQ_PayrollLog.Fields("TimecardID")
.Range("A" & EmptyRow & ":M" & EmptyRow).ShrinkToFit = True
If EmptyRow > 8 Then .Range("A" & EmptyRow & ":M" & EmptyRow).Borders.LineStyle = xlContinuous
.Range("C" & EmptyRow).HorizontalAlignment = xlLeft
.Range("D" & EmptyRow).NumberFormat = "mm/dd/yy"
.Range("D" & EmptyRow).HorizontalAlignment = xlLeft
.Range("E" & EmptyRow).HorizontalAlignment = xlCenter
.Range("F" & EmptyRow).NumberFormat = "0.0000"
.Range("F" & EmptyRow).HorizontalAlignment = xlCenter
If .Range("I" & EmptyRow) = "" Or IsEmpty(.Range("I" & EmptyRow)) Then
.Range("I" & EmptyRow) = .Range("I" & EmptyRow)
Else
.Range("I" & EmptyRow) = RemoveFirstChar(.Range("I" & EmptyRow))
End If
.Range("G" & EmptyRow & ":M" & EmptyRow).NumberFormat = "_($* #,##0.00_);_($* (#,##0.00);_($* ""-""??_);_(@_)"
.Range("K" & EmptyRow).Font.Bold = True
.Range("K" & EmptyRow).Font.Size = 12
EmptyRow = EmptyRow + 1
End If
End If
rstQ_PayrollLog.MoveNext
Loop
.Range("A" & EmptyRow - 1 & ":M" & EmptyRow - 1).Borders(xlEdgeBottom).LineStyle = xlContinuous
'the below will sort the Payroll Log
.Range("A7:N" & EmptyRow - 1).Select
.ActiveSheet.Sort.SortFields.Clear '??? BESIDES RANGE AS NOTED ABOVE, ANOTHER OBJECT WHICH ISN'T A CHILD OF APPLICATION (XL VARIABLE)
.ActiveSheet.Sort.SortFields.Add Key:=Range( _
"C8:C" & EmptyRow - 1), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
.ActiveSheet.Sort.SortFields.Add Key:=Range( _
"A8:A" & EmptyRow - 1), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
.ActiveSheet.Sort.SortFields.Add Key:=Range( _
"D8:D" & EmptyRow - 1), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With .ActiveSheet.Sort
.SetRange Range("A7:N" & EmptyRow - 1)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
.Range("B6").Select
'this sets the Print Area so the TimecardID column will not print
.ActiveSheet.PageSetup.PrintArea = "$A$1:$M$" & EmptyRow - 1
End With
rstQ_PayrollLog.Close
Set rstQ_PayrollLog = Nothing
End Sub
'***********************************************************************************************************************
Sub OpenPayrollLogExcelFile()
Dim fDialog As Office.FileDialog
Dim varFile As Variant
''Set varFile = Nothing 'REM THIS OUT?? CANNOT SET A BOOLEAN VARIABLE TO "NOTHING"
' Set up the File Dialog.
Set fDialog = Application.FileDialog(msoFileDialogFilePicker)
With fDialog
' Allow user to make multiple selections in dialog box '
.AllowMultiSelect = False
' Set the title of the dialog box. '
.Title = "Please select the Payroll Log file you wish to open."
' Clear out the current filters, and add our own.'
.Filters.Clear
.Filters.Add "Excel", "*.XLSX"
.Filters.Add "All Files", "*.*"
' Show dialog. If the .Show returns True, user picked a file. If the .Show returns False, user clicked Cancel. '
If .Show = True Then 'TRUE CAN BE ASCERTAINED AS "If .Show Then..."
For Each varFile In .SelectedItems
FileToOpen = varFile 'IF YOU ALLOWED MULTISELECT, THIS WOULD CREATE AN INVALID STRING OF FILE NAMES.
Next
Else
MsgBox "Operation cancelled."
Exit Sub
End If
End With
'this will actually open the Excel file
Set XL = CreateObject("Excel.Application")
Set WB = XL.Workbooks.Open(FileToOpen)
XL.Visible = True
End Sub
'*********************************************************************************************************************
Public Function RemoveFirstChar(RemFstChar As String) As String
If Left(RemFstChar, 1) = "$" Then
RemFstChar = Replace(RemFstChar, "$", "")
End If
RemoveFirstChar = RemFstChar
End Function