スケジュール管理表を作成 ⑩ まとめ
先日から、スケジュール管理表をExcelで作成している。
infoment.hatenablog.com
今日も、前回の続きから。
前回までで、一応の完成を見せたスケジュール表。
数日間にわたり、業務で使用してみた。結果、幾つかの細かな修正を経た
ものの、全体的に問題なく使用することが出来た。
ということで、今日は全体的なまとめを行う。
シートについて
基本的なシートは、以下の二つ。
- 定例業務 朝一番に行う業務を記すシート
- 原紙 日々のスケジュールを作成するシート
定例業務には、「定例業務テーブル」を作成。
記入例は、こんな感じで。
なお、定例業務シートのオブジェクト名はプロジェクトエクスプローラーで
一番上に来るように、Sheet0とした。
原紙シートのレイアウトは、こんな感じ。
入力するのは、黄色の4列のみ。
B1セルの日時は、Now関数とユーザー定義書式で表現している。
以下の4つは、任意に設定可能。なお、4つの時間および時刻を入力するセルは
それぞれ、各ラベルと同じ文字列で名前が付いている。
例)「08:00」と入力されたセルの名前は「始業時刻」
関数がセットされているのは、次の3列
- A列:=ROW()-ROW($A$2)
- D列:=IF([@作業予定時間]="","","●")
- I列:=IF([@実際作業時間]="","",[@実際作業時間]/[@作業予定時間])
ActiveXコントロールで、ボタンを4つ配置。
各オブジェクト名は、以下のとおり。
- cb作成
- cb更新
- cb中断
- cb昼休み
シートモジュール
定例業務
Public Property Get Tb() As ListObject Set Tb = Me.ListObjects(1) End Property
原紙
Private Property Get Tb() As ListObject Set Tb = Me.ListObjects(1) End Property Private Function wf() As WorksheetFunction Set wf = WorksheetFunction End Function Private Sub cb作成_Click() Call MakeNewSheet End Sub Private Sub cb更新_Click() Call UpdateTable End Sub Private Sub cb中断_Click() Call SetInterruption End Sub Private Sub cb昼休み_Click() Call SetLunchBreak End Sub Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean) If Intersect(Target, Tb.ListColumns("終了時刻").DataBodyRange) Is Nothing Then Exit Sub ElseIf Target.Offset(-1) = vbNullString Then MsgBox "一つ前の項目が未だ終わっていません。" Cancel = True Exit Sub End If Target = wf.MRound(Time, "00:15") Cancel = True End Sub Private Sub Worksheet_Change(ByVal Target As Range) Dim ListRow As Excel.ListRow Select Case Target.Column - Tb.Range(1).Column + 1 Case 列名.en作業予定時間, 列名.en終了時刻 Call UpdateTable End Select End Sub Private Sub Worksheet_SelectionChange(ByVal Target As Range) If Target.Count > 1 Then Exit Sub If Tb.ListRows.Count = 0 Then Exit Sub If Intersect(Target, Tb.ListColumns(列名.enNo).DataBodyRange) Is Nothing Then Exit Sub uf案件追加.Show End Sub
標準モジュール
Enum 列名 enNo = 1 en実施内容 en作業予定時間 en● en実際作業時間 en終了予定時刻 en残り時間 en終了時刻 en予実比率 en備考 [_eLast] End Enum Private Function wf() As WorksheetFunction Set wf = WorksheetFunction End Function Public Sub UpdateTable() ' 無限ループ回避のため、シートのチェンジイベントを一時停止。 Application.EnableEvents = False Application.ScreenUpdating = False On Error GoTo er: Dim Tb As ListObject Set Tb = ActiveSheet.ListObjects(1) ' 罫線クリア。 Tb.DataBodyRange.Borders.LineStyle = xlNone Dim arr As Variant arr = Tb.DataBodyRange Dim i As Long For i = 1 To UBound(arr) ' 終了予定時刻。 If arr(i, en実施内容) = vbNullString Then arr(i, en終了予定時刻) = vbNullString ElseIf i = 1 Then arr(i, en終了予定時刻) = CDate(arr(i, en作業予定時間)) + tm始業時刻 ElseIf arr(i - 1, en終了時刻) = vbNullString Then arr(i, en終了予定時刻) = CDate(arr(i, en作業予定時間)) + CDate(arr(i - 1, en終了予定時刻)) Else arr(i, en終了予定時刻) = CDate(arr(i, en作業予定時間)) + CDate(arr(i - 1, en終了時刻)) End If ' 残り時間。 If arr(i, en終了予定時刻) = vbNullString Or _ arr(i, 列名.en終了時刻) <> vbNullString Then arr(i, en残り時間) = vbNullString ElseIf CDate(arr(i, en終了予定時刻)) >= Time Then arr(i, en残り時間) = Format(CDate(arr(i, en終了予定時刻)) - Time, "hh:mm") Else arr(i, en残り時間) = Format(Time - CDate(arr(i, en終了予定時刻)), "-hh:mm") End If ' 実際作業時間。 If arr(i, en終了時刻) = vbNullString Then arr(i, en実際作業時間) = vbNullString ElseIf i = 1 Then arr(i, en実際作業時間) = CDate(arr(i, en終了時刻)) - tm始業時刻 Else arr(i, en実際作業時間) = CDate(arr(i, en終了時刻)) - CDate(arr(i - 1, en終了時刻)) End If Next i ' 更新後の値セット。 Tb.DataBodyRange = arr ' 数式の再セット。 Tb.ListColumns(enNo).DataBodyRange = "=ROW()-ROW(" & Tb.Range(1).Address & ")" Tb.ListColumns(en●).DataBodyRange = "=IF([@作業予定時間]="""","""",""●"")" Tb.ListColumns(en予実比率).DataBodyRange = "=IF([@実際作業時間]="""","""",[@実際作業時間]/[@作業予定時間])" Dim ListRow As Excel.ListRow For Each ListRow In Tb.ListRows With ListRow ' ●のサイズ修正。 .Range(en●).Font.Size = wf.MRound(6 * (Hour(.Range(en作業予定時間)) + _ Minute(.Range(en作業予定時間)) / 60) + 1, 2) ' 残り時間の文字色設定。 Select Case Left(.Range(en残り時間), 1) Case "-" .Range(en残り時間).Font.Color = 192 Case Else .Range(en残り時間).Font.Color = vbBlack End Select End With Next ' 罫線の再描画。 For Each ListRow In Tb.ListRows If ListRow.Range(en終了予定時刻) >= tm終業予定 Then ListRow.Range.Borders.Item(xlEdgeBottom).Weight = xlThin Exit For End If Next er: Application.EnableEvents = True Application.ScreenUpdating = True End Sub Public Sub SetLunchBreak() Dim Tb As ListObject Set Tb = ActiveSheet.ListObjects(1) Tb.DataBodyRange.Interior.Color = xlNone ' 直近の終了時刻セル。 Dim LatestRange As Range Set LatestRange = Tb.ListRows(Tb.ListRows.Count).Range(列名.en終了時刻).End(xlUp) If LatestRange >= TimeValue("13:00") Then MsgBox "既に昼休み以降の作業を実施中です。" Exit Sub Else LatestRange.Offset(1) = Range("昼休開始") End If ' 直近の終了時刻セルの、テーブル内の行番号。 Dim RowIndex As Long RowIndex = LatestRange.row - Tb.Range(1).row Dim ListRow As Excel.ListRow Set ListRow = Tb.ListRows.Add(RowIndex + 2) With ListRow .Range(列名.en実施内容) = "昼休み" .Range(列名.en作業予定時間) = Range("昼休時間") .Range(列名.en終了時刻) = Range("昼休開始") + Range("昼休時間") .Range.Interior.Color = RGB(240, 240, 240) End With Set ListRow = Tb.ListRows.Add(RowIndex + 3) ListRow.Range(列名.en実施内容) = Tb.ListRows(RowIndex + 1).Range(列名.en実施内容) ListRow.Range(列名.en作業予定時間) = Tb.ListRows(RowIndex + 1).Range(列名.en作業予定時間) ListRow.Range(列名.en備考) = Tb.ListRows(RowIndex + 1).Range(列名.en備考) End Sub Public Sub MakeNewSheet() Dim NewSheetName As String NewSheetName = Format(Date, "m月d日(aaa)") Dim Ws As Worksheet For Each Ws In Worksheets If Ws.Name = NewSheetName Then MsgBox "本日のシートは作成済みです。" Exit Sub End If Next Dim NewSheet As Worksheet ActiveSheet.Copy After:=ActiveSheet Set NewSheet = ActiveSheet NewSheet.Name = NewSheetName NewSheet.Range("終業予定") = "17:00" Dim Tb As ListObject Set Tb = NewSheet.ListObjects(1) Tb.Name = "Table_" & Format(Date, "yyyymmdd") Dim DeleteFlag As Boolean Dim i As Long For i = Tb.ListRows.Count To 1 Step -1 If Tb.ListRows(i).Range(列名.en終了時刻) <> vbNullString Then If Not DeleteFlag Then DeleteFlag = True Tb.ListRows(i).Range(列名.en終了時刻) = vbNullString Else Tb.ListRows(i).Delete End If End If Next Dim arr As Variant If Sheet0.Tb.ListRows.Count <> 0 Then arr = Sheet0.Tb.DataBodyRange For i = 1 To UBound(arr) Tb.ListRows.Add 1 Next Tb.DataBodyRange.Cells(1, 2).Resize(UBound(arr), 2) = arr End If Call UpdateTable End Sub Sub SetInterruption() Dim Tb As ListObject Set Tb = ActiveSheet.ListObjects(1) ' 現在作業中案件の終了時刻セル。 Dim PresentRange As Range Set PresentRange = Tb.ListRows(Tb.ListRows.Count).Range(列名.en終了時刻).End(xlUp).Offset(1) ' 現在作業中案件のテーブル行番号。 Dim RowIndex As Long RowIndex = PresentRange.row - Tb.Range(1).row ' 中断後に行う作業と、中断作業を再開するための行を一つずつ追加。 Tb.ListRows.Add RowIndex + 1 Tb.ListRows.Add RowIndex + 1 ' 中断作業再開の項目は、現在進行中の項目から移植する。 Tb.ListRows(RowIndex + 2).Range(2).Resize(, 2).Value = Tb.ListRows(RowIndex).Range(2).Resize(, 2).Value ' 中断する作業は、中断ボタンを押した時点で一旦終わったこととする。 ' 入力される時間がことなる場合(事後で中断ボタンを押す場合など)は、終了時刻を手入力で修正する。 PresentRange = wf.MRound(Time, "0:15") ' 中断後に行う実施内容をそのまま入力できるよう、該当セルを選択。 Tb.ListRows(RowIndex + 1).Range(2).Select End Sub Public Property Get tm始業時刻() As Date If ActiveSheet.Range("始業時刻") = vbNullString Then tm始業時刻 = TimeValue("8:00") Else tm始業時刻 = Range("始業時刻") End If End Property Public Property Get tm終業予定() As Date If ActiveSheet.Range("終業予定") = vbNullString Then tm終業予定 = TimeValue("17:00") Else tm終業予定 = Range("終業予定") End If End Property
ユーザーフォーム(uf案件追加)
※案件を全て手作業で編集する場合、これは割愛してもOK。
各オブジェクト名の頭に付した二文字は、以下を意味している。
- cb:コマンドボタン
- sb:スピンボタン
- tb:テキストボックス
- ob:オプションボタン
- fg:フラグ
Private Sub cb終了_Click() Unload Me End Sub Private Sub cb入力_Click() With Tb.ListRows.Add(TargetRowIndex) .Range(en実施内容) = tb実施内容 .Range(en作業予定時間) = tb作業予定時間 End With Unload Me End Sub Private Sub sb時間増減_SpinUp() Dim temp As Double temp = 数値時間 If temp <> sb時間増減.Min Then temp = wf.MRound(temp - 0.25, "0.25") tb作業予定時間 = wf.RoundDown(temp, 0) & ":" & _ Format((temp - wf.RoundDown(temp, 0)) * 60, "00") End If End Sub Private Sub sb時間増減_SpinDown() Dim temp As Double temp = 数値時間 If temp <> sb時間増減.Max Then temp = wf.MRound(temp + 0.25, "0.25") tb作業予定時間 = wf.RoundDown(temp, 0) & ":" & _ Format((temp - wf.RoundDown(temp, 0)) * 60, "00") End If End Sub Private Sub tb実施内容_Change() cb入力.Enabled = fg入力 End Sub Private Sub UserForm_Initialize() tb実施内容.SetFocus tb作業予定時間 = "0:15" ob下に追加 = True sb時間増減.Min = 0 sb時間増減.Max = 8 cb入力.Enabled = fg入力 End Sub Private Property Get Tb() As ListObject Set Tb = ActiveSheet.ListObjects(1) End Property Private Property Get fg入力() As Boolean Dim fg実施内容 As Boolean Dim fg作業予定時間 As Boolean If tb実施内容 <> vbNullString Then fg実施内容 = True If tb作業予定時間 <> vbNullString Then fg作業予定時間 = True fg入力 = fg実施内容 * fg作業予定時間 End Property Private Property Get 数値時間() As Double Dim myReg As Object Set myReg = CreateObject("VBScript.RegExp") myReg.Pattern = "^(\d{1,2}):(\d{1,2})$" Dim MC As Object Dim SM As Object If myReg.test(tb作業予定時間) Then Set MC = myReg.Execute(tb作業予定時間) Set SM = MC(0).SubMatches 数値時間 = SM(0) + SM(1) / 60 Else 数値時間 = 1 End If End Property Private Property Get TargetRowIndex() As Long TargetRowIndex = Selection.row - Tb.Range(1).row If ob下に追加 Then TargetRowIndex = TargetRowIndex + 1 End If End Property Private Function wf() As WorksheetFunction Set wf = WorksheetFunction End Function
最後に
今後も日々使ってみて、補修したり機能拡張するなどしてみよう。
まとまったら、改めて紹介するかもしれない。
ということで、このシリーズは今回でおしまい。
参考まで。