20190319xlVBA_根据考勤数据统计缺勤缺考数据
Sub SubtotalPickFile() Dim StartTime As Variant Dim UsedTime As Variant StartTime = VBA.Timer Dim firstday As Date, lastday As Date Dim Wb As Workbook Dim Sht As Worksheet Dim oSht As Worksheet Dim Dic As Object Dim d As Object Set d = CreateObject("Scripting.Dictionary") Set ud = CreateObject("Scripting.Dictionary") Set Dic = CreateObject("Scripting.Dictionary") Dim onDay, onTime, offTime Const ON_TIME = "8:30:00" Const OFF_TIME = "17:00:00" Const MID_TIME = "12:00:00" Dim onForget, offForget, onLate, offEarly, forgetTime, lateTime, earlyTime, duration Dim lateday, earlyday, forgetday Set Wb = ThisWorkbook '选取考勤数据文件 FilePath = FilePicker() If FilePath = "" Then Exit Sub Set OpenWb = Application.Workbooks.Open(FilePath) Set Sht = OpenWb.Worksheets(1) With Sht endrow = .Cells(.Cells.Rows.Count, 1).End(xlUp).Row Set Rng = .Range("A3:F" & endrow) arr = Rng.Value End With OpenWb.Close False '设置考勤起止日期 startday = Application.InputBox("请输入起始日期,格式为 2019/01/01 : ", "InputBox", , , , , , 2) If startday = False Then MsgBox "没有输入日期!" Exit Sub End If endday = Application.InputBox("请输入结束日期,格式为 2019/01/31 : ", "InputBox", , , , , , 2) If endday = False Then MsgBox "没有输入日期!" Exit Sub End If '计算工作日天数 On Error Resume Next firstday = CDate(startday) lastday = CDate(endday) 'wkdays = WorkdaysBetween(firstday, lastday) counter = 0 today = firstday Do Key = Format(today, "yyyy/mm/dd") If Weekday(today, vbMonday) <= 5 Then counter = counter + 1 d(Key) = "" ''debug.Print today; " 是工作日 "; counter Else ud(Key) = "" ''Debug.Print today; " 是工作日 "; counter End If today = DateAdd("d", 1, today) If today = DateAdd("d", 1, lastday) Then Exit Do Loop wkdays = counter If Err.Number <> 0 Then Exit Sub MsgBox "输入的日期范围可能有误!", vbInformation, "Information" End If Set oSht = Wb.Worksheets("result") For i = LBound(arr) To UBound(arr) Key = CStr(arr(i, 2)) td = CDate(arr(i, 4)) If DateDiff("d", firstday, td) >= 0 And DateDiff("d", td, lastday) >= 0 Then ''debug.Print td; " 符合要求" '截取上下班时间 onTime = CDate(Split(arr(i, 5), " ")(1)) offTime = CDate(Split(arr(i, 6), " ")(1)) onForget = False offForget = False '计算工作时长 duration = DateDiff("n", onTime, offTime) If Not Dic.Exists(Key) Then lateTime = 0 earlyTime = 0 forgetTime = 0 forgetday = "" lateday = "" earlyday = "" onDay = 1 '迟到判断 onLate = (DateDiff("s", CDate(ON_TIME), onTime) > 0) onForget = (DateDiff("s", CDate(MID_TIME), onTime) > 0) If onForget Then forgetTime = forgetTime + 1 forgetday = arr(i, 4) & "上午" Else If onLate Then If duration < 510 Then lateTime = lateTime + 1 If lateday = "" Then lateday = arr(i, 4) & "上午" Else lateday = lateday & vbCrLf & arr(i, 4) & "上午" End If End If End If End If '早退判断 offEarly = (DateDiff("s", offTime, CDate(OFF_TIME)) > 0) offForget = (DateDiff("s", CDate(MID_TIME), offTime) < 0) If offForget Then forgetTime = forgetTime + 1 If forgetday <> "" Then forgetday = forgetday & vbCrLf & arr(i, 4) & "下午" Else forgetday = arr(i, 4) & "下午" End If Else If offEarly Then If duration < 510 Then earlyTime = earlyTime + 1 If earlyday = "" Then earlyday = arr(i, 4) & "下午" Else earlyday = earlyday & vbCrLf & arr(i, 4) & "下午" End If End If End If End If ar = Array(arr(i, 1), arr(i, 2), arr(i, 3), wkdays, onDay, 0, Format(arr(i, 4), "yyyy/mm/dd"), lateTime, lateday, earlyTime, earlyday, forgetTime, forgetday) Dic(Key) = ar Else ar = Dic(Key) ar(4) = ar(4) + 1 ar(6) = ar(6) & ";" & Format(arr(i, 4), "yyyy/mm/dd") 'If Key = "2018000766" Then Debug.Print td; " ----------"; ar(6) '迟到判断 onLate = (DateDiff("s", CDate(ON_TIME), onTime) > 0) onForget = (DateDiff("s", CDate(MID_TIME), onTime) > 0) If onForget Then ar(11) = ar(11) + 1 If ar(12) <> "" Then ar(12) = ar(12) & vbCrLf & arr(i, 4) & "上午" Else ar(12) = arr(i, 4) & "上午" End If Else If onLate Then If duration < 510 Then ar(7) = ar(7) + 1 If ar(8) = "" Then ar(8) = arr(i, 4) & "上午" Else ar(8) = ar(8) & vbCrLf & arr(i, 4) & "上午" End If End If End If End If '早退判断 offEarly = (DateDiff("s", offTime, CDate(OFF_TIME)) > 0) offForget = (DateDiff("s", CDate(MID_TIME), offTime) < 0) If offForget Then ar(11) = ar(11) + 1 If ar(12) <> "" Then ar(12) = ar(12) & vbCrLf & arr(i, 4) & "下午" Else ar(12) = arr(i, 4) & "下午" End If Else If offEarly Then If duration < 510 Then ar(9) = ar(9) + 1 If ar(10) = "" Then ar(10) = arr(i, 4) & "下午" Else ar(10) = ar(10) & vbCrLf & arr(i, 4) & "下午" End If End If End If End If Dic(Key) = ar End If End If Next i '计算缺考天数和缺考日期 'On Error Resume Next For Each K In Dic.keys ar = Dic(K) ar(4) = UBound(ar(6)) + 1 ar(5) = ar(3) - ar(4) 'If K = "2018000766" Then Debug.Print "缺考天数 : "; ar(5) 'If K = "2018000766" Then Debug.Print ar(2); " 打卡日期: "; ar(6) s = "" For Each wd In d.keys 'If K = "2018000766" Then Debug.Print "工作日》》"; wd 'If K = "2018000766" Then Debug.Print "判断日期在不在工作日内:"; wd; " "; InStr(ar(6), wd) If InStr(ar(6), wd) <= 0 Then If s = "" Then s = wd & "缺考" Else s = s & vbCrLf & wd & "缺考" End If End If Next wd w = "" For Each u In ud.keys If K = "2018000766" Then Debug.Print "非工作日》》"; u If K = "2018000766" Then Debug.Print "判断日期在不在工作日内:"; u; " "; InStr(ar(6), u) If InStr(ar(6), u) > 0 Then If w = "" Then w = u & "加班" Else w = w & vbCrLf & u & "加班" End If End If Next u 'If K = "2018000766" Then Debug.Print ar(2); " 缺考日期: "; s 'If K = "2018000766" Then Debug.Print ar(2); " 加班日期: "; w ar(6) = s & vbCrLf & w Dic(K) = ar Next K With oSht .UsedRange.Offset(2).Clear Set Rng = .Range("A3") Set Rng = Rng.Resize(Dic.Count, 13) Rng.Value = Application.Rept(Dic.Items, 1) Sort_2003 Rng, False SetCenters .UsedRange SetBorders .UsedRange .Activate Rows("3:3").Select ActiveWindow.FreezePanes = True End With Call StepForward UsedTime = VBA.Timer - StartTime ''debug.Print "UsedTime :" & Format(UsedTime, "#0.0000 Seconds") MsgBox "UsedTime :" & Format(UsedTime, "#0.0000 Seconds") Set Dic = Nothing Set Wb = Nothing Set Sht = Nothing Set oSht = Nothing Set OpenWb = Nothing End Sub Private Sub SetBorders(ByVal Rng As Range) With Rng.Borders .LineStyle = xlContinuous .ColorIndex = xlAutomatic .TintAndShade = 0 .Weight = xlThin End With End Sub Private Sub SetCenters(ByVal Rng As Range) With Rng .HorizontalAlignment = xlCenter .VerticalAlignment = xlCenter .WrapText = True '.Columns.AutoFit End With End Sub 'FilePath=FilePicker(InitialPath) 'If FilePath = "" Then Exit Sub Function FilePicker(Optional InitialPath As String = "") Dim FilePath As String If InitialPath = "" Then InitialPath = Application.ActiveWorkbook.Path End If With Application.FileDialog(msoFileDialogFilePicker) .AllowMultiSelect = False .InitialFileName = InitialPath .Title = "请选择单个Excel工作簿" .Filters.Clear .Filters.Add "Excel工作簿", "*.xls*" If .Show = -1 Then FilePath = .SelectedItems(1) Else MsgBox "您没有选中任何文件,本次汇总中断!" End If End With FilePicker = FilePath End Function Function WorkdaysInMonth(ByVal month As Date) Dim counter counter = 0 firstday = CDate(Format(month, "yyyy/mm") & "/01") lastday = DateAdd("d", -1, CDate(Format(DateAdd("m", 1, month), "yyyy/mm") & "/01")) today = firstday Do If Weekday(today, vbFriday) <= 5 Then counter = counter + 1 today = DateAdd("d", 1, today) If today = lastday Then Exit Do Loop WorkdaysInMonth = counter End Function Function WorkdaysBetween(ByVal firstday As Date, ByVal lastday As Date) Dim counter today = firstday Do If Weekday(today, vbFriday) <= 5 Then counter = counter + 1 today = DateAdd("d", 1, today) If today = lastday Then Exit Do Loop WorkdaysBetween = counter End Function Function IsWorkday(ByVal OneDay As Date) As Boolean IsWorkday = (Weekday(OneDay, vbMonday) <= 5) ' ''debug.Print OneDay; " 是工作日 "; IsWorkday End Function Private Sub Sort_2003(ByVal Rng As Range, Optional WithHeader As Boolean = True) With Rng 'xlAscending .Sort _ Key1:=Rng.Cells(1, 1), Order1:=xlAscending, _ Header:=IIf(WithHeader, xlYes, xlNo), _ MatchCase:=False, _ Orientation:=xlTopToBottom, _ SortMethod:=xlPinYin End With End Sub
Public Sub StepForward() Dim Dic As Object Dim Wb As Workbook Dim Sht As Worksheet Dim oSht As Worksheet Set Wb = Application.ThisWorkbook Set Dic = CreateObject("Scripting.Dictionary") Set Sht = Wb.Worksheets("result") Set oSht = Wb.Worksheets("analyze") With Sht endrow = .Cells(.Cells.Rows.Count, 1).End(xlUp).Row Set Rng = .Range("A3:M" & endrow) arr = Rng.Value For i = LBound(arr) To UBound(arr) Key = CStr(arr(i, 2)) company = arr(i, 1) staff = arr(i, 3) IsSave = False If arr(i, 6) >= 1 Then debt = arr(i, 6) IsSave = True Else debt = "" End If If arr(i, 8) >= 3 Then late = arr(i, 8) IsSave = True Else late = "" End If If arr(i, 10) >= 3 Then early = arr(i, 10) IsSave = True Else early = "" End If If arr(i, 12) >= 3 Then forget = arr(i, 12) IsSave = True Else forget = "" End If If IsSave Then Dic(Key) = Array(company, Key, staff, debt, late, early, forget) Next i End With With oSht .UsedRange.Offset(2).Clear Set Rng = .Range("A3") Set Rng = Rng.Resize(Dic.Count, 7) Rng.Value = Application.Rept(Dic.Items, 1) SetCenters .UsedRange SetBorders .UsedRange Sort_2003 Rng, False .Activate Rows("3:3").Select ActiveWindow.FreezePanes = True End With UsedTime = VBA.Timer - StartTime End Sub Private Sub SetBorders(ByVal Rng As Range) With Rng.Borders .LineStyle = xlContinuous .ColorIndex = xlAutomatic .TintAndShade = 0 .Weight = xlThin End With End Sub Private Sub SetCenters(ByVal Rng As Range) With Rng .HorizontalAlignment = xlCenter .VerticalAlignment = xlCenter .WrapText = True '.Columns.AutoFit End With End Sub Private Sub Sort_2003(ByVal Rng As Range, Optional WithHeader As Boolean = True) With Rng 'xlAscending .Sort _ Key1:=Rng.Cells(1, 1), Order1:=xlAscending, _ Header:=IIf(WithHeader, xlYes, xlNo), _ MatchCase:=False, _ Orientation:=xlTopToBottom, _ SortMethod:=xlPinYin End With End Sub