NGW開発BLOG
← 記事一覧へ
·#021

時間外は「区間」で表す単一ブロック描画をやめた話

時間外勤務の自-至表示。合計から逆算する単一ブロック描画をやめ、勤務区間の配列を集計と共有する設計に直した記録。

合計値から見た目を逆算してはいけない

勤怠管理には「時間外勤務日時(自-至)」という項目がある。何時から何時まで残業したか、を帳票に印字するだけの、一見ありふれた欄だ。だがこの欄には落とし穴があった。

旧実装は、退勤時刻から「その日の時間外勤務の合計分数」を引き算し、その差から一本の連続したブロックを描いていた。たとえば合計が3時間なら、退勤時刻の3時間前を始点、退勤時刻を終点として「17:30〜20:30」のように単一区間で表示する。合計が正しければ表示も正しい、という前提だ。

この前提は、勤務が一続きである限りは成り立つ。壊れるのは、勤務が飛び飛びになったときだ。

勤務は一続きとは限らない

現実の勤務には空白がある。体調を崩して昼過ぎに早退し、夜になって戻ってきて再び働く。あるいは日中に長い私用外出を挟む。こうしたケースでは、時間外になった時間帯が2つ以上の区間に分かれる。

合計分数から逆算する方式は、この構造を表現できない。合計が「早朝1時間+夜2時間=3時間」でも、退勤時刻から3時間ぶんを一本で塗ってしまうため、実際には働いていない空白の時間帯まで区間に巻き込んでしまう。合計値だけは合っているのに、印字された自-至は「ずっと勤務していた」と嘘をつく。

原因ははっきりしている。合計という集約された数値には、元データが持っていた「飛び飛び」という構造が残っていない。合計を出した瞬間に区間の情報は捨てられており、そこから見た目を復元しようとすれば、どこかで辻褄合わせの仮定を置くしかない。逆算とは、失われた情報を勘で埋める行為だった。

一次データを集計と同じ境界で切り出す

正しい設計はシンプルだ。合計から逆算するのではなく、打刻という一次データから直接、時間外の区間の配列を組み立てる。

平日なら、始業前の早朝と、定時終了から一定のインターバルを置いた夕方以降が時間外にあたる。休日なら勤務区間そのものが丸ごと時間外になる。この境界に従って各勤務区間を切り出し、[ {自, 至}, {自, 至}, ... ] という自-至の並びを作る。空白があればその前後で区間が分かれ、配列は自然に複数要素になる。

// 合計から一本を逆算する(旧)
block = { from: clockOut - totalOvertime, to: clockOut }

// 勤務区間を時間外の境界で切り出す(新)
periods = []
for (seg in workSegments) {
  ot = intersect(seg, overtimeBoundaryOf(day))  // 集計と同じ関数
  if (ot) periods.push(ot)
}

肝は、この切り出しの境界を集計ロジックと共有することだ。表示用に別の切り出しを書けば、丸め方やインターバルの扱いがわずかに食い違い、合計と自-至がズレる。集計が使う境界判定と、表示が使う境界判定は、同じひとつの関数でなければならない。区間配列こそが唯一の基準であり、合計はその配列を足し上げた結果にすぎない、という順序が正しい。

帳票が3つあっても、真実は1つ

この項目は、申請書・命令簿(PDF)・命令簿(Excel)の3つの帳票に登場する。かつては各帳票が独自に自-至を組み立てていたが、それでは3通りの逆算ロジックが生まれ、修正のたびに3箇所を追いかける羽目になる。

そこで、前述の区間配列を一度だけ生成し、3帳票すべてがそれを列挙するように揃えた。各帳票がやることは「与えられた区間配列を並べて描く」だけになる。

もっとも、帳票ごとの都合は残る。区間が増えれば行が増えるので、PDFでは明細の行高と1ページあたりの行数を区間数に応じて調整する。Excelも同様だ。列幅が狭い申請書では、複数区間を律儀に縦積みすると溢れるため、1行に収める折り合いをつける。だがこれらはあくまで「同じ配列をどう見せるか」というレイアウトの問題であって、値そのものを作り直しているわけではない。データはひとつ、見せ方は帳票ごと、という切り分けだ。

AIとの協働作業での学び

得た教訓は3つある。

  1. 合計値から見た目を逆算すると、元データが持っていた構造(飛び飛びの区間)を失う。表示は、集計と同じ一次データ・同じ境界から導くべきだ。
  2. 集計と表示で境界のロジックを分けると、必ずどこかで値がズレる。境界は一箇所に持たせて共有する。
  3. 同じ項目を映す帳票が複数あるなら、各々で計算し直さず、正規化済みのデータをひとつ作って配り回す。

「合計は合っているのに表示がおかしい」という不具合は、たいてい逆算の匂いがする。数値ではなく、その数値を生んだ構造のほうを表示に渡せているか。時間外の区間表示は、それを確かめる小さな試金石だった。