結論:給与計算の検算は、給与ソフトの再計算ボタンではなく「勤怠CSV・賃金規程・給与計算結果」の3つを突き合わせる独立した再計算エンジンとして、Claude Codeで構築できます。割増率や端数処理といった法律由来のルールは厚生労働省の一次情報で固定してコードに落とし、AIに任せるのはCSVの正規化・規程の構造化・差分レポートの整理までです。
- 要点1:割増賃金は時間外25%以上・深夜25%以上・法定休日35%以上・月60時間超の時間外は50%以上(2023年4月から中小企業にも適用)。この率と重複ルールが検算エンジンの背骨になる(厚生労働省)
- 要点2:給与ソフトの計算そのものはまず間違えない。間違えるのは「設定」——割増基礎から除外できない手当を除外していた、規程改定が反映されていない、勤怠側の丸め設定が通達と合っていない、という層で起きる
- 要点3:だから検算は同じソフトの再計算では意味がなく、別系統で組んだ独立エンジンとの突合でなければ設定ミスを検出できない
対象読者:人事・労務・経理部門とAI活用を進める開発者・PM・経営企画
今日やること:自社の給与計算で「割増賃金の基礎に算入している手当の一覧」を賃金規程と突き合わせ、除外しているものがあれば根拠を確認する
先に断っておくと、本記事は複数の支援経験をもとに一般化した実装パターン解説です。登場する数値は想定モデルケースの試算で、特定企業の実測値ではありません。また、割増賃金や手当の取り扱いに関する法的な判断は最終的に社会保険労務士・労働基準監督署への確認が必要です。この記事が扱うのは「その手前の検知を機械化する」部分です。
バックオフィスのAI活用を支援していたとき、給与担当の方の机の上に電卓と蛍光ペンが置いてあるのを見たことがあります。毎月の給与確定前に、対象者を数名ランダムに選んで明細を電卓で検算する——それがその会社の「ダブルチェック」でした。担当者いわく「全員分やる時間はないので、抜き取りです。正直、当たったことはほとんどないんですけど、やらないと不安で」。この運用の問題は、手間ではありません。抜き取り検算では「全員に同じ方向に効いている設定ミス」が原理的に見つからないことです。
給与計算のミスは、担当者の手元の計算違いよりも、ソフトの設定・規程の反映・勤怠データの解釈という「上流の層」で起きます。そして設定ミスは全員に一律に効くので、1人ずつ電卓で検算しても「ソフトの画面と同じ数字」が出てくるだけです。この記事では、その層を検出するための独立検算エンジンをClaude Codeで組む実装を、コードと運用の両面から解説します。勤怠データの上限チェックを扱った36協定の超過先読みの続編にあたる位置づけです。
先に法律の数字を固定する——検算エンジンの背骨になる5つのルール
実装の前に、判定ルールの根拠になる数字を厚生労働省の一次情報で固定します。ここが曖昧なままコードを書くと、検算エンジン自体が間違った答えを正とする最悪の構図になります。
| ルール | 内容 | 根拠 |
|---|---|---|
| 法定労働時間 | 1日8時間・週40時間(常時10人未満の商業・保健衛生業等は特例で週44時間) | 労働基準法(厚労省FAQ) |
| 割増率 | 時間外25%以上・深夜(22時〜翌5時)25%以上・法定休日35%以上 | 労働基準法37条(厚労省FAQ) |
| 月60時間超 | 月60時間を超える時間外労働は50%以上(2023年4月から中小企業にも適用) | 厚労省・確かめよう労働条件 |
| 重複ルール | 時間外+深夜は合計50%以上、休日+深夜は合計60%以上。法定休日労働に時間外の割増は重複しない | 厚労省FAQ |
| 端数処理 | 月の時間外・休日・深夜それぞれの合計に1時間未満の端数がある場合、30分未満切捨て・30分以上切上げは違反として扱われない。日ごとの端数処理は不可。金額は50銭未満切捨て・50銭以上切上げ | 昭和63年基発150号(山口労働局リーフレット) |
実装者が最初に押さえるべきポイントは2つあります。
1つ目は、割増賃金の基礎から除外できる手当は7種類に限定列挙されていることです。家族手当・通勤手当・別居手当・子女教育手当・住宅手当・臨時に支払われた賃金・1か月を超える期間ごとに支払われる賃金——これだけです。しかも厚労省は「このような名称の手当であればすべて除外できるわけではない」と明記しています。実態が一律支給なら名前が住宅手当でも除外できません。検算エンジンの手当マスタは、この限定列挙を前提に「除外理由」を1件ずつ持たせる設計にします。
2つ目は、端数処理が「月の合計に対して」しか認められていないことです。日ごとに15分未満を切り捨てる運用は、労働局が名指しでダメと言っている典型パターンです。勤怠システムの丸め設定がこの通達に合っているかどうかは、給与計算結果の検算以前に確認すべき前提条件になります。
給与ソフトがあるのに、なぜ独立した検算が要るのか
「給与ソフトが計算しているんだから合っているのでは?」——この質問には、支援の現場で何度も答えてきました。答えはこうです。ソフトの計算ロジックはまず間違えない。間違えるのは人間が入れた設定と、設定に反映されない規程改定です。
実際に検算の仕組みを入れる価値が出るのは、次のどれかに当てはまる場合です。
- 手当マスタの設定が古い — 賃金規程を改定して手当を新設・増額したのに、割増賃金の基礎への算入設定が旧のまま。全員に一律・毎月同じ方向に効くので、抜き取り検算では永遠に見つからない
- 勤怠と給与でシステムが分かれている — 勤怠SaaSからCSVを吐いて給与ソフトに取り込む構成では、取り込み時の列マッピングや丸め設定という「継ぎ目」が生まれる。継ぎ目はどちらのベンダーのサポート範囲でもない
- 雇用区分・時給パターンが多い — 正社員・契約・パート・嘱託で割増の基礎も所定労働時間も違う。区分が増えるほど設定の組み合わせが爆発する
- 最低賃金の改定追従が手動 — 地域別最低賃金は都道府県ごとに毎年改定される(例年10月ごろ発効)。時給者だけでなく月給者も時間換算での確認が必要で、拠点が複数県にまたがると手動追従は現実的でない
ある支援先(想定モデル)では、住宅手当を割増賃金の基礎から除外する設定になっていましたが、規程を読むと支給額は全員一律でした。前述のとおり、一律支給の「住宅手当」は名前が住宅手当でも除外できません。この種のズレは給与ソフトの画面をいくら見ても分かりません。規程のテキストと設定値を突き合わせて初めて見える——ここがまさに、規程PDFを読んで構造化するのが得意なClaude Codeの出番です。
実装の全体像——正規化・独立再計算・差分レポートの3層
アーキテクチャは、経費精算の規程照合や月次決算の残高照合で使ったのと同じ「正規化→決定的な判定→確認リスト生成」の骨格です。動作環境はClaude Code(2026年7月時点の最新版)、Python 3.12、pandas。再計算層に機械学習は使いません。給与という1円単位の正確性が求められる領域で、AIの推論を計算経路に入れないのは絶対条件です。
payroll-recheck/
├── CLAUDE.md # プロジェクトの前提と禁止事項
├── config/
│ ├── rules.yaml # 割増率・端数処理など法律由来ルール(出典URL付き)
│ ├── wage_master.yaml # 手当マスタ(割増基礎への算入/除外と根拠)
│ └── mapping/ # 勤怠・給与システム別の列マッピング定義
├── input/
│ ├── attendance/ # 勤怠CSV(社員IDに仮名化済み)
│ └── payroll/ # 給与計算結果CSV(同上)
├── scripts/
│ ├── normalize.py # 列マッピング+時間データの正規化
│ ├── recalc.py # 割増賃金の独立再計算エンジン
│ ├── diff_report.py # ソフト計算値との差分レポート
│ └── minwage_check.py # 最低賃金との時間換算比較
├── tests/ # 境界値テスト(60.0h/60.5h、深夜またぎ等)
└── output/ # 月次検算レポート(Markdown)
CLAUDE.mdには、検算の前提と「AIが勝手にやってはいけないこと」を明記します。ここに書いた制約はセッションをまたいでも効き続けるので、このプロジェクトでは安全装置そのものです。
# CLAUDE.md(抜粋)
## このプロジェクトの前提
- 割増率・端数処理ルールは config/rules.yaml から読む。コードに直書きしない
- 端数処理は基発150号の月次合計方式のみ実装する。日次の丸めは実装しない
- 手当の算入/除外は wage_master.yaml の宣言に従う。yaml に無い手当が
給与CSVに現れたら、推測で分類せず「未分類手当」としてエラーにする
## 禁止事項
- 割増率・除外手当など法律由来のルールを推測で書かない。必ず出典を確認
- input/ のCSVに氏名・口座番号・住所が含まれていたら処理を止めて報告
- 差分の原因を「おそらく〜だろう」と断定しない。事実(どの列がいくつ違う)
の列挙までにとどめ、原因判断は人間に渡す
- 検算結果を根拠に「未払いがある」等の法的評価を書かない
Claude Codeへの最初の指示はこれだけです。
賃金規程(regulations.pdf)を読んで、手当の一覧を wage_master.yaml の
形式に構造化してください。各手当について「割増賃金の基礎に算入すべきか」を
判定するのではなく、規程に書かれている支給条件をそのまま転記し、
除外候補(家族・通勤・別居・子女教育・住宅・臨時・1か月超周期)に
名前が該当するものには「要確認」フラグを付けてください。
ポイントは、AIに判定させず転記と要確認フラグまでに留めることです。除外の可否は名称ではなく実態で決まるため、最終判断は規程を知る労務担当と社会保険労務士に渡します。AIの仕事はその判断材料を1時間で机に並べることです。
実装コード例1:勤怠と給与、2系統のCSVを正規化する
検算の材料は2系統です。勤怠システムから出る時間データと、給与ソフトから出る計算結果データ。どちらも「列名が何を意味しているか」の確定が実装の8割を占めます。36協定チェックの記事で詳しく書いた「所定外(法定内)と法定時間外の区別」はここでも必須なので繰り返しません。給与側で追加になるのは、時間帯の分解です。
import pandas as pd
import yaml
REQUIRED = ["employee_id", "month", "statutory_ot_min",
"ot_over60_min", "night_min", "holiday_min",
"holiday_night_min"]
def normalize_attendance(csv_path: str, mapping_path: str) -> pd.DataFrame:
"""勤怠CSVを検算用スキーマに変換する。
ot_over60_min: 月60時間を超えた部分の時間外(50%対象)
holiday_night_min: 法定休日かつ深夜(60%対象)の重複部分
"""
m = yaml.safe_load(open(mapping_path, encoding="utf-8"))
df = pd.read_csv(csv_path, encoding=m["encoding"]).rename(columns=m["columns"])
missing = [c for c in REQUIRED if c not in df.columns]
if missing:
raise ValueError(f"検算に必要な列が勤怠CSVから作れません: {missing}。"
"勤怠システムの出力設定で時間帯別の内訳を有効化してください")
# 60時間超の内訳が「累計」で出るシステムと「超過分のみ」で出るシステムがある
bad = df[df["ot_over60_min"] > df["statutory_ot_min"]]
if len(bad) > 0:
raise ValueError("60時間超の列が時間外合計を上回る行があります。"
"この列は累計値の可能性が高いので mapping の定義を見直してください")
return df
2つ目の検証がこのスクリプトの肝です。「月60時間超」の列を、超過分だけ出すシステムと累計で出すシステムが実在します。ここを取り違えると50%割増の検算が全員分ずれるので、論理的にあり得ない関係(超過分>合計)を検知したら止まるようにしておきます。列マッピングのyaml自体はClaude Codeにサンプル行を数行見せて下書きさせると早いですが、次のプロンプトのように必ず確認質問をさせます。
この勤怠CSVのヘッダーとサンプル3行から、mapping/kintai_a.yaml の
下書きを作ってください。各列の対応づけについて、確信が持てないものは
推測で埋めず「不明」として、勤怠システムの設定画面のどこを見れば
確定できるかを質問リストにしてください。
実装コード例2:割増賃金の独立再計算エンジン
再計算エンジンは、法律由来のルールをyamlから読み、通達準拠の端数処理を関数として固定します。ソフトウェアとしては地味ですが、この地味さが価値です。
from decimal import Decimal, ROUND_HALF_UP
def round_monthly_minutes(total_min: int) -> int:
"""基発150号: 月の合計時間の1時間未満端数は30分未満切捨て・30分以上切上げ。
日ごとの丸めは実装しない(通達上認められていないため関数自体を作らない)"""
hours, rem = divmod(total_min, 60)
return (hours + (1 if rem >= 30 else 0)) * 60
def round_yen(amount: Decimal) -> int:
"""基発150号: 50銭未満切捨て・50銭以上切上げ = 四捨五入と同値"""
return int(amount.quantize(Decimal("1"), rounding=ROUND_HALF_UP))
def recalc_overtime_pay(base_hourly: Decimal, mins: dict, rates: dict) -> dict:
"""時間帯別の割増賃金を独立再計算する。
rates は config/rules.yaml 由来: ot=0.25, ot_over60=0.50,
night=0.25, holiday=0.35(すべて法定の最低率。就業規則が上回る場合は上書き)"""
pay = {}
items = {
"ot": (mins["statutory_ot_min"] - mins["ot_over60_min"], 1 + rates["ot"]),
"ot_over60": (mins["ot_over60_min"], 1 + rates["ot_over60"]),
"night": (mins["night_min"], rates["night"]), # 深夜は上乗せ分のみ
"holiday": (mins["holiday_min"], 1 + rates["holiday"]),
}
for name, (m, rate) in items.items():
rounded = round_monthly_minutes(m)
pay[name] = round_yen(base_hourly * Decimal(rounded) / 60 * Decimal(str(rate)))
return pay
実装上の注意を3つ。
第一に、floatを使わないこと。0.1円のような誤差が数千人×12か月で蓄積すると、検算エンジン自身が偽の差分を量産します。金額は最初から最後までDecimalです。
第二に、深夜割増の扱いです。深夜労働が所定内の時間帯なら基礎賃金は既に支払われているので、上乗せする割増は25%「分」だけです。時間外と重なれば結果として合計150%、法定休日と重なれば160%になります。この重複ルールは厚労省FAQの記載どおりにテストへ落とします。
第三に、rates.yamlには法定率と就業規則率の2層を持たせることです。就業規則で法定を上回る率(たとえば時間外30%)を定めている会社では、検算の正解は法定率ではなく規則率です。36協定チェックの「法律値と協定値の厳しいほう」と同じ構図で、どちらの率が効いたかをレポートに明記します。
実装コード例3:差分レポート——「1円の差」をどう扱うか
独立再計算した値と給与ソフトの計算結果を突き合わせると、初回はほぼ確実に大量の差分が出ます。ここで重要なのは、差分を「エラー」ではなく「どちらかの前提が違うことを示すシグナル」として扱うことです。
def diff_report(recalc_df, payroll_df, threshold_yen: int = 0):
"""再計算値とソフト計算値の差分を人別・項目別に列挙する。
threshold_yen=0 が原則。端数処理方式の違いによる±1円を
握りつぶすためにthresholdを上げるのは最後の手段"""
merged = recalc_df.merge(payroll_df, on=["employee_id", "month"],
suffixes=("_recalc", "_soft"))
rows = []
for col in ("ot", "ot_over60", "night", "holiday"):
d = merged[f"{col}_recalc"] - merged[f"{col}_soft"]
hit = merged[d.abs() > threshold_yen]
for _, r in hit.iterrows():
rows.append({"employee_id": r["employee_id"], "item": col,
"recalc": r[f"{col}_recalc"], "soft": r[f"{col}_soft"],
"diff": int(d.loc[r.name])})
return pd.DataFrame(rows)
初回実行の差分レビューは、Claude Codeにこう指示して分類させます。
output/diff_202606.csv を読んで、差分を「全員に同方向に出ている項目」
「特定の雇用区分だけに出ている項目」「特定個人だけの項目」の3グループに
分類してください。各グループについて、差分額のパターン(一律◯円、
基礎時給に比例、など)を事実として記述してください。原因の断定はせず、
確認すべき設定箇所の仮説を優先度順に3つまで挙げてください。
この3分類が原因切り分けの定石です。全員同方向なら手当マスタか端数処理方式、区分単位なら所定労働時間か基礎単価の設定、個人単位なら勤怠データの個別事情。ある想定モデルケースでは、初回の差分1,900件のうち9割が「勤怠側が日次で丸めた時間を給与側が再度月次で丸めていた」という二重丸めに集約されました。差分の件数に圧倒されそうになりますが、原因の種類は数個しかないのが普通です。
最低賃金チェック——毎年10月の改定に回帰テストで追従する
検算エンジンの土台ができると、最低賃金チェックはほぼ無料で乗ります。地域別最低賃金は都道府県ごとに毎年改定され(例年10月ごろ発効)、金額は厚生労働省の「地域別最低賃金の全国一覧」で公表されます。実装は、拠点マスタに都道府県コードを持たせ、月給者は所定労働時間で時間換算してから比較するだけです。
def minwage_check(df, minwage_yaml: str):
"""時間換算賃金と地域別最低賃金を比較する。
minwage.yaml は厚労省の全国一覧から改定のたびに更新し、
発効日をキーに持つ(10月改定は同月内で新旧が混在するため)"""
mw = yaml.safe_load(open(minwage_yaml, encoding="utf-8"))
df["hourly_equiv"] = df.apply(to_hourly_equivalent, axis=1)
df["minwage"] = df.apply(
lambda r: lookup_rate(mw, r["prefecture"], r["pay_period_start"]), axis=1)
return df[df["hourly_equiv"] < df["minwage"]]
注意点は2つ。第一に、最低賃金の比較対象からは通勤手当や精皆勤手当など除外すべき賃金があり、割増基礎の除外リストとは別物です。混ぜて実装すると両方壊れます。第二に、yamlの改定は毎年の手作業として運用カレンダーに載せることです。金額をコードやAIの知識に埋め込むと、翌年そのまま古い値で回り続けます。改定作業自体はClaude Codeに一覧ページを渡して「発効日付きでyamlを更新し、前年からの差分を表にして」と指示すれば数分で終わります。
運用の3フェーズ——最初の2か月は「検算の検算」に使う
Phase 1(1〜2か月): 過去3か月分の給与データで再計算を回し、差分をゼロ件になるまで説明し切る。この期間の成果物は差分ゼロではなく「差分の全件に理由が付いている状態」です。ここで見つかった設定ミスの修正は必ず給与ソフト側で行い、検算エンジンを甘くして合わせない
Phase 2(2〜4か月): 毎月の給与確定前の工程に検算を組み込む。給与担当が確定ボタンを押す前に差分レポートがゼロ件(または全件説明済み)であることをチェックリスト化する
Phase 3(4〜6か月): 規程改定・手当新設・最低賃金改定のイベント駆動テストを整備する。賃金規程が変わったらwage_master.yamlとテストを同時に更新するフローを労務と開発で合意する
賃金台帳・労働者名簿側の整備がまだの場合は、法定三帳簿の点検の記事を先に読んでください。賃金台帳には労働時間数・時間外・深夜・休日労働時間数を記載する欄があり、そこが正しく埋まっていることが今回の検算の入力品質を支えます。
【要注意】よくある失敗パターンと回避策
失敗1:検算ロジックの正解を給与ソフトに合わせてしまう
❌ 差分が出たので検算エンジン側を修正して差分を消す
⭕ 差分の原因を一次情報(通達・規程)と突き合わせ、間違っている側を直す
初回の大量差分に負けて「ソフトと同じ計算式」に寄せると、独立検算の意味が消滅します。判断基準は常に厚労省の一次情報と自社規程であって、どちらのシステムでもありません。
失敗2:AIに割増計算そのものをやらせる
❌ 「この勤怠データから割増賃金を計算して」とClaude Codeに直接頼む
⭕ AIには正規化・規程の構造化・差分の分類をさせ、計算は決定的なコードに固定する
LLMの計算は同じ入力でも揺れることがあります。1円単位の正確性が要る給与では、AIの出力を計算経路に入れた時点で検算の信頼性がなくなります。
失敗3:氏名・口座情報入りのCSVをそのまま扱う
❌ 給与ソフトの標準エクスポートを無加工でinput/に置く
⭕ 社員IDに仮名化した検算用の中間ファイルを作り、個人情報列の検知ガードをコードに入れる
給与データは個人データの中でも機微性が高い部類です。仮名化を運用ルールではなくコードのガードで強制するのは、このシリーズ全記事で共通の作法です。
失敗4:初月に全社一斉で回して現場を巻き込む
❌ いきなり全従業員・当月データで検算し、差分を各部門に照会する
⭕ 過去月データ×1雇用区分から始め、差分の原因パターンを潰してから広げる
初回差分の大半は設定起因なので、現場に照会しても「勤怠は正しく付けています」と返ってくるだけです。現場を巻き込むのは、設定起因を潰し切った後の個別事情の確認だけで十分です。
よくある質問
Q. 給与ソフトのベンダーに任せるのと何が違いますか?
ベンダーが保証するのは「設定どおりに計算すること」で、「設定が自社の規程と法律に合っていること」は保証範囲外です。今回の検算エンジンが見るのはまさにその範囲外の部分です。手当マスタの算入・除外設定と賃金規程のズレは、規程のテキストを読める仕組みでないと検出できません。
Q. 差分ゼロが続いたら検算は止めてよいですか?
止めるのではなく、頻度と観点を変えるのがおすすめです。毎月の全件検算は継続コストがほぼゼロなので回し続け、人間のレビューは規程改定・手当新設・最低賃金改定・制度変更のイベント時に集中させます。検算が効くのは定常時ではなく変化点です。
Q. 社会保険料や所得税の検算はできますか?
本記事のスコープは労働時間×割増賃金の検算までです。社会保険料率や税額表は改定頻度が高く、料率マスタの鮮度管理という別の難しさがあります。骨格(正規化→独立再計算→差分レポート)は同じなので拡張は可能ですが、まず割増賃金で差分ゼロの運用を確立してからを推奨します。標準報酬月額の算定など制度側の判断は社会保険労務士の領分です。
Q. 就業規則の割増率が法定より高い場合、どちらで検算しますか?
就業規則の率です。法定率は最低ラインなので、規則がそれを上回る場合は規則率が自社の正解になります。実装ではrules.yamlに法定率と規則率の2層を持たせ、常に高いほうを適用しつつ、どちらが効いたかをレポートに明記します。規則率が法定を下回っていたら、それ自体が重大な検出事項です。
Q. 導入にはどれくらいの期間がかかりますか?
1システム構成なら、正規化の確定と過去データでの差分説明に1〜2か月、給与確定前チェックへの組み込みまで3〜4か月が目安です(想定モデルの試算)。最大の変数は手当の種類数と雇用区分の数で、コードを書く時間より「この手当を算入すべきか」の労務判断を待つ時間のほうが長くなります。判断待ちをブロッカーにしないため、要確認フラグ付きで先に走らせる設計にしてください。
まとめ:今日から始める3つのアクション
- 今日やること:給与ソフトの手当マスタ設定画面と賃金規程を並べ、割増賃金の基礎から除外している手当の一覧と除外理由をメモする。限定列挙7種に該当しない除外が1つでもあれば、それが最初の検出事項です
- 今週中:勤怠システムの丸め設定を確認する。日次で切り捨てる設定になっていたら、検算以前にそこから直す必要があります(基発150号の月次合計方式が基準)
- 今月中:先月分の給与データで1雇用区分だけ独立再計算を回し、差分の件数とパターンを見る。差分の出方が、自社の検算プロジェクトの規模感をそのまま教えてくれます
給与計算の検算は、36協定の上限チェック・有給5日取得義務の先読みと同じ「法令要件を決定的なコードに固定し、AIは正規化と整理を担う」パターンの給与版です。勤怠の正規化が済んでいる会社なら、そこに再計算エンジンを1枚足すだけで着手できます。
著者:佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。X(@SuguruKun_ai)フォロワー約10万人。100社以上の企業向けAI研修・導入支援。著書『AIエージェント仕事術』(SBクリエイティブ)。
参考・出典
- 厚生労働省 労働基準情報FAQ「法定労働時間と割増賃金について教えてください」(参照日: 2026-07-31)
- 厚生労働省 確かめよう労働条件「時間外・休日労働と割増賃金」(参照日: 2026-07-31)
- 厚生労働省 山口労働局「労働時間の『切り捨て』はダメ!適切な端数処理をしましょう」(昭和63年基発150号の解説リーフレット)(参照日: 2026-07-31)
- 厚生労働省「地域別最低賃金の全国一覧」(参照日: 2026-07-31)
- 厚生労働省 働き方改革特設サイト「時間外労働の上限規制」(参照日: 2026-07-31)
ご質問・ご相談は お問い合わせフォーム からお気軽にどうぞ。