# harukaze に「発注候補」コマンド（order-suggest）を追加する依頼

## 1. 目的
`python -m harukaze order-suggest --date YYYY-MM-DD` を実行すると、基準日の時点で有効在庫が発注点を下回っている「販売中」の商品と、その発注数が CSV で標準出力に出るようにします。並びは仕入先ごとで、列は既存の発注書 CSV（`po-export`）と同じ並びに「保留」を1列足したものにします。そのまま発注書づくりに使えることが目的です。

## 2. 背景
- 総務・購買の購買担当は、毎週月曜に発注点を切っている商品を目で探して発注書を作っています。時間がかかるうえに見落としが出ています。
- このコマンドで、指定した日（主に月曜日）の時点の発注候補と発注数を一覧にし、仕入先ごとの発注書づくりにそのまま使います。
- コマンドの形（`order-suggest --date`）は、担当者と情シスで決めてあります。

## 3. 触ってよい範囲
README.md の「開発の決まり」に従います。
- 新規ファイル: `harukaze/order_suggest.py`（候補の計算と行づくり）、`tests/test_order_suggest.py`（テスト）。この2つだけ作ってよいです。
- 既存で変更してよいもの: `harukaze/cli.py`（`@command` でサブコマンドを登録するだけ）、`README.md`（「コマンド一覧」の表に `order-suggest --date YYYY-MM-DD` の行を1行足すだけ）。

## 4. やらないこと
- `harukaze/orders.py`、`harukaze/stock.py`、`harukaze/store.py`、`harukaze/csvout.py`、`harukaze/__init__.py`（バージョン番号も含む）、`harukaze/__main__.py`、`data/` の中のファイル、既存のテストファイル（`tests/helpers.py` も含む）は変更しません。
- 3. に挙げた以外のファイルやフォルダは作りません。消しもしません。実行結果をファイルに保存して作業フォルダに残すこともしません。
- コミットはしません。
- 仕入先ごとの小計行や合計行は出しません。発注番号もつけません。ファイルへの書き出しもしません（出力は標準出力だけです）。
- 最低発注金額（suppliers.csv の「最低発注金額」）による判定や絞り込みはしません（「保留」列については 5. の要件9を参照）。
- 既存の `po-export` の動きと、その出力の列は変えません。

## 5. 決まり（要件）
1. `cli.py` の `COMMANDS` に、`@command` でサブコマンド `order-suggest` を登録します。`--date`（YYYY-MM-DD）は必須の引数とし、既存の `_date_arg` を使います。`--data-dir` は親パーサーから自動で付きます。
2. 対象にするのは、products.csv の「状態」が「販売中」の商品だけです。「終売予定」や、それ以外の状態の商品は候補にしません。
3. 有効在庫は「基準日時点の現在庫 + 発注済みで未入荷の数 − 引当済みの数」です。既存の関数をそのまま使って計算します。現在庫は `stock_as_of(store, 基準日)`、未入荷は `open_orders(store)` が返す行のうち、その商品の数量を合計したものです。引当は `allocated_qty(store, 商品コード)`、合算は `available_stock` を使います。
4. 候補にするのは、有効在庫が発注点より小さい（未満の）商品です。有効在庫が発注点と等しい商品は候補にしません。
5. 発注数は、まず「発注点 × 2 − 有効在庫」を出し、それを `orders.py` の `round_up_to_case(数, 入数)` で入数の倍数に切り上げた値です。
6. 1行の列は、`orders.py` の `PO_COLUMNS` と同じ並び（仕入先コード、仕入先名、商品コード、商品名、入数、発注数、仕入単価（税抜）、金額）の末尾に「保留」を1列足した9列です。金額は「発注数 × 仕入単価（税抜）」です。
7. 行の並びは、仕入先コードの昇順で、同じ仕入先の中では商品コードの昇順です。
8. 出力は既存の `csvout.write_csv` で書きます（UTF-8、改行 LF、見出し行あり）。候補が0件のときは見出し行だけを出し、終了コードは 0 にします。
9. 「保留」列の中身は未定です（担当者からは「末尾に保留を足す」とだけ回答があり、何を入れるかの説明がありません）。今回は列見出しだけを足し、値はすべての行で空欄にしてください。中身を推測して埋めてはいけません。完了の報告で「保留列の中身は未定のため空欄」と明記してください。
10. 商品の仕入先コードが suppliers.csv に無いなど、既存の `store.supplier` / `store.product` が `KeyError` を出す場合は、既存の `main` のエラー処理に任せます。標準エラーに「エラー: …」と出て、終了コードは 1 になります。

## 6. 注意点
- `PO_COLUMNS` はリストです。「保留」を足すときは新しいリストを作ってください。`PO_COLUMNS` そのものに追加（書き換え）すると、`po-export` の列まで変わってしまいます。
- `stock_as_of` は基準日を含みます（その日の入出庫も数えます）。既存の `stock` コマンドと同じ数え方です。
- `open_orders` と `allocated_qty` は日付を見ません。担当者の指示は「既存の関数をそのまま使う」なので、入荷予定日や発注日で絞り込まないでください。
- `stock.py` の関数名 `open_orders` は、ローカル変数の名前とぶつかりやすいので気をつけてください。`available_stock` の引数の順番は（現在庫, 未入荷数, 引当数）です。
- 発注点が 0 の商品も特別扱いしません。要件3〜5をそのまま当てはめます。同梱データでは、カッターナイフ（1014）が現在庫2・引当5で有効在庫 −3 になり、0 を下回るので候補に入ります。
- 商品の一覧は `store.products()` から取り、products.csv の並び（コード順ではありません）に頼らず、要件7のとおりに並べ替えてください。
- 新しいテストのデータは `tests/helpers.py` の `make_data_dir` で一時フォルダに作ってください（`tests/test_orders.py` を参考にしてください）。CLI を通したテストは、`tests/test_cli.py` の `run_cli` と同じやり方（作業フォルダで実行し、`PYTHONIOENCODING=utf-8` を付ける）に合わせます。Windows で実行されるので、出力に `\r` が混じらないことも確かめてください。
- 外部ライブラリは使いません（Python 3.11 以上の標準ライブラリだけを使います）。

## 7. 完了の条件と確かめ方
1. 作業フォルダで `python -m unittest discover -s tests` を実行し、既存のテストと新しく足したテストがすべて通ること。新しいテストでは、少なくとも次を確かめてください。
   - 有効在庫が発注点と等しい商品は出ないこと。
   - 未入荷を足すと発注点以上になる商品は出ないこと。
   - 引当を引くと発注点を下回る商品は出ること。
   - 「終売予定」の商品は出ないこと。
   - 発注数が入数の倍数に切り上がること。
   - 並び順と列（9列、末尾が「保留」で値は空欄）。
   - 候補0件のときに見出し行だけになること。
   - `--date` を省くと終了コードが 0 以外になること。
   - `--data-dir` を受け付けること。
2. 作業フォルダで `python -m harukaze order-suggest --date 2026-10-26` を実行すると（出力はファイルに保存しません）、終了コード 0 で、見出し1行と次の10行がこの順番で出ること（仕入先コード, 商品コード, 発注数, 金額。保留はすべて空欄）。
   - 201 さくら紙工（架空）: 1001 → 50, 19000 / 1022 → 50, 13000 / 1023 → 20, 10800
   - 202 みなと事務器（架空）: 1007 → 40, 18000 / 1009 → 18, 17640 / 1014 → 10, 2400
   - 203 あおば化成（架空）: 1004 → 80, 7600 / 1012 → 60, 7200
   - 204 こはる印刷（架空）: 1015 → 20, 17800 / 1018 → 15, 19500
   
   次の商品は出ないことも確かめてください。
   - 1019、1024: 有効在庫と発注点が等しい
   - 1006、1003: 未入荷を足すと発注点以上になる
   - 1011、1020: 終売予定
   - 1005: 10/28 の出庫は基準日より後なので数えない
3. `python -m harukaze po-export` の既存テストが変わらず通ること（`PO_COLUMNS` を書き換えていないことの確認です）。
4. `git status` で、変更・追加されたファイルが `harukaze/cli.py`、`README.md`、`harukaze/order_suggest.py`、`tests/test_order_suggest.py` の4つだけであること（`__pycache__` は .gitignore で除外されています）。
5. 完了の報告に、要件9の「保留列の中身は未定のため空欄」と書かれていること。

---

依頼文の外で、あなたにお伝えしたいことが2点あります。

- **「保留」列の意味が分かっていません。** 5問を使い切ってから出てきた答えなので、聞き返せていません。最低発注金額（仕入先ごとに 15,000〜50,000 円）に届かない仕入先の行に印をつける意味かもしれませんが、確認していません。そのため依頼文では「列見出しだけ足して値は空欄」にしてあります。この依頼文を渡す前に、担当者に意味を確認することをおすすめします。
- **発注点が 0 の商品は、決めた規則どおりに扱っています。** これは担当者に聞いていません。そのためカッターナイフ（1014）は、引当の分だけ有効在庫がマイナスになり、10個の候補として出ます。担当者が「発注点 0 = 発注しない商品」のつもりなら、規則を1つ足す必要があります。

分からない点があれば、変更せずに質問を列挙して終えてよい。
