riceshop のシステム構成・アプリケーション分割・URL ルーティング・ドメインモデル・主要な設計パターンを、開発者向けにまとめます。
本書は
specs/03_基本設計書.md(Ver.5.2) を土台に、現行コード(config/, 各 Django アプリ)と照合して再構成したものです。基本設計書との差分は本文中で明示します。
| 層 | 採用技術 |
|---|---|
| バックエンド | Django |
| フロントエンド | Bootstrap 5 / JavaScript(動的 UI は API 経由でバックエンドと非同期通信) |
| データベース | MySQL(config/settings.py の DATABASES は MySQL 固定。開発・本番とも MySQL 前提) |
| インフラ | Docker コンテナ。本番は traefik(リバースプロキシ)による HTTPS 化を想定 |
| 静的ファイル配信 | WhiteNoise(whitenoise.middleware.WhiteNoiseMiddleware) |
補助ライブラリとして bootstrap5、django_extensions、django.contrib.humanize、開発時の debug_toolbar を利用します(config/settings.py)。
機能ごとに責務を分割した Django アプリ群で構成されます。下表は config/settings.py の INSTALLED_APPS に登録された自作アプリと、その責務です。
| アプリ | 役割と責務 | モデル |
|---|---|---|
accounts |
人に関するデータ管理。顧客・お届け先の定義と認証(日本語 ID 対応) | User, Destination, CustomUnicodeUsernameValidator |
products |
モノの定義データ管理。品種・商品・在庫マスタ | ProductVariety, Product, Stock |
orders |
取引と物理的なモノの状態管理。注文・引当・袋・荷物などの中核モデル群 | Order, OrderItem, Bag, Allocation, Package, PackageItem, PackagePlanItem, EmptyBagStock, MillingRecord |
cart |
一時的な状態管理。セッションベースのショッピングカート | (モデルなし。cart/cart.py でセッション管理) |
system_settings |
システム全体の設定値管理。店舗情報・送料・各種係数・振込先口座 | SystemSetting, BankAccount |
sales_seasons |
販売年度マスタのデータ管理(Issue #17 Phase 4-A)。専用の URL・view は持たず、SalesSeason を保持するのみ |
SalesSeason |
year_end |
年度締め確認結果・実行記録のデータ管理(Issue #17 Phase 2/3b)。専用の URL・view は持たず、YearEndRun を保持するのみ |
YearEndRun |
dashboard |
管理者向け機能の統括(司令塔)。ToDo ダッシュボード・年度締めドライラン・デバッグ画面・データ移行・マニュアル管理 | (モデルなし。year_end を含む各アプリのモデルを操作) |
mg_orders |
【管理者】注文管理。一覧・受付・編集と関連 API | (モデルなし。orders を操作) |
mg_customers |
【管理者】顧客管理。一覧・新規作成・詳細編集と関連 API | (モデルなし。accounts を操作) |
mg_masters |
【管理者】マスタ管理。品種・商品・在庫の CRUD と関連 API | (モデルなし。products を操作) |
mg_workflow |
【管理者】出荷ワークフロー。袋詰管理・自動引当ボード・発送管理と API 群 | (モデルなし。orders を操作) |
common |
補助アプリ。共通テンプレートタグを提供(common/templatetags/common_extras.py の get_item フィルタ等) |
(モデルなし) |
基本設計書との差分:
commonは03_基本設計書.mdに未記載だが、INSTALLED_APPS(config/settings.py:44)に登録されている補助アプリ。models.py/views.pyは空のプレースホルダで、実体は共通テンプレートタグ。ordersのモデルは基本設計書が挙げるOrder/OrderItem/Allocation/Bag/Packageに加え、PackageItem/PackagePlanItem/EmptyBagStock/MillingRecordを実装で保持している。mg_*およびdashboardは独自モデルを持たず、accounts/products/orders/system_settings/sales_seasons/year_endのモデルを操作するビュー・API 層として機能する(例:dashboard.services.year_end.execute_year_end()は年度締め実行時にproducts.Productとsystem_settings.SystemSettingを更新し、year_end.YearEndRunを作成する)。year_end(Issue #17 Phase 2)はsystem_settingsと同じく、専用の URL・view を持たない「データ所有アプリ」。YearEndRun(年度締め確認結果のスナップショット)のみを保持し、画面・URL・集計/保存サービスはdashboard側(dashboard/views.py,dashboard/services/year_end.py)が持つ。配置判断の経緯は 検討用実装案「Phase 2: 年度切替ログモデル」節 を参照。sales_seasons(Issue #17 Phase 4-A)も同じ配置方針。SalesSeason(販売年度マスタ)のみを保持し、専用の URL・view は今回作らない。Phase 4-B でSystemSetting.active_season(FK, PROTECT)、Phase 4-C でproducts.Product.season(FK, PROTECT)から参照されるようになった。Order/Package等からの参照は Phase 4-D 以降。
accounts / products / orders / system_settings / sales_seasons / year_end)と、それを操作する管理 UI アプリ(mg_* / dashboard)を分離している。products(商品一覧)・cart(カート)・orders(注文)が担う。dashboard を入口に、mg_orders / mg_customers / mg_masters / mg_workflow が各領域を担当する。年度締め(year_end)のように、担当する管理 UI アプリを新設せず dashboard 自身が直接データ所有アプリを操作するケースもある。ルートは config/urls.py で定義され、管理者向け機能は dashboard 名前空間の下にネストされています。
/ → products.urls (購入者向け: 商品一覧など)
/accounts/ → accounts.urls (ログイン・認証)
/cart/ → cart.urls (カート操作 API)
/orders/ → orders.urls (注文確認・履歴・詳細)
/management/ → dashboard.urls (管理者向け入口)namespace='dashboard'
├ /management/ → ダッシュボード(ToDo)
├ /management/year-end/ → 年度締めドライラン・確認結果の保存(Issue #17)
├ /management/seasons/ → 販売年度一覧・新規作成(Issue #17 Phase 4-B2。編集・削除・状態変更は提供しない)
├ /management/debug/ → デバッグ画面
├ /management/import/ → 旧システムデータ移行
├ /management/manuals/ → オンラインマニュアル管理
├ /management/orders/ → mg_orders.urls (注文管理)
├ /management/customers/ → mg_customers.urls (顧客管理)
├ /management/masters/ → mg_masters.urls (マスタ管理)
└ /management/workflow/ → mg_workflow.urls (出荷ワークフロー)
/admin/ → Django 管理サイト
/__debug__/ → debug_toolbar(DEBUG 時のみ)
ポイント:
mg_*アプリはconfig/urls.pyから直接 include されず、すべてdashboard/urls.pyを経由して/management/配下にぶら下がる。管理者向け機能の入口をdashboardに集約する設計。
詳細なテーブル定義・リレーションは データベース設計 を参照してください。ここでは責務をまたぐ主要な関係のみ示します。
User(購入者)─ Destination(お届け先)は 1 対多。Order(注文)は User と Destination に紐づき、OrderItem を持つ。1 注文=1 お届け先。PaymentAdjustment(返金 / 次回支払いへ繰越 / 寄付)に記録する。繰越を選んだ金額は CustomerCreditTransaction の履歴として顧客別残高に反映し、次回以降の注文へ充当できる。Product は ProductVariety(品種)に属し、在庫は品種単位で Stock が玄米換算で管理する。Bag(物理的な袋)が Allocation を通じて OrderItem(注文明細)に引き当てられ、別途 PackageItem を通じて Package(箱)に格納される。Allocation(引当)と PackageItem(梱包)は同じ Bag を介した別リレーションである点に注意。orders/models.py の Order は次のステータスを持ちます。
| 区分 | 値(実装) |
|---|---|
注文ステータス (status) |
NEW 新規注文 / ACCEPTED 注文受付 / PACKED 梱包完了 / SHIPPED 発送済み / DELIVERED 手渡し済み / CANCELED キャンセル済み |
入金ステータス (payment_status) |
UNPAID 未入金 / PAID 入金済み |
payment_status は二値のまま維持し、部分的な繰越充当は UI 上で「繰越充当済み・残額あり」として区別します。繰越充当だけで請求残額が 0 円になった場合は PAID に更新します。繰越充当済み注文は、現金/振込入金との原資混在を避けるため、手動の payment_status 変更を拒否します。
Package(orders/models.py)のステータスは PACKAGING 準備中 / READY_TO_SHIP 発送準備完了 / LABEL_PRINTED 伝票印刷済み / SHIPPED 発送済み / DELIVERED 手渡し済み。箱ごとに lock_status(UNLOCKED 変更可 / APPEND_ONLY 追加のみ可 / LOCKED 変更不可)を持ち、自動梱包の挙動を制御します。
管理者向けの動的画面(自動引当ボード、発送管理、顧客編集、商品マスタ編集など)は、フロントエンド JS が司令塔となり、バックエンド API を非同期で呼び出して全状態を再取得・再描画する設計です。
| 画面 | フロントエンド | バックエンド API |
|---|---|---|
| 自動引当・パッケージングボード | mg_workflow/static/js/packaging_board.js |
mg_workflow/api_packaging.py |
| 発送管理 | mg_workflow/static/js/shipping_list.js |
mg_workflow/api_shipping.py |
| 顧客詳細・編集 | (顧客画面 JS) | mg_customers/api.py |
| 商品マスタ編集 | (マスタ画面 JS) | mg_masters/api.py |
| 注文編集 | (注文画面 JS) | mg_orders/api.py |
mg_workflow のみ API が api_packaging.py と api_shipping.py の 2 つに分割されている点に注意。
products.Stock が品種ごとに「総供給量」と「総注文量」を保持し、「現在在庫量」は両者の差分から動的計算する。dashboard.context_processors.stock_summary(dashboard/context_processors.py)が、購入者向け全ページに現在の在庫状況(カート内消費量を含む)を提供する。cart.context_processors(cart/context_processors.py)が扱う。Stock.total_ordered_kg を排他ロックして更新し、整合性を保証する。顧客別の繰越残高は残高テーブルを持たず、CustomerCreditTransaction.amount の合計で算出します。残高を増やす仕訳(キャンセル注文からの繰越、充当先キャンセルによる戻し)は正、注文への充当は負で記録します。
PaymentAdjustment(action=CARRY_OVER) と CustomerCreditTransaction(CARRY_OVER_FROM_CANCELED_ORDER) を同時に作成する。Order.total_price - 有効な繰越充当額。RETURNED_FROM_CANCELED_APPLIED_ORDER として顧客残高へ戻す。繰越充当で PAID になった注文は #11 の返金/繰越/寄付3択を通さない。order -> user の順で select_for_update() ロックを取り、同一注文のキャンセル/充当競合と同一顧客の並行充当を直列化する。Order は 2 つの送料フィールドを持ちます(orders/models.py)。
provisional_shipping_fee(参考送料)— 注文時にカート内容から梱包をシミュレーションして算出。final_shipping_fee(確定送料、nullable)— 管理者の梱包確定時に実際の箱数から自動計算。Order.is_shipping_fee_finalized(final_shipping_fee is not None)と Order.shipping_fee(確定があればそれを、なければ参考値を返す)プロパティで状態を判定します。確定送料の計算・更新は mg_workflow/api_shipping.py のパッケージステータス更新 API(PackageStatusUpdateView)が担い、内部の _finalize_shipping_fee_if_all_packed() が、宛先の受付済み注文に紐づく Package がすべて「発送準備完了」以上になった時点で箱数から最終送料を計算して対象注文の final_shipping_fee を更新します。同一宛先に複数の受付済み注文が含まれる場合、宛先単位の送料は1件の注文へだけ記録し、残りは 0 として確定済みにします(条件を満たさない場合は None にリセット)。注文の商品内容が変更された場合も final_shipping_fee は NULL に戻ります。
お客様管理番号 を PKG{package.id} 形式で発行し、伝票番号付き CSV の取り込み時のキーとする。/management/import/ で旧システムの UTF-8 SQL ファイルをアップロードし、User / Destination に取り込む。詳細は features/出荷ワークフロー および operations/データ移行 を参照してください。
元資料: specs/03_基本設計書.md (Ver.5.2) を現行コード(config/urls.py, config/settings.py, 各アプリ models.py / api*.py)と照合して再構成
関連: README, specs棚卸し表, Issue #1