リクエスト詳細
🐛 バグ報告
対応完了
対象アプリ: SupplierHub - 仕入先・外注先評価&取引管理システム
連続遅延アラートの判定ロジックが「最新順」データで正しく機能しない
## 1. 不具合の内容
`supplier_score_from_transactions()` 内の連続遅延カウントロジックが、取引データの並び順(最新順)と意図する判定方向(直近の連続遅延)が一致しておらず、実際には「最古側から数えた連続遅延」を検出してしまう。
## 2. 根拠・発生しそうな条件
`supplier_transactions()` は `ORDER BY ordered_at DESC, id DESC`(降順=最新が先頭)でデータを取得する。
一方、`lib.php` の連続遅延カウント部分は以下のコードになっている:
```php
$continuous_delay = 0;
foreach ($rows as $row) {
if (($row['result'] ?? '') === 'delay') {
$continuous_delay++;
} else {
break;
}
}
```
この `foreach` は配列の先頭(=最新取引)から順に見ていくため、最新側の連続遅延を正しくカウントするように見える。しかし `supplier_score_from_transactions()` は `supplier_detail.php` や `report.php` 以外に、`suppliers_with_scores()` 経由でダッシュボードや比較画面からも呼ばれる。
`suppliers_with_scores()` の内部実装(抜粋されていないが、全仕入先を一括取得して各仕入先の取引をまとめてスコア計算する構造が多い)では、取引行を `supplier_id` でグループ化してから各仕入先に割り当てる場合、`ordered_at DESC` の並び順が保証されないまま `supplier_score_from_transactions()` に渡される可能性がある。
さらに、サンプルデータ(`supplier_id=5` の海浜ロジスティクス)を例にとると、transactions テーブルには `id=11,12,13` がすべて `result='delay'` で登録されており、連続遅延アラートが発火すべきケースである。`ORDER BY ordered_at DESC` で取得した場合は先頭から3件連続 `delay` となりカウント3でアラートが出るが、`suppliers_with_scores()` 内でPHPの連想配列として仕入先IDでグループ化する際にデータの挿入順(昇順)になってしまうと、最初の要素が最古の取引になり、「最古から連続している遅延」を誤ってカウントしてしまう(偶然一致するケースもあるが、途中に 'ok' が挟まる仕入先では誤判定になる)。
また、`supplier_score_from_transactions()` に渡す `$rows` が降順前提なのか昇順前提なのかがコメントや引数仕様に明示されておらず、呼び出し元によって挙動が変わる潜在的バグとなっている。
## 3. 期待動作
「**直近N件の取引が連続して delay である**」ことを検出すること。すなわち最新の取引から遡って連続遅延を数え、3回以上でアラートを発火する。
## 4. 修正方針
`supplier_score_from_transactions()` 内の連続遅延カウント処理を、渡された `$rows` の並び順に依存しない形に修正する。具体的には以下のいずれかを採用する:
**推奨: 関数内でソートを保証する**
```php
// $rows を ordered_at DESC, id DESC の順に並べ直してからカウント
usort($rows_for_delay, function($a, $b) {
$cmp = strcmp((string)($b['ordered_at'] ?? ''), (string)($a['ordered_at'] ?? ''));
return $cmp !== 0 ? $cmp : ((int)$b['id'] - (int)$a['id']);
});
$continuous_delay = 0;
foreach ($rows_for_delay as $row) {
if (($row['result'] ?? '') === 'delay') {
$continuous_delay++;
} else {
break;
}
}
```
これにより `supplier_transactions()`経由でも `suppliers_with_scores()`経由でも、常に「最新取引から遡った連続遅延数」を正しく取得できる。合わせて関数のコメントに「$rows の並び順は問わない」旨を追記する。
`supplier_score_from_transactions()` 内の連続遅延カウントロジックが、取引データの並び順(最新順)と意図する判定方向(直近の連続遅延)が一致しておらず、実際には「最古側から数えた連続遅延」を検出してしまう。
## 2. 根拠・発生しそうな条件
`supplier_transactions()` は `ORDER BY ordered_at DESC, id DESC`(降順=最新が先頭)でデータを取得する。
一方、`lib.php` の連続遅延カウント部分は以下のコードになっている:
```php
$continuous_delay = 0;
foreach ($rows as $row) {
if (($row['result'] ?? '') === 'delay') {
$continuous_delay++;
} else {
break;
}
}
```
この `foreach` は配列の先頭(=最新取引)から順に見ていくため、最新側の連続遅延を正しくカウントするように見える。しかし `supplier_score_from_transactions()` は `supplier_detail.php` や `report.php` 以外に、`suppliers_with_scores()` 経由でダッシュボードや比較画面からも呼ばれる。
`suppliers_with_scores()` の内部実装(抜粋されていないが、全仕入先を一括取得して各仕入先の取引をまとめてスコア計算する構造が多い)では、取引行を `supplier_id` でグループ化してから各仕入先に割り当てる場合、`ordered_at DESC` の並び順が保証されないまま `supplier_score_from_transactions()` に渡される可能性がある。
さらに、サンプルデータ(`supplier_id=5` の海浜ロジスティクス)を例にとると、transactions テーブルには `id=11,12,13` がすべて `result='delay'` で登録されており、連続遅延アラートが発火すべきケースである。`ORDER BY ordered_at DESC` で取得した場合は先頭から3件連続 `delay` となりカウント3でアラートが出るが、`suppliers_with_scores()` 内でPHPの連想配列として仕入先IDでグループ化する際にデータの挿入順(昇順)になってしまうと、最初の要素が最古の取引になり、「最古から連続している遅延」を誤ってカウントしてしまう(偶然一致するケースもあるが、途中に 'ok' が挟まる仕入先では誤判定になる)。
また、`supplier_score_from_transactions()` に渡す `$rows` が降順前提なのか昇順前提なのかがコメントや引数仕様に明示されておらず、呼び出し元によって挙動が変わる潜在的バグとなっている。
## 3. 期待動作
「**直近N件の取引が連続して delay である**」ことを検出すること。すなわち最新の取引から遡って連続遅延を数え、3回以上でアラートを発火する。
## 4. 修正方針
`supplier_score_from_transactions()` 内の連続遅延カウント処理を、渡された `$rows` の並び順に依存しない形に修正する。具体的には以下のいずれかを採用する:
**推奨: 関数内でソートを保証する**
```php
// $rows を ordered_at DESC, id DESC の順に並べ直してからカウント
usort($rows_for_delay, function($a, $b) {
$cmp = strcmp((string)($b['ordered_at'] ?? ''), (string)($a['ordered_at'] ?? ''));
return $cmp !== 0 ? $cmp : ((int)$b['id'] - (int)$a['id']);
});
$continuous_delay = 0;
foreach ($rows_for_delay as $row) {
if (($row['result'] ?? '') === 'delay') {
$continuous_delay++;
} else {
break;
}
}
```
これにより `supplier_transactions()`経由でも `suppliers_with_scores()`経由でも、常に「最新取引から遡った連続遅延数」を正しく取得できる。合わせて関数のコメントに「$rows の並び順は問わない」旨を追記する。
💬 返信 (3)
🛠 開発を開始しました (バグ修正 (supplier-hub))
ご要望ありがとうございます。AI 開発ワーカーが実装を開始します。
通常 5〜30 分で Pull Request を作成し、レビュー後にリリースされます。
ご要望ありがとうございます。AI 開発ワーカーが実装を開始します。
通常 5〜30 分で Pull Request を作成し、レビュー後にリリースされます。
📝 開発が完了しました
ご要望いただいた内容の実装が完了し、最終チェック段階に入りました。
レビュー (自動) → リリース、の流れで進みます。
もう少々お待ちください。
ご要望いただいた内容の実装が完了し、最終チェック段階に入りました。
レビュー (自動) → リリース、の流れで進みます。
もう少々お待ちください。
✅ リリース完了のお知らせ
ご要望いただいた「SupplierHub - 仕入先・外注先評価&取引管理システム」を実装し、リリースいたしました。
【ご利用方法】
ダッシュボード: https://www.aiapps.jp/?action=dashboard
アプリ詳細: https://www.aiapps.jp/apps/show.php?slug=supplier-hub
デモ環境は 1 時間以内に自動構築されます:
https://www.aiapps.jp/demo/supplier-hub/
ご利用ありがとうございます!
(deploy 自動リカバリにより通知が遅延した可能性があります。 DEPLOY-RECOVERY-01)
ご要望いただいた「SupplierHub - 仕入先・外注先評価&取引管理システム」を実装し、リリースいたしました。
【ご利用方法】
ダッシュボード: https://www.aiapps.jp/?action=dashboard
アプリ詳細: https://www.aiapps.jp/apps/show.php?slug=supplier-hub
デモ環境は 1 時間以内に自動構築されます:
https://www.aiapps.jp/demo/supplier-hub/
ご利用ありがとうございます!
(deploy 自動リカバリにより通知が遅延した可能性があります。 DEPLOY-RECOVERY-01)
Echo
Iris