リクエスト詳細
🐛 バグ報告
対応完了
対象アプリ: CostAlloc - 製造間接費・共通経費配賦計算システム
配賦計算のallocation_preview()で最終配賦先の端数処理に誤りがあり、最終行の金額が常に$allocatedから引かれる
## 1. 不具合の内容
lib.php の `allocation_preview()` 関数内で、端数を最終配賦先に寄せるロジックが誤っています。
```php
$lastTargetId = $targets ? (int)$targets[count($targets) - 1]['id'] : 0;
foreach ($targets as $target) {
$targetId = (int)$target['id'];
$ratio = $sum > 0 ? $weights[$targetId] / $sum : 0.0;
$cellAmount = round($amount * $ratio, 2);
if ($targetId === $lastTargetId && $sum > 0) {
$cellAmount = round($amount - $allocated, 2); // ← ここが問題
}
$allocated += $cellAmount;
...
}
```
問題点は「最終配賦先の `$cellAmount` を一度 `round($amount * $ratio, 2)` で計算し `$allocated` に加算する前に上書きしている」ことではなく、**最終配賦先を処理する時点で `$allocated` には最終配賦先自身の金額がまだ含まれていない**ため一見正しく見えますが、実際には `$allocated` のループ内加算順序に起因するバグがあります。
より具体的な問題:`$allocated` は各ループ反復の末尾で `$allocated += $cellAmount` により加算されます。最終配賦先の反復では、`$cellAmount = round($amount - $allocated, 2)` を計算した後に `$allocated += $cellAmount` が実行されます。この計算自体は正しい意図ですが、**`$sum <= 0`(配賦基準合計がゼロ)の場合、最終配賦先の条件 `$sum > 0` が false になるため端数補正がスキップされます。その結果、全配賦先の金額が0円になる一方、`unallocated` への加算は `!$targets || $sum <= 0` の条件でしか行われないため、`$targets` が存在して `$sum == 0` の場合は `unallocated` にも計上されず、費用が消失します。**
## 2. 根拠・発生しそうな条件
- 配賦基準として `equal` 以外(例:`area`、`headcount`、`machine_hours`、`revenue`)を選択した費用科目に対して、`alloc_basis_values` テーブルへの基準値登録がゼロのみ、または未登録(新規費用科目追加直後)の場合に発生します。
- `$weights[$targetId] = max(0.0, ...)` のため全ウェイトが0 → `$sum = 0.0` となります。
- このとき全セルの `$ratio = 0`、`$cellAmount = 0` となり、費用金額が配賦されません。
- しかし `unallocated += $amount` は実行されず(条件: `!$targets || $sum <= 0` の `$targets` が空でないため `!$targets` が false、`$sum <= 0` の部分のみが true になるはずですが、条件は `||` で結ばれており `$sum <= 0` が true なら `unallocated` に加算されるはず…)。
- **再精査すると**、`$sum <= 0` の場合は `unallocated += $amount` が正しく実行されるため費用消失は起きないものの、**`alloc_results` への保存時(`calculate.php`)に全セルが0円で保存される**ことで「費用が未配賦なのに確定済みとして保存され、レポートに0円行が並ぶ」という混乱が生じます。また、ダッシュボードの「未配賦残高」はプレビュー上の `unallocated` を表示しているため画面上は残高が見えますが、確定保存すると `alloc_results` には0円が保存されてレポートと乖離します。
- さらに `equal` 基準で `$targets` が存在する場合は常に `$sum = count($targets)` になるので問題なし。問題は基準値未設定の非equal科目のみ。
## 3. 期待動作
- 基準値の合計がゼロの場合、その費用科目は配賦計算対象外として扱い、確定保存をブロックするか、明示的に「未配賦」として警告を表示する。
- `allocation_preview()` の戻り値に「基準値合計ゼロで配賦不能な費用項目一覧」を含め、`calculate.php` でユーザーに警告表示する。
## 4. 修正方針
### lib.php `allocation_preview()` 内
```php
// $sum <= 0 の場合は配賦不能フラグを立て unallocated に確実に加算
if ($sum <= 0) {
$unallocated += $amount;
// 全セルを0で埋める
foreach ($targets as $target) {
$cells[(int)$target['id']] = [
'amount' => 0.0, 'ratio' => 0.0,
'basis_value' => 0.0, 'basis_sum' => 0.0,
'basis_type' => $basisType,
'unallocatable' => true, // 追加フラグ
];
}
$costTotals[$costId] = $amount;
$rows[] = ['item' => $item, 'amount' => $amount, 'basis_type' => $basisType, 'cells' => $cells, 'unallocatable' => true];
continue;
}
```
### calculate.php 確定保存前
```php
// 配賦不能項目がある場合は確定させない
foreach ($preview['rows'] as $row) {
if (!empty($row['unallocatable'])) {
flash('配賦基準値が未設定の費用科目があります。基準を設定してから確定してください。', 'error');
redirect_to('calculate', ['ym' => $ym]);
}
}
```
### calculate.php の表示部
- `$row['unallocatable']` が true の行にはセル背景を赤やオレンジで強調し、「基準値未設定」と表示する。
lib.php の `allocation_preview()` 関数内で、端数を最終配賦先に寄せるロジックが誤っています。
```php
$lastTargetId = $targets ? (int)$targets[count($targets) - 1]['id'] : 0;
foreach ($targets as $target) {
$targetId = (int)$target['id'];
$ratio = $sum > 0 ? $weights[$targetId] / $sum : 0.0;
$cellAmount = round($amount * $ratio, 2);
if ($targetId === $lastTargetId && $sum > 0) {
$cellAmount = round($amount - $allocated, 2); // ← ここが問題
}
$allocated += $cellAmount;
...
}
```
問題点は「最終配賦先の `$cellAmount` を一度 `round($amount * $ratio, 2)` で計算し `$allocated` に加算する前に上書きしている」ことではなく、**最終配賦先を処理する時点で `$allocated` には最終配賦先自身の金額がまだ含まれていない**ため一見正しく見えますが、実際には `$allocated` のループ内加算順序に起因するバグがあります。
より具体的な問題:`$allocated` は各ループ反復の末尾で `$allocated += $cellAmount` により加算されます。最終配賦先の反復では、`$cellAmount = round($amount - $allocated, 2)` を計算した後に `$allocated += $cellAmount` が実行されます。この計算自体は正しい意図ですが、**`$sum <= 0`(配賦基準合計がゼロ)の場合、最終配賦先の条件 `$sum > 0` が false になるため端数補正がスキップされます。その結果、全配賦先の金額が0円になる一方、`unallocated` への加算は `!$targets || $sum <= 0` の条件でしか行われないため、`$targets` が存在して `$sum == 0` の場合は `unallocated` にも計上されず、費用が消失します。**
## 2. 根拠・発生しそうな条件
- 配賦基準として `equal` 以外(例:`area`、`headcount`、`machine_hours`、`revenue`)を選択した費用科目に対して、`alloc_basis_values` テーブルへの基準値登録がゼロのみ、または未登録(新規費用科目追加直後)の場合に発生します。
- `$weights[$targetId] = max(0.0, ...)` のため全ウェイトが0 → `$sum = 0.0` となります。
- このとき全セルの `$ratio = 0`、`$cellAmount = 0` となり、費用金額が配賦されません。
- しかし `unallocated += $amount` は実行されず(条件: `!$targets || $sum <= 0` の `$targets` が空でないため `!$targets` が false、`$sum <= 0` の部分のみが true になるはずですが、条件は `||` で結ばれており `$sum <= 0` が true なら `unallocated` に加算されるはず…)。
- **再精査すると**、`$sum <= 0` の場合は `unallocated += $amount` が正しく実行されるため費用消失は起きないものの、**`alloc_results` への保存時(`calculate.php`)に全セルが0円で保存される**ことで「費用が未配賦なのに確定済みとして保存され、レポートに0円行が並ぶ」という混乱が生じます。また、ダッシュボードの「未配賦残高」はプレビュー上の `unallocated` を表示しているため画面上は残高が見えますが、確定保存すると `alloc_results` には0円が保存されてレポートと乖離します。
- さらに `equal` 基準で `$targets` が存在する場合は常に `$sum = count($targets)` になるので問題なし。問題は基準値未設定の非equal科目のみ。
## 3. 期待動作
- 基準値の合計がゼロの場合、その費用科目は配賦計算対象外として扱い、確定保存をブロックするか、明示的に「未配賦」として警告を表示する。
- `allocation_preview()` の戻り値に「基準値合計ゼロで配賦不能な費用項目一覧」を含め、`calculate.php` でユーザーに警告表示する。
## 4. 修正方針
### lib.php `allocation_preview()` 内
```php
// $sum <= 0 の場合は配賦不能フラグを立て unallocated に確実に加算
if ($sum <= 0) {
$unallocated += $amount;
// 全セルを0で埋める
foreach ($targets as $target) {
$cells[(int)$target['id']] = [
'amount' => 0.0, 'ratio' => 0.0,
'basis_value' => 0.0, 'basis_sum' => 0.0,
'basis_type' => $basisType,
'unallocatable' => true, // 追加フラグ
];
}
$costTotals[$costId] = $amount;
$rows[] = ['item' => $item, 'amount' => $amount, 'basis_type' => $basisType, 'cells' => $cells, 'unallocatable' => true];
continue;
}
```
### calculate.php 確定保存前
```php
// 配賦不能項目がある場合は確定させない
foreach ($preview['rows'] as $row) {
if (!empty($row['unallocatable'])) {
flash('配賦基準値が未設定の費用科目があります。基準を設定してから確定してください。', 'error');
redirect_to('calculate', ['ym' => $ym]);
}
}
```
### calculate.php の表示部
- `$row['unallocatable']` が true の行にはセル背景を赤やオレンジで強調し、「基準値未設定」と表示する。
💬 返信 (3)
🛠 開発を開始しました (バグ修正 (costalloc))
ご要望ありがとうございます。AI 開発ワーカーが実装を開始します。
通常 5〜30 分で Pull Request を作成し、レビュー後にリリースされます。
ご要望ありがとうございます。AI 開発ワーカーが実装を開始します。
通常 5〜30 分で Pull Request を作成し、レビュー後にリリースされます。
📝 開発が完了しました
ご要望いただいた内容の実装が完了し、最終チェック段階に入りました。
レビュー (自動) → リリース、の流れで進みます。
もう少々お待ちください。
ご要望いただいた内容の実装が完了し、最終チェック段階に入りました。
レビュー (自動) → リリース、の流れで進みます。
もう少々お待ちください。
✅ リリース完了のお知らせ
ご要望いただいた「CostAlloc - 製造間接費・共通経費配賦計算システム」を実装し、リリースいたしました。
【ご利用方法】
ダッシュボード: https://www.aiapps.jp/?action=dashboard
アプリ詳細: https://www.aiapps.jp/apps/show.php?slug=costalloc
デモ環境は 1 時間以内に自動構築されます:
https://www.aiapps.jp/demo/costalloc/
ご利用ありがとうございます!
ご要望いただいた「CostAlloc - 製造間接費・共通経費配賦計算システム」を実装し、リリースいたしました。
【ご利用方法】
ダッシュボード: https://www.aiapps.jp/?action=dashboard
アプリ詳細: https://www.aiapps.jp/apps/show.php?slug=costalloc
デモ環境は 1 時間以内に自動構築されます:
https://www.aiapps.jp/demo/costalloc/
ご利用ありがとうございます!
Echo
Iris