====== 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 程度に |