位置づけ: クライアントERP(ValueOS)
本ページはValueOS(正本 github.com/stanaka-blip/valueos)の全テーブルをカタログとして一覧化する。概要・アーキテクチャが5本柱を軸にした構造の要約、業務フローが決済区分別の判定ロジックを扱うのに対し、本ページはテーブル単位の主キー・主要カラム・関連(外部キー)を網羅する参照ページである。
情報源はlib/database.types.ts(Supabase生成の型定義)・supabase/migrations配下のDDL・各テーブルの登録画面(app/**/new/page.tsx)のコードである。lib/database.types.tsは主要28テーブルを型定義済みだが、コメントに明記される通り「既存テーブルはライブスキーマから抽出した互換型」であり、外部キー制約・登録画面のコードから実在を確認できる関連テーブルが型定義の範囲外になお存在する。本ページは型定義済みの28テーブルに加え、そうした関連テーブルも含めて整理する。確認できた主要テーブルは39本である(別途、API側のレート制御専用テーブルが1本ある。本ページ末尾の「インフラ用テーブル」の節を参照)。フィールドの型・enum・制約はsupabase/migrationsのDDL(CHECK制約・NOT NULL・DEFAULT)と、lib/workflow/statusCatalog.ts・lib/caseSettlementTypes.ts・lib/threeParty/moneyEventStatus.ts等のアプリ側定数を突き合わせて確認した。
ValueOSの主キー設計は一貫している。全テーブルがUUID(idまたはrequest_id)を主キーに持ち、case_no(VE-…)・order_no・invoice_noのような人間可読の業務番号は別列として保持する。当方のProduct.model(型番を主キーとする設計)やWholesaleCase.id(W-XXXX形式の文字列を主キーとする設計)とは主キー設計の方針が異なる。詳細は当方データモデルとの対応を参照。
このページの対象はValueOSのテーブル構造(エンティティ・主キー・フィールド・enum・制約・関連)と、テーブル単位の状態遷移・登録系RPCのシーケンスである。決済区分別の発注可否・請求可否の判定ロジック(WorkflowEngineのcanOrder/canInvoice導出)は対象外とし、業務フローを参照する。当方CRMとの項目突合・機能差分の詳細は対象外とし、差分・突合を参照する。
| テーブル | 主キー | 主要カラム | 関連 |
|---|
cases | id | case_no、dealer_id、customer_name/customer_phone、site_address、order_type、order_received_date、desired_delivery_date、construction_desired_date/construction_completed_date、status、department、assigned_user、priority | dealers(dealer_idでID参照) |
case_products | id | case_id、line_type、product_id、package_id、supplier_id、quantity、purchase_price/sales_price/gross_profit(価格スナップショット)、sales_price_id/purchase_price_id(採用した価格レコードの参照)、is_manual_price、price_fetched_at | cases、products、packages、sales_prices、purchase_prices |
case_packages | id | case_id、package_id、quantity、case_product_id(case_productsとの1対1紐付) | case_products、packages |
case_package_items | id | case_package_id、product_id、source_package_item_id、quantity、unit_purchase_price/total_purchase_price(NULL可。案件登録時に価格未確定でも保存できる)、requirement_type、selection_group、商品名等のスナップショット列群、is_selected/is_added_manually/is_hidden | case_packages、products |
tasks | id | case_id(null可)、title、status、due_date、assigned_user、priority | cases(null可=未紐付けを許容) |
| テーブル | 主キー | 主要カラム | 関連 |
|---|
sales_prices | id | dealer_id、price_target_type(PRODUCT/PACKAGE)、product_id/package_id、sales_price(単価)、is_active、start_date/end_date | dealers、products/packages |
purchase_prices | id | supplier_id、price_target_type、product_id/package_id、purchase_price(単価)、is_active、start_date/end_date | suppliers、products/packages |
有効価格の判定条件は両テーブル共通で、is_active = trueかつstart_date <= 基準日かつ(end_dateが未設定またはend_date >= 基準日)を満たすレコードのうちstart_date降順の先頭1件を採用する。purchase_pricesにdealer_id列はない(仕入価格は仕入先軸のみ)。基準日は原則order_received_date。金額はROUND(単価×数量。Math.round相当)で保存し、画面プレビューは単価のみを返す。
| テーブル | 主キー | 主要カラム | 関連 |
|---|
case_settlements | id | case_id(1対1)、settlement_type(正式値: 前金/売掛/3社間決済/カード)、fee_rate/fee_amount、deposit_rate/deposit_amount、payment_terms、card_brand/card_status/card_status_updated_at、finance_company/approval_number、loan_status/loan_status_updated_at | cases |
settlement_typeは2026-08-01のマイグレーションで正規化済み。旧値「掛売」は「売掛」へ、「ローン」「三社間決済」は「3社間決済」へ移行し、「その他」はレガシー値として保持するが正式区分には含めない。判定ロジックの詳細は業務フローを参照。
| テーブル | 主キー | 主要カラム | 関連 |
|---|
three_party_money_requests | request_id | action、case_id、resource_id、status、payload_hash、response | cases(3社間金銭アクションの冪等リクエスト表) |
finance_receipts | id | case_id、finance_company、scheduled_date/scheduled_amount、actual_date/actual_amount、status、cancelled_at/cancel_reason、corrects_id(訂正元への自己参照) | cases、finance_receipts(自己参照) |
dealer_settlements | id | case_id、dealer_id、statement_no、issue_date、finance_receipt_id、invoice_id、credit_received_amount、ve_share_amount、adjustment_total_amount、payout_amount、scheduled_payout_date/actual_payout_date/actual_payout_amount、contract_date/delivery_date、status、corrects_id | cases、dealers、finance_receipts、invoices、dealer_settlements(自己参照) |
dealer_settlement_lines | id | dealer_settlement_id、sort_order、line_kind(credit_in/ve_share/transfer_fee/discount/offset/other)、description、amount、memo | dealer_settlements |
supplier_payments | id | case_id、supplier_id、order_id、due_date、scheduled_amount、paid_date/paid_amount、status、cancelled_at/cancel_reason、corrects_id | cases、suppliers、orders、supplier_payments(自己参照) |
payments | id | invoice_id、case_id、payment_date、payment_amount、payment_method、payer_name、bank_account、status(確認待ち/入金確認済/取消) | invoices |
finance_receipts(信販会社からの入金)・dealer_settlements+dealer_settlement_lines(販売店への仕切精算)・supplier_payments(仕入先への支払)は、3社間決済の案件が持つ3系統の独立した金銭実体である。paymentsは既存の入金管理(前金・売掛向け)と独立しており、3社間決済では使わない。
| テーブル | 主キー | 主要カラム | 関連 |
|---|
orders | id | case_id、supplier_id、order_no、order_date、expected_delivery_date、delivered_date、order_amount、status、memo | cases、suppliers |
order_items | id | order_id、product_id、case_product_id、quantity、unit_price、amount、memo、sort_order | orders、products、case_products |
| テーブル | 主キー | 主要カラム | 関連 |
|---|
invoices | id | case_id、invoice_no、invoice_date、due_date、invoice_amount、subtotal_ex_tax/tax_amount(2026-08-07追加、NULL可)、status | cases |
invoice_line_items | id | invoice_id、sort_order、line_kind(product/package/custom)、description、quantity、unit、unit_price_ex_tax/amount_ex_tax、tax_rate、case_product_id、source_product_id/source_package_id | invoices、case_products、products/packages |
請求明細はスナップショットであり、パッケージ内の構成商品は展開せず1行として計上する。
| テーブル | 主キー | 主要カラム | 関連 |
|---|
products | id | manufacturer_id、series_id、category、model_no、name、capacity、unit、product_type、specification、price_list_category、default_supplier_id、is_active | manufacturers、product_series、suppliers |
packages | id | manufacturer_id、series_id、name、package_code、capacity/capacity_unit、system_type、warranty_years、specification、pricing_method、default_supplier_id、is_active | manufacturers、product_series、suppliers |
package_items | id | package_id、product_id、quantity、requirement_type、sort_order | packages、products |
manufacturers | id | name、company_type、contact_name、phone、email、memo | — |
product_series | id | manufacturer_id、name、説明、is_active | manufacturers |
suppliers | id | name、supplier_type、contact_name、phone、email、order_method(発注方法)、closing_day(締日)、payment_site(支払サイト)、credit_limit(買掛上限)、memo | — |
dealers | id | name、contact_name、phone、email、address、payment_type、credit_limit(与信枠)、sales_person、memo | — |
contractors | id | name、postal_code/address/phone、delivery_name/delivery_address/delivery_phone(配送先)、receiver_name、memo、is_active | 案件への外部キー・同期は持たない独立マスタ |
company_settings | id(boolean。単一行運用) | company_name、postal_code/address/phone/fax/email、invoice_registration_number(インボイス登録番号)、bank_name/bank_branch/bank_account_type/bank_account_number/bank_account_holder | — |
staff_profiles | id(auth.users.idと一致) | display_name、is_active、is_admin | auth.users(emailはauth.users側を正式値とし本テーブルには持たない) |
すべてrequest_id(UUID)を主キーとし、status・payload_hash(入力の正規化ハッシュ)・error_code/error_message・response(結果JSON)・completed_atという共通カラム構成を持つ。書込はservice_role専用(RLSは無効化し、GRANT/REVOKEによるACLでPUBLIC/anon/authenticatedから権限を剥奪、service_roleにのみSELECT/INSERT/UPDATEを付与)。
| テーブル | 対象RPC | 参照先(対象エンティティ) |
|---|
case_registration_requests | create_case_registration(案件新規登録) | cases |
case_line_append_requests | append_case_line(既存案件への明細追加) | cases、case_products、case_packages |
product_setup_requests | create_product_setup(新規商品セットアップ) | products |
existing_product_price_setup_requests | create_existing_product_price_setup(既存商品への価格追加) | products |
supplier_purchase_price_bulk_requests | create_supplier_purchase_prices(仕入価格一括登録) | suppliers |
dealer_sales_price_bulk_requests | create_dealer_sales_prices(販売価格一括登録) | dealers |
package_bulk_setup_requests | create_package_bulk_setup(パッケージ+構成商品一括登録) | manufacturers |
product_bulk_setup_requests | create_product_bulk_setup(商品一括登録) | manufacturers |
purchase_order_create_requests | create_purchase_orders(発注一括作成。仕入先ごとにorders+order_itemsをトランザクション生成) | cases |
| テーブル | 主キー | 主要カラム | 関連 |
|---|
case_attachments | id | case_id、attachment_type、original_filename、content_type、byte_size、storage_bucket/storage_path、uploaded_by_sid/uploaded_by_user_id/uploaded_by_label、is_active、deleted_at/deleted_by_sid/deleted_by_user_id | cases |
case_attachment_upload_intents | id | attachment_id、case_id、attachment_type、original_filename、content_type、declared_byte_size、storage_bucket/storage_path、status、expires_at、completed_at | cases、case_attachments |
ファイル本体はSupabase Storageの非公開バケットcase-attachmentsにDirect-to-Storageで保存し、case_attachmentsはメタデータのみを持つ。case_attachment_upload_intentsはアップロード発行から完了までの中間状態(署名URLの有効期限管理)を扱う。
gateway_rate_limits(主キーbucket_key)はAPIゲートウェイのレート制御専用テーブルであり、業務データを持たないため上記9分類の対象外とする。
以下は各テーブルの全カラムを列挙した詳細仕様である。「必須」はNOT NULL(値の未入力を許さない)を指し、UUID主キー・created_at/updated_atのようなDB共通列は型のみ簡潔に記す。enum・CHECK制約はDDL(supabase/migrations)またはアプリ側定数(lib/workflow/statusCatalog.ts等)で確認できたものを記載し、値の集合が固定されていない自由記述列は「制約なし(自由記述)」と明記する。
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
created_at | timestamptz | ○ | now() | — |
case_no | text | - | なし | 業務番号VE-…。登録RPCで未指定時は'VE-' || epoch_msを自動採番 |
dealer_id | uuid | - | なし | FK dealers.id。登録RPCはdealers.is_active = trueの販売店のみ許可 |
customer_name | text | - | なし | 登録RPCでは必須(500字以内)。カラム自体はNULL許容 |
customer_phone | text | - | なし | 500字以内 |
site_address | text | - | なし | 登録RPCでは必須(2000字以内) |
order_type | text | - | なし | 制約なし(自由記述) |
product_name | text | - | なし | 登録RPC専用の表示名スナップショット。明細のdisplay_nameを/連結して自動生成(案件一覧の品名表示用) |
quantity | numeric | - | なし | 登録RPC専用。明細数量の合計値を自動計算して保存 |
order_received_date | date | - | なし | 登録RPCでは必須。価格の有効判定・売上/粗利集計の基準日 |
desired_delivery_date | date | - | なし | — |
delivery_address | text | - | なし | 2000字以内 |
construction_desired_date | date | - | なし | — |
construction_completed_date | date | - | なし | 完工日(実績)。2026-07-26追加。3社間決済の請求トリガーに使用(業務フロー参照) |
construction_detail | text | - | なし | 2000字以内 |
status | text | - | なし | 制約なし(自由記述)。登録RPCの初期値は新規受付 |
department | text | - | なし | 制約なし |
assigned_user | text | - | なし | 500字以内 |
priority | text | - | なし | 制約なし |
memo | text | - | なし | 2000字以内 |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
created_at | timestamptz | ○ | now() | — |
case_id | uuid | - | なし | FK cases.id |
line_type | text | ○ | なし | enum PRODUCT / PACKAGE。CHECK制約case_products_line_target_check(NOT VALID=既存行は未検証、新規行のみ拘束)でXOR:PRODUCTならproduct_id必須かつpackage_idはNULL、PACKAGEならその逆 |
product_id | uuid | - | なし | FK products.id |
package_id | uuid | - | なし | FK packages.id |
supplier_id | uuid | - | なし | FK suppliers.id。案件登録RPCでは常にNULL保存(後続の発注登録時に確定) |
quantity | numeric | - | なし | — |
purchase_price | numeric | - | なし | 仕入単価スナップショット。案件登録RPCでは常にNULL |
sales_price | numeric | - | なし | 販売単価スナップショット。同上、常にNULL |
gross_profit | numeric | - | なし | sales_price - purchase_price相当のスナップショット |
sales_price_id | uuid | - | なし | FK sales_prices.id(採用レコード) |
purchase_price_id | uuid | - | なし | FK purchase_prices.id(採用レコード) |
is_manual_price | boolean | ○ | false | 手入力価格フラグ。案件登録RPCはtrueのペイロードをINVALID_INPUTで拒否(手動価格は登録時利用不可) |
price_fetched_at | timestamptz | - | なし | 価格確定時刻。2026-07-26追加、legacy行はNULL |
memo | text | - | なし | 2000字以内 |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
created_at | timestamptz | ○ | now() | — |
case_id | uuid | - | なし | FK cases.id |
package_id | uuid | - | なし | FK packages.id |
quantity | numeric | - | なし | — |
memo | text | - | なし | — |
case_product_id | uuid | - | なし | FK case_products.id(1対1紐付) |
package_name_snapshot 他7列 | text/numeric | - | なし | package_code_snapshot/manufacturer_name_snapshot/series_name_snapshot/capacity_snapshot/capacity_unit_snapshot/system_type_snapshot/warranty_years_snapshot/specification_snapshot。登録時点のpackagesマスタ内容をコピー(スナップショット設計) |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
case_package_id | uuid | - | なし | FK case_packages.id |
product_id | uuid | - | なし | FK products.id |
source_package_item_id | uuid | - | なし | FK package_items.id(展開元) |
quantity | numeric | - | なし | package_items.quantity × case_packages.quantityで自動計算 |
unit_purchase_price / total_purchase_price | numeric | - | なし | 案件登録時はNULL保存(2026-08-01でNOT NULL制約を除去)。既存行の値は維持 |
requirement_type | text | - | なし | package_itemsから継承(必須/選択構成品の区分) |
selection_group | text | - | なし | 選択グループのキー |
product_name_snapshot/model_no_snapshot/display_name_snapshot/product_type_snapshot/category_snapshot/unit_snapshot/specification_snapshot | text | - | なし | 登録時点のproductsマスタ内容をコピー |
is_selected | boolean | - | true | 案件登録RPCは全構成品をtrueで保存 |
is_added_manually | boolean | - | false | 案件登録RPCはfalse固定 |
is_hidden | boolean | - | false | package_items.is_hidden = trueの構成品は登録RPCの展開対象から除外される |
sort_order | integer | - | 0 | package_items.sort_orderを継承 |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
case_id | uuid | - | なし | FK cases.id。NULL可(未紐付けタスクを許容) |
title | text | ○ | なし | — |
status | text | - | なし | 制約なし(自由記述) |
due_date | date | - | なし | — |
assigned_user | text | - | なし | — |
priority | text | - | なし | 制約なし |
memo | text | - | なし | — |
database.types.tsの型定義対象外だが、DDLで確認できる列構成は次のとおり。2テーブルとも同一パターンで、sales_pricesのみdealer_idを持つ。
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
dealer_id(sales_pricesのみ) | uuid | - | なし | FK dealers.id |
supplier_id(purchase_pricesのみ) | uuid | - | なし | FK suppliers.id |
price_target_type | text | ○ | 'PRODUCT' | enum PRODUCT / PACKAGE。2026-07-26追加、既存行はPRODUCTとして扱う |
product_id | uuid | - | なし | FK products.id。CHECK制約*_target_checkでXOR: price_target_type='PRODUCT'ならproduct_id必須かつpackage_idはNULL |
package_id | uuid | - | なし | FK packages.id。price_target_type='PACKAGE'なら必須かつproduct_idはNULL |
sales_price(sales_prices)/purchase_price(purchase_prices) | numeric | - | なし | 税抜単価 |
is_active | boolean | - | なし | 有効価格判定に使用 |
start_date / end_date | date | - | なし | 反映日。end_date未設定=無期限 |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
case_id | uuid | ○ | なし | FK cases.id(1対1) |
settlement_type | text | ○ | なし | 正式値: 前金/売掛/3社間決済/カード。旧値その他はレガシー保持のみで新規RPC受付対象外(caseSettlementTypes.ts) |
fee_rate | numeric | - | なし | 手数料率(%) |
fee_amount | numeric | ○ | 0(DDL DEFAULT)/登録RPCは0固定で作成 | 手数料額 |
deposit_rate / deposit_amount | numeric | - | なし | 前金の頭金率・頭金額 |
payment_terms | text | - | なし | 売掛の支払条件 |
card_brand | text | - | なし | カード決済で必須(登録RPC) |
card_status | text | - | なし | enum 未決済/処理中/決済成功/決済失敗/取消(statusCatalog.ts。コメントでは「拡張可能」と明記され、DB CHECK制約はない) |
card_status_updated_at | timestamptz | - | なし | — |
finance_company / approval_number | text | - | なし | 3社間決済で必須(登録RPC) |
loan_status | text | - | なし | enum 未申請/申請中/承認済/否認(同上、DB CHECK制約はない) |
loan_status_updated_at | timestamptz | - | なし | — |
memo | text | - | なし | — |
登録RPC(create_case_registration)は決済区分ごとに次の必須項目をアプリ層で検証する:3社間決済=finance_company・approval_number必須、カード=card_brand必須、前金・売掛=追加項目なし(該当外の列はNULL固定で上書き)。
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
request_id | uuid | ○ | なし | PK。クライアント発行UUID |
action | text | ○ | なし | enum: finance_receipt.create/.confirm/.cancel/.correct、dealer_settlement.create/.confirm/.pay/.cancel/.correct、supplier_payment.create/.pay/.cancel/.correct(moneyActionPaths.ts)。CHECK制約は非空文字列のみ強制 |
case_id | uuid | - | なし | FK cases.id(ON DELETE SET NULL) |
resource_id | uuid | - | なし | 完了時に作成/対象となった金銭レコードID |
status | text | ○ | なし | CHECK制約 enum PROCESSING/COMPLETED/FAILED |
payload_hash | text | ○ | なし | 正規化JSONのsha256(hex)。gatewayが算出 |
error_code / error_message | text | - | なし | 失敗時のみ |
response | jsonb | - | なし | 結果JSON |
completed_at | timestamptz | - | なし | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
case_id | uuid | ○ | なし | FK cases.id |
finance_company | text | ○ | なし | — |
scheduled_date / scheduled_amount | date/numeric | - | なし | 入金予定 |
actual_date / actual_amount | date/numeric | - | なし | 実入金 |
status | text | ○ | なし | DB値 enum 予定/入金済/取消(moneyEventStatus.ts)。画面表示は未入金/入金予定/入金済/期限超過/取消にさらに導出(後述「状態遷移」参照) |
cancelled_at / cancel_reason | timestamptz/text | - | なし | 取消時のみ |
corrects_id | uuid | - | なし | FK finance_receipts.id(自己参照。訂正元) |
finance_receipt.createアクションはDB制約ではなくアプリ層のガードとして、同一case_idに有効(予定/入金済)な行が既に存在する場合はCONFLICTで二重登録を拒否する。
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
case_id | uuid | ○ | なし | FK cases.id |
dealer_id | uuid | ○ | なし | FK dealers.id |
statement_no | text | - | なし | 精算書番号 |
issue_date | date | - | なし | 発行日 |
finance_receipt_id | uuid | - | なし | FK finance_receipts.id |
invoice_id | uuid | - | なし | FK invoices.id。既存のinvoicesとは別書類(仕切清算書) |
credit_received_amount | numeric | ○ | なし | 信販入金額スナップショット |
ve_share_amount | numeric | ○ | なし | VE取り分 |
adjustment_total_amount | numeric | ○ | なし | dealer_settlement_linesの調整明細合計(正数で保持) |
payout_amount | numeric | ○ | なし | 算出式は「業務ルール」節を参照 |
scheduled_payout_date / actual_payout_date / actual_payout_amount | date/date/numeric | - | なし | — |
contract_date / delivery_date | date | - | なし | — |
status | text | ○ | なし | DB値 enum 下書き/確定/支払済/取消。画面表示は下書き/支払予定/支払済/期限超過/取消 |
cancelled_at / cancel_reason | timestamptz/text | - | なし | — |
corrects_id | uuid | - | なし | FK dealer_settlements.id(自己参照) |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
dealer_settlement_id | uuid | ○ | なし | FK dealer_settlements.id |
sort_order | integer | - | なし | — |
line_kind | text | ○ | なし | enum credit_in/ve_share/transfer_fee/discount/offset/other(dealerSettlementCalc.ts)。payout_amount算出の対象はtransfer_fee/discount/offset/other(調整4種)のみ、credit_in/ve_shareはヘッダ表示用の内訳行で算出には使わない |
description | text | ○ | なし | — |
amount | numeric | ○ | なし | 控除額を正数で保持 |
memo | text | - | なし | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
case_id | uuid | ○ | なし | FK cases.id |
supplier_id | uuid | ○ | なし | FK suppliers.id |
order_id | uuid | - | なし | FK orders.id。1発注に対して支払は1:N |
due_date | date | - | なし | 支払予定日 |
scheduled_amount | numeric | ○ | なし | — |
paid_date / paid_amount | date/numeric | - | なし | — |
status | text | ○ | なし | DB値 enum 予定/支払済/取消。画面表示は支払予定/支払済/期限超過/取消 |
cancelled_at / cancel_reason | timestamptz/text | - | なし | — |
corrects_id | uuid | - | なし | FK supplier_payments.id(自己参照) |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
invoice_id | uuid | - | なし | FK invoices.id |
case_id | uuid | - | なし | FK cases.id |
payment_date | date | - | なし | — |
payment_amount | numeric | - | なし | — |
payment_method | text | - | なし | — |
payer_name / bank_account | text | - | なし | 2026-07-26の入金管理拡張で追加 |
status | text | - | '未入金'(DDL既定のレガシー値) | 制約なし(自由記述)。アプリの運用値は確認待ち/入金確認済/取消(旧値入金確認中は確認待ち相当)。DDL既定未入金は将来確認待ちへ整理予定とコメント明記 |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
case_id | uuid | - | なし | FK cases.id |
supplier_id | uuid | - | なし | FK suppliers.id |
order_no | text | - | なし | 部分UNIQUE(NULL/空文字を除く)が任意で付与可能 |
order_date | date | - | なし | — |
expected_delivery_date / delivered_date | date | - | なし | — |
order_amount | numeric | - | なし | create_purchase_ordersRPCはクライアント値を信用せず、サーバ側でorder_itemsのamount合計を再計算して上書きする |
status | text | - | なし | 制約なし(自由記述) |
memo | text | - | なし | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
order_id | uuid | ○ | なし | FK orders.id(ON DELETE CASCADE) |
product_id | uuid | - | なし | FK products.id(ON DELETE SET NULL) |
case_product_id | uuid | - | なし | FK case_products.id(ON DELETE SET NULL)。移行・照合用の任意紐付け |
quantity | numeric | ○ | 1 | — |
unit_price | numeric | - | なし | 仕入単価 |
amount | numeric | - | なし | — |
memo | text | - | なし | — |
sort_order | integer | ○ | 0 | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
case_id | uuid | ○ | なし | FK cases.id |
invoice_no | text | ○ | なし | — |
invoice_date | date | ○ | なし | — |
due_date | date | - | なし | — |
invoice_amount | numeric | ○ | なし | 税込金額 |
subtotal_ex_tax / tax_amount | numeric | - | なし | 2026-08-07追加。NULL可(無ければfloor(invoice_amount / 1.1)で印刷時に導出) |
status | text | - | なし | 制約なし(自由記述)。取消は各種集計から除外される運用値 |
memo | text | - | なし | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
invoice_id | uuid | ○ | なし | FK invoices.id |
sort_order | integer | - | なし | — |
line_kind | text | ○ | なし | enum product/package/custom |
description | text | ○ | なし | 500字以内(画面バリデーション) |
quantity | numeric | - | 0 | 画面バリデーションは1以上を要求 |
unit | text | - | なし | — |
unit_price_ex_tax | numeric | - | 0 | 値引き行は負値を許容 |
amount_ex_tax | numeric | - | 0 | Math.round(unit_price_ex_tax × quantity) |
tax_rate | numeric | - | 0 | 画面既定0.1。バリデーションは0〜1の範囲を要求 |
case_product_id | uuid | - | なし | FK case_products.id |
source_product_id / source_package_id | uuid | - | なし | FK products.id/packages.id。パッケージ明細は展開せず1行のままsource_package_idのみ設定 |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
manufacturer_id | uuid | - | なし | FK manufacturers.id |
series_id | uuid | - | なし | FK product_series.id |
category | text | - | なし | — |
model_no | text | - | なし | 型番 |
name | text | - | なし | — |
capacity | text | - | なし | — |
unit | text | - | なし | — |
product_type | text | - | なし | — |
specification | text | - | なし | — |
price_list_category | text | - | なし | — |
default_supplier_id | uuid | - | なし | FK suppliers.id |
is_active | text(boolean値の格納ではない) | - | なし | 型定義上string | null。packages.is_activeがboolean型であるのとは異なる(後述「エッジケース」参照) |
memo | text | - | なし | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
manufacturer_id / series_id | uuid | - | なし | FK |
name | text | - | なし | — |
package_code | text | - | なし | — |
capacity | numeric | - | なし | — |
capacity_unit | text | - | なし | — |
system_type | text | - | なし | — |
warranty_years | numeric | - | なし | — |
specification | text | - | なし | — |
pricing_method | text | - | なし | — |
default_supplier_id | uuid | - | なし | FK suppliers.id |
is_active | boolean | - | なし | products.is_active(text型)とは型が異なる |
memo | text | - | なし | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
package_id | uuid | - | なし | FK packages.id |
product_id | uuid | - | なし | FK products.id |
quantity | numeric | - | なし | 1パッケージあたりの構成数量 |
requirement_type | text | - | なし | 必須/選択の区分 |
selection_group | text | - | なし | 選択グループのキー |
display_name | text | - | なし | 案件登録時のスナップショット元 |
sort_order | integer | - | なし | — |
is_hidden | boolean | - | false | trueの構成品は案件登録RPCの展開対象外 |
これらは業務ロジックによる状態遷移を持たないマスタであり、既存の「データ(分類別テーブル一覧)」節の主要カラム一覧を正とする。個別に補足すべき制約のみ記す。
| テーブル | 補足 |
|---|
suppliers | closing_day/payment_siteは"月末"/"20日"・"翌月末払い"/"翌々月10日払い"のような自然文で保持され、DBレベルのenum制約はない(当方supplierPaymentDueDateのようなパーサ側で解釈) |
dealers | credit_limit(与信枠)は数値列のみで、消込・残高計算のロジックはValueOS側テーブル定義には存在しない(業務フロー側の判定を参照) |
contractors | 案件(cases)への外部キーを持たない独立マスタ。配送先はdelivery_*列に別途保持 |
company_settings | idがboolean型のPKという特殊設計で、単一行(true固定)運用を強制する |
staff_profiles | idはFK auth.users.id(ON DELETE CASCADE)。is_active = falseはValueOSログイン拒否・セッション無効化を意味する。is_admin = trueは/staffユーザー管理(招待・無効化・再招待)の実行権限のみを意味し、「本格的なRBACではない」とDDLコメントに明記される |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
request_id | uuid | ○ | なし | PK。クライアント発行UUID |
対象FK(case_id/product_id/supplier_id/dealer_id等、テーブルにより異なる) | uuid | - | なし | 完了時にのみ設定されるものが多い |
status | text | ○ | なし | CHECK制約 enum PROCESSING/COMPLETED/FAILED(case_registration_requests・purchase_order_create_requests・product_setup_requests・dealer_sales_price_bulk_requests等で確認) |
payload_hash | text | ○ | なし | md5(payload::text)。jsonbはキー順正規化されるため入力順序差を吸収する |
error_code / error_message | text | - | なし | 失敗時のみ |
response | jsonb | - | なし | 成功時の結果JSON |
created_at | timestamptz | ○ | now() | — |
completed_at | timestamptz | - | なし | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK。case_attachment_upload_intents.attachment_idと事前採番で一致させる |
case_id | uuid | ○ | なし | FK cases.id |
attachment_type | text | ○ | なし | enum estimate/contract/drawing/photo/invoice_doc/other(constants.ts) |
original_filename | text | ○ | なし | 255字以内 |
content_type | text | ○ | なし | 拡張子とMIMEの組み合わせをEXTENSION_MIME_MAPで検証 |
byte_size | integer | ○ | なし | 1バイト以上・20MB以下 |
storage_bucket | text | ○ | 'case-attachments' | — |
storage_path | text | ○ | なし | cases/{case_id}/{attachment_id}/file.{ext}。元ファイル名は使わずASCII固定名で保存 |
uploaded_by_sid / uploaded_by_user_id / uploaded_by_label | text/uuid/text | - | なし | uploaded_by_user_idはFK staff_profiles.id(2026-08-08追加) |
is_active | boolean | ○ | true | 論理削除フラグ |
deleted_at / deleted_by_sid / deleted_by_user_id | timestamptz/text/uuid | - | なし | — |
1案件あたり有効添付は20件・合計100MBまで(pending状態の予約分も合算してカウント)。
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
id | uuid | ○ | 自動採番 | PK |
attachment_id | uuid | ○ | なし | 完了時に作成されるcase_attachments.idと一致(事前採番) |
case_id | uuid | ○ | なし | FK cases.id |
attachment_type / original_filename / content_type | — | ○ | なし | case_attachmentsと同一制約 |
declared_byte_size | integer | ○ | なし | クライアント申告サイズ。完了時にStorage実体のサイズと突合 |
storage_bucket / storage_path | text | ○ | なし | — |
status | text | ○ | なし | enum pending/completed/expired/abandoned |
uploaded_by_sid / uploaded_by_user_id | text/uuid | - | なし | — |
expires_at | timestamptz | ○ | なし | 発行から2時間(UPLOAD_INTENT_TTL_SECONDS) |
completed_at | timestamptz | - | なし | — |
| フィールド | 型 | 必須 | 既定 | 制約/備考 |
|---|
bucket_key | text | ○ | なし | PK |
window_started_at | timestamptz | ○ | なし | — |
hit_count | integer | ○ | 0 | — |
updated_at | timestamptz | ○ | now() | — |
以下はテーブルの列に格納される値そのものの遷移である。「いつ発注/請求してよいか」という業務判定は対象外とし、業務フローを参照する。
case_registration_requestsをはじめとする9つの申請系テーブルとthree_party_money_requestsは、すべてPROCESSING→COMPLETED/FAILEDという共通の状態遷移を持つ。同一request_idの再送はpayload_hashが一致する限り安全に再処理でき、FAILEDからは同一request_idで再試行できる(payload_hashが異なる場合はREQUEST_ID_CONFLICTで拒否)。
stateDiagram-v2
[*] --> PROCESSING: INSERT(新規request_id)
PROCESSING --> COMPLETED: RPCトランザクション成功
PROCESSING --> FAILED: 例外発生(バリデーション/DB制約違反)
FAILED --> PROCESSING: 同一request_id・同一payload_hashで再送
COMPLETED --> COMPLETED: 同一request_id・同一payload_hashで再送(idempotent_replay)
PROCESSING --> [*]: 別セッションが処理中に再送された場合はエラー返却(行はPROCESSINGのまま)
DB保存値は最小集合(3〜4値)で、画面表示は期限超過・未入金などを純関数で導出する。取消(取消)は集計から除外される。
stateDiagram-v2
[*] --> 予定: create(finance_receipts/supplier_paymentsはscheduled、dealer_settlementsは下書きから開始)
予定 --> 入金済_支払済: confirm/pay
予定 --> 取消: cancel
入金済_支払済 --> 取消: cancel(訂正が必要な場合はcorrects_idで新規行を追加し元行は変更しない)
入金済_支払済 --> [*]
取消 --> [*]
note right of 予定
画面表示はscheduled_date/due_dateと当日を比較し
「未入金/入金予定/支払予定」または「期限超過」に導出
(dealer_settlementsのみ予定の前に「下書き」を持つ)
end note
stateDiagram-v2
[*] --> pending: createUploadIntent(signed upload URL発行)
pending --> completed: completeUploadIntent成功(Storage実体・サイズ検証OK)
pending --> expired: expires_at経過後にcleanup検出(Storageオブジェクトはbest-effortで削除)
pending --> abandoned: 署名URL発行失敗/サイズ不一致/20MB超過
completed --> completed: 同一intentへの再complete呼び出し(既存case_attachmentsを返すだけの冪等応答)
sequenceDiagram
participant UI as 社内フォーム
participant API as APIルート
participant RPC as create_case_registration(SECURITY INVOKER)
participant Req as case_registration_requests
participant DB as cases/case_settlements/case_products/case_packages/case_package_items
UI->>API: POST {request_id, case, settlement, lines}
API->>RPC: rpc(payload) ※service_roleのみEXECUTE可
RPC->>Req: SELECT ... WHERE request_id FOR UPDATE(advisory lock併用)
alt COMPLETED(再送)
Req-->>RPC: 既存response
RPC-->>API: ok:true, idempotent_replay:true
else PROCESSING(処理中に再送)
RPC-->>API: ok:false, error_code=REGISTRATION_FAILED
else FAILED(再試行)
RPC->>Req: UPDATE status=PROCESSING(created_at更新)
else 新規
RPC->>Req: INSERT status=PROCESSING
end
RPC->>RPC: settlement_type検証(前金/売掛/3社間決済/カードのみ)+区分別必須項目検証
RPC->>DB: INSERT cases(status='新規受付')
RPC->>DB: INSERT case_settlements(fee_amount=0固定、区分別詳細列)
loop 明細ごと(PRODUCT or PACKAGE)
RPC->>DB: INSERT case_products(価格3列は常にNULL、is_manual_price=false)
opt line_type=PACKAGE
RPC->>RPC: package_items(is_hidden=false)が1件以上か検証(0件はPACKAGE_ITEMS_NOT_FOUND)
RPC->>DB: INSERT case_packages(マスタ内容をスナップショット)
loop 構成品ごと
RPC->>DB: INSERT case_package_items(数量=構成数量×明細数量、価格2列はNULL)
end
end
end
RPC->>Req: UPDATE status=COMPLETED, case_id, response
RPC-->>API: ok:true, case_id, case_no
API-->>UI: 登録完了
sequenceDiagram
participant UI as 社内フォーム
participant API as APIルート
participant Intents as case_attachment_upload_intents
participant Storage as Storage(case-attachments, private)
participant Attach as case_attachments
UI->>API: POST /intent {case_id, attachment_type, filename, content_type, byte_size}
API->>API: 期限切れpendingのbest-effortクリーンアップ
API->>API: 拡張子×MIME一致・20MB以下を検証
API->>API: 案件存在確認・件数(20)/合計サイズ(100MB)クォータ確認
API->>Intents: INSERT status=pending, expires_at=+2h
API->>Storage: createSignedUploadUrl(storage_path)
alt 発行失敗
API->>Intents: UPDATE status=abandoned
API-->>UI: エラー
end
API-->>UI: signed_url, token, expires_at
UI->>Storage: PUT signed_url(直接アップロード)
UI->>API: POST /complete {intent_id}
API->>Intents: SELECT(status=pending・expires_at未経過を検証)
API->>API: クォータ再検証(自分自身のpending分は除外)
API->>Storage: list(folder)で実体・実サイズ確認
alt サイズ超過/不一致
API->>Storage: remove(storage_path)
API->>Intents: UPDATE status=abandoned
API-->>UI: エラー(BYTE_SIZE_MISMATCH/FILE_TOO_LARGE)
end
API->>Attach: INSERT(is_active=true)
API->>Intents: UPDATE status=completed, completed_at
API-->>UI: 添付メタデータ
sequenceDiagram
participant GW as Gateway API
participant RPC as execute_three_party_money
participant Ledger as three_party_money_requests
participant Target as finance_receipts/dealer_settlements/supplier_payments
GW->>GW: finance_receipt.createのみ事前ガード:同一case_idの有効行(予定/入金済)があればCONFLICT
GW->>RPC: rpc(payload {request_id, action, case_id, resource_id?, ...})
RPC->>Ledger: request_id照合(COMPLETED→replay/PROCESSING→REQUEST_IN_PROGRESS/新規→INSERT PROCESSING)
alt action = *.create
RPC->>Target: INSERT(status=予定 or 下書き)
else action = *.confirm / *.pay
RPC->>Target: UPDATE status=入金済/確定/支払済, actual_*
else action = *.cancel
RPC->>Target: UPDATE cancelled_at, cancel_reason, status=取消
else action = *.correct
RPC->>Target: INSERT 新規行(corrects_id=元id)※元行はUPDATE/DELETEしない
end
RPC->>Ledger: UPDATE status=COMPLETED, resource_id, response
RPC-->>GW: ok:true, resource_id, status
- スナップショット設計:
case_products/case_packages/case_package_items/invoice_line_itemsは登録時点の価格・商品名をコピー保持する。マスタ側の価格・名称変更は既存の案件・請求データに影響しない。
- 価格の有効判定:
sales_prices/purchase_pricesとも、is_activeかつ有効期間内のレコードをstart_date降順で1件採用する。共通ロジックであり画面ごとの個別実装はない。
- 価格対象のXOR制約:
sales_prices/purchase_prices/case_productsはいずれもCHECK制約で「商品を指す列」と「パッケージを指す列」の同時設定・同時NULLを禁止する(price_target_typeまたはline_typeで分岐)。case_products側の制約はNOT VALIDで追加されており、既存行は未検証のまま残る場合がある。
- 請求税ルール: 消費税率は10%固定。明細(
invoice_line_items)単位では課税せず、請求書(invoices)単位で1回だけtax = Math.floor(subtotal_ex_tax × 0.10)を計算する。invoice_amount(税込)はsubtotal_ex_tax + tax。印刷時はスナップショット列(subtotal_ex_tax/tax_amount)があればその値、無ければfloor(invoice_amount / 1.1)で税抜を逆算する。
- 金額の丸め: 価格・請求明細の金額は
Math.round(単価 × 数量)(roundMoneyTotal)で統一する。値引き行は単価に負値を許容する。
- 仕切精算(dealer_settlements)の算出式:
payout_amount = credit_received_amount − ve_share_amount − Σ(adjustment_lines.amount)。合算対象はdealer_settlement_lines.line_kindがtransfer_fee/discount/offset/otherの行のみで、credit_in/ve_share行はヘッダ表示用の内訳でありこの合算には含めない。各入力値はMath.floorで円未満を切り捨ててから演算する。
- 仕切初期額の提案値: 調整なしの初期仕切額は
Math.max(0, 信販入金額 − 有効請求額合計)。取消の請求は合計から除外する。transfer_feeは自動控除しない(手動で調整明細に追加する運用)。
- 申請系の冪等性: 申請系テーブルはすべて
request_id(クライアント発行UUID)とpayload_hash(md5(payload::text))により、同一リクエストの再送を安全に処理する。payload_hashが一致すればCOMPLETEDはそのまま再送応答(idempotent_replay:true)、不一致ならREQUEST_ID_CONFLICTで拒否する。書込権限はservice_roleに限定し、クライアントから直接INSERT/UPDATEはできない。
- 案件登録時の価格非確定:
create_case_registrationはcase_products/case_package_itemsの価格列を常にNULLで保存し、is_manual_price=trueのペイロードをINVALID_INPUTで拒否する(手動価格は登録時利用不可)。価格確定は別工程(発注・請求登録時)に委ねる設計である。
- 発注金額の再計算:
create_purchase_ordersはクライアントが送ったorder_amount/item.amountを信用せず、サーバ側でorder_itemsから再計算して保存する。
- 訂正・取消の履歴保全:
finance_receipts/dealer_settlements/supplier_paymentsは確定済み金額を直接UPDATEしない。訂正はcorrects_idで元レコードを参照する新規行、取消はcancelled_at/cancel_reasonの記録で扱い、常に履歴が残る。
- 添付ファイルの上限: 1ファイル20MBまで、1案件あたり有効添付20件・合計100MBまで(
pending予約分も合算)。署名アップロードURLの有効期限は2時間、ダウンロードURLは60秒。
- 発注可否・請求可否の判定:
case_settlements.settlement_typeを起点にWorkflowEngineが機械判定する。テーブル構造上の前提条件(例: 売掛の請求可否はorders全件のstatus/delivered_dateに依存)は本ページの各テーブルに現れるが、判定ロジックそのものは業務フローを正本とする。
erDiagram
cases ||--o| case_settlements : "1:1"
cases ||--o{ case_products : "line_type"
case_products ||--o| case_packages : "1対1紐付"
case_packages ||--o{ case_package_items : "展開明細"
cases ||--o{ orders : "supplier_id別"
orders ||--o{ order_items : "明細"
cases ||--o{ invoices : "請求"
invoices ||--o{ invoice_line_items : "明細スナップショット"
invoices ||--o{ payments : "入金(複数可)"
cases ||--o{ three_party_money_requests : "3社間ledger"
cases ||--o{ finance_receipts : "信販入金"
cases ||--o{ dealer_settlements : "仕切精算"
dealer_settlements ||--o{ dealer_settlement_lines : "明細"
cases ||--o{ supplier_payments : "仕入先支払"
cases ||--o{ tasks : "case_id(null可)"
cases ||--o{ case_attachments : "添付"
case_attachments ||--o| case_attachment_upload_intents : "アップロード予約"
products }o--|| manufacturers : "manufacturer_id"
products }o--o| product_series : "series_id"
packages ||--o{ package_items : "構成部材"
products ||--o{ sales_prices : "dealer別価格"
products ||--o{ purchase_prices : "supplier別原価"
テーブル群と対応する主要画面の関係を示す。各画面の業務仕様は概要・アーキテクチャ、判定ロジックは業務フローを参照する。
| 画面領域 | 対応テーブル群 |
|---|
app/cases | cases、case_products、case_packages、case_package_items、case_settlements |
app/orders | orders、order_items |
app/invoices | invoices、invoice_line_items |
app/payments | payments |
app/tasks | tasks |
app/products、app/packages、app/series、app/manufacturers | products、packages、package_items、product_series、manufacturers |
app/suppliers、app/dealers、app/contractors | suppliers、dealers、contractors |
app/prices、app/sales-prices | purchase_prices、sales_prices |
app/dealer-settlements | dealer_settlements、dealer_settlement_lines |
app/queues | 申請系テーブル全般(処理状況の監視) |
app/dealer/orders/new | cases、case_products/case_packages/case_package_items、case_settlements(販売店発注の入口。現行はRPC非経由の直接INSERT) |
app/settings、app/staff、app/admin | company_settings、staff_profiles |
案件の添付ファイル操作(アップロード予約・完了・一覧・署名ダウンロード・論理削除)はcase_attachments/case_attachment_upload_intentsに対応し、案件詳細画面から呼び出される。すべてサーバー側API(lib/caseAttachments/caseAttachmentsCore.ts)を経由し、クライアントから当該テーブルへの直接INSERT/UPDATEはない。
社内ユーザーの認証はSupabase Authが担い、staff_profiles.is_activeでアカウント有効性(falseはログイン拒否・セッション無効化)、is_adminで/staffユーザー管理(招待・無効化・再招待)の実行可否のみを判定する。DDLコメントに「本格的なRBACではない」と明記される限定的な権限フラグであり、それ以外の画面・操作の権限分岐はテーブル定義からは確認できない。
テーブルへの書込経路は、マイグレーション(supabase/migrations)のRLS/ACL設定から2系統に分かれることが確認できる。
- 旧来型(RLS無効化+open policy):
cases/case_products/case_packages/orders/order_items/invoices/payments/case_settlementsなど、Phase1以前から存在する中核業務テーブルの多くはRLSを無効化した上でanon/authenticatedロールにFOR ALL USING (true)の開放ポリシーとGRANT SELECT, INSERT, UPDATE, DELETEを与えている(例: case_settlementsは2026-07-24のマイグレーションで「既存のpublishable key運用に合わせる」目的で明示的にこの方式へ変更された)。したがってこれらのテーブルは、Supabaseのpublishable keyを持つクライアントであればテーブルレベルの制限なく直接書き込める設計である。
- 新設型(service_role専用): 申請系9テーブル・
three_party_money_requestsはRLSを無効化した上でPUBLIC/anon/authenticatedからREVOKE ALLし、service_roleにのみSELECT/INSERT/UPDATEを付与する。staff_profiles/case_attachments/case_attachment_upload_intentsはRLSを有効化した上でポリシーを一切定義せず(=anon/authenticatedはデフォルト拒否)、同様にservice_roleのみへGRANTする。いずれの方式でもservice_roleはRLSを迂回するため、実効的な結果は同じ「service_role専用」である。
この2系統の併存は、案件本体(cases系)やレガシーの受発注・請求・入金テーブルには行レベルのアクセス制御が実質的に存在せず、Phase1以降に新設されたテーブル(申請系RPC・3社間金銭・添付・スタッフ管理)のみがservice_role限定で保護されている、という設計上の非対称を意味する。販売店発注フォーム(app/dealer/orders/new)は社内認証の外側にあり、案件系テーブル(open policy側)へ直接INSERTする(申請系の冪等RPC経路は現行未接続)。
- CHECK制約が
NOT VALID: case_products.line_typeのXOR制約は既存行を検証しないモードで追加されている。したがって新規INSERT/UPDATEは拘束されるが、制約導入以前に作成された行がproduct_idとpackage_idを両方NULL(またはいずれも非NULL)で保持している可能性はDBレベルでは排除できない。
products.is_activeの型不整合: products.is_activeはtext | null型で、真偽値としてではなく文字列(もしくは未設定)として保存される。同じ役割を持つpackages.is_activeはboolean型であり、2テーブル間で型が揃っていない。フィルタ処理では文字列比較が必要になる。
- 申請系リクエストの競合: 同一
request_idで異なるpayload_hashのリクエストはREQUEST_ID_CONFLICTで拒否される。PROCESSING中に同一request_idが再送されると(同一ペイロードであっても)REGISTRATION_FAILED系のエラーで一旦拒否され、処理完了後の再送でのみCOMPLETEDの再送応答(idempotent_replay:true)を受け取れる。
- パッケージ構成品ゼロ件: 案件登録時に指定した
package_idのpackage_items(is_hidden=false)が0件の場合はPACKAGE_ITEMS_NOT_FOUNDで登録全体が失敗し、casesを含む全INSERTがロールバックされる。
- 添付アップロードの整合性検証失敗:
completeUploadIntent時にStorage実体のサイズが申告値(declared_byte_size)を超えていた場合はオブジェクトを削除しintentをabandonedにしてBYTE_SIZE_MISMATCHを返す。申告より小さいサイズは許容される(実体確認を優先)。
- 添付予約の期限切れ孤児:
expires_atを過ぎたpendingのcase_attachment_upload_intentsは、対応するcase_attachmentsが既に存在すればcompletedへ同期し、存在しなければStorageオブジェクトをbest-effortで削除した上でexpiredにする(cleanupExpiredAttachmentIntents)。この処理は失敗しても呼び出し元の処理を止めない。
- 3社間金銭の重複作成防止:
finance_receipt.createはDB制約ではなくアプリ層のガードとして、同一案件に有効な(予定/入金済)行が既にあればCONFLICTで拒否する。他の.createアクション(dealer_settlement.create/supplier_payment.create)に同様のガードがあるかは本ページの情報源からは確認できない。
- 旧
settlement_type値の残存: その他は2026-08-01の正規化以降、新規登録では受け付けないレガシー値として残る。既存行の値は変更されないため、settlement_type = 'その他'の案件が存在し得る。
orders.status/tasks.status/cases.statusは自由記述: これら3列にDBレベルのenum・CHECK制約はなく、画面・RPCが渡す任意の文字列がそのまま保存される(cases.statusの初期値新規受付のみRPCが固定する)。
dealer_settlement.create/supplier_payment.createアクションにfinance_receipt.createと同様の重複作成ガードが存在するかは、本ページで参照したexecuteMoneyActionCore.ts冒頭部分のみでは確認できていない。
case_products.line_typeのCHECK制約がNOT VALIDのままVALIDATE CONSTRAINTされているかどうかは今回確認したマイグレーション時点では未実施であり、以降の対応状況は本ページの情報源からは追跡できない。
当方の新エンティティ全体像は先行スコープのデータモデルを参照。ValueOSとの構造的な対応は次のとおりである。
当方(/data-model/entities-phase1/) | ValueOS | 対応関係 |
|---|
WholesaleCase | cases | 概ね一致。当方はIDW-XXXXが主キー、ValueOSはUUID主キー+case_no表示番号 |
WOrder/WInvoice/WPayment(WholesaleCaseに内包する配列) | orders/invoices/payments(独立テーブル) | 構造差。ValueOSは番号・日付・金額を個別エンティティで保持し、当方はフラグ集約に近い内包配列 |
WholesalePrice(品目×仕入先の単一テーブル) | sales_prices(販売店×商品/パッケージ)/purchase_prices(仕入先×商品/パッケージ) | 軸差。ValueOSは販売価格・仕入価格を別テーブルに分離し、対象を商品/パッケージで型分けする |
WholesaleTask | tasks | フィールド構成が一致(case_idnull許容/status/due_date/assignee/priority) |
EventCompany(代理店・名称一致でWholesaleCaseと結合) | dealers(cases.dealer_idでID参照) | ValueOSはID参照、当方は名称一致。当方側の正規化課題 |
Supplier | suppliers | 概ね一致。ValueOSはorder_method/closing_day/payment_site/credit_limitなど経理項目を持ち、当方のSupplierマスタは薄い |
Product(型番modelが主キー) | products(UUID主キー+model_no列) | 主キー設計が異なる。ValueOSは型番変更をUUID固定のまま列更新で扱える |
なし(決済区分はWholesaleCase.settlementに内包) | case_settlements(案件と1対1の独立テーブル) | ValueOSは決済詳細(手数料・頭金・カード情報・ローン情報)を専用テーブルに分離 |
WDealerSettlement(adjustmentを「符号付き調整額」としてpayout = creditReceived − veShare + adjustmentで計算) | dealer_settlements + dealer_settlement_lines(adjustment_total_amountは正数の控除合計) | 算出式の符号規約が異なる。ValueOSはpayout_amount = credit_received_amount − ve_share_amount − Σ(調整明細の正数控除額)で、調整明細は常に控除額(正数)として積み上げる設計 |
| なし | three_party_money_requests/finance_receipts/dealer_settlements(+明細)/supplier_payments | 当方にWFinanceReceipt/WSupplierPayment/WDealerSettlementとして概ね対応する型がある一方、冪等リクエストledger(three_party_money_requests)に相当するテーブルは当方に無い |
項目単位の詳細な突合・差分は差分・突合を正本とする。用語の対応表は用語統一を参照する。