見積・価格表
位置づけ: 後続フェーズ
見積・価格表モジュールは、仕入先または催事から受領する価格表を基に部材の仕入原価・希望小売を管理し、そこから販売価格を時間軸付きで導出し、案件に紐づく御見積書を作成・印刷する一連の機能である。価格データは外部システム参照ではなく CRM 側で保持する。
設計の出自は、クライアント自作 ERP ValueOS のテーブル設計(products / suppliers / dealer_sales_prices / supplier_purchase_prices)と、実物の価格表 PDF・御見積書ひな形である。crm_app には lib/pricing.ts とストア(store/usePricing.ts)、および価格表・見積・帳票印刷・販売価格マスタの画面一式が動作する形で実装済みである。現場では現状、仕入(原価)内訳をすべて手入力しており、価格表からの自動引き当てが最優先要望である(定例会ログ 参照)。
スコープ/対象
Section titled “スコープ/対象”第1弾リリース(催事+アイテム+商社)の範囲外であり、後続フェーズで実装する。第1弾との接続点は次の3つである。
| 接続先 | 内容 |
|---|---|
| アイテム(商品マスタ) | productNo(型番)で部材・販売価格・商品マスタを横断して同定する |
| 卸金額 | 仕入価格・販売店向け卸価格の時間軸設計を本モジュールと統一する |
| 催事 | 催事専用価格表を独立の source として管理し、催事・会場とひも付ける |
Supabase への永続化・移行は未実装である。現時点はフロント内ストア(zustand persist、キー crm:pricing)で完結する。
構造は「価格表(PriceBook)が版のヘッダ」「部材(Part)がその明細行」「パッケージ(PackageSet)が部材の組合せ」「販売価格(SalesPrice)が売値の時間軸」「見積(Quote)が出口」の5層で組み立てる。
正規化方針は、仕入価格を価格表側、販売価格を SalesPrice 側に分離することである。同じ商品でも仕入軸と販売軸を独立に改定できる。商品の同定キーは productNo(型番)であり、Part・SalesPrice・アイテム(商品マスタ)を横断で結ぶ共通キーとする。
版管理は「行を書き換えず、反映日付きの新しい行を積む」イミュータブル方式を採る。過去見積の再現性を担保する設計である。
| エンティティ | 軸(キー) | 主な属性 | ValueOS対応 |
|---|---|---|---|
| PriceBook | source × manufacturerId × title × effectiveDate | category, note | 対応する版管理テーブルなし(ギャップ参照) |
| Part | bookId × no | productNo, retailPrice, netPrice, tier | products |
| PackageSet | bookId × id | members(no・qty・tier), setPrice | packages, case_packages, case_package_items |
| SalesPrice | productNo × channel × startDate | price, endDate, memo | dealer_sales_prices |
| Quote | id(dealId で案件と相互参照) | systemItems, workItems, discounts, taxRate | 対応するテーブルなし。確定後は orders 相当に接続する |
価格表(PriceBook)
Section titled “価格表(PriceBook)”PriceBook は { source(仕入先/催事名), category, manufacturerId, title(件名), effectiveDate(反映日)} で構成する。同一の仕入先×メーカーで反映日違いの版が並存する。
category は「商社 / 施工店 / アライアンス / 催事」の4区分とし、受領した価格表 PDF 群のフォルダ構成に対応する。現在版の導出は currentBooks() が「反映日 <= 基準日」の版のうち、source×メーカー×件名ごとに最新の1件を採用する。
催事専用価格表は独立の source として扱い、催事 モジュールの会場・催事とひも付ける。
部材(Part)
Section titled “部材(Part)”Part は価格表の1行であり、{ no(表内No), name, productNo, retailPrice(希望小売価格), netPrice(貴社仕入=原価), tier } で構成する。実装コード(lib/pricing.ts)上の型コメントは netPrice を「貴社仕切(仕入)」と表記するが、卸金額モジュールでの「仕切」(販売店向け卸価格)とは別概念であり、表記の整理は確認事項とする。
netPrice は値引き行を負値で表現する。これは値引きも部材の1行として価格表に載る実 PDF の構造をそのまま採用したものである。retailPrice は欠損を許容する任意項目である。
見積明細(QuoteEditor)で価格表の部材から引き当てる際、売値(amount)は「retailPrice があればそれを採用し、無ければ netPrice」の2段で決定し、cost には常に netPrice(仕入原価)を保持する。下記の SalesPrice を参照した自動引当は未実装であり、currentSalesPrice() は見積作成フローから呼び出されていない。
tier(区分)は「必須 / 選択必須 / オプション」の3値である。見積作成時、必須は自動投入、選択必須は択一、オプションは任意追加とする。
パッケージ(PackageSet)
Section titled “パッケージ(PackageSet)”PackageSet は { name, members: PackageMember[], setPrice } で構成する。members は価格表内の部材No+数量の組合せである。
PackageMember は tier を持つ。部材そのものの区分(Part.tier)とは別に、そのパッケージでの必要度を表現する二重構造である。setPrice は部材積上げと独立に設定でき、セット割引を表現する。パッケージは価格表(bookId)に従属し、版が変わればパッケージも新版に載せ直す。
卸金額マスタ(wholesalePricing.ts)側でも itemKind として「商品 / パッケージ」を区別しており、本モジュールのパッケージ単位の卸価格設定と整合させる(卸金額 参照)。
販売価格(SalesPrice)と時間軸
Section titled “販売価格(SalesPrice)と時間軸”SalesPrice は { productNo, channel(商流/代理店。空=標準), price(税抜), startDate(反映日), endDate } で構成する。ValueOS の dealer_sales_prices に対応する。
現在価格の導出関数 currentSalesPrice() は、productNo × channel で「startDate <= 基準日 <= endDate(endDate 無しは無期限)」に該当するもののうち、反映日が最新の1件を採用する。ただし SalesPrice.channel は自由文字列であり(シードには「訪問販売」「催事」のほか「カインズ」のような個別代理店名も混在する)、CRM の Deal.channel(訪問販売/催事/反響/パートナー の4値enum)とは語彙が一致しない。案件から自動で該当商流の価格を引く連携も未実装であり、currentSalesPrice() は見積作成フロー(QuoteEditor)からは呼び出されていない。見積の売値は前述のとおり価格表 Part 側(retailPrice→netPrice)で引き当てており、SalesPriceは現状、参照系マスタとして独立している。
同じ「反映日パターン」を卸金額(仕入価格+販売店向け卸価格 × 仕入先 × 反映日)にも適用し、価格系テーブルの時間軸設計を統一する。過去日を基準日にすれば当時の価格を再現できるため、見積・帳票の事後検証、および価格改定の予約登録(未来日の反映日)が可能である。
| 価格種別 | 軸 | 金額項目 | 導出関数 |
|---|---|---|---|
| 価格表(PriceBook × Part、仕入) | source × manufacturerId × title × effectiveDate | netPrice / retailPrice | currentBooks() |
| 販売価格(SalesPrice) | productNo × channel × startDate/endDate | price | currentSalesPrice() |
| 卸金額(WholesalePrice、商社モジュール) | 仕入先 × effectiveDate | purchasePrice / 卸価格 | 本モジュールと様式を統一する |
試作画面は次の7画面であり、サイドバー「価格・見積」に集約する(実装 app/src/app/routes.tsx の NAV 構成に準拠)。メーカーマスタ(/manufacturers)は Manufacturer の一覧・登録画面として動作するが、サイドバー「マスタ」側に集約されており本グループには含まれない。帳票(/quote-print/$quoteId)は見積詳細画面から遷移するルートとして動作するが、NAVには掲載されない。
| 画面 | パス | 内容 |
|---|---|---|
| 価格表 | /pricing | 価格表一覧、部材+パッケージの詳細 |
| 価格照合 | /price-compare | 仕入先横断の価格照合・最安抽出 |
| 仕切(原価) | /costbook | 品目別の仕入原価・粗利(画面名は実装NAVラベルに準拠。用語整理は確認事項参照) |
| 販売価格 | /sales-prices | SalesPrice の一覧・登録 |
| 卸金額 | /master-wholesale-price | 仕入価格・販売店向け卸価格の反映日管理(卸金額 と共通画面) |
| 手数料条件 | /fees | 商流別の手数料条件 |
| 見積 | /quotes | 見積の一覧・新規作成・編集 |
登録UI(新規価格表・部材/パッケージ追加)は動作する。PDF/CSVからの一括取込は未実装である。
業務ルール/ワークフロー
Section titled “業務ルール/ワークフロー”見積は宛名・件名・支払条件+システム(商品)明細①・工事明細②・割引(負値・赤字表示)・消費税で構成し、実物の御見積書ひな形(①小計+②小計−割引+税)を踏襲する。
明細 QuoteItem は amount(販売額)と cost(仕入原価)を併記し、粗利=amount − cost を見積単位で導出する(quoteGrossProfit())。これは後続の KPI予実 における名目粗利の接続点になる。
作成フローは、価格表またはパッケージを選択し、tier に従って部材を投入(必須=自動、選択必須=択一、オプション=任意)した後、数量・割引を調整する。案件(Deal)連携では、DealDetail から dealId 付きで見積を作成し、案件⇔見積を相互参照する。
帳票は /quote-print/$quoteId(見積詳細から遷移。NAV非掲載)で御見積書PDFのレイアウトを画面再現する(マイナス表記・担当者・注記位置を含む)。精算書・発注書への展開は本モジュールの対象外とする。
アイテムマスタ・卸金額との正規化
Section titled “アイテムマスタ・卸金額との正規化”第1弾のアイテム(商品マスタ)を商品の正本とし、Part.productNo / SalesPrice.productNo はアイテムの型番へ外部キー参照する形に寄せる(アイテム(商品マスタ) 参照)。
仕入先の名称は商社モジュールの仕入先マスタ(SUPPLIERS)を単一参照とし、価格表 source との名寄せルールを定める。仕切(原価)マスタ(costbook:画面名は実装NAVラベルに準拠、内容は品目別の仕入原価・粗利)は価格表 Part へ統合する暫定データである。手数料条件(fees: 商流別)は商流軸のデータを持つが、粗利計算等への接続は未実装である。加えて SalesPrice.channel(自由文字列)とは軸のキー表記が揃っておらず、商流軸の統一は確認事項とする。
ValueOS では価格の版管理を独立テーブルで持たず、case_products に価格スナップショット(purchase_price / sales_price / gross_profit)を案件登録時点の値として記録する方式を採る。本モジュールの PriceBook / SalesPrice による時間軸管理とは設計思想が異なるため、正規化・接続方針は ValueOS連携ギャップ と データモデル を参照する。
将来、価格表PDF・商社明細のスキャンをAIによって自動取込(OCR/構造化)する構想があり、価格表の登録工数削減を目的とする(AIエージェント 参照)。代理店へのSaaS提供時は、価格表・卸金額を商社機能側のマスタとして切り出し、CRMは参照する構成へ再編する可能性がある。
productNoの同定キーをアイテムマスタの型番採番規約とどう一致させるか。現試作は文字列一致による仮実装である。- 価格表の
netPrice(仕入原価)と卸金額のpurchasePrice(仕入原価)は別テーブルで併存しており、重複整理の方針を商社モジュール側と合意する必要がある。 - 「仕切」の呼称が実装内で不統一である。卸金額モジュール(
wholesalePricing.ts)では仕切=販売店向け卸価格(wholesalePrice。仕入先からのpurchasePriceとは別軸)を指すが、本モジュールのPart.netPriceはコード上「貴社仕切(仕入)」と表記され、実質は仕入原価を指す。画面名(NAVの「仕切(原価)」)も含め、用語統一を商社モジュール側と合意する必要がある。 SalesPrice.channel(自由文字列)とDeal.channel(訪問販売/催事/反響/パートナーの4値enum)の語彙不一致をどちらに寄せるか、また案件からの自動引当(現状未実装)を実装する場合の変換ルールを確定する必要がある。- 見積の値引きに対する承認フロー(上長承認・値引き権限)は未実装であり、要件ヒアリングが必要である。
- Supabase 永続化のテーブル設計、価格改定の監査ログ、税率変更時の扱いを確定する必要がある。
- 催事価格表と催事モジュールの参照方向・キー設計を確定する必要がある。