コンテンツにスキップ

データモデル

位置づけ: 新CRM全体設計

先行リリース第1弾は催事・アイテム(商品マスタ)・商社(卸)の3ドメインで構成する。本ページはこの3ドメインの新エンティティと、後続フェーズ(商談CRM本体・見積・KPI予実)との接続点をデータ構造の観点で定義する。各エンティティは試作(React+TS)にコードとして実装済みであり、以下の記述は試作実装とクライアント定例会での合意事項に基づく。

マスタ系エンティティはすべて共通の定型画面(テーブル+編集ドロワー+変更履歴)に載る。画面仕様は各機能ページ(催事アイテム商社(卸))に委譲し、本ページはデータ構造とエンティティ間関係に集中する。実データ(実在の店舗名・取引先名・金額・件数)は記載せず、必要箇所は「(実データで確定)」と表記する。

対象は先行リリース第1弾の3ドメイン、催事 / アイテム(商品マスタ) / 商社(卸)。顧客・商談CRM本体、見積、KPI予実、AIエージェントは後続フェーズであり本ページの対象外とする(参照関係が生じる箇所のみ言及する)。リリース段階の合意経緯は 定例会・意思決定ログ、フェーズ区分の詳細は スコープ・フェーズ分割 を参照する。

ドメインエンティティ役割主キー/識別子
催事EventStore店舗マスタid
催事EventCompany代理店マスタ(催事会社を統合)id
催事EventShiftシフト(1店舗×1代理店)idS-MMDD-NN
催事StartReport開始報告(朝礼)シフトに内包
催事EndReport終了報告(現場ファネル)シフトに内包
アイテムProduct商品マスタ(型番)model
アイテムSeriesシリーズマスタid
アイテムSupplier仕入先マスタid
アイテムVendor施工店マスタid
アイテムLoanCompanyローン会社(信販)マスタid
アイテムManufacturerメーカーマスタ(価格・見積ドメインlib/pricing.ts側)id
商社(卸)WholesalePrice卸金額(品目×仕入先)id
商社(卸)WholesaleCase卸案件idW-XXXX
商社(卸)WOrder発注idorderNo=PO-XXXX
商社(卸)WInvoice請求idinvoiceNo=INV-XXXX
商社(卸)WPayment入金id
商社(卸)WholesaleTask卸まわりの汎用タスクidWT-XXXX
商社(卸)DealerIntakeForm販売店発注フォーム— (独立エンティティではなくWholesaleCase生成の入口画面)

旧・独立の代理店マスタAgencylib/masters.ts)は2026-07-22の合意によりEventCompanyに統合し、マスタ画面もEventCompany一覧を表示する実装に置き換えている。廃止済みのコードはコメント付きで残置しているが、参照はしない。

erDiagram
EventStore ||--o{ EventShift : "storeId(ID参照)"
EventCompany ||--o{ EventShift : "companyId(ID参照)"
EventShift ||--o| StartReport : "内包"
EventShift ||--o| EndReport : "内包"
Manufacturer ..o{ Series : "name一致(破線)"
Series ..o{ Product : "series name一致(破線)"
Manufacturer ..o{ Product : "maker name一致(破線)"
Supplier ..o{ WholesalePrice : "supplier name一致(破線)"
WholesalePrice ..o{ Product : "itemName未連携(破線)"
WholesaleCase ||--o{ WOrder : "内包"
WholesaleCase ||--o{ WInvoice : "内包"
WholesaleCase ||--o{ WPayment : "内包"
WholesaleCase ||--o{ WholesaleTask : "caseId(ID参照/null許容)"
EventCompany ..o{ WholesaleCase : "dealer name一致(破線)"
EventShift ..o{ Deal_外部 : "channel/venue/日付 突合(破線・後続フェーズ)"
Vendor ..o{ Deal_外部 : "construction.vendor name一致(破線)"

実線はID参照、破線は名称一致による関連を示す。名称一致箇所の棚卸しと対応方針は後述する。Deal_外部は後続フェーズで確定する商談エンティティを指す。

催事ドメイン: Store / Company / Shift / Report

Section titled “催事ドメイン: Store / Company / Shift / Report”

EventStore { id, name, area?, category? }が店舗マスタである。category(系列)はTS型としては自由文字列で、系列追加時はシード側で値を追加する運用とする。

EventCompany(代理店){ id, name, contact?, business?: "催事" | "訪販" | "催事・訪販" }。代理店と催事会社のマスタは分けず、代理店マスタに一本化する(2026-07-22合意)。代理店は催事・訪販の両方を担い得るためbusiness1値で事業区分を表すが、未設定の代理店も許容する任意フィールドである。

EventShift { id, date, storeId, companyId, startTime, endTime, plannedLeader?, staffCount, note?, startReport?, endReport? }。1シフト=1店舗×1代理店の粒度で持つ。個人スタッフ単位の管理は行わず、「責任者名(plannedLeader、氏名の自由文字列)+人数(staffCount)」で運用する(2026-07-22合意)。

ShiftStatusは保存値ではなく基準時刻から都度導出する。判定順は次のとおり。

  1. 終了報告あり → 終了済み(時刻を問わない)
  2. 開始報告あり・終了報告なし・現在時刻が終了時刻超過 → 終了未報告
  3. 開始報告あり・終了報告なし・終了時刻前 → 開始済み
  4. 開始報告なし・現在時刻が開始時刻超過 → 未開始
  5. 開始報告なし・開始時刻前 → 予定

StartReport(開始報告=朝礼){ leader, emergencyContact, checks: Record<チェック項目id, boolean>, comment?, photos?, at }。既定チェック項目は7件(挨拶・声かけ/身だしなみ・気持ちの準備/ブース設営の確認/バックヤード整理/私語・スマホのルール確認/店舗ルールの確認/判断に迷ったら責任者へ)で運用する。

EndReport(終了報告=現場ファネル){ department?, lottery, seated, appointments, photos?, at }lottery/seated/appointmentsはいずれもFunnelCount { total, both(両面), single(片面) }で持ち、抽選→着座→アポの各段階を両面提案/片面提案の内訳つきで記録する。

催事↔商談の突合はchannel === "催事" && venue === 店舗名 && 作成日 === シフト日による疎結合突合とし、専用の中間テーブルは持たない(2026-07-22合意)。商談CRM本体が後続フェーズであるための設計判断である。

エンティティ主要フィールドVE社内代理店
EventStorename, area, category作成・編集参照不可(シフト経由で表示名のみ)
EventCompanyname, contact, business作成・編集自社情報の参照のみ
EventShiftdate, storeId, companyId, plannedLeader, staffCount作成・編集自社シフトの閲覧のみ
StartReportleader, emergencyContact, checks閲覧自社シフトへの登録・編集
EndReportdepartment, lottery/seated/appointments閲覧自社シフトへの登録・編集

アイテムドメイン: Product を中心とした周辺マスタ

Section titled “アイテムドメイン: Product を中心とした周辺マスタ”

Product { model(型番=主キー), kind, maker(必須), series?, name?, spec?, leadTimeDays?, stock?, note? }。型番(model)を主キーとする。

kindItemKindとして"PV" | "BT" | "EQ" | "IH" | "AC" | "付帯"の6分類を持つ。stock"潤沢" | "少" | "欠品"の3値のみを持ち、在庫数量は管理しない(在庫数量管理は本モジュールの非対象)。

Productは価格を持たない(2026-07-17方針確定)。価格は商社ドメインのWholesalePrice、および価格・見積ドメイン(lib/pricing.tsPriceBook/Part/PackageSet)側が保持する。アイテムマスタは識別・在庫情報を担い、価格は別ドメインが保持する責務分離が設計原則である。

マスタフィールド他エンティティとの関連
Seriesid, name, maker?, note?maker(文字列)がManufacturer.nameと名称一致
Supplierid, name, type(商社/メーカー直送), note?WholesalePrice.supplierと名称一致
Vendorid, name, type(自社/他社), tool(CRMチャット/ANDPAD/LINE), area?Deal.construction.vendorConstruction.vendor)と名称一致
LoanCompanyid, name, daysDirect(2社間), daysViaTrader(3社間)入金予定算出に使用する単独マスタ
Manufacturerid, namePriceBook.manufacturerIdからのみID参照。Productからは未参照

階層関係(Manufacturer > Series > Product)は名称一致(文字列一致)で成立する。対応方針はリレーションの現状を参照する。廃番概念は現状持たない(確認事項を参照)。

商社(卸)ドメイン(1): 卸金額と決済フロー

Section titled “商社(卸)ドメイン(1): 卸金額と決済フロー”

WholesalePrice { id, itemKind: "商品"|"パッケージ", itemName(フリーテキスト), supplier(商社名), purchasePrice(仕入), wholesalePrice(卸), startDate(反映日), endDate?, memo? }。現在価格はstartDate <= 基準日かつ(endDate未設定またはendDate >= 基準日)を満たす候補のうちstartDate最新(同日なら最安)を採用する。itemNameはフリーテキストでProductと未連携であり、正規化の対象とする(正規化方針を参照)。

決済区分WSettlement"前金" | "掛売" | "三社間" | "カード" | "その他"の5種で持つ。区分ごとに通る工程が異なる点が本ドメインの核である。

工程(ステップ)は7種類、settlement(決済条件確定/経理)・approval(承認番号確認/経理)・prepay(前金請求入金/経理)・order(商社/メーカーへ発注/発注担当)・deliver(納品確認/発注担当)・invoice(販売店へ請求/経理)・payment(入金消込/経理)からなる。各ステップに担当部門(経理/発注担当)が紐づく。

決済区分工程順
前金settlement → prepay → order → deliver
掛売settlement → order → deliver → invoice → payment
三社間approval → order → deliver → payment
カードapproval → order → deliver → payment
その他settlement → order → deliver → invoice → payment

前金はpaymentを独立ステップとして持たず、prepay完了(入金確認)自体が金銭ゲートを兼ねる。三社間・カードはinvoice(販売店への請求)を通らない工程列であり、この妥当性の経理確認は確認事項に記載する。

商社(卸)ドメイン(2): 案件・発注・請求・入金・タスク

Section titled “商社(卸)ドメイン(2): 案件・発注・請求・入金・タスク”

WholesaleCase { id, createdAt, dealer, settlement, approvalNo?, items[], settlementConfirmed, paymentReceived, ordered, delivered, invoiced, orders?, invoices?, payments?, memo? }。1案件が複数の発注・請求・入金を配列で束ねる集約構造である。dealerは発注元の代理店名(自由文字列、EventCompany.nameと名称一致)。元請案件(販売店/deals)とは別軸で持ち、商社目線の業務を独立して扱う設計とする。

WOrder(状態: "発注済" | "納品済")/WInvoice(状態: "請求済" | "一部入金" | "入金済")/WPaymentamountの積み上げで分割入金・残高消込に対応、専用の状態フィールドは持たない)を独立エンティティとして持つ。

WBucket(案件の現在地)は"決済待ち" | "発注待ち" | "納品待ち" | "請求待ち" | "入金待ち" | "完了"の6分類で、決済区分別の工程列と各フラグ・子エンティティの状態から都度導出する。ShiftStatusと同じ「保存せず導出する」パターンを採る。

DealerIntakeFormは独立した保存エンティティではなく、代理店からの発注入力(発注元・決済区分・明細)を受けてWholesaleCaseを直接生成する入口画面である。発注元の選択肢はEventCompany(催事ドメインの代理店マスタ)から、明細候補はWholesalePriceから取得し、両ドメインをまたぐ実装上の接続点となる。

WholesaleTask { id, caseId(null許容), title, status: "未対応"|"対応中"|"完了", assignee, dueDate, priority: "高"|"中"|"低", memo?, createdAt }。工程に載らない対応を拾う汎用タスクで、案件(WholesaleCase)に紐付くがcaseIdは未紐付け(null)も許容する。

現状の試作は「ID参照」と「名称一致(文字列一致)」の2種類の結合が混在する。名称一致箇所の棚卸しと対応方針は次のとおり。

参照元(フィールド)参照先結合キー破綻シナリオ対応方針
Product.makerManufacturer.namelib/pricing.ts文字列一致メーカー名の改称・表記変更でProduct群との紐付きが一斉に切れる第2段階でID化
Product.seriesSeries.name文字列一致シリーズ名の表記ゆれ(全角/半角、カナ表記差)で別シリーズ扱いになる第2段階でID化
Series.makerManufacturer.name文字列一致メーカー改称の影響がSeries経由でも波及する第2段階でID化
WholesalePrice.supplierSupplier.name文字列一致商社名の表記ゆれで卸金額が別商社扱いになる第1段階でID化
WholesalePrice.itemNameProduct(未連携)参照なし(フリーテキスト)品名の言い換え(型番違い・表記違い)で同一商品の卸価格が分裂する第1段階でID参照を任意追加
WholesaleCase.dealerEventCompany.name文字列一致代理店名の表記ゆれで卸案件が正しい代理店に紐付かない第1段階でID化
Deal.construction.vendorConstruction.vendorVendor.name文字列一致施工店名の表記ゆれで案件と施工店マスタの紐付きが外れる後続フェーズでID化
EventShift.plannedLeader(マスタなし)自由文字列個人名の表記ゆれで担当者集計ができない確認事項を参照

催事↔商談の疎結合突合(channel === "催事" かつ venue === 店名 かつ日付一致)も名称一致の一種であり、店名表記ゆれが起きると突合が外れるリスクを持ったまま運用する。密結合にしない理由は商談CRM本体が後続フェーズであるためである。

名称一致箇所は段階的にID参照へ置き換える。一括移行はせず、先行リリースへの影響が大きい順に3段階で進める。

  1. 第1段階:商社系のsupplier / itemNameを優先する。WholesalePrice.itemNameはフリーテキスト併用のままProduct参照(型番)を任意フィールドとして追加し、既存入力を壊さない移行とする。WholesaleCase.dealerのID化も同段階で行う。
  2. 第2段階:アイテム階層(maker/series)のID化。Manufacturer/SeriesにIDを導入し、ProductからはID参照+表示用名称のキャッシュという構成にする。名称変更時の追従が自動になる。
  3. 第3段階:催事↔商談の突合キー強化(店舗マスタIDを商談側venueに持たせる)。商談CRM設計と同時に決定する。

移行時のデータ整備(表記ゆれの名寄せ)は人手確認を要する作業として別途タスク化する。件数規模は(実データで確定)。

代替案として「名称一致を維持し、保存時バリデーション(マスタに存在する名称のみ許可)で凌ぐ」案も検討したが、段階的ID化を採用する。

横断仕様: 変更履歴・導出ステータス・権限

Section titled “横断仕様: 変更履歴・導出ステータス・権限”

変更履歴はフィールド単位で before/after を記録する共通機構である。現状Seriesと催事系エンティティ(EventStore/EventCompany/EventShift)は履歴対象外であり、対象化の要否は確認事項に記載する。

導出ステータスの共通パターンとして、ShiftStatus(催事)とWBucket(商社)はどちらも「保存せず都度導出」で統一する。状態の二重管理をしない設計原則を横断で貫く。

権限は催事ドメインのみ社内/代理店の2ロールを実装済みである(詳細は役割・権限(先行リリース第1弾)を参照)。アイテム・商社ドメインの権限設計(経理/発注担当/販売店それぞれの見え方)は工程の担当部門定義(STEP_METAassignee)から示唆されるが、ロールとしては未実装であり後続フェーズで確定する。

マスタ画面の共通定型(テーブル+ドロワー+変更履歴)に本ページの全マスタが載ることで、エンティティ追加時のUI実装コストを低く抑えている。

クライアント自作ERP「ValueOS」(正本 github.com/stanaka-blip/valueos)は受発注〜請求〜入金(後段)に強く、当方CRMは案件獲得〜見積〜催事(前段)に強い。両者は相補的な関係にあり、以下のとおり用語・テーブルが対応する。

当方(試作)ValueOS対応関係
WholesaleCasecases卸案件↔案件。dealerdealer_id(当方は名称直持ち、ValueOSはID参照)
WholesalePricedealer_sales_prices / supplier_purchase_prices卸金額
WSettlement(前金/掛売/三社間/カード/その他)case_settlements.settlement_type決済区分(2026-08-01正規化)。三社間はfinance_company/approval_number/loan_status、カードはcard_brand/card_status、前金はdeposit_rate/deposit_amountで詳細を保持
WOrderorders発注
WInvoiceinvoices請求
WPaymentpayments入金
WholesaleTasktasksタスク
DealerIntakeFormapp/dealer/orders/new(→case_registration_requests販売店発注の受付画面

詳細な機能差分・項目突合は ValueOS 差分・項目突合 を参照する。

  • 店舗マスタcategory(系列)の自由文字列運用を継続するか、enum化・マスタ化するか。
  • Product.model(型番)の変更時・重複時の扱い(別型番として再登録するか、履歴を引き継ぐか)。
  • 廃番概念の要否。現状は削除のみで廃番の概念を持たない。
  • 催事開始報告のチェック項目(既定7件)のカスタマイズ可否。
  • 三社間・カードの工程列がinvoice(販売店への請求)を通らない設計の経理確認。
  • DealerIntakeFormの認証方式・公開範囲。
  • WholesaleCaseを元請案件(deals)と別軸で持つ設計の先にある分社化の要否。
  • 正規化(第1段階のID化)を先行リリース第1弾に含めるか、後続で実施するか。
  • Series・催事系エンティティ(EventStore/EventCompany/EventShift)を変更履歴の対象に含めるか。
  • 監査観点(履歴の保持期間、削除時の履歴の扱い、代理店側操作の履歴可視範囲)。
  • EventShift.plannedLeader(責任者名)を自由文字列のまま運用するか、担当者マスタを設けるか。