# 予約シリーズ（マスタの先付け変更）運用ガイド

| 項目 | 内容 |
|---|---|
| 対象読者 | 社内の開発・保守担当。先方（ジェントス様）からの問い合わせに一次対応する人 |
| 目的 | 2026/10/1 の一斉値上げまわりの問い合わせに、この資料だけで答えられるようにする |
| 範囲 | 予約4機能（得意先単価ランク・商品金額・営業担当・トラスコ支店別営業担当）、日付で切り替わる単価（ランク別商品単価・特単）、確認用の帳票（得意先別価格一覧・商品一覧） |
| 作成 | 2026-09-24 酒井。件数は 2026-09-24 01:00 時点の本番データ。コードの行番号も同日時点 |
| 更新 | 2026-09-25 特単終了の第3弾を反映（§2・§6-3・§7-4・§9）。同日、商品一覧（report200）の予約列（#51750）を反映（§2・Q11・§5-1・§5-2 F・§8-5・§9）<br>2026-09-30 10/1 前日の件数（本番 9/30 早朝）とリハーサルの結果を反映（§2・§3）。営業担当予約バッチの受注残の定義修正（本番デプロイ済み）を反映（§7-5・§8-1・§9） |

詳しい検討・検証の記録は git 管理外にある（§9）。この資料だけで対応できるように書いている。

---

## 0. 酒井の不在中（2026-09-29〜30）の対応（期間は終了）

- 10/1 の一斉値上げの直前。先方は **9/30 まで特単・ランク別商品単価・予約を登録する可能性がある**
- 問い合わせの多くは §4 で答えられる。確認は画面と §5 の読み取り専用SQLで足りる
- **本番データを SQL で直接直さない。** 監査（audits）に残らず、後から追えなくなる
- **予約の適用バッチを手で流さない。** 10/1 の 0:05〜0:25 に自動で流れる。
  特に `--date=2026-10-01` を付けて前日に流すと、10/1 の予約が前倒しで適用される
- 直す必要がありそうなものは、得意先・商品・日付・画面・再現手順を記録し、10/1 に酒井へ回す
- 連絡先: （出発前に記入）

---

## 1. 全体像

### 1-1. 先付けで変えられるもの

| 対象 | 切り替わり方 | 反映 | 登録する場所 | CSV | 既存の受注への反映 |
|---|---|---|---|---|---|
| 得意先の単価ランク（標準／品目別） | バッチ | 0:05 | 得意先マスタ → 編集 → グループ価格設定 → 予約登録 | あり | なし（受注入力は出荷日で先読みする） |
| トラスコ支店別の営業担当 | バッチ | 0:15 | トラスコ中山ダッシュボード → 支店マスタ → 担当 → 予約 | なし | 未出荷の受注の担当を直す |
| 営業担当（得意先×大分類） | バッチ | 0:20 | マスタ設定 → 取引先 → 営業担当予約 | あり（個別変更のみ） | 未出荷の受注の担当を直す |
| 商品の金額6項目 | バッチ | 0:25 | 商品マスタ → 編集 → 金額予約 | あり | 粗利算出用単価だけ、未出荷の受注明細の原価を直す |
| ランク別商品単価 | 日付 | 適用開始日の0時 | 商品マスタ → 編集 → 販売／価格設定 | あり | なし |
| 特単（得意先・配送先特別単価） | 日付 | 適用開始日の0時 | マスタ設定 → 取引先 → 得意先・配送先特別単価一覧 | あり | なし |

- 金額6項目は 粗利算出用単価・標準仕入単価・購入価格・付属原価・上代・参考売価。販売単価そのものではない
- 開発時の呼び名は ①得意先単価ランク変更予約／②商品金額変更予約／③トラスコ支店別営業担当予約。仕様書（§9）はこの番号で書かれている

### 1-2. 切り替わり方は2通り

**バッチ型（上の4つ）**

- 登録した時点ではマスタは変わらない。適用日の深夜にバッチがマスタを書き換え、予約が「待機中」→「適用済み」になる
- 状態は 待機中／適用済み／キャンセル の3つ。取消できるのは待機中だけ（キャンセルになり、行は残る）
- 登録後に直せるのは **適用開始日と備考だけ**。対象や変更後の値を間違えたら、取消して登録し直す
- 適用開始日は翌日以降しか指定できない
- 同じ対象（得意先×分類、商品、支店×大分類）に待機中の予約は1件まで。営業担当予約だけはこの制限が無い
- マスタの編集画面で、予約が入っている項目の下に「待機中」のバッジが出る

**日付型（ランク別商品単価・特単）**

- 適用開始日つきの行を登録しておくと、その日から有効な行が変わる。バッチも状態も無い
- ランク別商品単価は、新しい適用開始日の単価を登録すると、前の単価の適用終了日が自動で「新しい開始日の前日」になる（画面・CSVとも）
- 特単は、**画面から登録しても前の特単は自動で終わらない**。CSV は処理区分1（新規／価格改定）のときだけ、直前の1件を終わらせる（§7-1）

### 1-3. 受注・売上で単価が決まる日付

単価の優先順位は **配送先特単 → 得意先特単 → 中分類ランク → 大分類ランク → 標準ランク**。

| 経路 | 単価を決める日付 | 補足 |
|---|---|---|
| 受注入力 | 出荷日 | 単価ランク予約も出荷日で先読みする。適用前でも、出荷日が 10/1 以降なら 10/1 のランクと単価になる |
| 売上入力 | 売上日 | 売上日を変えると単価を取り直す作り（画面での確認はしていない） |
| EDI 受注反映（ヨドバシ） | 受注日 | 納品が 10/1 以降でも、受注日が9月なら9月の単価 |
| EDI 受注反映（トラスコ） | ― | 送られてきた単価をそのまま使う |
| CSV 受注取込 | ― | CSV の単価をそのまま使う。単価が空欄のときだけマスタから引く（到着日 → 受注日 → 取込日の順） |

**保存済みの受注明細の単価は据え置き。** 出荷日を変えても単価は変わらない（2026-09-01 からの仕様）。
ただし **数量を変えると、その時点の出荷日で単価を取り直す。**

---

## 2. 2026/10/1 に起きること

| 時刻 | 内容 | 9/30 早朝の件数（本番） |
|---|---|---|
| 10/1 0:00 | 10/1 付の特単が有効になる | 13,330行（339商品） |
| 〃 | 9/30 で終わる特単が切れ、ランクの単価に戻る（特単の一括終了 第1〜3弾を含む。§9） | 33,691行（1,849商品） |
| 〃 | 10/1 付のランク別商品単価が有効になる | 3,966行（661商品） |
| 0:05 | 得意先単価ランク予約を適用 | 1,690件（845社 × ヘルメット／ヘルメットパーツ。すべて品目別ランク設定） |
| 0:15 | トラスコ支店別営業担当予約を適用 | 0件 |
| 0:20 | 営業担当予約を適用 | 376件（314社。すべて個別変更・大分類指定。LED 156・ヘルメット 220）。あわせて出荷指示前の受注残の担当を新担当へ（§8-1） |
| 0:25 | 商品金額変更予約を適用 | 677件。粗利算出用単価は未出荷の受注明細 約2,120件へ遡及 |
| 0:45 | S3 送信（予約の適用後の値を送る） | ― |

件数は本番 9/30 早朝のもので、9/30 の日中に登録された分は入っていない。最新は §5-2 の A・B で確認する。

2026-09-30 に、この 9/30 早朝のデータで 10/1 の 0:05〜0:45 のバッチを本番と同じ順序で先に流した（リハーサル。§9）。
4本ともエラー0で、マスタ・受注明細の原価・受注の担当は予約どおりになり、それ以外の表は変わらなかった。
このとき見つかった営業担当予約バッチの不具合は同日に直して本番へ反映した（§7-5）。

10/1 の朝は、§5-2 の C（適用漏れ）がすべて 0件であることを確かめる。リハーサルで作った確認SQL（§9 の `prod-check-1001-morning.sql`）なら、適用件数・マスタと予約の一致・受注残の担当までまとめて確かめられる。

商品一覧（§5-1）の予約の列は、ランク単価は 10/1 0:00、商品金額は 0:25 の適用で空になる（今の値の列が新しい値になる）。
予約の内容を今の値と並べて確かめられるのは 9/30 まで。

---

## 3. 予約・単価の状態（2026-09-30 早朝の本番）

| 対象 | 状態 |
|---|---|
| 得意先単価ランク予約 | 待機中 1,690件（適用日はすべて 10/1。9/18 に CSV で一括登録） |
| 商品金額変更予約 | 待機中 677件（適用日はすべて 10/1。9/24〜9/29 に登録）。ほかに取消 510件 |
| トラスコ支店別営業担当予約 | 0件。支店ごとの伝票分割も無効のまま（§4 Q7） |
| 営業担当予約 | 待機中 376件（適用日はすべて 10/1。9/28 に登録。最後の適用は 2026-06-01） |
| ランク別商品単価の 10/1 付 | 3,966行（661商品） |
| 特単の 10/1 付 | 13,330行（339商品） |

---

## 4. よくある問い合わせと答え方

### Q1. 予約を登録したのに、マスタ（ランク・担当・金額）が変わっていない

適用日の深夜に反映される。登録しただけではマスタは変わらない。予約は各予約一覧で「待機中」として確認できる。
単価ランク予約は、受注入力が出荷日で先読みするので、出荷日が適用日以降の受注では適用前でも新しいランクで単価が出る。

### Q2. 得意先別価格一覧の「予約中の単価」が今の単価と同じ（2026-09-24 に実際にあった問い合わせ）

10/1 付のランク別商品単価が登録されていないため。表示は正しい。

- 「予約中の単価」は、次に変わる日（例: 10/1）の時点の単価を、その日のランク・特単で計算したもの。今日の単価ではない
- 単価ランク予約で「ランク２」→「品目特別ランク２」のように**今と同じランクを品目別に設定する**場合、参照する単価表は同じなので、単価は変わらない。変わるのはランク名に「品目特別」が付くことだけ
- 9/24 時点で、品目別ランク設定の予約 1,690件のうち **843件（663社）が今と同じランクを設定するもの**
- 10/1 付のランク別商品単価を登録すれば、その直後から「予約中の単価」に新しい単価が出る（旧単価は 9/30 で自動的に終わる）

### Q3. 「予約中の単価」が空欄

次のどれか。

- 次に変わる予定が無い（予約も 10/1 付の単価も特単も無い）
- 終了日の無い特単が効いている行。ランクや単価表が変わっても特単が勝ち続けるので、変化なしとして出さない
- 配送先単位の特単・数量条件付きの特単は、この帳票の計算の対象外
- 10/1 付のランク別商品単価で、そのランクの単価が空欄のまま登録されている（そのランクは 10/1 から単価なし）

予定が複数あっても、出るのは**いちばん近い変化1件だけ**。

### Q4. 10/1 以降に出荷する受注の単価はどうなるか

受注入力は出荷日時点の単価を使う（§1-3）。9月中に入力しても、出荷日が 10/1 以降なら 10/1 の単価・ランクで計算される。
ただし 10/1 付の単価（ランク別商品単価・特単）が登録されていなければ、今の単価のまま。

### Q5. 登録済みの受注（出荷日が 10/1 以降）の単価が変わらない／変わった

保存済みの明細の単価は据え置きで、出荷日を変えても変わらない。
一方、数量を変えるとその時点の出荷日で単価を取り直すので、「数量を直したら単価が変わった」は仕様どおり。
10/1 の単価にしたい場合は、明細の単価を直すか、行を入れ直してもらう。

### Q6. 予約の内容を間違えた／やめたい

待機中なら、予約一覧から取消して登録し直す（直せるのは適用開始日と備考だけ）。
適用済みの予約は取消できない。元に戻すには、マスタを画面で直すか、元の値に戻す予約を登録する。
トラスコ支店別の営業担当は、担当が入っている大分類を予約でしか変えられない。

### Q7. トラスコの受注伝票が支店ごとに分かれない／担当が支店別にならない

仕様どおり。支店ごとの分割は切替日が未設定のため動いていない（`config/edi_trusco.php` の `branch_split_start_date` が `null`）。
支店×大分類の担当データも未投入なので、従来どおり口座の代表担当が入る。

### Q8. 予約一覧に「適用日を過ぎたのに待機中」の警告が出ている

適用バッチが動いていない。§6-2。

### Q9. CSV 取込でエラーになった

§6-1。

### Q10. 特単を登録したのに、10/1 以降も古い特単が使われる

特単の期間が重なっている可能性が高い（§7-1）。§5-2 の E で確認する。

### Q11. 商品一覧の予約の列が空欄／今と同じ値になっている

商品一覧（§5-1）の予約の列は、登録された値をそのまま出す。

- ランクの予約の列で、ランク単価適用予定日が入っているのに単価が空欄なら、**その日からそのランクは単価なし**。
  ランク別商品単価を単価の欄を空けて登録（CSV 取込を含む）するとこうなる。§5-2 の F でまとめて拾える
- ランクの予約の列が全部空欄で日付も空欄なら、次の日付の単価が登録されていない
- 商品金額の予約の列の空欄は「その項目は変えない」
- 今と同じ値が出ているのは、同じ値で登録されているため（誤りではない）
- 得意先別価格一覧の「予約中の単価」は、得意先ごとに値が変わる日だけを出すので、出方が違う

---

## 5. 確かめ方

### 5-1. 画面

**得意先別価格一覧**（参照 → レポート → 得意先別価格一覧、`/reports/1`）

予約の結果を得意先×商品ごとにまとめて見る帳票。参照専用で、ここから予約は登録・修正できない。

| 列 | 内容 |
|---|---|
| 予約中の単価 | 次に変わる日の単価 |
| 変更後のランク | その日のランク。ランクが変わらず単価だけ変わるときは今のランクがそのまま出る |
| 単価適用予定日 | 次に変わる日 |
| 予約中の上代／予約中の参考売価 | 商品金額変更予約の値 |
| 上代・参考売価適用予定日 | 商品金額変更予約の適用日（単価適用予定日とは別の日付になりうる） |

- 単価の3列が拾うのは、ランク別商品単価の未来分、単価ランク予約、**得意先単位**の特単の未来分と終了（配送先単位・数量条件付きは拾わない）
- 利用者のブラウザに古い列の並びが保存されているときは「列設定リセット」を押してもらう

**商品一覧**（参照 → マスタ → 商品一覧、`/reports/200`。2026-09-25 追加）

商品ごとに、ランク別商品単価と商品金額変更予約の予約中の値を、今の値の右隣に黄色の列で出す帳票。参照専用。

| 列 | 内容 |
|---|---|
| 予約中の原価／粗利算出用単価／付属原価／上代／参考売価／購入単価 | 商品金額変更予約（待機中）の変更後の値。空欄は「その項目は変えない」 |
| 商品金額適用予定日 | 商品金額変更予約の適用日（購入単価の予約も同じ日） |
| 予約中のランク１〜６ | ランク単価適用予定日からのランク単価。今と同じ値でも出る。日付があって空欄ならその日から単価なし |
| ランク単価適用予定日 | ランク別商品単価の、次に変わる日（今日より後の最も早い適用開始日） |

- 出るのは次に変わる日の分だけ。その先の予定は商品マスタ → 編集 → 販売／価格設定の「予約中の単価（未来適用）」で見る
- 2つの適用予定日は登録先が別なので、別の日付になることがある
- 「原価」の列の中身は商品マスタの「標準仕入単価」（2026-09-25 に、この画面だけ「下代」から表示名を変えた）
- 購入単価は外貨建ての値そのまま（通貨は右隣の通貨名）。予約中の購入単価も同じ
- 過去日の待機中の予約もそのまま出る（適用バッチが止まっている印。§6-2）
- 同じ商品に待機中の予約が2件あると、その商品が2行になる（通常は起きない。起きたら予約一覧で片方を取消す）
- 色は画面だけで、Excel には出ない。Excel の列の並びは画面の表示順
- 予約の列は反映されると空になる（ランク単価は適用開始日の 0:00、商品金額は 0:25）
- 検索欄は無い。全件を Excel に出して絞る

**その他**

| 画面 | 場所 |
|---|---|
| 得意先単価ランク予約（一覧） | マスタ設定 → 取引先 → 得意先単価ランク予約（`/customer-price-rank-schedules`） |
| 商品金額変更予約（一覧） | マスタ設定 → 商品・分類 → 商品金額変更予約（`/product-price-change-schedules`） |
| 営業担当予約 | マスタ設定 → 取引先 → 営業担当予約（`/sales-rep-schedules`） |
| トラスコ支店別営業担当予約（一覧） | EDI連携 → トラスコ中山 のダッシュボードのタイル（`/edi-trusco-branch-schedules`）。サイドバーには出ない |
| ランク別商品単価 | 商品マスタ → 編集 → 販売／価格設定。「現在の単価（有効中）」「予約中の単価（未来適用）」「単価履歴」の3区分で見える |
| 特単 | マスタ設定 → 取引先 → 得意先・配送先特別単価一覧。現在／予約中の区別表示は無い。「過去の単価データは表示しない」で絞れる |

### 5-2. 確認SQL（読み取り専用）

**A. 予約4機能の状態**（待機中と、直近1週間の適用・取消）

```sql
SELECT '1_単価ランク予約' AS kind, status, effective_date, COUNT(*) AS cnt, MAX(created_at) AS last_created, MAX(applied_at) AS last_applied
FROM customer_price_rank_schedules
WHERE deleted_at IS NULL AND (status = 1 OR effective_date >= CURDATE() - INTERVAL 7 DAY)
GROUP BY status, effective_date
UNION ALL
SELECT '2_商品金額予約', status, effective_date, COUNT(*), MAX(created_at), MAX(applied_at)
FROM product_price_change_schedules
WHERE deleted_at IS NULL AND (status = 1 OR effective_date >= CURDATE() - INTERVAL 7 DAY)
GROUP BY status, effective_date
UNION ALL
SELECT '3_トラスコ支店別担当予約', status, effective_date, COUNT(*), MAX(created_at), MAX(applied_at)
FROM edi_trusco_branch_sales_rep_schedules
WHERE deleted_at IS NULL AND (status = 1 OR effective_date >= CURDATE() - INTERVAL 7 DAY)
GROUP BY status, effective_date
UNION ALL
SELECT '4_営業担当予約', status, effective_date, COUNT(*), MAX(created_at), MAX(applied_at)
FROM sales_rep_change_schedules
WHERE deleted_at IS NULL AND (status = 1 OR effective_date >= CURDATE() - INTERVAL 7 DAY)
GROUP BY status, effective_date
ORDER BY kind, effective_date, status;
```

status は 1=待機中、2=適用済み、9=キャンセル。

**B. 日付で切り替わる単価の未来分**

```sql
SELECT 'ランク別商品単価' AS kind, valid_from, COUNT(*) AS cnt, COUNT(DISTINCT product_id) AS products, MAX(created_at) AS last_created
FROM product_prices
WHERE deleted_at IS NULL AND valid_from > CURDATE()
GROUP BY valid_from
UNION ALL
SELECT '特単', valid_from, COUNT(*), COUNT(DISTINCT product_id), MAX(created_at)
FROM customer_delivery_product_prices
WHERE deleted_at IS NULL AND valid_from > CURDATE()
GROUP BY valid_from
ORDER BY kind, valid_from;
```

**C. 適用漏れ**（適用日を過ぎたのに待機中のまま）。10/1 の朝にすべて 0件であること

```sql
SELECT '1_単価ランク予約' AS kind, COUNT(*) AS overdue FROM customer_price_rank_schedules WHERE deleted_at IS NULL AND status = 1 AND effective_date <= CURDATE()
UNION ALL
SELECT '2_商品金額予約', COUNT(*) FROM product_price_change_schedules WHERE deleted_at IS NULL AND status = 1 AND effective_date <= CURDATE()
UNION ALL
SELECT '3_トラスコ支店別担当予約', COUNT(*) FROM edi_trusco_branch_sales_rep_schedules WHERE deleted_at IS NULL AND status = 1 AND effective_date <= CURDATE()
UNION ALL
SELECT '4_営業担当予約', COUNT(*) FROM sales_rep_change_schedules WHERE deleted_at IS NULL AND status = 1 AND effective_date <= CURDATE();
```

**D. ランク別商品単価の期間の重なり**（10/1 に有効な行が2行以上ある商品×ランク）。0件であること

```sql
SELECT COUNT(*) AS overlapping_pairs
FROM (
    SELECT product_id, price_rank_id
    FROM product_prices
    WHERE deleted_at IS NULL
      AND valid_from <= '2026-10-01'
      AND (valid_to >= '2026-10-01' OR valid_to IS NULL)
    GROUP BY product_id, price_rank_id
    HAVING COUNT(*) > 1
) x;
```

**E. 特単の期間の重なり**（同じ得意先・配送先・商品・数量条件で、10/1 に有効な行が2行以上）。§7-1

```sql
SELECT c.customer_code, p.product_code, x.delivery_id, x.quantity_condition_type,
       COUNT(*) AS rows_cnt,
       GROUP_CONCAT(CONCAT(x.id, ':', x.valid_from, '~', IFNULL(x.valid_to, ''), ' ', CAST(x.unit_price AS DECIMAL(12,0)), '円') ORDER BY x.id SEPARATOR ' / ') AS detail
FROM customer_delivery_product_prices x
JOIN customers c ON c.id = x.customer_id
JOIN products p ON p.id = x.product_id
WHERE x.deleted_at IS NULL
  AND x.valid_from <= '2026-10-01'
  AND (x.valid_to >= '2026-10-01' OR x.valid_to IS NULL)
GROUP BY x.customer_id, c.customer_code, x.delivery_id, x.product_id, p.product_code, x.quantity_condition_type, x.min_quantity
HAVING COUNT(*) > 1;
```

**F. 次の日付で単価なしになるランク**（今は単価があるのに、次の適用開始日の単価が空欄か行が無い）。
ランク別商品単価の CSV 取込の後に流す。9/25 時点の本番は 0件。出たものが意図どおりか（そのランクを本当にやめるのか）は先方が決める

```sql
SELECT p.product_code, p.product_name, n.next_from, cur.price_rank_id, cur.unit_price AS current_price
FROM (SELECT product_id, MIN(valid_from) AS next_from
      FROM product_prices WHERE valid_from > CURDATE() GROUP BY product_id) n
JOIN product_prices cur ON cur.product_id = n.product_id
     AND cur.valid_from <= CURDATE() AND (cur.valid_to >= CURDATE() OR cur.valid_to IS NULL)
     AND cur.unit_price IS NOT NULL
LEFT JOIN product_prices nx ON nx.product_id = n.product_id
     AND nx.price_rank_id = cur.price_rank_id AND nx.valid_from = n.next_from
JOIN products p ON p.id = n.product_id
WHERE nx.unit_price IS NULL
ORDER BY p.product_code, cur.price_rank_id;
```

---

## 6. 困ったとき

### 6-1. CSV 取込でエラーになった

入口はデータ管理 → インポート → CSVインポート。10/1 まわりで使うのは次の4つ。

**4つに共通すること**

- 1行でもエラーがあると **全件取り消し**（何も登録されない）
- 画面に出るエラーは **最初の1行だけ**。ほかにも不正な行が残っていることがある
- 処理時間の上限は 300秒
- 取込の履歴・詳細・エラーファイルは **取り込んだ本人のアカウントでしか見えない**。
  代わりの担当者が見るときは DB の `csv_imports`（取込ごとの記録）と `csv_import_logs`（エラー行）を見る

**取込ごとに最初に疑うこと**

| 取込（Step1 の選択肢） | 最初に疑うこと |
|---|---|
| 得意先・配送先特別単価マスタ | 確認画面ではほとんど検証されないので、**「エラー0」でも実行で全件失敗しうる**。E002・E003・E004 は得意先・商品・配送先のコードが見つからない（無効化したものも「見つからない」になる）。E010 は「単価がマイナス」と「処理区分1で、同じ条件・同じ開始日の行が既にある」の2つの意味があるので、メッセージ本文で見分ける |
| ランク別商品単価マスタ | E002 は商品コードが無いか無効。E011 は同じ商品コード×適用開始日の行が2行ある。商品コード×適用開始日が既にあれば、新規ではなく単価と備考の更新になる |
| 得意先単価ランク変更予約 | E008 は同じ得意先×分類（標準ランクは得意先）に待機中の予約が既にある。予約一覧で取消してから取り込み直す。区分は「標準」「品目別」「解除」の3つ |
| 商品金額変更予約 | E008 は同じ商品に待機中の予約が既にある（どの項目の予約でも商品ごとに1件まで）。変えない項目は空欄にする。0 を入れると「0円に変える」になる |

特単の処理区分: 1=新規／価格改定（直前の1件を自動で終わらせる）、2=期間限定の追加（既存は触らない）、3=更新（キーが完全に一致する行）、4=削除。

### 6-2. 予約の適用バッチが動かなかった

- **気づき方**: 予約一覧の警告、または §5-2 の C が1件以上。メールなどの自動通知は無い
- **影響**:
  - マスタが古いまま残る
  - 単価ランクは受注入力が出荷日で先読みするので、新しい受注の単価は正しく出る
  - 商品金額には先読みが無いので、原価・上代などが古いまま使われる
  - 担当の受注残補正も走らない
- **戻り方**: 翌日のバッチが、過去日の待機中予約も拾って適用する（自動で追いつく）
- **手で流すかどうか**: 酒井（または開発責任者）が判断する。手順は §8-1
- **ログ**: 既定では `storage/logs/laravel.log`（本番の設定は未確認）

### 6-3. 10/1 以降に単価がおかしい

| 症状 | 最初に見るもの |
|---|---|
| 特単が古いまま | §7-1。§5-2 の E |
| ランクの単価が出ない | 商品マスタの販売／価格設定で、10/1 付の単価が登録されているか、そのランクが空欄になっていないか |
| 特単を終わらせた口座で単価が空欄 | §7-2・§7-4。得意先の単価ランクが空欄か、その商品にランクの単価が無い |
| ランクの単価が古いまま | §5-2 の D（0件のはず）。10/1 付の単価が登録されているか |
| ヨドバシの受注が旧単価 | 受注日基準なので仕様どおり（§1-3） |

---

## 7. 既知の注意点（2026-09-24 時点・09-30 追記）

### 7-1. 特単は、前の特単を自動で終わらせない

- 画面からの登録と CSV の処理区分2は、ほかの特単の適用終了日を変えない。処理区分3は指定した行そのものを直すだけ。処理区分1も、終わらせるのは直前の1件だけ
- 同じ得意先・配送先・商品・数量条件で有効な特単が2行あると、**受注入力でどちらが使われるかは決まっていない**
  （`CustomerDeliveryProductPriceRepository::getPrice()` は数量条件でしか並べていない）。
  得意先別価格一覧は適用開始日が新しい方を出すので、**帳票と受注入力で単価が食い違うことがある**
- 9/24 時点の本番で、上州屋（036021）の3商品がこの状態。**対応は未定（酒井が確認中）**

| 商品コード | 10/1 に有効な特単 | 受注入力と同じ条件で先に返る行 |
|---|---|---|
| 00005019 | 869円（4/3〜 終了日なし）と 1,254円（10/1〜。9/15 登録） | 869円（古い方） |
| 00005293 | 2,904円（4/3〜 終了日なし）と 2,568円（秋季セール企画 8/26〜11/8） | 2,904円 |
| 00005295 | 4,389円（4/3〜 終了日なし）と 4,188円（秋季セール企画 8/26〜11/8） | 4,389円 |

  9/24 時点では 00005200・00005201 もセール期間と重なっている（旧行は 9/30 で終わるので 10/1 には解消）。
  8/26 以降の該当受注 4行は、いずれも手入力の単価で登録されていて、この重なりによる実害は見つかっていない

### 7-2. EC 用の口座 099974・099975 には特単もランクも無い

9/23 に行った得意先指定の特単終了（第2弾）の作業で判明し、依頼元に確認中（9/23 時点）。
EC の受注は CSV の単価を使うので実害は出にくいと見ているが、未検証。

### 7-3. 得意先別価格一覧と受注入力で、ランク別商品単価の選び方が違う

ランク別商品単価の期間が重なったとき、帳票は古い行、受注入力は新しい行を採る。
登録時の自動の締めがあるので通常は重ならない（9/24 時点の本番で 0件）。§5-2 の D が 0件なら影響しない。

### 7-4. 特単を終わらせた口座で、10/1 から単価が出ない9行（9/25 時点）

9/24 依頼の特単終了（第3弾）の作業で判明した。特単は 9/30 で終わるが、その後に使うランクの単価が無い。
受注入力では単価欄が空になり、手入力になる。**依頼元の意図どおりかは未確認**（9/25 時点）。

| 得意先 | 商品 | 原因 |
|---|---|---|
| 099908 ※社販専用8%口座 | 特単のあった8商品すべて（HYT-4L・GIC-DLIC-01 ほか） | 得意先の単価ランクが空欄。同じ8商品の特単を第2弾で終わらせた 099910 ※社販専用10%口座 はランク1 |
| 099990 ９９９９０海外営業贈呈口座 | 00002539 ﾃﾞﾝﾁ | ランク別商品単価が1行も無い（特単は0円だった） |

9/24 のローカルDBで確認したもので、本番の得意先マスタ・単価は見ていない。
問い合わせが来たら、ランクを付けるか（得意先マスタ）単価を登録するかは先方が決めることなので、記録して 10/1 に酒井へ回す。

9/30 追記: 本番 9/30 早朝のデータで数え直すと、9/30 で得意先単位の特単が切れて 10/1 から単価が空欄になる組（有効な得意先・商品）は **359組**あった（得意先にランクが無い 358組・そのランクに単価が無い 1組）。
大半は BE（食品）の得意先で、9/16 の先方の特単CSV取込（2,407行を 9/30 で終了）の後に、10/1 付の特単を一部の商品だけ登録した差分。上の9行も含む。
7月以降に注文があった組は4組（048040 近江屋 3・048046 東京ジャーミィ 1）で、10/1 以降出荷の受注残は0件。一覧はリハーサル記録（§9）の README §5-3。

### 7-5. 営業担当予約バッチが、出荷済みの受注の担当まで書き換えていた（2026-09-30 修正・本番デプロイ済み）

- 受注残の付け替えで、受注ヘッダの status（未納・分納中）を見て受注を選んでいた。新システムでは受注ヘッダの status は出荷後も更新されないので、**予約の得意先の受注を、出荷済みかどうかに関係なく書き換えていた**。逆に、出荷予定日が空欄の受注残は付け替えから漏れていた
- 5/18 の #50018（出荷予定日を過ぎた未出荷受注も対象に加える改修）から。**5/19 と 6/1 の適用で、出荷済みの 413受注・415行の担当が書き換わっている**
- 売上の担当は出荷指示の時点で受注からコピーされ、後から引き直されないので、**売上・売上系の帳票・請求・S3 連携には影響していない**。食い違って見えるのは受注照会・受注修正の担当欄と、受注一覧 Excel の担当列（過去の期間を出したとき）
- 修正: 受注残を「出荷指示前の未納・分納中の明細を1行以上持つ受注」とし、出荷予定日は問わない（`2277fd90`）。10/1 の適用では、付け替えは 15,684行 → 756行になる（リハーサルの実測）
- 5/19・6/1 の 415行を売上の担当へ戻す SQL は用意してあるが、**本番では流していない**（流すかは未定）。「過去の受注の担当が売上と違う」と聞かれたらこれ。記録は §9

---

## 8. 仕組み（開発者向け）

### 8-1. 予約4機能

| 機能 | テーブル | コマンド | 時刻 | 処理の本体 | メニューID |
|---|---|---|---|---|---|
| ①得意先単価ランク | `customer_price_rank_schedules` | `customer-price-rank:apply-schedules` | 0:05 | `CustomerPriceRankScheduleService` | 162 |
| ③トラスコ支店別担当 | `edi_trusco_branch_sales_rep_schedules` | `edi-trusco-branch:apply-schedules` | 0:15 | `EdiTruscoBranchScheduleService` | 021（EDI-トラスコ中山と共有） |
| 営業担当 | `sales_rep_change_schedules` | `sales-rep:apply-schedules` | 0:20 | `ApplySalesRepSchedulesCommand`（コマンド内に実装） | 103 |
| ②商品金額 | `product_price_change_schedules` | `product-price:apply-schedules` | 0:25 | `ProductPriceChangeScheduleService` | 166 |

**共通**

- スケジュールは `routes/console.php:79-115`（`onOneServer()`・`withoutOverlapping()`）
- 対象は `status = 1 AND effective_date <= 処理日`。過去日の待機中も拾い、適用済みは二度適用しない
- 予約1件ごとに1トランザクション。失敗した予約だけ飛ばして続ける
- 手で流すとき: `php artisan <コマンド> --dry-run` で内容を確かめてから、`--dry-run` を外して流す。
  `--date=YYYY-MM-DD` は処理日を変える。**未来の日付を入れるとその日までの予約が前倒しで適用される**

**先読み（適用前でも新しい値を使う）**

| 機能 | 先読みする場所 |
|---|---|
| ①得意先単価ランク | 受注入力。`OrderAssist::getPriceRank()`（`app/Services/Logic/User/Order/OrderAssist.php:470-`）が出荷日時点の待機中予約を見る |
| ③トラスコ支店別担当 | EDI 受注反映のみ。`EdiTruscoBranchRepository::getEffectiveBranchSalesRep()` |
| 営業担当 | EDI 受注反映・業務データ取込・受注残補正。`CustomerCategorySalesRepRepository::getEffectiveSalesRep()` |
| ②商品金額 | なし |

**遡って直すもの**

| 機能 | 内容 |
|---|---|
| ②商品金額 | 粗利算出用単価だけ。未出荷（未納・分納中）の受注明細の `cost_price`（`ProductPriceChangeScheduleService::applyToOrderDetails()`）。件数は `retroactive_count` に残る |
| ③トラスコ支店別担当 | 未出荷の受注のうち、支店が1つに決まるものの担当（`EdiTruscoBranchScheduleService::applyToBackorders()`） |
| 営業担当 | 未出荷の受注の担当（`ApplySalesRepSchedulesCommand::updateBackorderSalesReps()`）。未出荷＝出荷指示前（`shipping_instruction_status` が1または NULL）で未納・分納中の明細を1行以上持つ受注。出荷予定日は問わない（空欄も対象。2026-09-30 修正・§7-5）。③の支店担当が効く受注は上書きしない |
| ①得意先単価ランク | なし |

### 8-2. 日付で切り替わる単価

- **ランク別商品単価**: 登録・更新・削除のたびに `ProductPricePeriodService::rebuild()` が、商品単位で適用開始日の並びから適用終了日を計算し直す。
  呼び出し元は画面の `ProductPriceService` と CSV の `ProductPriceProcessor`。
  解決は `ProductPriceRepository::getPrice()`（重なったときは適用開始日が新しい方）
- **特単**: 画面の `CustomerDeliveryProductPriceService` は自動で締めない。CSV の `CustomerDeliveryProductPriceProcessor` は処理区分1のときだけ直前の1件を締める。
  解決は `CustomerDeliveryProductPriceRepository::getPrice()`（重なったときの順位付けが無い）
- **優先順位**: `OrderAssist::computeUnitPrice()`（`OrderAssist.php:309-368`）
- **保存済み明細の据え置き**: `app/Livewire/Partials/User/Order/EntryOrderDetailRow.php:181-185`（出荷日を変えたとき）。数量を変えたとき（同ファイルの `changeOrderQty()`）は保存済みでも取り直す

### 8-3. 得意先別価格一覧（report1）

- proc は `database/view/01_000_得意先別価格一覧｜proc_customer_price_list.sql`。予約の単価3列は Step 3a〜3d
- 変化日の候補: ランク別商品単価の未来の開始日と終了日の翌日、得意先単位（配送先なし・数量条件なし）の特単の同じもの、単価ランク予約の適用日
- 候補日ごとに単価・ランク表記・販売不可を当日と同じ組み立てで計算し、当日と違う最初の日を出す
- 商品金額予約の3列は Step 0c で別に計算する（単価の候補日には混ぜない）
- 列定義・proc は `ReportXxSeeder` の管轄。デプロイは `php artisan db:seed --class=ReportXxSeeder`（migration ではない）

### 8-4. CSV 取込

- 4つとも `CsvImportController::executeInventoryImport()` で実行する（全件取り消し・最初のエラーで停止）
- 確認画面で専用の検証を走らせる一覧（`CsvImportController.php:1088`・`:1300`）に特単（`customer_delivery_product_prices`）が入っていない。これが「確認画面は通るのに実行で失敗する」原因
- 時間上限は `config/csv_tables.php` の `execution_time_limit`（300秒）
- 履歴・詳細・エラーファイルは実行ユーザーに絞っている（`app/Services/Logic/User/CsvImport/CsvImportService.php:475`・`:612`・`:649`）

### 8-5. 商品一覧（report200）

- VIEW は `database/view/200_001_商品一覧｜v_product_list_report.sql`。予約の列は `scheduled_*`（2026-09-25 #51750・84fdb7f3）
- ランク単価の予約: 商品ごとに valid_from が当日より後の最小の日（`npf`）を求め、各ランクはその日の行の単価を出す（同じ日に複数あれば id 最小）。
  期間の締め（`ProductPricePeriodService::rebuild()`）が商品単位なので、ランクごとに次の行は探さない（行の無いランクも同じ日に単価が切れる）
- 商品金額の予約: `product_price_change_schedules` の status=1 を商品で結合する。適用日では絞らない。
  待機中が2件あると行が2行になる（report1 のようにエラーにはできないので、黙って1件を選ばずに見せる）
- 上代はオープンプライスでも置き換えない（report200 の今の上代が生値のため。report1 は0に置き換える）
- 列定義・VIEW は `ReportXxSeeder` の管轄。デプロイは `php artisan db:seed --class=ReportXxSeeder`（migration ではない）
- 列の色 `bg-amber-50` は tailwind の safelist に無く、CSV 取込の書式説明の画面で使われているおかげでビルドに入っている（report1 も同じ）

---

## 9. 関連資料（git 管理外。酒井のPCにある）

| 場所 | 内容 |
|---|---|
| `docs/reservation-series-manual/reservation-series-map.html` | 利用部門向けの機能マップ（マニュアルの元資料） |
| `docs/archive/reservation-series/reservation-series-deploy/01-デプロイ手順書.md` | 本番デプロイの記録。§6 の残作業の表は 2026-09-30 に更新してアーカイブした |
| `docs/archive/reservation-series-batch-rehearsal-20260930/` | 10/1 前日のリハーサル（本番 9/30 早朝データで 10/1 のバッチを先に流した記録）。10/1 朝の確認SQL `prod-check-1001-morning.sql` もここ |
| `docs/archive/sales-rep-schedule-backorder-scope-fix/` | 営業担当予約バッチの受注残の定義修正（2026-09-30・§7-5）。過去分 415行を戻す SQL（未実施）もここ |
| `.kiro/specs/customer-price-rank-schedule/` ほか | 各機能の要件・設計・タスク |
| `.kiro/steering/schedule-feature-standards.md` | 予約機能の実装標準 |
| `docs/archive/reservation-series/` | 企画・検討・検証の記録 |
| `docs/archive/report200-schedule-columns/` | 商品一覧（report200）の予約列の追加（2026-09-25・#51750）の方針・E2E の記録 |
| `docs/archive/special-price-bulk-end-20260930/`・`docs/archive/special-price-end-by-customer-20260923/`・`docs/archive/special-price-end-by-customer-20260924/` | 特単の一括終了 第1弾（9/1・共通単価商品31件）・第2弾（9/23・7得意先 1,448行）・第3弾（9/24 依頼・3得意先 1,282行）の記録 |
