36協定チェック
MFクラウド勤怠の「月別データ」xlsxを読み込み、36協定のチェック結果を出力する社内ツール。 PHP標準機能のみで動作し、外部ライブラリ・データベース不要。
⚠ 取り扱い注意
従業員の勤怠情報を扱います。
* このリポジトリはPrivateのまま運用してください * `master.csv`(氏名を含む)と `.htpasswd` は `.gitignore` で除外済み。コミットしないでください * 公開ディレクトリに置く場合は、必ずBasic認証またはIP制限をかけてください
セットアップ
1. サーバーに配置
/36kyotei/ ├─ index.php ├─ master.csv ← サーバー上で管理(Git管理外) ├─ .htaccess ← アクセス制限 └─ .htpasswd
`genpass.php` をアップロードしてブラウザで開き、ID・パスワードを入力すると `.htaccess` と `.htpasswd` を自動生成します。
設定後は genpass.php と check.php を削除してください。
エックスサーバーの場合は、サーバーパネルの「アクセス制限」機能を使うほうが確実です。
2. 所属係マスタ
`master.csv.example` をコピーして `master.csv` を作り、サーバーに置きます。
画面からアップロードすることもできます(「サーバーに保存して次回から使う」にチェックすると保存されます)。
氏名,所属部,所属係 山田 太郎,第一事業部,設計1係
文字コードは自動判定(UTF-8 / UTF-8 BOM / Shift_JIS)。
Excelで編集して保存したCSVがそのまま使えます。
`master.csv` が無い場合は、勤怠データの「役職」列(例:`設計1係 担当`)から係名を自動判定します。
GitHub Actions で自動デプロイ
`main` にプッシュすると、PHPの構文チェックを通してから rsync over SSH でサーバーに反映します。
Secrets の登録
Settings → Secrets and variables → Actions → New repository secret で登録します。
| Secret名 | 内容 | 例 |
|---|---|---|
| `SSH_HOST` | ホスト名 | `sv637.xbiz.ne.jp` |
| `SSH_USER` | サーバーID | `colorspro` |
| `SSH_KEY` | 秘密鍵の全文(`—–BEGIN` 〜 `—–END` を含む) | |
| `DEPLOY_PATH` | 配置先の絶対パス(末尾スラッシュ必須) | `/home/colorspro/example.co.jp/public_html/overtime-check/` |
| `SSH_PORT` | 任意。未設定なら `10022` |
SSH鍵の作り方
エックスサーバーは公開鍵認証のみ(パスワード認証は不可)、ポートは 10022 です。
* サーバーパネル →「SSH設定」→「+公開鍵を登録」 * ラベルを入力し「登録して秘密鍵をダウンロードする」 * ダウンロードした `.key` をテキストエディタで開き、全文をコピーして `SSH_KEY` に貼り付け
秘密鍵はGitHubのSecrets欄以外に貼り付けないでください。 メールやチャットに載せた場合は、必ず鍵を再発行してください。
接続確認
登録前に手元で確認できます。
# Mac / Windows(PowerShell) 共通 ssh -i "秘密鍵のパス" -p 10022 サーバーID@ホスト名 "pwd; ls -d 配置先の相対パス"
`/home/サーバーID` とフォルダ名が返ってくれば、`DEPLOY_PATH` はその2つをつなげたものになります。
デプロイされないファイル
`master.csv` `.htaccess` `.htpasswd` `data/` `*.xlsx` および Docker・CI関連ファイルは除外されます。
`–delete` は使っていないため、サーバー上のファイルが自動デプロイで消えることはありません。
ワークフローの流れ
構文チェック → SSH設定の確認 → SSH鍵を配置 → 接続テスト → rsyncで同期 → 鍵の後片付け
Secretsが未登録の場合は失敗せず、警告を出してスキップします。
`DEPLOY_PATH` が絶対パスでない、または末尾スラッシュが無い場合は転送前にエラーで停止します。
手動実行
Actions タブ → Deploy to Xserver → Run workflow
ローカル環境の起動
Windows・Mac のどちらでも、スクリプトを1つ実行するだけで起動します。
中身は同じで、動くOSが違うだけです。
| やること | Windows | Mac |
|---|---|---|
| ローカル起動 | `setup.bat` をダブルクリック | `bash setup.sh` |
| 停止 | `stop.bat` | `bash stop.sh` |
| ログを見る | `logs.bat` | `bash logs.sh` |
| 社内LAN公開 | `lan-access.bat` | `bash lan-access.sh` |
| サーバーから取り込む | `pull-remote.bat` | `bash pull-remote.sh` |
| 手動デプロイ | `deploy.bat` | `bash deploy.sh` |
Macでは `bash` を付けて実行してください。
Windowsで作られたファイルは実行権限が付かないため、`./setup.sh` だと `Permission denied` になることがあります。
気になる場合は一度だけ `chmod +x *.sh` を実行すれば `./setup.sh` でも動きます。
事前に必要なもの
# 1) Docker Desktop を公式サイトからインストール # ★ Intel版(amd64) と Apple Silicon版(arm64) でインストーラが別です # uname -m → x86_64 なら Intel / arm64 なら Apple Silicon # https://www.docker.com/products/docker-desktop/ # 2) Mac で Homebrew が未導入なら https://brew.sh/ # 3) 以下を実行 brew install git
初回セットアップ
Mac
mkdir -p ~/Docker-work && cd ~/Docker-work git clone https://github.com/Colors-ryomorita/36kyotei.git cd 36kyotei cp .env.example .env bash setup.sh
Windows(PowerShell)
cd C:\Docker-work git clone https://github.com/Colors-ryomorita/36kyotei.git cd 36kyotei copy .env.example .env .\setup.bat
`setup` はDockerの起動確認 → `.env` の作成 → ビルド → 起動確認までを行い、成功するとブラウザが自動で開きます。
.env の設定
`deploy` と `pull-remote` を使う場合だけ編集します。
ローカル起動だけなら初期値のままで動きます。
APP_PORT=8080 # ポートを変えたいとき SSH_HOST=sv637.xbiz.ne.jp # サーバーパネル →「サーバー情報」 SSH_USER=colorspro # サーバーID SSH_PORT=10022 SSH_KEY=docker/ssh/xserver.key # 秘密鍵の置き場所 DEPLOY_PATH=/home/colorspro/example.co.jp/public_html/overtime-check/
`.env` と秘密鍵は `.gitignore` で除外済みです。
コミットされません。
特徴
* ソースをマウントしているので、index.php を編集して保存 → ブラウザ再読み込みだけで反映されます。ビルドし直す必要はありません * `display_errors = On` なので、PHPのエラーが画面にそのまま表示されます * `data/` フォルダに勤怠xlsxを置くと、画面のプルダウンから選べます * `.htaccess` が有効(`AllowOverride All`)なので、Basic認証の動作も確認できます
チップの種類について(Intel / Apple Silicon)
どちらでもそのまま動きます。
`docker-compose.yml` やスクリプトを書き換える必要はありません。
ただし Docker Desktop のインストーラだけは別物なので、ここを間違えないでください。
uname -m # x86_64 → Intel / arm64 → Apple Silicon
| Mac | ダウンロードするもの |
|---|---|
| Intel チップ | Docker Desktop for Mac with Intel chip(amd64) |
| Apple Silicon(M1〜M4) | Docker Desktop for Mac with Apple silicon(arm64) |
使用しているイメージは両方のCPUに対応しているので、`platform:` の指定は不要です。
| イメージ | Intel (amd64) | Apple Silicon (arm64) |
|---|---|---|
| `php:8.2-apache` | ✅ | ✅ |
Intel Mac で気をつけること
| 内容 | |
|---|---|
| macOSのバージョン | Docker Desktop がサポートするのは最新+その前2つのメジャーバージョンまで。古いIntel Macで macOS を上げられない場合、最新版の Docker Desktop は入りません(旧バージョンを探すことになります) |
| メモリ | 最低2GB。Settings > Resources で割り当てを確認してください(このツールはPHP+Apacheの1コンテナのみなので軽量です) |
| Homebrewの場所 | Intelは `/usr/local`、Apple Siliconは `/opt/homebrew`。`brew install` のあとに `command not found` が出たら下のコマンドを `~/.zshrc` に追記 |
| Rosetta 2 | 不要です(Intelネイティブのため) |
| 速度 | Apple Silicon より遅いので、後述の VirtioFS の設定を推奨します |
# Intel Mac で git / gh が見つからない場合 echo 'eval "$(/usr/local/bin/brew shellenv)"' >> ~/.zshrc source ~/.zshrc
Apple Silicon で気をつけること
特別な設定は要りません。
Rosetta 2 は必須ではありませんが、一部のコマンドラインツールが必要とするため入れておくと無難です。
softwareupdate --install-rosetta
動作が遅いと感じたら
Docker Desktop > Settings > General で VirtioFS が選ばれているか確認してください(macOSのファイル共有はこれが一番速いです)。
ポート8080が使われている場合
`.env` の `APP_PORT` を変更して `setup` を実行し直してください。
APP_PORT=8081
使用中のプロセスは Mac なら `lsof -i :8080`、Windows なら `netstat -ano | findstr :8080` で確認できます。
SSH鍵を置いたとき
Macでは権限を絞らないと ssh が鍵を読み込みません。
chmod 600 docker/ssh/xserver.key
`setup.sh` と `deploy.sh` は自動で直しますが、手で ssh を叩くときは必要です。
フォルダ構成
36kyotei/
├─ index.php ← 本体
├─ check.php ← 環境チェック(本番では削除)
├─ genpass.php ← 認証ファイル生成(本番では削除)
├─ master.csv ← 所属係マスタ。実名のためGit管理外
├─ master.csv.example
├─ .env ← 環境設定。Git管理外
├─ .env.example
├─ data/ ← 勤怠xlsxの置き場。Git管理外
├─ docker/
│ ├─ php.ini
│ └─ ssh/ ← 秘密鍵の置き場。Git管理外
├─ Dockerfile
├─ docker-compose.yml
├─ setup.bat / setup.sh
├─ stop.bat / stop.sh
├─ logs.bat / logs.sh
├─ lan-access.bat / lan-access.sh
├─ pull-remote.bat / pull-remote.sh
├─ deploy.bat / deploy.sh
└─ .github/workflows/
├─ ci.yml ← 構文チェック・起動確認・Dockerビルド
└─ deploy.yml ← SSH(rsync)自動デプロイ
動作環境の差分
| Docker(ローカル) | エックスサーバー | |
|---|---|---|
| PHP | 8.2 | サーバーパネルで選択 |
| エラー表示 | 画面に出る | 通常は出ない |
| upload_max_filesize | 16M | 初期値は環境による |
ローカルで動けばサーバーでも動きます。
サーバーだけで失敗する場合は、PHPバージョンかアップロード上限が原因のことがほとんどです。
`check.php` で確認してください。
CI(GitHub Actions)
プッシュのたびに自動で検証します。
| ジョブ | 内容 |
|---|---|
| 構文チェック | 全 `.php` に対して `php -l` |
| スモークテスト | 実際に起動してHTTP 200を確認。Parse error / Fatal error / Warning が出ていないかも検査 |
| バリデーション確認 | 月次基本時間を空でPOSTし、必須チェックが効くか確認 |
| Dockerビルド確認 | イメージがビルドできるか |
| 機密情報チェック | `master.csv` `.htpasswd` `.htaccess` `data/` `*.xlsx` がコミットされていないか |
いずれかが失敗するとデプロイされません。
使い方
MFクラウド勤怠からのエクスポート
全権管理者メニュー > 連携 > エクスポート > 月別データ
| 設定項目 | 当月チェック | 前月チェック |
|---|---|---|
| 対象年月 | 単月(当月) | 複数月(前月を最終月とする直近6か月) |
| 出力単位 | 従業員 > 全て | 従業員 > 全て |
| 時間フォーマット | 10進数(切り上げ) | 10進数(切り上げ) |
| 実質労働時間 | 含めない | 含めない |
| 年月を含める | - | 含める |
| 形式 | xlsx | xlsx |
時間フォーマットは必ず10進数にしてください。
60進数(`1.30`=1時間30分)のままだと判定と割り算が狂います。
読み込み時に自動換算しますが、MF側の設定が確実です。
ファイル名は変更しないでください。
単月出力には対象年月の列が無いため、ファイル名から対象月を判定しています。
出力
| モード | 判定 | 内容 |
|---|---|---|
| 前月(対象月が過去) | 実績 | 45時間以上の対象者/複数月平均80時間超/45時間超3回以上/通算バランス |
| 当月(対象月が今月) | 月末見込み | 見込み30時間以上の対象者 |
共通で「月別 超過者一覧」(月次基本時間との比較)を出力します。
計算仕様
判定基準
| 残業時間または月末見込み | 判定 |
|---|---|
| 80時間以上 | 危険 |
| 45〜80時間未満 | 36協定超過 |
| 30〜45時間未満 | 要注意 |
| 20〜30時間未満 | 要監視 |
| 20時間未満 | 問題なし |
法令との対応
| 基準 | 対象範囲 | 使用する値 |
|---|---|---|
| 月45時間・年360時間 | 時間外労働のみ | `法定外時間(平日・所定休日)` |
| 2〜6か月平均で月80時間以内 | 時間外労働+休日労働 | 残業時間+休日稼働時間 |
| 月45時間超は年6回まで | 時間外労働のみ | `法定外時間(平日・所定休日)` |
当月の月末見込み
見込みA = 残業時間 ÷ 経過営業日数 × 所定労働日数 見込みB =(総労働時間 - 所定時間)÷ 経過営業日数 × 所定労働日数 判定には A と B の大きいほうを使用(安全側)
見込みBを併用するのは、法定外時間が週単位で確定するため月初〜週の途中では0のままになるためです。
総労働時間-所定時間は初日から反応します。
通算バランス(帳尻)
各月の「総労働時間 - 月次基本時間」を積み上げた累計。
ある月に超過しても翌月に下回れば累計は戻ります。
集計途中の当月は累計から自動的に除外されます。
含めると全員が大きくマイナスになり、帳尻が合って見えるためです。
月次基本時間の初期値は `MONTHLY_BASE_DEFAULT`(162.67時間=年間所定労働時間1952時間 ÷ 12)。
画面で変更できます。
カスタマイズ(index.php 冒頭の定数)
| 定数 | 内容 | 初期値 |
|---|---|---|
| `EXCLUDE_TITLES` | 集計対象外の役職 | 代表取締役・取締役・監査役・役員・部長 |
| `TH_DANGER` / `TH_OVER` / `TH_WATCH` / `TH_MONITOR` | 判定しきい値 | 80 / 45 / 30 / 20 |
| `LIST_MIN_ACTUAL` | 前月モードの一覧下限 | 45 |
| `LIST_MIN_FORECAST` | 当月モードの一覧下限 | 30 |
| `MONTHLY_BASE_DEFAULT` | 月次基本時間の初期値 | 162.67 |
| `MASTER_CSV` | 所属係マスタのファイル名 | master.csv |
役職が空欄の人も集計対象外になります。
動作要件
* PHP 7.4 以上 * 拡張:`zip` `xml` `mbstring` * `upload_max_filesize` / `post_max_size` 2MB以上
`check.php` をアップロードして開くと、これらを一括で確認できます。
トラブルシューティング
| メッセージ | 対処 |
|---|---|
| PHPのZipArchive拡張が有効になっていません | サーバーで `zip` 拡張を有効化 |
| 必要な列「◯◯」が見つかりません | 出勤簿ではなく「月別データ」をダウンロード |
| 対象年月を判定できません | ファイル名を変更せずにアップロード |
| 対象年月が7か月以上あります | 前月を最終月とする6か月以内で出し直す |
| ファイルサイズが上限を超えています | `php.ini` の `upload_max_filesize` / `post_max_size` を 8M 程度に |