====== 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` の作成 → ビルド → 起動確認までを行い、成功するとブラウザが自動で開きます。
**http://localhost:8080**
==== .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 程度に |