参考送料・確定送料の二系統からなる送料モデルを、システム横断でまとめます。送料は購入者向け機能・注文管理・出荷ワークフローにまたがるため、本書がその集約点です。
本書は
specs/40_送料計算モデル変更 実装手順書.md(Ver.1.5) を土台に、現行コード(orders/mg_orders/mg_workflow/cart/accounts)と照合して再構成したものです。
関連: 購入者向け機能 / 注文管理 / 出荷ワークフロー / design/データベース設計
送料はお届け先(Destination)の属性と箱数で決まる。消費税は内税(管理者設定価格に込み)。
| 要素 | フィールド | 値と扱い |
|---|---|---|
| 配送方法 | Destination.delivery_method |
TEWATASHI(直接手渡し)なら送料 0。YAMATO は下記の区分単価で計算 |
| 送料区分 | Destination.shipping_zone |
NORMAL(通常)→ NORMAL_SHIPPING_FEE、ENCHI(遠地)→ REMOTE_SHIPPING_FEE |
| 箱数 | — | 梱包した(またはシミュレートした)箱の数 |
送料単価(config/settings.py): NORMAL_SHIPPING_FEE=1200、REMOTE_SHIPPING_FEE=1800。送料 = 箱単価 × 箱数(手渡しは 0)。
Order は 2 つの送料フィールドを持つ(データベース設計 §2.3)。
| 参考送料 | 確定送料 | |
|---|---|---|
| フィールド | provisional_shipping_fee(必須) |
final_shipping_fee(nullable) |
| 意味 | 注文時の見込み額(購入者提示用) | 梱包確定後の最終額 |
| 箱数の出どころ | カート内容からの梱包シミュレーション | 宛先の受付済み注文に紐づく中身入り対象箱数 |
| 算出箇所 | orders.views._calculate_provisional_shipping_fee |
mg_workflow.services.shipping_fee.finalize_shipping_fee_if_all_packed |
Order のプロパティ(orders/models.py):
is_shipping_fee_finalized … final_shipping_fee is not Noneshipping_fee … 確定があればそれ、無ければ参考値を返すtotal_price … items_subtotal + shipping_fee_calculate_provisional_shipping_fee)orders/views.py。配送グループ(お届け先ごとのカート)単位に算出する。
mg_workflow.api_packaging._pack_bags()(出荷ワークフローの自動梱包と同じアルゴリズム)で梱包をシミュレートして見込み箱数を得る(PACKAGE_MAX_WEIGHT_KG=20.0 / DEFAULT_BOX_WEIGHT_KG=0.5)。算出・保存タイミング:
order_confirm): 表示用に各グループの参考送料・参考合計(group_total_price_provisional / grand_total_provisional)を計算。order_create): 確定した参考送料を Order.provisional_shipping_fee に保存。order_update / 管理者 mg_orders.order_edit): 購入者側は新規注文の更新成功時に変更後数量・お届け先で参考送料を再計算する。管理者側は商品内容またはお届け先が変わった場合に参考送料を再計算する。いずれも再計算時は final_shipping_fee を None にリセット。finalize_shipping_fee_if_all_packed)mg_workflow/services/shipping_fee.py。発送管理のパッケージステータス更新(PackageStatusUpdateView)に加え、パッケージングボード側の箱作成・箱削除・箱内商品の追加/削除/移動・予約消費・梱包提案適用(mg_workflow/api_packaging.py)でも、対象宛先ごとに操作後に呼ばれる。
PACKAGING)の Package が1つでも残るなら、中身の有無に関わらず、宛先の受付済み(ACCEPTED)注文の final_shipping_fee を None にリセットする(Issue #8: 空の準備中箱を追加しただけでも未確定に戻る)。Package(中身入りのもの。過去に発送済み/手渡し済みとなった注文の箱は含めない)が1つもなければ、同様に None にリセットする。Package(中身入りのもの)がすべて「発送準備完了」以上(READY_TO_SHIP/LABEL_PRINTED/SHIPPED/DELIVERED)なら、対象箱数 × 区分単価(手渡し 0)を計算する。箱数は中身入りの対象箱のみでカウントし、空箱は箱数に含めない。final_shipping_fee=0 として確定済みにする。これにより請求書で宛先単位送料が注文数分だけ重複加算されない。final_shipping_fee が None(未確定)に戻るのは次の場合。
orders.order_update)。注文側で直接 None を代入。mg_orders.order_edit の product_content_changed。単価のみ変更は対象外)。注文側で直接 None を代入。PACKAGING)の箱が残る状態になった(差し戻し等)。いずれも finalize_shipping_fee_if_all_packed() 経由(Issue #8 対応済み: 発送管理のステータス更新時に限らず、パッケージングボード側の操作でも再評価される)。
is_shipping_fee_finalized で「送料未確定」バッジの有無を切り替え。generate_invoice_pdf): いずれかの注文で送料未確定なら any_fee_not_finalized=True とし、「送料(未確定)」「合計(暫定)」を明記。全確定時のみ最終合計を表示(購入者向け機能 §7)。cart.cart.Cart.get_shipping_fee() は箱数を考慮しない簡易送料(手渡し 0/遠地・通常の区分単価をそのまま 1 箱分)で、カート表示用。注文確認・確定で扱う「参考送料」は、上記のとおり梱包シミュレーションで箱数を反映した値であり、別系統である点に注意。
元資料: specs/40_送料計算モデル変更 実装手順書.md (Ver.1.5) を現行コード(orders/models.py, orders/views.py, mg_orders/views.py, mg_workflow/api_shipping.py, cart/cart.py, config/settings.py)と照合して再構成
関連: README, specs棚卸し表, Issue #1