リクエスト詳細
🐛 バグ報告
対応完了
対象アプリ: ShiftCost - 製造ライン別・時間帯別 人件費シミュレーター
深夜割増の割増額(premium)計算が誤っている:overtime/midnight/holiday すべて同じ計算式で混在
## 1. 不具合の内容
`lib.php` の `premium_amount()` 関数が、シフト行に `hourly_wage` カラムが存在することを前提にしていますが、`shifts` テーブルには `hourly_wage` カラムが存在しません(`schema.sql` 参照)。そのため `$shift['hourly_wage']` は常に `null` → `0` になり、`premium_amount()` は常に `calc_wage` 全額を「割増額」として返します。
```php
// lib.php
function premium_amount(array $shift): float {
$base = (float)$shift['hourly_wage'] * (float)$shift['calc_hours']; // ← hourly_wage は shifts テーブルに存在しない
return max(0, (float)$shift['calc_wage'] - $base); // → base=0 なので常に calc_wage 全額が割増扱い
}
```
`shifts` テーブルのカラム定義(schema.sql):
```
id, work_date, worker_id, line_id, start_time, end_time,
shift_type, calc_hours, calc_wage, created_at
```
→ `hourly_wage` カラムは存在しない。
この結果、ダッシュボードの「割増額 推定」メトリクス(`$todayStats['premium']`)が実際の割増上乗せ分ではなく人件費合計と同じ値になり、ドーナツグラフの「基本給相当」が常に 0 または負値になります。
## 2. 根拠・発生条件
- `dashboard_stats()` が内部で `premium_amount()` を呼び出してダッシュボードの `premium` 値を集計していると推定される
- `list.php` で `$todayStats['premium']` をドーナツグラフに渡しており、`premiumTotal = max(0, total - premium)` の計算で基本給相当が 0 になる
- `shifts` テーブルには `hourly_wage` がなく JOIN も行われていない状態で `premium_amount()` に渡すと必ず `$shift['hourly_wage'] === null`
- 通常シフト(shift_type='normal'、割増なし)でも `calc_wage` 全額が割増としてカウントされる
## 3. 期待動作
- 割増額 = `calc_wage` - `(hourly_wage × calc_hours)` で計算される
- 通常シフトの割増額は 0
- ドーナツグラフで「基本給相当」と「割増額」が正しい比率で表示される
## 4. 修正方針
**方針A(推奨):`report_rows()` で workers を JOIN して hourly_wage を取得し premium_amount に渡す**
`dashboard_stats()` 内で shifts を集計する際、`workers` テーブルを JOIN して `hourly_wage` を取得するよう SQL を修正する。
```sql
-- dashboard_stats() 内の SELECT に追加
SELECT s.*, w.hourly_wage
FROM shifts s
JOIN workers w ON w.id = s.worker_id
WHERE s.work_date BETWEEN ? AND ?
```
**方針B(シンプル):summary_cache の overtime_wage + midnight_wage を使う**
`dashboard_stats()` のプレミアム計算を `premium_amount()` ではなく `summary_cache.overtime_wage + summary_cache.midnight_wage` の合算に変更する。すでに `recalc_summary()` でこれらを正しく集計しているため、整合性が取れる。
方針Bが既存コードへの影響が最小限のため推奨。`premium_amount()` 関数自体は `report_rows()` が workers JOIN 済みのデータを渡す用途に限定し、ダッシュボードの premium 集計は summary_cache から取得するよう `dashboard_stats()` を修正する。
`lib.php` の `premium_amount()` 関数が、シフト行に `hourly_wage` カラムが存在することを前提にしていますが、`shifts` テーブルには `hourly_wage` カラムが存在しません(`schema.sql` 参照)。そのため `$shift['hourly_wage']` は常に `null` → `0` になり、`premium_amount()` は常に `calc_wage` 全額を「割増額」として返します。
```php
// lib.php
function premium_amount(array $shift): float {
$base = (float)$shift['hourly_wage'] * (float)$shift['calc_hours']; // ← hourly_wage は shifts テーブルに存在しない
return max(0, (float)$shift['calc_wage'] - $base); // → base=0 なので常に calc_wage 全額が割増扱い
}
```
`shifts` テーブルのカラム定義(schema.sql):
```
id, work_date, worker_id, line_id, start_time, end_time,
shift_type, calc_hours, calc_wage, created_at
```
→ `hourly_wage` カラムは存在しない。
この結果、ダッシュボードの「割増額 推定」メトリクス(`$todayStats['premium']`)が実際の割増上乗せ分ではなく人件費合計と同じ値になり、ドーナツグラフの「基本給相当」が常に 0 または負値になります。
## 2. 根拠・発生条件
- `dashboard_stats()` が内部で `premium_amount()` を呼び出してダッシュボードの `premium` 値を集計していると推定される
- `list.php` で `$todayStats['premium']` をドーナツグラフに渡しており、`premiumTotal = max(0, total - premium)` の計算で基本給相当が 0 になる
- `shifts` テーブルには `hourly_wage` がなく JOIN も行われていない状態で `premium_amount()` に渡すと必ず `$shift['hourly_wage'] === null`
- 通常シフト(shift_type='normal'、割増なし)でも `calc_wage` 全額が割増としてカウントされる
## 3. 期待動作
- 割増額 = `calc_wage` - `(hourly_wage × calc_hours)` で計算される
- 通常シフトの割増額は 0
- ドーナツグラフで「基本給相当」と「割増額」が正しい比率で表示される
## 4. 修正方針
**方針A(推奨):`report_rows()` で workers を JOIN して hourly_wage を取得し premium_amount に渡す**
`dashboard_stats()` 内で shifts を集計する際、`workers` テーブルを JOIN して `hourly_wage` を取得するよう SQL を修正する。
```sql
-- dashboard_stats() 内の SELECT に追加
SELECT s.*, w.hourly_wage
FROM shifts s
JOIN workers w ON w.id = s.worker_id
WHERE s.work_date BETWEEN ? AND ?
```
**方針B(シンプル):summary_cache の overtime_wage + midnight_wage を使う**
`dashboard_stats()` のプレミアム計算を `premium_amount()` ではなく `summary_cache.overtime_wage + summary_cache.midnight_wage` の合算に変更する。すでに `recalc_summary()` でこれらを正しく集計しているため、整合性が取れる。
方針Bが既存コードへの影響が最小限のため推奨。`premium_amount()` 関数自体は `report_rows()` が workers JOIN 済みのデータを渡す用途に限定し、ダッシュボードの premium 集計は summary_cache から取得するよう `dashboard_stats()` を修正する。
💬 返信 (3)
🛠 開発を開始しました (バグ修正 (shiftcost))
ご要望ありがとうございます。AI 開発ワーカーが実装を開始します。
通常 5〜30 分で Pull Request を作成し、レビュー後にリリースされます。
ご要望ありがとうございます。AI 開発ワーカーが実装を開始します。
通常 5〜30 分で Pull Request を作成し、レビュー後にリリースされます。
📝 開発が完了しました
ご要望いただいた内容の実装が完了し、最終チェック段階に入りました。
レビュー (自動) → リリース、の流れで進みます。
もう少々お待ちください。
ご要望いただいた内容の実装が完了し、最終チェック段階に入りました。
レビュー (自動) → リリース、の流れで進みます。
もう少々お待ちください。
✅ リリース完了のお知らせ
ご要望いただいた「ShiftCost - 製造ライン別・時間帯別 人件費シミュレーター」を実装し、リリースいたしました。
【ご利用方法】
ダッシュボード: https://www.aiapps.jp/?action=dashboard
アプリ詳細: https://www.aiapps.jp/apps/show.php?slug=shiftcost
デモ環境は 1 時間以内に自動構築されます:
https://www.aiapps.jp/demo/shiftcost/
ご利用ありがとうございます!
ご要望いただいた「ShiftCost - 製造ライン別・時間帯別 人件費シミュレーター」を実装し、リリースいたしました。
【ご利用方法】
ダッシュボード: https://www.aiapps.jp/?action=dashboard
アプリ詳細: https://www.aiapps.jp/apps/show.php?slug=shiftcost
デモ環境は 1 時間以内に自動構築されます:
https://www.aiapps.jp/demo/shiftcost/
ご利用ありがとうございます!
Echo
Iris