商社・卸
位置づけ: Phase 2(保留)。卸の業務が固まっていないため 2026-09-02 で保留、9/9 で範囲外が確定。実装は削除済み
商社・卸モジュールは、商社(卸)を軸に販売店からの発注を管理する業務ドメインである。元請案件(販売店・エンドユーザー向け商談を扱う本体案件)とはエンティティを分離し、WholesaleCase を集約ルートとして発注・請求・入金を内包する独立したライフサイクルで運用する。
決済区分(前金/掛売/三社間/カード/その他)ごとに発注可否・請求可否を判定するワークフローエンジンを備え、案件は工程の進捗に応じてパイプライン列(WBucket)へ自動配置される。ワークフローエンジンで発注可否・請求可否を判定する構造は ValueOS の WorkflowEngine(canOrder / canInvoice)と同一の思想であり、case_settlements.settlement_type による決済区分管理にも対応する。決済区分の定義・審査観点・工程順序(FLOW)の詳細は /features/wholesale-settlement/ を正本とする。本ページでは、案件・卸金額マスタ・発注フォーム・受注〜入金・パイプライン・タスクを扱う。
スコープ/対象
Section titled “スコープ/対象”| 対象 | 内容 |
|---|---|
| 別軸案件 | WholesaleCase(商社軸の案件。orders[] / invoices[] / payments[] を内包) |
| 卸金額マスタ | WholesalePrice(アイテム × 商社の卸価格。反映日付き。販売店(dealer)軸で個別卸価格を持ち、未設定時は標準価格へフォールバック。実装済み) |
| 販売店発注フォーム | DealerIntakeForm(送信で WholesaleCase を自動生成) |
| 受注〜入金 | WOrder / WInvoice / WPayment |
| 決済条件の詳細 | WSettlementDetail(手数料/頭金/支払条件/カード/信販会社の詳細項目。WholesaleCase.settlementDetail に保持) |
| 三社間マネー | WFinanceReceipt / WSupplierPayment / WDealerSettlement(信販入金・仕入先支払・販売店精算) |
| パイプライン | WBucket(決済待ち/発注待ち/納品待ち/請求待ち/入金待ち/完了) |
| タスク | WholesaleTask(卸業務専用のタスクリスト) |
| 対象外(委譲) | 決済区分 WSettlement の定義・審査観点、工程 FLOW の全パターン → /features/wholesale-settlement/ |
アイテム(商品マスタ)は /features/item-master/、価格表・見積は /features/quote-pricing/ を参照。
データ(エンティティ表)
Section titled “データ(エンティティ表)”| エンティティ | 主なフィールド | 役割 | ValueOS対応 |
|---|---|---|---|
WholesaleCase | id(W-XXXX), createdAt, orderReceivedDate?(受注日。未設定時はcreatedAt), dealer, settlement, approvalNo, items[], 進捗フラグ5種, cancelled?(キャンセル。集計対象外) | 商社軸の案件。発注・請求・入金の集約ルート | cases(決済関連は case_settlements) |
WItem | name, kind, qty, unitPrice(実際の請求単価), purchasePriceSnapshot?(受注時点の仕入価), wholesalePriceSnapshot?(受注時点の卸価。unitPriceの取得元), isManualPrice?(価格表を使わず手入力したか), priceFetchedAt?(価格取得時点 YYYY-MM-DD) | WholesaleCase.items[]。受注時点の価格を明細にスナップショットとして固定し、WholesalePrice(卸金額マスタ)の以後の改定の影響を受けない。実装済み | case_products(purchase_price / sales_price / gross_profit / is_manual_price / price_fetched_at) |
WholesalePrice | id, itemKind, itemName, productModel?(商品のとき Product.model に紐付け。ID連携。実装済み), supplier, purchasePrice, wholesalePrice, startDate, endDate, memo, dealer?(販売店名。空=標準価格。実装済み) | アイテム×商社の卸価格。反映日により価格改定と履歴を表現。dealer により販売店ごとの個別卸価格を表現 | dealer_sales_prices / supplier_purchase_prices に相当。dealer は ValueOS sales_prices の dealer_id 軸に相当 |
WOrder | id, orderNo(PO-XXXX), orderDate, supplier, amount, status, deliveredDate | 商社・メーカーへの発注記録 | orders |
WInvoice | id, invoiceNo(INV-XXXX), invoiceDate, dueDate, amount, status | 販売店への請求記録 | invoices |
WPayment | id, paymentDate, amount, method | 分割入金レコード | payments |
WSettlementDetail | feeRate, feeAmount, depositRate, depositAmount, paymentTerms, cardBrand, cardStatus, financeCompany, loanStatus, approvalNo | 決済条件の詳細。WholesaleCase.settlementDetail に保持 | case_settlements |
WFinanceReceipt | id, financeCompany, scheduledDate, scheduledAmount, actualDate, actualAmount, status(予定/入金済) | 信販会社→VEの入金(予定・実績) | finance_receipts |
WSupplierPayment | id, supplier, orderNo, dueDate, scheduledAmount, paidDate, paidAmount, status(予定/支払済) | VE→仕入先への支払。dueDate は仕入先の締日・支払サイトから自動算出できる(supplierPaymentDueDate()。実装済み) | supplier_payments |
WDealerSettlement | id, statementNo, issueDate, creditReceived, veShare, adjustment, payout, status(予定/精算済) | VE→販売店への精算 | dealer_settlements |
WholesaleTask | id(WT-XXXX), caseId, title, status, assignee, dueDate, priority, memo | 卸業務専用タスク。案件への任意接続 | ValueOSの tasks(case_id, title, status, due_date, assigned_user, priority) |
WholesaleCase は決済区分に応じたゲートフラグ(settlementConfirmed / paymentReceived / ordered / delivered / invoiced)を持ち、これらがワークフロー判定の入力になる。
フィールド詳細
Section titled “フィールド詳細”上記エンティティ表の要約に対し、実装(crm_app・lib/wholesale.ts / lib/wholesalePricing.ts / lib/wholesaleTask.ts)の型定義どおりの全フィールドを示す。
WholesaleCase
Section titled “WholesaleCase”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string(W-XXXX) | 必須 | — | newWholesaleId() で全案件横断の連番採番(4桁ゼロ埋め) |
createdAt | string(YYYY-MM-DD) | 必須 | — | 起票日 |
orderReceivedDate | string(YYYY-MM-DD) | 任意 | 未設定時は createdAt | orderReceivedDateOf() が解決。KPI集計の期間フィルタ基準 |
dealer | string | 必須 | — | 発注元の販売店(代理店)名。EventCompany.name との一致を前提とする緩い参照(外部キー無し) |
settlement | WSettlement(前金/掛売/三社間/カード/その他) | 必須 | — | 工程順(FLOW)を決定する |
approvalNo | string | 任意 | — | 三社間/カードの承認番号。後方互換のため残置。settlementDetail.approvalNo と二重管理になり得る |
settlementDetail | WSettlementDetail | 任意 | — | 決済条件の詳細 |
items | WItem[] | 必須(空配列可) | [] | 案件明細 |
settlementConfirmed | boolean | 必須 | — | ゲートフラグ(決済条件確定) |
paymentReceived | boolean | 必須 | — | ゲートフラグ(入金完了)。前金の「前金入金確認」と他区分の「入金消込」を兼ねる |
ordered | boolean | 必須 | — | ゲートフラグ(発注済) |
delivered | boolean | 必須 | — | ゲートフラグ(納品済) |
invoiced | boolean | 必須 | — | ゲートフラグ(請求済) |
orders | WOrder[] | 任意 | 未設定は実質空配列扱い | 発注実績 |
invoices | WInvoice[] | 任意 | 同上 | 請求実績 |
payments | WPayment[] | 任意 | 同上 | 入金実績(分割可) |
financeReceipts | WFinanceReceipt[] | 任意 | — | 三社間案件でのみ値を持つ想定 |
supplierPayments | WSupplierPayment[] | 任意 | — | 三社間案件でのみ値を持つ想定 |
dealerSettlement | WDealerSettlement | 任意(単数) | — | 三社間案件でのみ値を持つ想定 |
cancelled | boolean | 任意 | 未設定=false相当 | true の案件は wholesaleMetrics() の集計から除外 |
memo | string | 任意 | — | — |
WItem(案件明細)
Section titled “WItem(案件明細)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
name | string | 必須 | — | 品名 |
kind | WItemKind(商品/パッケージ) | 必須 | — | — |
qty | number | 必須 | — | — |
unitPrice | number | 必須 | — | 実際の請求単価(税抜)。snapshotWItem() 経由では wholesalePrice をそのまま複写 |
purchasePriceSnapshot | number | 任意 | — | 受注時点の仕入価スナップショット |
wholesalePriceSnapshot | number | 任意 | — | 受注時点の卸価スナップショット(unitPrice の取得元) |
isManualPrice | boolean | 任意 | — | 価格表を使わず手入力したか |
priceFetchedAt | string(YYYY-MM-DD) | 任意 | — | 価格取得時点 |
WSettlementDetail(決済条件の詳細)
Section titled “WSettlementDetail(決済条件の詳細)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
feeRate | number(%) | 任意 | — | 手数料率 |
feeAmount | number | 任意 | — | 手数料額 |
depositRate | number(%) | 任意 | — | 頭金率(前金) |
depositAmount | number | 任意 | — | 頭金額(前金) |
paymentTerms | string | 任意 | — | 支払条件(掛売・その他の締め/サイト) |
cardBrand | string | 任意 | — | カードブランド |
cardStatus | WCardStatus(未処理/承認済/決済済) | 任意 | — | — |
financeCompany | string | 任意 | — | 信販会社名 |
loanStatus | WLoanStatus(審査中/承認済/実行済) | 任意 | — | — |
approvalNo | string | 任意 | — | 承認番号 |
決済区分ごとに SETTLEMENT_FIELDS が表示・入力対象のフィールドを絞り込む(上記「決済条件の詳細」節の表を参照)。
WOrder(発注)
Section titled “WOrder(発注)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string | 必須 | — | — |
orderNo | string(PO-XXXX) | 必須 | — | newOrderNo() で全案件横断の連番採番 |
orderDate | string(YYYY-MM-DD) | 必須 | — | — |
supplier | string | 任意 | — | 仕入先(商社/メーカー) |
amount | number | 必須 | — | 発注金額(=原価。wholesaleCost() の合算対象) |
status | WOrderStatus(発注済/納品済) | 必須 | — | 手動更新 |
deliveredDate | string(YYYY-MM-DD) | 任意 | — | — |
WInvoice(請求)
Section titled “WInvoice(請求)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string | 必須 | — | — |
invoiceNo | string(INV-XXXX) | 必須 | — | newInvoiceNo() で全案件横断の連番採番 |
invoiceDate | string(YYYY-MM-DD) | 必須 | — | — |
dueDate | string(YYYY-MM-DD) | 必須 | — | — |
amount | number | 必須 | — | 請求金額(税抜卸金額)。wholesaleBilled() の合算対象 |
status | WInvoiceStatus(請求済/一部入金/入金済) | 必須 | — | 手動更新 |
WPayment(入金レコード)
Section titled “WPayment(入金レコード)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string | 必須 | — | — |
paymentDate | string(YYYY-MM-DD) | 必須 | — | — |
amount | number | 必須 | — | wholesalePaid() の合算対象 |
method | string | 任意 | — | 振込/カード/ローン会社 等 |
WFinanceReceipt(信販会社→VE入金)
Section titled “WFinanceReceipt(信販会社→VE入金)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string | 必須 | — | — |
financeCompany | string | 必須 | — | — |
scheduledDate | string(YYYY-MM-DD) | 必須 | — | — |
scheduledAmount | number | 必須 | — | — |
actualDate | string(YYYY-MM-DD) | 任意 | — | — |
actualAmount | number | 任意 | — | — |
status | ”予定” | “入金済” | 必須 | — | 手動更新 |
WSupplierPayment(VE→仕入先支払)
Section titled “WSupplierPayment(VE→仕入先支払)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string | 必須 | — | — |
supplier | string | 必須 | — | — |
orderNo | string | 任意 | — | 紐づく発注番号(WOrder.orderNo) |
dueDate | string(YYYY-MM-DD) | 必須 | — | supplierPaymentDueDate() で自動算出可(下記節を参照) |
scheduledAmount | number | 必須 | — | — |
paidDate | string(YYYY-MM-DD) | 任意 | — | — |
paidAmount | number | 任意 | — | — |
status | ”予定” | “支払済” | 必須 | — | 手動更新 |
WDealerSettlement(VE→販売店精算)
Section titled “WDealerSettlement(VE→販売店精算)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string | 必須 | — | — |
statementNo | string(DS-XXXX) | 必須 | — | — |
issueDate | string(YYYY-MM-DD) | 必須 | — | — |
creditReceived | number | 必須 | — | 信販から受領した総額 |
veShare | number | 必須 | — | VEの取り分(粗利) |
adjustment | number(±) | 必須 | — | 調整額(仕入原価控除など) |
payout | number | 必須 | — | 販売店への支払額。= creditReceived − veShare + adjustment |
scheduledPayoutDate | string(YYYY-MM-DD) | 任意 | — | — |
actualPayoutDate | string(YYYY-MM-DD) | 任意 | — | — |
status | ”予定” | “精算済” | 必須 | — | 手動更新 |
memo | string | 任意 | — | — |
WholesalePrice(卸金額マスタ)
Section titled “WholesalePrice(卸金額マスタ)”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string | 必須 | — | — |
itemKind | ”商品” | “パッケージ” | 必須 | — | — |
itemName | string | 必須 | — | フリーテキスト。currentWholesalePrice() の突合キー |
productModel | string | 任意 | — | Product.model(型番)と一致。currentWholesalePriceByModel() の突合キー |
supplier | string | 必須 | — | SUPPLIERS.name と一致する想定 |
purchasePrice | number | 必須 | — | 仕入価格(税抜) |
wholesalePrice | number | 必須 | — | 販売店向け卸価格(税抜) |
startDate | string(YYYY-MM-DD) | 必須 | — | 反映日。価格引き当ての起点 |
endDate | string(YYYY-MM-DD) | 任意 | 未設定=無期限 | — |
memo | string | 任意 | — | — |
dealer | string | 任意 | 未設定=標準価格 | 販売店名。EventCompany.name と一致する想定 |
WholesaleTask
Section titled “WholesaleTask”| フィールド | 型・enum | 必須 | 既定 | 制約・備考 |
|---|---|---|---|---|
id | string(WT-XXXX) | 必須 | — | newWholesaleTaskId() で全タスク横断の連番採番 |
caseId | string | null | 必須 | — | 卸案件 WholesaleCase.id への任意接続。null は案件に紐付かない全社共通タスク |
title | string | 必須 | — | — |
status | WTaskStatus(未対応/対応中/完了) | 必須 | — | — |
assignee | string | 必須 | — | — |
dueDate | string(YYYY-MM-DD) | 必須(空文字可) | — | taskOverdue() の判定に使用 |
priority | WTaskPriority(高/中/低) | 必須 | — | — |
memo | string | 任意 | — | — |
createdAt | string(YYYY-MM-DD) | 必須 | — | — |
サイドバーの情報設計(工程キュー型)
Section titled “サイドバーの情報設計(工程キュー型)”商社・卸グループのサイドバーは、書類種別ではなく業務工程の順で並べる。担当者は上から下へなぞるだけで、案件を受注から入金まで進められる(案件を探して開く「案件起点」ではなく、工程を開けば今やるべき案件が並ぶ「工程起点」)。この設計は、クライアントの実運用(月1000案件規模・営業/経理の分業・漏れ防止が導入動機)と、ValueOS の作業キュー(queues/orders・queues/deliveries・queues/collections・queues/payments-management)の思想に整合する。
サイドバー並び順(工程順):
- 卸案件(ハブ) — 全案件の俯瞰・詳細・例外対応の背骨
- 発注管理 — これから発注が必要な案件の作業キュー
- 納品管理 — 発注済み未納品の作業キュー
- 回収管理(請求入金) — 請求待ち+未収の作業キュー
- 発注・支払(買掛) — 発注実績と仕入先支払(買掛)の横断管理
各工程キューは「その工程で今アクションが要る案件だけ」を緊急度順に出す。判定は wholesaleFlow()(canOrder / canInvoice / bucket / currentStep)を唯一の情報源とし、案件の状態から自動導出する。工程キューは表示・導線であり、状態を書き換える操作(発注・納品確認・請求・入金消込)は案件詳細および各操作画面に集約する。
| 画面 | 機能 | 備考 |
|---|---|---|
サマリー(WholesaleDashboard、実装済み) | 経営KPI(売上/実粗利/粗利率/未入金)+5本柱スタット(案件/受注残/発注済/請求済/入金済/未収)+バケット別・決済区分別・タスク集計 | 経営KPIは期間トグル(今月/先月/全期間)付き。詳細は下記「経営KPIダッシュボード」節 |
| 卸案件一覧(ハブ・パイプライン) | 6バケットでの進捗表示。商社軸での絞り込みを既定にする。俯瞰・例外対応の背骨として工程キューの先頭に置く | バケット間の手動移動は不可。工程完了に応じた自動導出のみ |
| 発注管理(工程キュー、実装済み) | !ordered かつ発注工程を持つ案件を、受注日の古い順に表示。前金の入金確認待ち等、まだ発注できない案件は「次にやること」に理由(=ブロック理由)を出す | 「入金→連携→発注」の属人受け渡し(前金なのに入金前発注する事故)を可視化で防ぐ。行は案件詳細へのリンク |
| 納品管理(工程キュー、実装済み) | ordered かつ !delivered の案件を、発注日の古い順に表示 | 行は案件詳細へのリンク |
| 回収管理(請求入金、実装済み) | 「請求待ち」(canInvoice かつ未請求)+請求済の未収を、緊急度(期限超過→請求待ち→未収→入金済)の順に表示。信販入金・代理店精算の消込も含む | 従来の未収一覧に、まだ請求していない「請求待ち」案件を追加して回収の入口とした |
発注・支払(買掛)横断一覧(WholesalePayables、実装済み) | 発注(WOrder)のステータス管理と、全案件の supplierPayments(VE→仕入先支払)横断集計。未払を支払期日の近い順に | 詳細は下記「発注→支払予定(買掛)の横断一覧」節 |
| 卸金額マスタ | アイテム×商社の卸価格の登録・改定 | startDate による価格予約・履歴を兼ねる。区分「商品」の行は「型番」列(productModel)を持ち、Product.model とID連携する |
販売店発注フォーム(DealerIntakeForm) | 販売店からの発注入力を受け付け、送信時に WholesaleCase を自動生成 | 手入力での案件直接作成も併存する |
| タスク一覧 | 卸業務専用タスクの一覧・期限管理 | 案件詳細内タブ表示と独立リスト表示のいずれを既定にするかは確認事項(下記) |
工程キューの緊急度は各工程に適した日付で並べる(発注管理=受注日、納品管理=発注日、回収管理=請求期日、支払管理=支払期日)。商社軸の案件は施主情報・工事日を持たない(別軸原則)ため、ValueOS の「工事日近い順」に代えて上記の日付を用いる。堅牢性要件として、ゼロ件キューの表示、長い品名・販売店名の折返し、件数過多時のページングまたは仮想スクロールを満たす。
実装状況(工程キュー化): 試作
crm_app・本番crm_nextの双方で実装・マージ済み。担当ロールは、発注管理=営業/発注担当、回収管理(請求入金)・発注・支払(買掛)=経理(7/29 定例会の分業に対応。支払〈買掛〉の担当が経理であることは 2026-08-31 に確定)。
業務ルール/ワークフロー
Section titled “業務ルール/ワークフロー”卸金額マスタの価格引き当て
Section titled “卸金額マスタの価格引き当て”現在価格の引き当ては「startDate ≤ 基準日 かつ(endDate 未設定 または endDate ≥ 基準日)」を満たす行のうち、startDate が新しい順、同日なら卸価格が安い順で先頭を採用する。この絞り込み・並び替えロジックは currentWholesalePrice()(品名 itemName 一致)と currentWholesalePriceByModel()(型番 productModel 一致)で共通である。
区分「商品」の WholesalePrice 行は、productModel に Product.model(型番)を設定することでアイテムをID連携できる(ValueOS の dealer_sales_prices が商品IDで卸価格を引く方式に準拠。試作 crm_app・lib/wholesalePricing.ts で実装済み)。currentWholesalePriceByModel(prices, productModel, baseDate) は型番から現在有効な卸価格を引き当てる新設関数であり、品名フリーテキスト一致に依存しない。従来の currentWholesalePrice()(品名一致)は、productModel を持たない行やパッケージ区分のために残置する。
| 引き当て関数 | 突合キー | 用途 |
|---|---|---|
currentWholesalePrice(prices, itemName, kind?, baseDate?) | itemName(フリーテキスト) | 従来方式。全区分(商品・パッケージ)で利用可 |
currentWholesalePriceByModel(prices, productModel, baseDate?) | productModel(Product.model と一致) | ID連携方式。productModel を設定済みの商品行が対象 |
currentWholesalePriceForDealer(prices, {productModel?, itemName?, kind?, dealer?}, baseDate?) | productModel または itemName に加えて dealer(販売店名) | 販売店軸の引き当て。販売店指定価格を優先し、無ければ標準価格(dealer 未設定行)へフォールバック |
シード9件のうち、実在型番(CS-340B81 / S40ZTEP / CS6.1)を持つ4件(WP03 / WP07 / WP08 / WP09)は productModel を連携済みである。残る5件(パッケージ2件・型番未設定の商品3件)は品名フリーテキストのままであり、型番連携は正規化の残タスクとする。
卸金額マスタの販売店(dealer)軸(ValueOS sales_prices 準拠・実装済み)
Section titled “卸金額マスタの販売店(dealer)軸(ValueOS sales_prices 準拠・実装済み)”WholesalePrice は dealer?(販売店名。EventCompany.name と一致させる想定)を持つ。同一アイテム(品名または型番)× 同一仕入先でも、販売店ごとに異なる卸価格を別行として登録できる。dealer 未設定の行は「標準価格」を表す。
currentWholesalePriceForDealer() は、dealer を指定した引き当てにおいて次の優先順位で価格を解決する。
- 指定した
dealerに一致する行の中から現在有効な価格を引く(productModelがあれば型番一致、無ければ品名一致) - 該当が無ければ
dealer未設定(標準価格)の行にフォールバックして同様に引く
これにより、確認事項「卸価格は販売店ごとに異なり得るか」(YES)を設計・試作実装(crm_app・lib/wholesalePricing.ts)の両面で解決した。シードでは WP09(dealer: "フィールドクリエイト"、同一型番 CS-340B81、卸価格55,000円)が、標準価格 WP03(同一型番・卸価格58,000円)より低い販売店別特価の例である。発注フォーム(DealerIntakeForm)・案件詳細の明細追加(NewWholesaleSheet)の双方とも、明細の品名選択時の自動引き当てを currentWholesalePriceForDealer() へ切り替え済みである(下記「明細への卸金額スナップショット自動引き当て」節を参照。実装済み)。
代理店(販売店)・仕入先マスタの卸属性拡張(ValueOS dealers / suppliers 準拠・実装済み)
Section titled “代理店(販売店)・仕入先マスタの卸属性拡張(ValueOS dealers / suppliers 準拠・実装済み)”卸金額の販売店軸に合わせ、代理店マスタ・仕入先マスタにも卸取引の属性を追加した。いずれも試作 crm_app(lib/events.ts / lib/masters.ts)で実装済みである。
| マスタ | 追加フィールド | 意味 | ValueOS対応 |
|---|---|---|---|
EventCompany(代理店=販売店マスタ) | paymentType? | 既定支払区分(前金/掛売/三社間/カード/その他) | dealers 相当 |
EventCompany(代理店=販売店マスタ) | defaultProfitAmount? | 既定利益額(円) | dealers 相当 |
Supplier(仕入先マスタ) | closingDay? | 締日(例: 月末) | suppliers の支払条件相当 |
Supplier(仕入先マスタ) | paymentSite? | 支払サイト(例: 翌月末払い) | suppliers の支払条件相当 |
Supplier(仕入先マスタ) | creditLimit? | 与信枠(円) | suppliers の支払条件相当 |
いずれも任意項目であり、既存レコードとの後方互換を保つ。paymentType / defaultProfitAmount は DealerIntakeForm や案件起票時に決済区分・利益額の初期値として参照できる想定の既定値であり、案件ごとの settlement / settlementDetail を上書き確定するものではない。Supplier の締日・支払サイトは、WSupplierPayment(VE→仕入先への支払)の支払予定日(dueDate)の自動算出に用いる(下記「仕入先支払予定日の自動算出」節を参照。実装済み)。与信枠(creditLimit)は与信管理の参照値として用いる想定であり、与信チェックの自動判定は未実装である。
仕入先支払予定日の自動算出(ValueOS suppliers 準拠・実装済み)
Section titled “仕入先支払予定日の自動算出(ValueOS suppliers 準拠・実装済み)”Supplier.closingDay(締日)・paymentSite(支払サイト)から WSupplierPayment.dueDate(支払予定日)を自動算出する純関数 supplierPaymentDueDate(supplierName, orderDateISO) を、試作 crm_app(lib/supplierPayment.ts)で実装済みである。副作用を持たず、仕入先マスタ(SUPPLIERS)を仕入先名で引いて算出する。
算出ロジックは次の順で行う。
- 仕入先名で
SUPPLIERSを引き、closingDayとpaymentSiteの両方が設定されている行のみを対象とする - 締め月の決定:発注日が当月の締日(表記は
"月末"または"N日")以前ならその月で締め、締日を超えていれば翌月に繰り越して締める - 支払予定月の決定:締め月に、支払サイトの表記(
"当月"/"翌"/"翌々")が示す月数を加算する - 支払予定日の決定:支払サイトが指定する日(
"月末"または"N日")を、支払予定月の日付として解決する
仕入先が見つからない、締日・支払サイトが未設定、または表記を解釈できない場合は undefined を返す。呼び出し側は undefined のとき dueDate の手入力へフォールバックする想定である。この関数は dueDate 一項目の算出に限られ、WSupplierPayment エンティティ自体を発注実績から自動生成する処理ではない(下記「確認事項」を参照)。
flowchart TD
S0["仕入先名 + 発注日"] --> S1{"SUPPLIERSに該当行があり\nclosingDayとpaymentSiteが両方設定?"}
S1 -->|No| U1["undefined を返す(dueDate手入力へフォールバック)"]
S1 -->|Yes| S2{"締日/支払サイトの表記を解釈できる?\n(月末 / N日 / 当月・翌・翌々)"}
S2 -->|No| U1
S2 -->|Yes| S3{"発注日 <= 当月の締日?"}
S3 -->|Yes| S4["締め月 = 発注月"]
S3 -->|No| S5["締め月 = 発注月の翌月"]
S4 --> S6["支払予定月 = 締め月 + オフセット月数\n(当月=0 / 翌=1 / 翌々=2)"]
S5 --> S6
S6 --> S7["支払予定日 = 支払予定月の指定日\n(月末 or N日)"]
S7 --> S8["YYYY-MM-DD を返す"]
発注フォームからの案件起票
Section titled “発注フォームからの案件起票”DealerIntakeForm の送信時、進捗フラグ(settlementConfirmed / paymentReceived / ordered / delivered / invoiced)は全て未完了で初期化される。これによりワークフロー判定は自動的に先頭工程(決済区分に応じて「決済条件の確定」または「審査OK確認」)を指し、対応する WBucket(決済待ち)へ配置する。明細の品名を卸金額マスタから選択すると区分・卸単価を自動で引き当てる(下記「明細への卸金額スナップショット自動引き当て」節を参照。実装済み)。マスタに無い品名は単価を手入力する。
明細への卸金額スナップショット自動引き当て(ValueOS case_products 準拠・実装済み)
Section titled “明細への卸金額スナップショット自動引き当て(ValueOS case_products 準拠・実装済み)”卸案件の明細作成画面(DealerIntakeForm=発注フォーム、および案件詳細の明細追加シート NewWholesaleSheet)は、いずれも品名選択時に currentWholesalePriceForDealer()(上記「卸金額マスタの販売店(dealer)軸」節)で卸価格を自動引き当てる、試作 crm_app(features/wholesale/DealerIntakeForm.tsx / features/wholesale/WholesalePages.tsx)で実装済みの挙動である。品名一致に加え、案件の dealer(販売店名)を渡すことで、販売店指定の個別卸価格があればそれを優先し、無ければ標準価格へフォールバックする。
明細確定時(フォーム送信時)は、明細組み立てヘルパー snapshotWItem(base, opts) を用いて受注時点の価格を WItem に固定する。
snapshotWItem の複写先 | 内容 |
|---|---|
unitPrice / wholesalePriceSnapshot | 入力欄の卸単価(opts.wholesalePrice)をそのまま複写 |
purchasePriceSnapshot | 引き当てにヒットしたマスタ行の仕入価(hit?.purchasePrice)。ヒット無しなら未設定 |
isManualPrice | マスタ引き当て結果の卸価格と入力欄の値が不一致(またはヒット無し)のとき true |
priceFetchedAt | 送信時点の日付(YYYY-MM-DD) |
purchasePriceSnapshot・isManualPrice・priceFetchedAt はオプションであり、値を渡した場合のみ明細に反映される。画面上は、マスタ卸価をそのまま採用した明細に「マスタ卸価」(販売店別価格がヒットした場合は「マスタ卸価(販売店別)」)バッジ、単価を手で書き換えた・マスタに無い明細に「手入力」バッジを付け、案件詳細の明細一覧では仕入価・価格取得日も併記する。
sequenceDiagram
participant User as 担当者
participant Form as DealerIntakeForm / NewWholesaleSheet
participant Pricing as currentWholesalePriceForDealer()
participant Master as WholesalePrice[](卸金額マスタ)
User->>Form: 品名を選択(案件の dealer を保持)
Form->>Pricing: currentWholesalePriceForDealer(prices, {itemName/productModel, dealer}, baseDate)
Pricing->>Master: dealer一致行を検索
alt dealer一致行がヒット
Master-->>Pricing: 販売店別の現在有効価格
else dealer一致行が無い
Pricing->>Master: dealer未設定(標準価格)行へフォールバックして再検索
Master-->>Pricing: 標準価格の現在有効価格
end
Pricing-->>Form: WholesalePrice | undefined
Form->>Form: snapshotWItem(base, {wholesalePrice, purchasePrice, manual, fetchedAt})
Form-->>User: 明細に unitPrice / wholesalePriceSnapshot / purchasePriceSnapshot / isManualPrice / priceFetchedAt を固定
受注時の価格スナップショット(ValueOS case_products 準拠・実装済み)
Section titled “受注時の価格スナップショット(ValueOS case_products 準拠・実装済み)”WItem(案件明細)は unitPrice(実際の請求単価)に加えて、受注時点の価格を固定する4項目 purchasePriceSnapshot?(仕入価)/wholesalePriceSnapshot?(卸価)/isManualPrice?(価格表を使わず手入力したか)/priceFetchedAt?(価格取得時点 YYYY-MM-DD)を持つ。ValueOS の case_products(purchase_price / sales_price / gross_profit / is_manual_price / price_fetched_at)に対応し、試作 crm_app(lib/wholesale.ts)で実装済みである。
これにより、WholesalePrice(卸金額マスタ)を後日改定しても、既に受注済みの WItem(スナップショット済み明細)は改定前の価格のまま維持され、マスタ改定の影響を受けない。スナップショット項目を持たない従来型の明細(unitPrice のみ)も後方互換として引き続き扱える。
決済区分別の工程順(FLOW)
Section titled “決済区分別の工程順(FLOW)”| 決済区分 | 工程順 | 請求工程 | 入金確認の位置づけ |
|---|---|---|---|
| 前金 | 決済条件確定 → 前金入金確認 → 発注 → 納品 | 持たない | 発注より前(prepay 工程内) |
| 掛売 | 決済条件確定 → 発注 → 納品 → 請求 → 入金消込 | 持つ | 請求後(payment 工程) |
| 三社間 | 審査OK確認 → 発注 → 納品 → 入金消込 | 持たない | 納品後(payment 工程) |
| カード | 審査OK確認 → 発注 → 納品 → 入金消込 | 持たない | 納品後(payment 工程) |
| その他 | 決済条件確定 → 発注 → 納品 → 請求 → 入金消込 | 持つ | 請求後(payment 工程) |
工程の名称・担当・審査観点の詳細は /features/wholesale-settlement/ を正本とする。本ページは接続点(工程順とバケットの対応)のみを保持する。
FLOW(決済区分別の工程配列)と BUCKET_BY_STEP(工程→バケットの対応表)を接続すると、決済区分ごとのバケット遷移は次のとおりになる。前金のみ「入金待ち」が「発注待ち」より前に来る点、三社間・カードには「請求待ち」が存在しない点が他区分と異なる。
flowchart LR
subgraph 前金
A1[決済待ち] --> A2[入金待ち] --> A3[発注待ち] --> A4[納品待ち] --> A5[完了]
end
subgraph 掛売_その他["掛売/その他"]
B1[決済待ち] --> B2[発注待ち] --> B3[納品待ち] --> B4[請求待ち] --> B5[入金待ち] --> B6[完了]
end
subgraph 三社間_カード["三社間/カード"]
C1[決済待ち] --> C2[発注待ち] --> C3[納品待ち] --> C4[入金待ち] --> C5[完了]
end
決済条件の詳細(ValueOS case_settlements 準拠・実装済み)
Section titled “決済条件の詳細(ValueOS case_settlements 準拠・実装済み)”WholesaleCase は決済区分の値に加えて settlementDetail(WSettlementDetail 型)を持ち、決済条件の詳細を保持する。ValueOS の case_settlements(fee_rate / fee_amount / deposit_rate / deposit_amount / payment_terms / card_brand / card_status / finance_company / loan_status / approval_number)に対応する、試作 crm_app(lib/wholesale.ts)で実装済みの項目である。
| 当方の決済区分 | ValueOS正式値 |
|---|---|
| 前金 | 前金 |
| 掛売 | 売掛 |
| 三社間 | 3社間決済 |
| カード | カード |
| その他 | (正式区分外) |
WSettlementDetail フィールド | 意味 | ValueOS対応 |
|---|---|---|
feeRate / feeAmount | 手数料率(%) / 手数料額 | fee_rate / fee_amount |
depositRate / depositAmount | 頭金率(%) / 頭金額 | deposit_rate / deposit_amount |
paymentTerms | 支払条件(掛売の締め・サイト) | payment_terms |
cardBrand / cardStatus | カードブランド / カード状態(未処理・承認済・決済済) | card_brand / card_status |
financeCompany / loanStatus | 信販会社 / ローン状態(審査中・承認済・実行済) | finance_company / loan_status |
approvalNo | 承認番号 | approval_number |
SETTLEMENT_FIELDS により、決済区分ごとに表示・入力する項目を出し分ける。
| 決済区分 | 表示・入力する項目(SETTLEMENT_FIELDS) |
|---|---|
| 前金 | 頭金率・頭金額・手数料率・手数料額 |
| 掛売 | 支払条件・手数料率・手数料額 |
| 三社間 | 信販会社・承認番号・ローン状態・手数料率・手数料額 |
| カード | カードブランド・承認番号・カード状態・手数料率・手数料額 |
| その他 | 支払条件・手数料率・手数料額 |
発注ゲートのうち三社間・カードの先頭工程「審査OK確認」は settlementApproved() で判定する。案件本体の approvalNo(後方互換のため残置)または settlementDetail.approvalNo のいずれかに値があるか、loanStatus が「承認済」もしくは「実行済」、または cardStatus が「承認済」もしくは「決済済」であれば承認済みとみなし、上表「決済区分別の工程順(FLOW)」の審査OK確認工程を完了扱いにする。案件詳細画面での表示は settlementDetailEntries() が、決済区分ごとの表示対象項目のうち値がある行のみを整形して返す。
パイプライン列(WBucket)の導出
Section titled “パイプライン列(WBucket)の導出”| バケット | 対応工程 | 決済区分による有無 | 導出条件 |
|---|---|---|---|
| 決済待ち | 決済条件確定/審査OK確認 | 全区分で通過 | 未完了の先頭工程がこの2つのいずれか |
| 発注待ち | 発注 | 全区分で通過 | 直前工程が完了かつ未発注 |
| 納品待ち | 納品確認 | 全区分で通過 | 発注済みかつ未納品 |
| 請求待ち | 請求 | 掛売・その他のみ(三社間/カード/前金は通らない) | 工程順に「請求」が含まれる場合のみ |
| 入金待ち | 前金入金確認/入金消込 | 前金は前者、他区分は後者 | 対応工程が未完了 |
| 完了 | — | 全区分共通 | 全工程が完了 |
発注可否(canOrder)は、工程順で「発注」より前の工程が全て完了し、かつ未発注のときに真となる。請求可否(canInvoice)は、工程順に「請求」が含まれ、それより前の工程が全て完了し、かつ未請求のときに真となる。この判定構造は ValueOS の WorkflowEngine(canOrder / canInvoice)と対応する。
flowchart TD
Q1{"FLOWに「発注」工程を含むか"} -->|含まない| NO1["canOrder = false"]
Q1 -->|含む| Q2{"「発注」より前の全工程が完了?"}
Q2 -->|No| NO1
Q2 -->|Yes| Q3{"ordered = false?"}
Q3 -->|No| NO1
Q3 -->|Yes| YES1["canOrder = true"]
Q4{"FLOWに「請求」工程を含むか"} -->|含まない| NO2["canInvoice = false"]
Q4 -->|含む| Q5{"「請求」より前の全工程が完了?"}
Q5 -->|No| NO2
Q5 -->|Yes| Q6{"invoiced = false?"}
Q6 -->|No| NO2
Q6 -->|Yes| YES2["canInvoice = true"]
受注から入金までの状態と金額
Section titled “受注から入金までの状態と金額”WOrder の状態は「発注済」「納品済」の2値、WInvoice の状態は「請求済」「一部入金」「入金済」の3値であり、いずれもフォーム操作による手動更新で遷移する。金額は次の式で自動算出する。
- 請求ベース金額(
wholesaleBilled): 請求書があればその合計、無ければ明細の卸金額合計 - 入金済み合計(
wholesalePaid): 入金レコードの合計 - 残高(
wholesaleBalance):wholesaleBilled − wholesalePaid(下限0)
入金は複数レコードによる分割入金に対応する。入金をどの請求へ充当するかの按分ルールは定めておらず、入金レコードの単純合算で残高を算出する。
WOrder.status / WInvoice.status はいずれも画面操作による手動更新であり、下図は想定される遷移方向を示す(コード上は逆行を防ぐ検証を持たず、どの値へも書き換え可能。下記「エッジケース/異常系」を参照)。
stateDiagram-v2 [*] --> 発注済 発注済 --> 納品済: 納品確認(手動更新)
stateDiagram-v2 [*] --> 請求済 請求済 --> 一部入金: WPayment登録(残高>0のまま) 請求済 --> 入金済: WPayment登録(残高=0) 一部入金 --> 入金済: 追加のWPayment登録(残高=0)
マイルストーン投影(タイムライン表示・実装済み)
Section titled “マイルストーン投影(タイムライン表示・実装済み)”wholesaleMilestones(c) は createdAt を起点に、決済区分ごとのリードタイム定数から「いつ発注・請求/入金すべきか」を投影する純関数である。試作 crm_app(lib/wholesale.ts。案件一覧・詳細のタイムライン表示 features/wholesale/WholesalePages.tsx で使用)で実装済みである。実績フラグ(ordered / invoiced / paymentReceived)が立っている工程は done: true として実績表示に切り替わる。
| リードタイム定数 | 値 | 意味 |
|---|---|---|
ORDER_LEAD(決済区分別) | 前金5日/掛売1日/三社間2日/カード2日/その他3日 | createdAt → 発注予定日 |
PREPAY_LEAD | 2日 | 前金:createdAt → 入金確認予定日(発注予定日より前になるよう ORDER_LEAD.前金 より小さく設定) |
DELIVER_LEAD | 10日 | 発注予定日 → 納品予定 |
BILL_LEAD | 3日 | 納品予定 → 請求/入金予定 |
前金は「入金確認 → 発注」の順で2件のマイルストーン(入金: createdAt + PREPAY_LEAD、発注: createdAt + ORDER_LEAD.前金)を返す。他区分は「発注 → 請求/入金」の順で2件(発注: createdAt + ORDER_LEAD[settlement]、後続: 発注予定日 + DELIVER_LEAD + BILL_LEAD)を返し、FLOW に「請求」工程を含む区分(掛売・その他)は種別を「請求」、含まない区分(三社間・カード)は「入金」とする。リードタイム定数はいずれもコード上の固定値(モック)であり、マスタ化・案件ごとの調整は未対応である(下記「確認事項」)。
経営KPIダッシュボード(ValueOS経営基準・実装済み)
Section titled “経営KPIダッシュボード(ValueOS経営基準・実装済み)”サマリー画面(WholesaleDashboard)は、ValueOSの経営ダッシュボード集計基準に整合した4つのKPIを表示する。集計層は試作 crm_app(lib/wholesaleMetrics.ts の wholesaleMetrics())で実装済みである。
| KPI | 定義 | 期間との連動 | ValueOS対応 |
|---|---|---|---|
| 売上 | 期間内に受注日を持つ有効案件の卸金額(wholesaleTotal)合計 | 連動(受注日ベース) | sales_price 合計相当 |
| 実粗利 | 同条件の実粗利(wholesaleGrossProfit = 卸金額 − 原価)合計 | 連動(受注日ベース) | gross_profit 合計相当 |
| 粗利率 | 実粗利 ÷ 売上(売上が0のときは0) | 連動(分子・分母とも受注日ベース) | — |
| 未入金 | 有効案件の現在の未収残高(wholesaleBalance = 請求ベース金額 − 入金済み合計、下限0)の合計 | 非連動(常に現時点) | 未収残高合計相当 |
集計ルールは次のとおりとする。
- 受注日は
orderReceivedDate(未設定時はcreatedAtで代替。orderReceivedDateOf()が解決)を用いる。ValueOSのorder_received_dateに相当する。 - 期間フィルタ(
from/to)は売上・実粗利・粗利率にのみ適用し、未入金には適用しない。未入金は常に全期間の現時点残高で表示する。 - キャンセル案件(
cancelled = true)は売上・実粗利・未入金を含む全ての集計から除外する。ValueOSのactiveRecords除外に相当する。 - 画面には期間トグル(今月/先月/全期間)を設ける。今月・先月は当月・前月の1日〜末日を受注日で絞り込み、全期間はフィルタなしとする。
wholesaleMetrics() は上記4KPIに加え、5本柱スタットとバケット別・決済区分別の内訳を1回の走査で返す。
| スタット | 集計条件 | opts.from/to との連動 |
|---|---|---|
案件(total) | キャンセル以外の全案件(件数・卸金額合計) | 非連動(常に全期間) |
受注残(unordered) | wholesaleFlow(c).canOrder が真、または(ordered=false かつ flow.done=false) | 非連動 |
発注済(ordered) | ordered=true | 非連動 |
請求済(invoiced) | invoiced=true | 非連動 |
入金済(received) | paymentReceived=true | 非連動 |
未収(uncollected) | invoiced=true かつ paymentReceived=false | 非連動 |
バケット別内訳(byBucket) | wholesaleFlow(c).bucket を WBUCKETS の6区分固定順で集計 | 非連動 |
決済区分別内訳(bySettlement) | c.settlement を WHOLESALE_SETTLEMENTS の5区分固定順で集計 | 非連動 |
期間トグルが実際に連動するのは経営KPIの売上・実粗利・粗利率のみである。5本柱スタット・バケット別・決済区分別の内訳、および未入金KPIは、wholesaleMetrics() 内部では期間フィルタ(opts.from/to)を適用せず、常にキャンセルを除く全件・現時点の状態で算出する。画面の期間トグルがこれらの表示にも見た目上影響するかはUI実装側の出し分け次第であり、集計関数自体は期間非連動である。
三社間マネー(ValueOS lib/threeParty 準拠・実装済み)
Section titled “三社間マネー(ValueOS lib/threeParty 準拠・実装済み)”決済区分「三社間」の案件は、決済条件の詳細に加えて、信販会社・VE・販売店の三者間で動く金銭を実体化する3つのエンティティを持つ。ValueOS の lib/threeParty(finance_receipts / supplier_payments / dealer_settlements)に対応し、試作 crm_app(lib/wholesale.ts)で実装済みである。資金の流れは「信販会社が工事後にVEへ入金 → VEが仕入先へ支払 → VEが販売店へ精算」の順で確定する。
| エンティティ | 主なフィールド | 役割 | ValueOS対応 |
|---|---|---|---|
WFinanceReceipt | financeCompany, scheduledDate / scheduledAmount, actualDate / actualAmount, status(予定/入金済) | 信販会社→VEの入金(予定・実績) | finance_receipts |
WSupplierPayment | supplier, orderNo, dueDate, scheduledAmount, paidDate / paidAmount, status(予定/支払済) | VE→仕入先への支払。発注番号(WOrder.orderNo)に紐付け可 | supplier_payments |
WDealerSettlement | statementNo, issueDate, creditReceived, veShare, adjustment, payout, status(予定/精算済) | VE→販売店への精算 | dealer_settlements |
WDealerSettlement.payout(販売店への支払額)は creditReceived(信販から受領した総額)− veShare(VEの取り分=粗利)+ adjustment(調整額。仕入原価控除などを±で反映)で算出する。各エンティティは WholesaleCase.financeReceipts[] / supplierPayments[] / dealerSettlement(単数)として案件に内包し、いずれも三社間案件でのみ値を持つ(他区分では未設定のまま)。
threePartySummary() は案件単位で次を集計する。
| 集計値 | 内容 |
|---|---|
creditScheduled / creditReceived | 信販入金の予定合計 / 実績合計 |
supplierScheduled / supplierPaid | 仕入先支払の予定合計 / 実績合計 |
veShare / dealerPayout | 精算書のVE取り分 / 販売店への支払額 |
dealerSettled | 販売店精算が完了しているか(status = 精算済) |
三社間マネーは既存の WOrder(VEから仕入先への発注記録)・WPayment(残高消込のための入金合算レコード)と並存する。wholesaleBalance の残高消込は従来どおり payments[] の単純合算で行い、WFinanceReceipt 以下の3エンティティは信販入金の予定・実績、仕入先支払、販売店精算という資金フローの内訳を別立てで管理する。
シード W-0005 の資金フロー(信販入金→仕入先支払→販売店精算が全て完了済みの例)を時系列で示す。
sequenceDiagram participant Finance as 信販会社 participant VE as VE(自社) participant Supplier as 仕入先 participant Dealer as 販売店 VE->>Supplier: 発注(WOrder登録) Supplier-->>VE: 納品 Finance->>VE: 信販入金(WFinanceReceipt: scheduledDate/scheduledAmount → actualDate/actualAmount、status=入金済) VE->>Supplier: 仕入先支払(WSupplierPayment: dueDate=supplierPaymentDueDate()、status=支払済) VE->>Dealer: 販売店精算(WDealerSettlement.payout = creditReceived − veShare + adjustment、status=精算済)
発注→支払予定(買掛)の横断一覧(実装済み)
Section titled “発注→支払予定(買掛)の横断一覧(実装済み)”WSupplierPayment(VE→仕入先支払)は案件(WholesaleCase.supplierPayments[])単位で保持するため、仕入先横断で「今どれだけ支払予定があるか」を見る画面が別途必要になる。これに対応する画面 WholesalePayables(/wholesale-payables、サイドバー「発注・支払(買掛)」)を、試作 crm_app(features/wholesale/WholesalePayables.tsx)で実装済みである。未払は支払期日の近い(超過を含む)順に並べる。「回収管理(請求入金)」一覧(WholesaleReceivables、/wholesale-billing。販売店からの請求・入金・未収を横断集計。請求待ちを含む)の対(買掛側)に位置づく。
全案件を横断走査し、supplierPayments[] の各レコードを1行としてフラット化する。
| 集計・表示項目 | 内容 |
|---|---|
| 表示列 | 案件(WholesaleCase.id へリンク)/仕入先/発注番号/金額/支払予定日/状態(予定・支払済) |
| 支払予定日 | WSupplierPayment.dueDate があればそれを使用。未設定なら、該当発注(orderNo 一致の WOrder.orderDate。無ければ案件の受注日 orderReceivedDateOf())を起点に supplierPaymentDueDate()(仕入先の締日・支払サイトからの自動算出。上記「仕入先支払予定日の自動算出」節)で補完し、自動算出した行には「(自動)」を併記する |
| 金額 | 状態が「支払済」なら paidAmount(無ければ scheduledAmount)、それ以外は scheduledAmount |
| 期限超過 | 状態が「予定」かつ支払予定日(自動算出分を含む)が本日より前の行を期限超過として強調表示 |
| サマリー | 支払予定総額/支払済/未払残/期限超過(件数付き) |
| フィルタ・検索 | 状態(すべて/未払/支払済。既定は未払)と、案件ID・仕入先・発注番号によるキーワード検索 |
これにより、既存の「請求・入金(未収)」(販売店からの受取=売掛サイド)と、新設の「支払予定(買掛)」(仕入先への支払=買掛サイド)の両輪で、卸案件の資金繰りを横断的に把握できる。
催事⇄卸の代理店(販売店)串刺しリンク(別軸維持・参照リンクのみ・実装済み)
Section titled “催事⇄卸の代理店(販売店)串刺しリンク(別軸維持・参照リンクのみ・実装済み)”催事(前段の代理店営業)と卸(後段の商社発注)は、/features/events/ に定義するとおり別エンティティ・別ライフサイクルの独立した軸であり、本節の対応後もその原則(別軸原則)は変えない。両者に共通して現れる代理店(販売店)名を突合キーとして、案件詳細どうしを相互参照できる導線のみを試作 crm_app に追加した。軸を統合する変更や、催事側のデータを卸側の集計・KPIに合算する変更は行っていない。
| 表示先 | コンポーネント | 突合方法 | 表示内容 | 実装ファイル |
|---|---|---|---|---|
卸案件詳細(WholesaleDetail) | DealerEventsCard(「販売店の催事」) | 案件の dealer(販売店名)と一致する EventCompany.name を検索 | 該当代理店に紐づく催事シフト件数・直近日付+/events へのリンク | crm_app・features/wholesale/WholesalePages.tsx |
催事シフト詳細(EventShiftDetail) | DealerWholesaleCard(「この販売店の卸案件」) | シフトの companyId から EventCompany を解決し、その name と一致する WholesaleCase.dealer を検索 | 該当販売店に紐づく卸案件件数+/wholesale へのリンク | crm_app・features/crm/EventShiftDetail.tsx |
いずれも該当する相手方データが1件も無い場合はカード自体を非表示にし、ゼロ件時の破綻を避ける。突合は名称一致(催事側)/IDから解決した名称一致(卸側)によるゆるい参照であり、外部キーによる強い紐付けではない。上記「確認事項」の「元請案件(deals)と卸案件(WholesaleCase)の紐付け方式」とは別論点であり、本節は代理店(販売店)軸に限定した参照リンクの追加にとどまる。
タスク(WholesaleTask)
Section titled “タスク(WholesaleTask)”タスクは title / status(未対応・対応中・完了)/ assignee / dueDate / priority(高・中・低)を持ち、卸案件(caseId)への任意接続に対応する。案件に紐付けない全社共通タスク(caseId = null)も登録できる。期限超過は「status ≠ 完了 かつ dueDate が基準日より前」で判定する。経理系工程(決済条件確定/審査OK確認/前金入金確認/請求/入金消込)と発注担当系工程(発注/納品確認)の担当分掌に沿って assignee を運用する。
主要関数リファレンス
Section titled “主要関数リファレンス”商社・卸モジュールの業務ロジックは、副作用を持たない純関数として crm_app の5ファイルに実装されている。設計レビュー・実装引き継ぎ時の参照点として、シグネチャと役割を一覧する。
| 関数 | シグネチャ | 役割 | 実装ファイル |
|---|---|---|---|
wholesaleTotal | (c: WholesaleCase) => number | 明細の unitPrice × qty 合計(卸金額) | lib/wholesale.ts |
wholesaleBilled | (c: WholesaleCase) => number | 請求ベース金額。invoices があればその合計、無ければ wholesaleTotal | lib/wholesale.ts |
wholesalePaid | (c: WholesaleCase) => number | payments の合計 | lib/wholesale.ts |
wholesaleBalance | (c: WholesaleCase) => number | max(0, wholesaleBilled − wholesalePaid) | lib/wholesale.ts |
orderReceivedDateOf | (c: WholesaleCase) => string | orderReceivedDate ?? createdAt | lib/wholesale.ts |
wholesaleCost | (c: WholesaleCase) => number | orders の amount 合計(原価) | lib/wholesale.ts |
wholesaleGrossProfit | (c: WholesaleCase) => number | wholesaleTotal − wholesaleCost | lib/wholesale.ts |
settlementApproved | (c: WholesaleCase) => boolean | 承認番号/ローン状態/カード状態から審査OKを判定 | lib/wholesale.ts |
settlementDetailEntries | (c: WholesaleCase) => {label, value}[] | 決済区分別の表示対象項目のうち値がある行を整形 | lib/wholesale.ts |
threePartySummary | (c: WholesaleCase) => ThreePartySummary | 三社間マネー3エンティティの予定/実績集計 | lib/wholesale.ts |
wholesaleFlow | (c: WholesaleCase) => WholesaleFlow | FLOW 定義から現在工程・canOrder・canInvoice・bucket を導出 | lib/wholesale.ts |
wholesaleMilestones | (c: WholesaleCase) => WMilestone[] | リードタイム定数から発注/請求/入金の予定日を投影 | lib/wholesale.ts |
newWholesaleId / newOrderNo / newInvoiceNo | (existing: string[]) => string | 全案件横断の連番採番(4桁ゼロ埋め) | lib/wholesale.ts |
newWholesaleTaskId | (existing: string[]) => string | 全タスク横断の連番採番 | lib/wholesaleTask.ts |
currentWholesalePrice | (prices, itemName, kind?, baseDate?) => WholesalePrice | undefined | 品名一致・反映日による現在価格引き当て | lib/wholesalePricing.ts |
currentWholesalePriceByModel | (prices, productModel, baseDate?) => WholesalePrice | undefined | 型番一致(ID連携)による現在価格引き当て | lib/wholesalePricing.ts |
currentWholesalePriceForDealer | (prices, {productModel?, itemName?, kind?, dealer?}, baseDate?) => WholesalePrice | undefined | 販売店軸優先・標準価格フォールバックの引き当て | lib/wholesalePricing.ts |
wholesaleItemOptions | (prices) => {name, kind}[] | 明細プルダウン用の品名選択肢(重複除去・50音順) | lib/wholesalePricing.ts |
supplierPaymentDueDate | (supplierName, orderDateISO) => string | undefined | 締日・支払サイトから支払予定日を算出 | lib/supplierPayment.ts |
taskOverdue | (t: WholesaleTask, todayISO: string) => boolean | status ≠ 完了 かつ dueDate < today で期限超過判定 | lib/wholesaleTask.ts |
wholesaleMetrics | (cases, {from?, to?}) => WholesaleMetrics | 経営KPI・5本柱・バケット別・決済区分別の集計 | lib/wholesaleMetrics.ts |
エッジケース/異常系
Section titled “エッジケース/異常系”| ケース | 挙動・扱い |
|---|---|
| 卸金額マスタ未マップ(型番未連携) | シード9件中5件(パッケージ2件・型番未設定商品3件)は productModel を持たず品名フリーテキスト一致(currentWholesalePrice)に依存する。表記揺れがあると引き当てに失敗し、価格欄が空のまま手入力(isManualPrice=true)になる |
未請求(invoices 未登録) | wholesaleBilled() は invoices が無ければ wholesaleTotal()(明細合計)を代用する。請求書登録前でも残高(wholesaleBalance)は明細合計を基準に算出され、請求登録後に金額が確定すると差分が生じ得る |
未発注(orders 未登録) | wholesaleCost() は orders の合計であり、発注前は0。したがって発注登録前の wholesaleGrossProfit() は原価未計上のまま卸金額全額が粗利として算出され、実態より過大に出る。経営KPIの実粗利はこの前提を踏まえて読む必要がある |
| 分割入金の按分未定義 | payments[] は単純合算のみで、複数請求(invoices[])にまたがる案件でどの入金がどの請求に対応するかを追跡する仕組みは無い |
| 名称不一致 | dealer(販売店名)は EventCompany.name、supplier は SUPPLIERS.name との一致を前提にした緩い参照(外部キー無し)。表記揺れがあると、販売店別卸価格のフォールバック(標準価格化)、催事⇄卸カードの非表示、supplierPaymentDueDate() の undefined 化(締日・支払サイト行が引けない)が同時に起こり得る |
| 0件 | 卸金額マスタが0件のときは明細への自動引き当てが常に undefined となり全明細が手入力扱いになる。WBUCKETS の6区分・WHOLESALE_SETTLEMENTS の5区分は固定列挙のため、該当案件が0件のバケット・決済区分も一覧・集計上は空枠として表示され続ける |
| 重複行 | 同一品名(または型番)×同一 startDate の WholesalePrice 行が複数存在する場合、引き当て関数群は卸価格が安い順で先頭を機械的に採用する。旧行に endDate を設定して重複を避ける運用が前提(下記「確認事項」) |
| ステータスの手動不整合 | WOrder.status / WInvoice.status はフォーム操作による手動更新であり、wholesaleBalance() など金額計算との自動連動や、逆行(例: 入金済→請求済)を防ぐ検証は実装されていない |
| キャンセル案件 | cancelled=true は wholesaleMetrics() の集計・KPIから除外されるが、wholesaleFlow() / wholesaleBalance() など個々の計算関数自体はキャンセル状態を考慮しない。案件詳細で個別参照した場合は通常どおりの工程・金額が表示され続ける |
現行実装は発注フォーム・案件操作の認証・権限ガードを持たず、入力主体が社内(VE)担当か販売店本人かを技術的に区別する手段を持たない。販売店による直接入力(ポータル化)と、その際の認証・権限モデルは確認事項とする。
- 元請案件(
deals)と卸案件(WholesaleCase)の紐付け方式(参照キー追加か疎結合突合か) WholesalePriceの価格改定時、旧行のendDateを必ず設定する運用とするかitemName(フリーテキスト)のProduct.model(型番)参照への正規化はproductModelフィールド追加とID連携の引き当て関数currentWholesalePriceByModel()により着手済み。残る確認事項は、未マップ5件(パッケージ2件・型番未設定の商品3件)の解消時期と、DealerIntakeForm/ 明細入力側の引き当てを品名一致から型番一致へ切り替える時期(currentWholesalePriceForDealer()はproductModelを渡せる構造だが、現状の呼び出し側はいずれもitemNameのみを渡している)- 卸価格の販売店軸・明細への自動引き当ては
WholesalePrice.dealer?と引き当て関数currentWholesalePriceForDealer()、およびDealerIntakeForm/NewWholesaleSheetからの呼び出しにより設計・実装済み(上記「明細への卸金額スナップショット自動引き当て」節)。残る確認事項は、EventCompany.paymentType/defaultProfitAmountを案件起票時の初期値として自動反映するかどうか DealerIntakeFormの入力主体(社内代行かポータル型か)と認証・権限モデルWOrder/WInvoiceのステータスを入金・納品実績から自動遷移させるか、手動更新のまま運用するか- 前金の工程順で「入金消込」を独立工程として持たず「前金入金確認」に統合している設計の妥当性
WholesaleTaskを案件詳細画面内タブとして表示するか、独立リストとして表示するか- 発注・請求明細のOCR/AI取込(明細スキャン)構想の採否と実装時期
WFinanceReceipt/WSupplierPayment/WDealerSettlementを発注・入金の実績から自動生成するか、経理による手動登録のまま運用するか(WSupplierPayment.dueDateの算出自体はsupplierPaymentDueDate()により実装済み。残る確認事項はエンティティ生成のトリガー)- 「支払予定(買掛)」一覧(
WholesalePayables)の入金充当(按分)ルールは未定義。現状は案件横断でsupplierPayments[]を単純にフラット化・合算するのみで、WPayment(販売店からの入金)側との自動連携は持たない - マイルストーン投影(
wholesaleMilestones())のリードタイム定数(ORDER_LEAD/PREPAY_LEAD/DELIVER_LEAD/BILL_LEAD)はコード内の固定値(モック)であり、決済区分・仕入先ごとの実績に基づくマスタ化や案件ごとの調整を可能にするかは未定
- /features/wholesale-settlement/ — 決済区分・工程順序の正本
- /features/item-master/ — アイテム(商品)マスタ
- /features/quote-pricing/ — 価格表・見積
- /features/events/ — 催事モジュール(代理店権限モデルの先行事例)
- /redesign/valueos-gap/ — ValueOSとの対応・差分
- /valueos/data-model/ — ValueOSのテーブル定義
- /redesign/meeting-log/ — 7/29定例(卸の新要件確定回)