コンテンツにスキップ

見積・価格表

位置づけ: 後続フェーズ

見積・価格表モジュールは、仕入先または催事から受領する価格表を基に部材の仕入原価・希望小売を管理し、そこから販売価格を時間軸付きで導出し、案件に紐づく御見積書を作成・印刷する一連の機能である。価格データは外部システム参照ではなく CRM 側で保持する。

設計の出自は、クライアント自作 ERP ValueOS のテーブル設計(products / suppliers / dealer_sales_prices / supplier_purchase_prices)と、実物の価格表 PDF・御見積書ひな形である。crm_app には lib/pricing.ts とストア(store/usePricing.ts)、および価格表・見積・帳票印刷・販売価格マスタの画面一式が動作する形で実装済みである。現場では現状、仕入(原価)内訳をすべて手入力しており、価格表からの自動引き当てが最優先要望である(定例会ログ 参照)。

第1弾リリース(催事+アイテム+商社)の範囲外であり、後続フェーズで実装する。第1弾との接続点は次の3つである。

接続先内容
アイテム(商品マスタ)productNo(型番)で部材・販売価格・商品マスタを横断して同定する
卸金額仕入価格・販売店向け卸価格の時間軸設計を本モジュールと統一する
催事催事専用価格表を独立の source として管理し、催事・会場とひも付ける

Supabase への永続化・移行は未実装である。現時点はフロント内ストア(zustand persist、キー crm:pricing)で完結する。

構造は「価格表(PriceBook)が版のヘッダ」「部材(Part)がその明細行」「パッケージ(PackageSet)が部材の組合せ」「販売価格(SalesPrice)が売値の時間軸」「見積(Quote)が出口」の5層で組み立てる。

正規化方針は、仕入価格を価格表側、販売価格を SalesPrice 側に分離することである。同じ商品でも仕入軸と販売軸を独立に改定できる。商品の同定キーは productNo(型番)であり、Part・SalesPrice・アイテム(商品マスタ)を横断で結ぶ共通キーとする。

版管理は「行を書き換えず、反映日付きの新しい行を積む」イミュータブル方式を採る。過去見積の再現性を担保する設計である。

エンティティ軸(キー)主な属性ValueOS対応
PriceBooksource × manufacturerId × title × effectiveDatecategory, note対応する版管理テーブルなし(ギャップ参照)
PartbookId × noproductNo, retailPrice, netPrice, tierproducts
PackageSetbookId × idmembers(no・qty・tier), setPricepackages, case_packages, case_package_items
SalesPriceproductNo × channel × startDateprice, endDate, memodealer_sales_prices
Quoteid(dealId で案件と相互参照)systemItems, workItems, discounts, taxRate対応するテーブルなし。確定後は orders 相当に接続する

PriceBook は { source(仕入先/催事名), category, manufacturerId, title(件名), effectiveDate(反映日)} で構成する。同一の仕入先×メーカーで反映日違いの版が並存する。

category は「商社 / 施工店 / アライアンス / 催事」の4区分とし、受領した価格表 PDF 群のフォルダ構成に対応する。現在版の導出は currentBooks() が「反映日 <= 基準日」の版のうち、source×メーカー×件名ごとに最新の1件を採用する。

催事専用価格表は独立の source として扱い、催事 モジュールの会場・催事とひも付ける。

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 は { name, members: PackageMember[], setPrice } で構成する。members は価格表内の部材No+数量の組合せである。

PackageMembertier を持つ。部材そのものの区分(Part.tier)とは別に、そのパッケージでの必要度を表現する二重構造である。setPrice は部材積上げと独立に設定でき、セット割引を表現する。パッケージは価格表(bookId)に従属し、版が変わればパッケージも新版に載せ直す。

卸金額マスタ(wholesalePricing.ts)側でも itemKind として「商品 / パッケージ」を区別しており、本モジュールのパッケージ単位の卸価格設定と整合させる(卸金額 参照)。

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 側(retailPricenetPrice)で引き当てており、SalesPriceは現状、参照系マスタとして独立している。

同じ「反映日パターン」を卸金額(仕入価格+販売店向け卸価格 × 仕入先 × 反映日)にも適用し、価格系テーブルの時間軸設計を統一する。過去日を基準日にすれば当時の価格を再現できるため、見積・帳票の事後検証、および価格改定の予約登録(未来日の反映日)が可能である。

価格種別金額項目導出関数
価格表(PriceBook × Part、仕入)source × manufacturerId × title × effectiveDatenetPrice / retailPricecurrentBooks()
販売価格(SalesPrice)productNo × channel × startDate/endDatepricecurrentSalesPrice()
卸金額(WholesalePrice、商社モジュール)仕入先 × effectiveDatepurchasePrice / 卸価格本モジュールと様式を統一する

試作画面は次の7画面であり、サイドバー「価格・見積」に集約する(実装 app/src/app/routes.tsx の NAV 構成に準拠)。メーカーマスタ(/manufacturers)は Manufacturer の一覧・登録画面として動作するが、サイドバー「マスタ」側に集約されており本グループには含まれない。帳票(/quote-print/$quoteId)は見積詳細画面から遷移するルートとして動作するが、NAVには掲載されない。

画面パス内容
価格表/pricing価格表一覧、部材+パッケージの詳細
価格照合/price-compare仕入先横断の価格照合・最安抽出
仕切(原価)/costbook品目別の仕入原価・粗利(画面名は実装NAVラベルに準拠。用語整理は確認事項参照)
販売価格/sales-pricesSalesPrice の一覧・登録
卸金額/master-wholesale-price仕入価格・販売店向け卸価格の反映日管理(卸金額 と共通画面)
手数料条件/fees商流別の手数料条件
見積/quotes見積の一覧・新規作成・編集

登録UI(新規価格表・部材/パッケージ追加)は動作する。PDF/CSVからの一括取込は未実装である。

見積は宛名・件名・支払条件+システム(商品)明細①・工事明細②・割引(負値・赤字表示)・消費税で構成し、実物の御見積書ひな形(①小計+②小計−割引+税)を踏襲する。

明細 QuoteItemamount(販売額)と 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 永続化のテーブル設計、価格改定の監査ログ、税率変更時の扱いを確定する必要がある。
  • 催事価格表と催事モジュールの参照方向・キー設計を確定する必要がある。