ValueOS 業務フロー
位置づけ: クライアントERP(ValueOS)
ValueOSは案件(cases)に対して1つの決済区分(case_settlements.settlement_type)を紐付け、決済区分ごとに宣言された条件(SETTLEMENT_RULES)をWorkflowEngineが解釈して発注可否(canOrder)・請求可否(canInvoice)・現在の状態(currentState)・次アクション(nextAction)・担当(assignee)を機械的に導出する。判定ロジックはすべてlib/workflow配下の純関数に集約し、画面はこの判定結果を表示するレイヤーに徹する。if/elseの分岐をUIや各フォームに直接書かず、決済区分を追加・変更する場合はSETTLEMENT_RULESという設定オブジェクトを増やすことで対応する設計である。
決済区分が「3社間決済」の案件は、発注・請求とは別に、信販会社からの入金(finance_receipts)・販売店への仕切精算(dealer_settlements)・仕入先への支払(supplier_payments)という3つの金銭実体を持つ。これらはexecute_three_party_moneyという冪等なRPC経路を通じてのみ書き込まれ、three_party_money_requestsテーブルが実行結果を記録する。販売店発注フォーム(app/dealer/orders/new)による案件起票は、この金銭フローとは別の独立した登録経路であり、本ページで両者を区別して扱う。
スコープ/対象
Section titled “スコープ/対象”| 項目 | 内容 |
|---|---|
| 対象 | WorkflowEngine(canOrder/canInvoice/現在状態の判定)、決済区分別の工程進行、三社間金銭フロー(信販入金・仕切精算・仕入先支払)、販売店発注フォームの登録経路、各テーブルの全フィールド仕様と権限(RLS/GRANT) |
| 対象外 | ダッシュボード集計基準(valueos/overviewを参照)、cases/orders/invoices本体のフィールド網羅(valueos/data-modelを参照) |
| 情報源 | ValueOS正本(github.com/stanaka-blip/valueos)のlib/workflow・lib/threeParty・app/dealer/orders/new・supabase/migrations |
| 関連ページ | features/wholesale-settlement、redesign/valueos-gap、glossary |
データ(エンティティ)— 全フィールド仕様
Section titled “データ(エンティティ)— 全フィールド仕様”case_settlements(案件決済条件・1案件1行)
Section titled “case_settlements(案件決済条件・1案件1行)”| フィールド | 型 | 必須 | 既定値 | 制約・備考 |
|---|---|---|---|---|
id | uuid | ○ | gen_random_uuid() | PK |
case_id | uuid | ○ | — | cases.id参照(ON DELETE CASCADE)。UNIQUE制約により1案件1行 |
settlement_type | text | ○ | — | DB側にCHECK制約なし。正式値・エイリアスの妥当性はSETTLEMENT_RULES(アプリ側)のみで担保する |
fee_rate | numeric | – | NULL | 決済手数料率(%) |
fee_amount | numeric | ○ | 0 | 決済手数料額 |
deposit_rate | numeric | – | NULL | 前金率(%) |
deposit_amount | numeric | – | NULL | 前金額。前金のcanOrder判定(入金確認)の比較基準 |
payment_terms | text | – | NULL | 支払条件の自由記述 |
card_brand | text | – | NULL | カード会社名(カード区分で使用) |
finance_company | text | – | NULL | 信販会社名(3社間決済で使用)。create_case_registrationRPC(Ver1.2)は3社間決済で必須とする |
approval_number | text | – | NULL | 承認番号(3社間決済で使用)。同RPCで3社間決済必須 |
loan_status | text | – | NULL | ローン進捗。DB側にCHECK制約なし。値の妥当性はアプリ側LOAN_STATUSES(未申請/申請中/承認済/否認)のみ |
loan_status_updated_at | timestamptz | – | NULL | |
card_status | text | – | NULL | カード決済状況。DB側にCHECK制約なし。値の妥当性はアプリ側CARD_STATUSES(未決済/処理中/決済成功/決済失敗/取消)のみ |
card_status_updated_at | timestamptz | – | NULL | |
memo | text | – | NULL | 自由記述。DDL未適用環境向けの__workflow_v1__:{...}フォールバック格納先も兼ねる(後述) |
created_at/updated_at | timestamptz | ○ | now() | updated_atはトリガーで自動更新(決済区分正規化などの一括UPDATE時は一時的に無効化して実行された実績あり) |
cases(本ページで参照する列のみ)
Section titled “cases(本ページで参照する列のみ)”| フィールド | 型 | 必須 | 既定値 | 制約・備考 |
|---|---|---|---|---|
construction_completed_date | date | – | NULL | 完工日(実績)。3社間決済のcanInvoice判定に使用 |
finance_receipts(信販会社からの入金)
Section titled “finance_receipts(信販会社からの入金)”| フィールド | 型 | 必須 | 既定値 | 制約・備考 |
|---|---|---|---|---|
id | uuid | ○ | gen_random_uuid() | PK |
case_id | uuid | ○ | — | cases.id参照(ON DELETE RESTRICT) |
finance_company | text | ○ | — | 非空チェック |
scheduled_date | date | – | NULL | |
scheduled_amount | numeric | ○ | 0 | >= 0 |
actual_date | date | – | NULL | 確定(入金済)時に設定 |
actual_amount | numeric | – | NULL | NULLまたは>= 0 |
status | text | ○ | '予定' | CHECK: 予定/入金済/取消 |
memo | text | – | NULL | |
cancelled_at/cancel_reason | timestamptz/text | – | NULL | 取消時に設定 |
corrects_id | uuid | – | NULL | 自テーブル参照(ON DELETE RESTRICT)。訂正チェーンの元レコードを指す |
dealer_settlements(仕切清算書兼販売店支払)
Section titled “dealer_settlements(仕切清算書兼販売店支払)”| フィールド | 型 | 必須 | 既定値 | 制約・備考 |
|---|---|---|---|---|
id | uuid | ○ | gen_random_uuid() | PK |
case_id | uuid | ○ | — | cases.id参照(RESTRICT) |
dealer_id | uuid | ○ | — | dealers.id参照(RESTRICT)。案件のdealer_idと一致必須(RPCで検証。案件側のdealer_idがNULLの場合のみ検証をスキップ) |
statement_no | text | – | NULL | |
issue_date | date | – | NULL | |
finance_receipt_id | uuid | – | NULL | finance_receipts.id参照(RESTRICT)。同一案件・取消以外のみ許可 |
invoice_id | uuid | – | NULL | invoices.id参照(RESTRICT)。同一案件のみ許可 |
credit_received_amount | numeric | ○ | 0 | >= 0。信販入金額スナップショット |
ve_share_amount | numeric | ○ | 0 | >= 0。当社取り分スナップショット |
adjustment_total_amount | numeric | ○ | 0 | >= 0。調整明細(控除)の合計スナップショット |
payout_amount | numeric | ○ | 0 | credit_received_amount − ve_share_amount − adjustment_total_amount。負値は作成・訂正時にAPI層・RPC層の両方で拒否 |
scheduled_payout_date | date | – | NULL | |
actual_payout_date | date | – | NULL | |
actual_payout_amount | numeric | – | NULL | NULLまたは>= 0 |
contract_date/delivery_date | date | – | NULL | |
status | text | ○ | '下書き' | CHECK: 下書き/確定/支払済/取消 |
memo | text | – | NULL | |
cancelled_at/cancel_reason | timestamptz/text | – | NULL | |
corrects_id | uuid | – | NULL | 自テーブル参照(RESTRICT) |
dealer_settlement_lines(仕切調整明細)
Section titled “dealer_settlement_lines(仕切調整明細)”| フィールド | 型 | 必須 | 既定値 | 制約・備考 |
|---|---|---|---|---|
id | uuid | ○ | gen_random_uuid() | PK |
dealer_settlement_id | uuid | ○ | — | dealer_settlements.id参照(RESTRICT) |
sort_order | int | ○ | 1 | |
line_kind | text | ○ | — | CHECK: credit_in/ve_share/transfer_fee/discount/offset/other |
description | text | ○ | — | 非空チェック |
amount | numeric | ○ | 0 | >= 0。調整種別(transfer_fee/discount/offset/other)は控除額を正数で保持し、payout_amount計算に合算する。credit_in/ve_shareは表示用行で合算対象外 |
memo | text | – | NULL |
明細はアプリ層で最大50件(MAX_LINES)に制限される(DB側に件数上限のCHECKはない)。品名・摘要(description)は最大200文字、memoは最大2000文字。
supplier_payments(仕入先支払)
Section titled “supplier_payments(仕入先支払)”| フィールド | 型 | 必須 | 既定値 | 制約・備考 |
|---|---|---|---|---|
id | uuid | ○ | gen_random_uuid() | PK |
case_id | uuid | ○ | — | cases.id参照(RESTRICT) |
supplier_id | uuid | ○ | — | suppliers.id参照(RESTRICT) |
order_id | uuid | – | NULL | orders.id参照(RESTRICT)。同一案件の発注のみ、かつ発注側supplier_idと一致必須(発注に仕入先が設定されている場合)。NULL可=案件単位の支払も許容し、同一order_idへの複数行も許容する |
due_date | date | – | NULL | |
scheduled_amount | numeric | ○ | 0 | >= 0 |
paid_date | date | – | NULL | |
paid_amount | numeric | – | NULL | NULLまたは>= 0 |
status | text | ○ | '予定' | CHECK: 予定/支払済/取消 |
memo | text | – | NULL | |
cancelled_at/cancel_reason | timestamptz/text | – | NULL | |
corrects_id | uuid | – | NULL | 自テーブル参照(RESTRICT) |
信販入金が完了していることは仕入先支払登録の前提条件としない(supplier_paymentsはfinance_receiptsのステータスを参照しない独立系統)。
three_party_money_requests(3社間金銭APIの冪等リクエスト台帳)
Section titled “three_party_money_requests(3社間金銭APIの冪等リクエスト台帳)”| フィールド | 型 | 必須 | 既定値 | 制約・備考 |
|---|---|---|---|---|
request_id | uuid | ○ | — | PK。クライアントのIdempotency-Keyヘッダとセッション情報からderiveThreePartyMoneyRequestIdが導出 |
action | text | ○ | — | MoneyActionのいずれか(下表)。非空チェック |
case_id | uuid | – | NULL | cases.id参照(ON DELETE SET NULL) |
resource_id | uuid | – | NULL | 完了時に対象金銭レコードのIDを格納 |
status | text | ○ | — | CHECK: PROCESSING/COMPLETED/FAILED |
payload_hash | text | ○ | — | md5(payload::text)。同一request_idで異なる内容が来た場合はREQUEST_ID_CONFLICT |
error_code/error_message | text | – | NULL | 失敗時のみ設定 |
response | jsonb | – | NULL | 完了・失敗時のレスポンス本体 |
created_at/completed_at | timestamptz | ○/– | now()/NULL |
case_registration_requests(案件登録RPCの冪等リクエスト台帳)
Section titled “case_registration_requests(案件登録RPCの冪等リクエスト台帳)”| フィールド | 型 | 必須 | 既定値 | 制約・備考 |
|---|---|---|---|---|
request_id | uuid | ○ | — | PK |
case_id | uuid | – | NULL | cases.id参照(ON DELETE SET NULL) |
status | text | ○ | — | CHECK: PROCESSING/COMPLETED/FAILED |
payload_hash | text | ○ | — | md5(payload::text) |
error_code/error_message | text | – | NULL | |
response | jsonb | – | NULL | |
created_at/completed_at | timestamptz | ○/– | now()/NULL |
create_case_registrationRPCから使用されるが、現行のapp/dealer/orders/new画面はこのRPCを呼び出していない(後述)。
決済区分と正規化
Section titled “決済区分と正規化”case_settlements.settlement_typeの正式値は4つ。旧値は読み取り互換のエイリアスとしてresolveSettlementRuleが吸収する。DB側にはsettlement_typeの値を制限するCHECK制約は存在せず、正式値・エイリアスの判定はすべてアプリ側SETTLEMENT_RULESの設定に委ねられている。
| 正式値 | エイリアス(読取互換の旧値) |
|---|---|
| 前金 | 前金 |
| 売掛 | 売掛、掛売 |
| 3社間決済 | 3社間決済、ローン、三社間決済、3社間、三社間 |
| カード | カード |
「その他」はレガシー値として保持されるが正式区分には含まれない。settlementTypeがいずれの正式値・エイリアスにも一致しない場合、WorkflowEngineはruleKey: null・currentState: "決済条件未設定"・canOrder: false・canInvoice: falseを返し、警告に「未対応の決済区分です」または「決済区分が未設定です」を積む。
当方のWholesaleCaseは決済区分を前金/掛売/三社間/カード/その他の5値で管理しており、ValueOSの正式値(前金/売掛/3社間決済/カード)とは表記が異なる。対応は本ページ末尾の対応表を参照。
WorkflowEngineの判定モデル
Section titled “WorkflowEngineの判定モデル”evaluateWorkflow(ctx: WorkflowContext)は決済区分ごとの設定(SETTLEMENT_RULES)から次を導出し、WorkflowResultとして返す。
| フィールド | 内容 |
|---|---|
canOrder | 発注可否。canOrderWhen条件配列がすべて真のとき真 |
canInvoice | 請求可否。canInvoiceWhen条件配列がすべて真のとき真 |
currentState / assignee / nextAction | 現在の工程表示。phases配列を先頭から走査し、completeWhenが未達成の最初のフェーズを返す(canOrder/canInvoiceとは別ロジック) |
warnings | 発注・請求がブロックされている理由の文言(複数可、重複排除) |
billingClosingDate / paymentDueDate | 売掛のみ算出する請求締め日・支払期日 |
canOrder/canInvoiceとcurrentStateは同じ条件セットを参照するが、評価の入口が異なる独立したロジックである。settlement_confirmed条件は決済区分が正式値に解決できた時点で常に真になる(別途「確定」操作を要しない)。
判定ロジックのフロー
Section titled “判定ロジックのフロー”flowchart TD
A["evaluateWorkflow(ctx)"] --> B{"resolveSettlementRule<br/>正式値/エイリアスに一致?"}
B -- 不一致 --> B1["ruleKey=null<br/>currentState=決済条件未設定<br/>canOrder=false / canInvoice=false<br/>warning: 未対応の決済区分です / 決済区分が未設定です"]
B -- 一致 --> C["settlementConfirmed = true"]
C --> D["canOrder = canOrderWhen 全条件を評価"]
C --> E["canInvoice = canInvoiceWhen 全条件を評価"]
C --> F["resolveCurrentPhase:<br/>phases を先頭から走査し<br/>completeWhen 未達成の最初の phase を返す"]
D --> G{"rule.datePolicy.trigger<br/>= all_orders_delivered?(売掛のみ)"}
G -- Yes --> H["computeCreditDates:<br/>最終納品日 起点に<br/>billingClosingDate/paymentDueDate を算出"]
G -- No --> I["billingClosingDate/paymentDueDate = null"]
D --> J{"canOrder = false?"}
J -- Yes --> K["orderBlockReason で警告文言を1件追加"]
E --> L{"canInvoice = false かつ rule.key ≠ 売掛?"}
L -- Yes --> M["invoiceBlockReason で警告文言を1件追加"]
H --> N{"canInvoice = false?"}
N -- Yes --> O["納品日欠損 or 未納品の個別警告を追加(売掛専用文言)"]
K --> P["warnings を重複排除して返却"]
M --> P
O --> P
I --> P
F --> P
P --> Q["WorkflowResult"]
決済区分別の発注可否・請求可否条件
Section titled “決済区分別の発注可否・請求可否条件”| 決済区分 | canOrder条件 | canInvoice条件 |
|---|---|---|
| 前金 | 入金確認(payment_confirmed_from_billing) | 決済区分の解決(実質常時true) |
| 売掛 | 決済区分の解決(実質常時true) | 案件内の全発注が納品済(all_orders_delivered) |
| 3社間決済 | loan_statusが「承認済」 | construction_completed_date(完工日)が設定済 |
| カード | card_statusが「決済成功」 | card_statusが「決済成功」(発注と同一条件) |
前金は請求可否が実質常時trueになる点、カードは発注と請求が同一条件で同時に解放される点が、区分間で構造が異なる箇所である。
入金確認(payment_confirmed_from_billing)は、案件に紐づく入金(payments)のうち確認済みステータスの金額を合算し、deposit_amount(頭金予定額)が設定されていれば合計がそれ以上か、未設定なら合計が0円超かで判定する。全発注納品済(all_orders_delivered)は、キャンセル等を除いたアクティブな発注(lib/status/activeRecords.tsと同一の判定)が1件以上あり、かつ全件のstatusが「納品済」かつdelivered_dateが入力済みであることを要求する。発注が0件の案件は未達とみなす。
決済区分別の工程(phases)
Section titled “決済区分別の工程(phases)”| 決済区分 | STEP1 | STEP2 | STEP3 | STEP4 |
|---|---|---|---|---|
| 前金 | 決済条件未設定(経理) | 入金確認待ち(経理) | 発注可能(発注担当) | — |
| 売掛 | 決済条件未設定(経理) | 発注可能(発注担当、全納品まで) | 請求可能(経理) | — |
| 3社間決済 | 決済条件未設定(経理) | ローン承認待ち(経理) | 完工報告待ち(営業事務) | 請求可能(経理) |
| カード | 決済条件未設定(経理) | カード決済待ち(経理) | 発注・請求可能(発注担当) | — |
「決済条件未設定」フェーズはsettlement_confirmedが常時真であるため実質的に即時通過し、UI上で長く滞在することはない。
工程がもっとも長い3社間決済を例に、案件が辿る状態遷移を示す。
stateDiagram-v2
[*] --> 決済条件未設定
決済条件未設定 --> ローン承認待ち: settlement_confirmed(常時true・即通過)
ローン承認待ち --> 完工報告待ち: loan_status = 承認済
完工報告待ち --> 請求可能: construction_completed_date 設定
請求可能 --> [*]
note right of ローン承認待ち
canOrder = true になるのも
loan_status = 承認済 の時点
end note
note right of 完工報告待ち
canInvoice = true になるのも
completion_date_set の時点
end note
発注・請求ブロック時の警告文言
Section titled “発注・請求ブロック時の警告文言”| 状況 | 警告文言 |
|---|---|
| 前金・入金未確認で発注不可 | 前金の入金確認前のため発注できません(請求・入金データで判定) |
| 3社間決済・ローン未承認で発注不可 | ローン未承認のため発注できません |
| カード・決済未成立で発注不可 | カード決済前のため発注できません |
| カード・決済未成立で請求不可 | カード決済前のため請求できません |
| 3社間決済・完工日未登録で請求不可 | 完工日登録前のため請求できません |
売掛・納品済だがdelivered_date欠損 | 納品日が登録されていません |
| 売掛・未納品の発注が残っている | 売掛の請求は、案件内の全発注が納品済になってから可能です |
売掛の請求不可は上記の個別メッセージのみを出し、汎用メッセージ(「現在の決済区分ルールでは請求できません」)は出さない実装になっている。
売掛の請求締め日・支払期日
Section titled “売掛の請求締め日・支払期日”売掛(datePolicy.trigger: all_orders_delivered)は、案件内の全発注が納品済となった時点の最終納品日(アクティブな発注のdelivered_dateの最大値)を起点に、月末締め・翌月末払いでbillingClosingDate・paymentDueDateを自動算出する(endOfMonth/endOfNextMonth。UTC正午基準でタイムゾーン境界の日跨ぎを避ける)。他の決済区分にはこの日付ポリシーはない。
請求後の画面表示は原則invoices.due_date(保存値)を優先し、resolvePaymentDueDisplayが次のいずれかを返す。
kind | 条件 | 表示ラベル |
|---|---|---|
confirmed | 有効請求(取消以外)のdue_dateが存在(複数あれば最も早い日を採用) | 支払期限 |
planned | 有効請求のdue_dateがなく、ルール計算値(paymentDueDate)が存在 | 入金予定日(予定) |
none | いずれも存在しない、または売掛以外の決済区分 | 表示なし |
保存値がルール計算値と異なる場合はisMismatch: trueを返すのみで、自動UPDATEは行わない。案件横断でこの不一致を検出するfindCreditDueDateMismatchesも同じ比較ロジックを使うSELECT専用関数であり、backfillは提供しない。3社間決済にはこの表示ロジックを適用しない(ruleKey !== "売掛"で即座に空値を返す)。
決済条件の読み取りフォールバック(memo埋め込み)
Section titled “決済条件の読み取りフォールバック(memo埋め込み)”case_settlementsにloan_status/card_status/construction_completed_date(cases側)の正式カラムが未適用な環境向けに、workflowMeta.tsがcase_settlements.memoへ__workflow_v1__:{"loan_status":...,"card_status":...,"construction_completed_date":...}という形式でメタデータを埋め込むフォールバックを持つ。buildWorkflowContextは正式カラムを優先し、nullのときのみmemo内のメタデータを参照する。書き込み時はwriteWorkflowMetaが既存メモ本文(メタ部分を除いた前半)を保持したまま末尾に追記する。
案件Workflow評価の呼び出し経路
Section titled “案件Workflow評価の呼び出し経路”決済の読取方法が異なる2つの評価入口がある。
loadCaseWorkflow:クライアント/サーバー双方から使える汎用版。cases/orders/invoices/payments/決済を通常のSupabaseクライアント(publishable key)で読むloadCaseWorkflowAdmin:サーバー専用(server-only)。決済(case_settlements)のみservice_role(admin)で読み、orders/invoices/paymentsは通常クライアントのまま読む。案件詳細画面と同じ読み方に揃えるための実装であり、case_settlementsのRLSがanon/authenticatedへ開放されている(後述)こととは独立に、サーバー経路では意図的にadmin読みへ寄せている
evaluateCaseWorkflowFromSettlementは決済行の「取得失敗」と「本当の未設定(行が存在しない)」を区別する。取得失敗(ok: false)はWorkflow評価そのものを行わずエラーを返し、「決済条件未設定」と誤表示しない。
sequenceDiagram
participant UI as 案件詳細画面(サーバー)
participant LCA as loadCaseWorkflowAdmin
participant Admin as getCaseSettlementByCaseIdAdmin(service_role)
participant DB as Supabase(publishable client)
participant WF as evaluateCaseWorkflowFromSettlement / evaluateWorkflow
UI->>LCA: caseId
par 決済行の取得
LCA->>Admin: case_settlements 取得(service_role)
and 関連データの取得
LCA->>DB: cases.construction_completed_date
LCA->>DB: orders(id,status,delivered_date)
LCA->>DB: invoices(id,status,invoice_amount)
LCA->>DB: payments(id,invoice_id,status,payment_amount)
end
Admin-->>LCA: settlementResult(ok/エラー種別)
DB-->>LCA: related データ or エラー
LCA->>WF: settlementResult, related, relatedError
alt relatedError あり
WF-->>UI: ok=false, RELATED_READ_FAILED
else settlementResult.ok = false
WF-->>UI: ok=false, error_code(SETTLEMENT_READ_FAILED等)
else 正常
WF->>WF: buildWorkflowContext → evaluateWorkflow
WF-->>UI: ok=true, result, settlementMissing(true/false)
end
販売店発注(app/dealer/orders/new)の登録経路
Section titled “販売店発注(app/dealer/orders/new)の登録経路”販売店発注フォームは4ステップ(案件情報→商品選択→決済区分選択→確認)で入力を受け付け、送信時にsaveDealerOrder.tsが次の順でSupabaseへ直接INSERTする。
casesへ案件本体を作成する。ステータスは常に「新規受付」固定、case_noはVE-{timestamp}形式で発番する- パッケージ発注なら
case_packages+case_package_items(パッケージ構成のスナップショット・仕入価格)、部材のみ発注ならcase_productsを作成する case_settlementsに選択された決済区分(settlement_type)を保存する- いずれかのステップが失敗した場合は
cleanupCaseが作成済みの行を逆順で削除し、中間状態の案件を残さない
この経路はブラウザから直接複数テーブルへ書き込む非トランザクションな実装であり、失敗時のクリーンアップで整合性を保っている。
sequenceDiagram
participant Form as 販売店発注フォーム(STEP1-4)
participant Save as saveDealerOrder
participant DB as Supabase(publishable client)
Form->>Save: caseForm, productForm, settlementType
Save->>DB: dealers から dealer_id 解決(resolveDealerId)
alt 入力名に一致する有効な販売店なし
DB-->>Save: 最初に作成された有効な販売店へフォールバック
end
Save->>DB: cases へ INSERT(status=新規受付, case_no=VE-{timestamp})
alt パッケージ発注
Save->>DB: case_packages へ INSERT
Save->>DB: package_items 取得 → case_package_items へ INSERT(仕入価格スナップショット)
else 部材のみ発注
Save->>DB: case_products へ INSERT(仕入価格スナップショット)
end
Save->>DB: case_settlements へ INSERT(settlement_type そのまま保存)
alt いずれかのINSERT失敗
Save->>DB: cleanupCase(作成済み行を逆順DELETE)
Save-->>Form: ok=false, errorMessage
else 全件成功
Save-->>Form: ok=true, caseId, caseNo
end
販売店解決のフォールバック挙動
Section titled “販売店解決のフォールバック挙動”resolveDealerIdは入力されたdealer_name(画面初期値はダミー文字列)と完全一致する有効な販売店を検索し、見つからない場合は作成日時が最も古い有効な販売店へ黙って(エラーなしに)フォールバックする。販売店名の候補入力・選択UIがないため、この経路で作成された案件のdealer_idは入力内容と一致しない可能性がある。仕入価格(supplierId)の解決も同様に、案件のdealer_idに紐づく既定仕入先(resolveDealerDefaultSupplierId)が未設定の場合は価格スナップショットを0円のまま保存し、案件作成自体は継続する(コンソール警告のみ)。
STEP3(決済区分選択)の入力値とWorkflowEngine正式値の対応
Section titled “STEP3(決済区分選択)の入力値とWorkflowEngine正式値の対応”STEP3で選択できる決済区分はDEALER_ORDER_SETTLEMENT_TYPES(掛売/ローン/現金/カード/その他の5値)であり、WorkflowEngineの正式値(前金/売掛/3社間決済/カード)とは選択肢の構成そのものが異なる。入力値はそのままcase_settlements.settlement_typeにINSERTされ、以降resolveSettlementRuleが正式値へ解決する。
| STEP3の選択肢 | resolveSettlementRuleでの解決 |
|---|---|
| 掛売 | 売掛(エイリアス一致) |
| ローン | 3社間決済(エイリアス一致) |
| カード | カード(正式値) |
| 現金 | 未対応(ruleKey: null、canOrder/canInvoiceとも常時false) |
| その他 | 未対応(ruleKey: null、canOrder/canInvoiceとも常時false) |
「前金」はSTEP3の選択肢に存在せず、この販売店発注フォームからは前金の案件を作成できない。「現金」「その他」を選択して作成した案件はresolveSettlementRuleがいずれの正式値・エイリアスにも一致せずruleKey: nullを返すため、発注・請求ともにブロックされた状態(警告「未対応の決済区分です: 現金」等)で登録される。案件詳細側で決済区分を正式値へ手動修正しない限りこの状態は解消しない。
create_case_registration RPC(未接続の冪等登録経路)
Section titled “create_case_registration RPC(未接続の冪等登録経路)”これとは別に、DB側には冪等な案件登録用のcreate_case_registrationRPC(SECURITY INVOKER・service_role専用実行権限・request_idとpayload_hashによる冪等性)と、その結果を記録するcase_registration_requestsテーブルが用意されている。この経路はservice_role経由のトランザクション登録用として設計されており、現行のapp/dealer/orders/new画面からは呼び出されていない。案件起票の正本は現時点で「フォーム→直接INSERT」であり、RPC経路は未接続のバックエンド機能として存在する。
このRPCは複数バージョンを経て仕様が変わっており、フォーム側との差分に注意が必要である。
- 決済区分の検証がフォームより厳格:Ver1.2(
20260801160000)以降、settlement_typeは前金/売掛/3社間決済/カードの4値のみ受理し、それ以外(その他や旧エイリアス)はINVALID_INPUTで登録自体を拒否する。STEP3の掛売/ローン/現金/カード/その他という5値をそのまま渡すとエイリアス変換なしに弾かれるため、仮に将来この画面をRPC経由へ切り替える場合はSTEP3の選択肢自体を正式値へ合わせる必要がある - 決済区分別の必須項目:
3社間決済はfinance_company(信販会社名)とapproval_number(承認番号)を必須、カードはcard_brand(カード会社名)を必須とし、区分に応じた項目のみ保存する(該当しない項目はNULLで保存) - 登録時は価格を持たない:明細(
lines、1〜100件)はproduct_idまたはpackage_id(XOR)とquantity(1〜9999の整数)のみを受け取り、クライアントからの単価入力は受け付けない。旧バージョン(Ver1.0/1.1)はsales_prices/purchase_pricesをサーバー側でorder_received_date基準に取得してスナップショットしていたが、現行版(Ver1.2)はマスタ価格を参照せず(「supplier/価格は常にNULL。マスタ価格は参照しない」)、case_productsの価格関連列をNULLのまま保存し、価格確定は後続の発注フローに委ねる設計へ変わっている - PACKAGE明細の構成0件は拒否:
package_itemsが1件も無い(かつis_hidden=false)パッケージはPACKAGE_ITEMS_NOT_FOUNDで登録全体を失敗させる - 手動価格は不可:
is_manual_priceがtrueのペイロードはINVALID_INPUTで即座に拒否する - 内部のUUID・SQLエラー・スタックトレースはレスポンスに含めず、
error_codeはINVALID_INPUT/PACKAGE_ITEMS_NOT_FOUND/REQUEST_ID_CONFLICT/REGISTRATION_FAILEDのいずれかに正規化される
三社間の金銭フロー(信販入金・仕切精算・仕入先支払)
Section titled “三社間の金銭フロー(信販入金・仕切精算・仕入先支払)”3社間決済の案件は、発注・請求可否のワークフローとは独立に、次の3系統の金銭実体を持つ。いずれもexecute_three_party_moneyRPCへのアクションとして実行され、moneyActionsLogic.tsが入力検証・正規化を行う。
| アクション | 対象テーブル | 主な操作 |
|---|---|---|
finance_receipt.* | finance_receipts | create(入金予定登録)→confirm(実入金確認:実績日・実績額)。cancel/correctで取消・訂正 |
dealer_settlement.* | dealer_settlements + dealer_settlement_lines | create(仕切清算書作成)→confirm(確定)→pay(実績支払日・実績支払額)。cancel/correctで取消・訂正 |
supplier_payment.* | supplier_payments | create(支払予定登録)→pay(実績支払日・実績支払額)。cancel/correctで取消・訂正 |
想定される進行順序は「ローン承認(loan_status=承認済)→発注→工事完了(construction_completed_date)→請求→信販会社からの入金(finance_receipt)→販売店への仕切精算(dealer_settlement)/仕入先への支払(supplier_payment)」である。信販入金・仕切精算・仕入先支払の3系統は互いに独立したアクション列であり、dealer_settlements.finance_receipt_id・invoice_idで該当する信販入金・請求と任意に紐付けられる。
アクションごとの遷移前提とエラー
Section titled “アクションごとの遷移前提とエラー”RPC(execute_three_party_money)は各アクションの実行前に対象行をFOR UPDATEでロックし、状態遷移が不正なら例外(APP:<code>:<message>形式)を投げてロールバックする。主なガードは次のとおり。
| アクション | 遷移前提 | 主なエラー |
|---|---|---|
finance_receipt.confirm | 対象が予定であること | 取消済→CONFLICT、入金済済→IMMUTABLE(訂正へ誘導) |
finance_receipt.correct | 対象が取消でないこと | 取消済→CONFLICT |
dealer_settlement.confirm | 対象が下書きであること | 取消済→CONFLICT、確定/支払済→IMMUTABLE |
dealer_settlement.pay | 対象が確定であること | 下書き→CONFLICT(先に確定が必要)、支払済→IMMUTABLE、取消→CONFLICT |
dealer_settlement.create/correct | dealer_idが案件のdealer_idと一致、finance_receipt_id/invoice_id(指定時)が同一案件かつ取消以外、payout_amount ≥ 0 | 不一致・負値はいずれもINVALID_INPUT |
supplier_payment.pay | 対象が予定であること | 取消→CONFLICT、支払済→IMMUTABLE |
supplier_payment.create/correct | order_id(指定時)が同一案件の発注、かつ発注側supplier_idが設定されていれば一致必須 | 不一致はINVALID_INPUT |
各エンティティの状態遷移
Section titled “各エンティティの状態遷移”stateDiagram-v2
[*] --> 予定: finance_receipt.create
予定 --> 入金済: confirm
予定 --> 取消: cancel
入金済 --> 取消: cancel
予定 --> 予定: correct(元を取消し新規予定を作成)
入金済 --> 予定: correct(元を取消し新規予定を作成)
取消 --> [*]
stateDiagram-v2
[*] --> 下書き: dealer_settlement.create
下書き --> 確定: confirm
確定 --> 支払済: pay
下書き --> 取消: cancel
確定 --> 取消: cancel
支払済 --> 取消: cancel
下書き --> 下書き: correct(元を取消し新規下書きを作成)
確定 --> 下書き: correct
支払済 --> 下書き: correct
取消 --> [*]
stateDiagram-v2
[*] --> 予定: supplier_payment.create
予定 --> 支払済: pay
予定 --> 取消: cancel
支払済 --> 取消: cancel
予定 --> 予定: correct(元を取消し新規予定を作成)
支払済 --> 予定: correct
取消 --> [*]
いずれのテーブルも物理DELETEを行う権限をservice_roleに付与していない(GRANTはSELECT, INSERT, UPDATEのみ)。取消・訂正は状態値の更新と新規行の追加で表現し、履歴を必ず残す。
仕切精算の金額計算
Section titled “仕切精算の金額計算”dealer_settlement.create時、払戻額(payout_amount)は次の式で自動計算される。
payout_amount = credit_received_amount − ve_share_amount − Σ(調整明細のamount)調整明細(dealer_settlement_lines.line_kind)はtransfer_fee(振込手数料)・discount(値引)・offset(相殺)・other(その他控除)の4種で、控除額を正数で保持する。credit_in(信販入金額行)・ve_share(当社取り分行)は明細に計上できるが調整合計には含めない。金額はすべて円単位の整数として扱い、入力値はfloor(切り捨て)で丸めてから演算する(アプリ層のfloorMoneyとRPC側のfloor()が同じ丸め規則)。計算結果payout_amountが負になる登録・訂正は、アプリ層のバリデーション(parseDealerCreateBody)とRPC側の両方で拒否される(二重ガード)。
参考として、仕切初期額(調整なし)はsuggestedDealerPayoutFromFinanceが「信販入金額 − 有効請求額合計(取消請求を除く)」で算出する初期値提案のロジックを持つが、これは画面入力の初期値補助であり、確定計算式(credit_received_amount − ve_share_amount − Σ調整)そのものではない。振込手数料はこの初期提案には含めない。
冪等性と訂正の仕組み
Section titled “冪等性と訂正の仕組み”すべての3社間金銭アクションはrequest_id(クライアント発行のUUID、Idempotency-Keyヘッダとセッションから導出)を伴い、three_party_money_requestsにpayload_hash(キー順正規化したJSONのMD5ハッシュ)とともに記録される。RPC内部ではpg_advisory_xact_lockで同一request_idのリクエストを直列化したうえで、次の分岐で冪等性を担保する。
- 既存レコードなし →
PROCESSINGで新規INSERTし、本処理へ進む - 既存レコードの
payload_hashが今回と異なる →REQUEST_ID_CONFLICT(同じキーで違う内容は拒否) - 既存レコードが
COMPLETED→ 本処理を実行せず、保存済みレスポンスをidempotent_replay: trueで返す(再送安全) - 既存レコードが
PROCESSING→REQUEST_IN_PROGRESS(同時実行中の重複送信を拒否) - 既存レコードが
FAILED→PROCESSINGへ戻して再実行を許可する(同一request_idでの再試行が可能)
確定済み金額の直接UPDATEは扱わず、訂正はcorrectアクション(新しい内容で作り直し、corrects_idで元レコードを参照し、元レコードを同一トランザクション内で取消にする)、取消はcancelアクション(cancel_reasonを記録)という形で、常に監査可能な履歴として残す設計である。訂正時にcancel_reason未指定の場合はRPCが既定値「訂正のため取消」を自動設定する。ledgerの更新(COMPLETED/FAILED確定)と業務テーブルの更新は同一トランザクションで行われ、PROCESSINGのまま取り残される状態は発生しない。
金銭アクション実行のシーケンス
Section titled “金銭アクション実行のシーケンス”sequenceDiagram
participant UI as 画面
participant GW as API Route(runMoneyGateway)
participant Val as validateMoneyActionInput
participant Exec as executeMoneyAction(service_role)
participant RPC as execute_three_party_money(RPC)
participant DB as finance_receipts/dealer_settlements/...
UI->>GW: POST(Idempotency-Key ヘッダ必須)
GW->>GW: オリジン検証 / セッション検証 / CSRF検証 / Content-Type検証
alt いずれか失敗
GW-->>UI: 401/403/415(安全なエラーDTO)
end
GW->>GW: deriveThreePartyMoneyRequestId(session, Idempotency-Key)
GW->>Val: action, caseId, resourceId, body
alt 入力不正
Val-->>GW: INVALID_INPUT + field_errors
GW-->>UI: 400
end
GW->>Exec: requestId, ValidatedMoneyAction
alt finance_receipt.create
Exec->>DB: 有効な予定/入金済の既存行チェック(二重登録ガード)
alt 既存あり
Exec-->>GW: CONFLICT
end
end
Exec->>RPC: execute_three_party_money(payload)
RPC->>RPC: advisory lock → ledger冪等判定 → 業務更新 → ledger COMPLETED/FAILED(同一tx)
RPC-->>Exec: ok / error_code
Exec-->>GW: ExecuteMoneyActionResult
GW->>GW: toSafeMoneyError/toSafeMoneySuccess(内部情報のサニタイズ)
GW-->>UI: 200 or エラーステータス
toSafeMoneyErrorは、レスポンスに含めるerror_messageがUUID・SQL文・constraint/pg_/SQLSTATE/permission denied等の内部情報を含んでいないか、200文字以内かを検査し、該当する場合は「処理を完了できませんでした」という汎用メッセージへ差し替える。error_codeも許可リスト外の値はACTION_FAILEDへ正規化する。
信販入金の二重登録ガード
Section titled “信販入金の二重登録ガード”finance_receipt.createはRPC本体のCHECK制約ではなく、executeMoneyActionWithClient内のアプリケーションコードが、対象案件のfinance_receiptsに有効な行(入金済または予定)が既に存在するかを事前SELECTで確認し、存在すればCONFLICTで拒否する(マイグレーションレベルのユニーク制約は無い)。「予定」が残っている場合は「入金済にするか取消してから登録してください」、「入金済」の場合は「二重登録はできません」とメッセージが分かれる。
ステータス(DB値と表示値)
Section titled “ステータス(DB値と表示値)”DB上のステータスは最小集合とし、期限超過などの表示状態は登録時の日付から都度導出する。
| エンティティ | DB値 | 表示値 |
|---|---|---|
finance_receipts | 予定 / 入金済 / 取消 | 未入金 / 入金予定 / 入金済 / 期限超過 / 取消 |
dealer_settlements | 下書き / 確定 / 支払済 / 取消 | 下書き / 支払予定 / 支払済 / 期限超過 / 取消 |
supplier_payments | 予定 / 支払済 / 取消 | 支払予定 / 支払済 / 期限超過 / 取消 |
期限超過は「予定日(scheduled_date/scheduled_payout_date/due_date)が当日より前」で判定し、当日中は超過扱いにしない。取消済み行は集計から除外する。finance_receiptsのみ、予定日が未設定かつ予定ステータスのとき「未入金」という追加の表示値を持つ(dealer_settlements/supplier_paymentsにこの区分はない)。
実質回収額・未入金残高の算出
Section titled “実質回収額・未入金残高の算出”案件の資金回収状況はcomputeThreePartyRecoveryAmountsが次式で導出する(finance_receipts/dealer_settlementsを正とし、paymentsとは混ぜない)。
実質回収額 = 入金済の信販実入金額(未登録なら0) − 支払済の販売店支払額未入金残高 = 有効請求額合計(取消請求を除く) − 実質回収額販売店支払額は支払済のactual_payout_amount(実績)を優先し、実績が無い場合のみpayout_amount(確定額)にフォールバックする。
| 対象 | 権限方式 | 実装根拠 |
|---|---|---|
case_settlements | RLS無効化+anon/authenticated双方にFOR ALL USING(true) WITH CHECK(true)の開放ポリシー+GRANT SELECT,INSERT,UPDATE,DELETE。ブラウザの publishable key から直接CRUD可能 | 20260724170000_fix_case_settlements_rls.sql |
finance_receipts / dealer_settlements / dealer_settlement_lines / supplier_payments | RLS有効化のうえポリシー0件(anon/authenticatedは明示REVOKE ALL)。service_roleにのみSELECT,INSERT,UPDATE(DELETE権限なし) | 20260810150000_three_party_money_events.sql |
three_party_money_requests | RLS無効化だがanon/authenticatedはREVOKE ALL。service_roleにのみSELECT,INSERT,UPDATE | 20260810160000_three_party_money_request_ledger.sql |
execute_three_party_money(RPC) | SECURITY INVOKER・search_path固定。EXECUTEはanon/authenticatedから明示REVOKE、service_roleのみGRANT | 20260810161000_execute_three_party_money_rpc.sql |
case_registration_requests | RLS無効化。anon/authenticatedはREVOKE ALL。service_roleにのみSELECT,INSERT,UPDATE | 20260726210000_case_registration_requests.sql |
create_case_registration(RPC) | SECURITY INVOKER・search_path固定。EXECUTEはservice_roleのみ | 20260726210100_create_case_registration_rpc.sql |
3社間金銭系のテーブル・RPCはDB権限だけでなく、Next.jsのAPI Route(runMoneyGateway)でオリジン検証・セッションCookie検証・CSRF検証・Idempotency-Key必須化までを行い、ブラウザへservice roleキーを渡さない構成になっている。一方でcase_settlements(決済区分そのものを含む)はDBレベルではanon/authenticatedに無条件で開放されており、書き込みを制限するアプリケーション側の認可チェック(誰が決済区分やローン/カードステータスを変更してよいか)は本ページで参照した範囲のコードには見当たらない。
SETTLEMENT_RULESのphasesが持つassignee(経理/発注担当/営業事務)は画面表示用のラベルであり、その担当者しか操作できないようにするアクセス制御ではない。ロールベースの権限管理(担当者以外の操作を防ぐ仕組み)は本ページで確認した実装の範囲では未実装である。
エッジケース・異常系
Section titled “エッジケース・異常系”- 未対応の決済区分:
settlement_typeが正式値・エイリアスのいずれにも一致しない場合(STEP3の「現金」「その他」を含む)、ruleKey: nullで発注・請求とも恒久的にブロックされる。案件詳細で決済区分を手動修正しない限り解消しない - 発注0件:
areAllOrdersDelivered/areAllOrderStatusesDeliveredはアクティブな発注が0件のとき「未達」を返す(売掛の請求は永久に不可のまま) delivered_date欠損の納品済発注:status=納品済だがdelivered_dateが空の発注が1件でもあると、売掛の請求は個別警告「納品日が登録されていません」でブロックされる- 決済行そのものが存在しない案件:
evaluateCaseWorkflowFromSettlementはsettlementMissing: trueとして区別し、DB取得エラー(ok: false)とは異なる扱いにする。呼び出し側はこの2つを混同して「未設定」と即断してはならない - 請求期日の保存値とルール計算値の不一致:
resolvePaymentDueDisplay/findCreditDueDateMismatchesはSELECTで不一致を検出するのみで自動修正しない。運用上は個別に案件を確認して手動修正する前提 - 3社間金銭アクションの二重送信:同一
Idempotency-Key(→同一request_id)で内容が同じ再送はidempotent_replay: trueで安全に処理される。内容が異なればREQUEST_ID_CONFLICT、処理中の重複送信はREQUEST_IN_PROGRESS - 信販入金の二重登録:案件に有効な
finance_receipts(予定/入金済)が既にある状態でのcreateはCONFLICT - 仕切清算の負の払戻額:調整額の合計が信販入金額と当社取り分の差を超える入力は、アプリ層・RPC層の双方で
INVALID_INPUTとして登録前に拒否される - 確定済み金銭イベントの直接変更:
confirm/pay済みの行に対する再度のconfirm/payはIMMUTABLE。変更するにはcorrect(新規作成+元取消)を使う dealer_settlement.payの前提未達:下書きのままpayを呼ぶとCONFLICT(先にconfirmが必要)- 販売店・信販入金・請求の不一致参照:
dealer_settlement.create/correctで指定したdealer_idが案件のdealer_idと異なる、またはfinance_receipt_id/invoice_idが別案件のものを指す場合はINVALID_INPUT - 仕入先支払と発注の不一致:
supplier_paymentのorder_idが別案件の発注を指す、または発注に設定済みのsupplier_idと異なるsupplier_idを指定した場合はINVALID_INPUT - 販売店発注フォームの販売店名フォールバック:入力(初期値はダミー文字列)に完全一致する有効な販売店が見つからない場合、
resolveDealerIdはエラーにせず最も古い有効な販売店へ黙って差し替える。案件のdealer_idが意図と異なるまま登録され得る - 仕入価格マスタ未設定:販売店発注フォームで既定仕入先(
default_supplier_id)が未設定の場合、価格スナップショットは0円のまま保存され、案件作成自体は継続する(後続の仕入発注画面での補完が前提) - 販売店発注フォームの途中失敗:STEP4送信中にいずれかのテーブルへのINSERTが失敗すると、
cleanupCaseが作成済み行を逆順DELETEする。ただしこれは単一トランザクションではなく、クリーンアップ自体が失敗した場合の後始末は実装上保証されない create_case_registrationとSTEP3選択肢の不整合:RPC(Ver1.2)は正式4値以外をINVALID_INPUTで即拒否するため、現行STEP3の選択肢(掛売/ローン/現金/カード/その他)をそのまま渡すと大半が弾かれる。この画面をRPC接続へ切り替える場合はSTEP3側の見直しが前提になる
当方の決済フローとの対応
Section titled “当方の決済フローとの対応”| 観点 | 当方(WholesaleCase) | ValueOS |
|---|---|---|
| 決済区分の値 | 前金 / 掛売 / 三社間 / カード / その他(5値) | 前金 / 売掛 / 3社間決済 / カード(正式4値、旧値はエイリアス吸収) |
| 発注可否・請求可否の判定 | wholesaleFlow(工程配列上の位置関係から導出) | WorkflowEngine.evaluateWorkflow(決済区分別の条件配列canOrderWhen/canInvoiceWhen) |
| 承認番号・審査情報 | WholesaleCase.approvalNo(単一・三社間とカードで共用) | case_settlements.finance_company/approval_number(3社間決済)、card_brand/card_status(カード)に分離 |
| 入金消込 | 案件のpayments[]を残高計算方式で合算 | 前金はpayments確認済み合計とdeposit_amount比較、売掛は納品完了トリガーで締め日算出、3社間決済はfinance_receiptsが信販入金を個別管理 |
| 三社間の下流精算 | 未実装(案件メモでの運用) | dealer_settlements(販売店への仕切精算)・supplier_payments(仕入先への支払)として構造化。冪等RPC+advisory lockで多重実行を防止 |
| 販売店発注の起票 | DealerIntakeForm送信→WholesaleCaseをクライアント側で生成 | app/dealer/orders/new→saveDealerOrder.tsが直接INSERT。冪等RPC(create_case_registration)はDB側に存在するが未接続、かつ受理する決済区分の値が現行フォームの選択肢より厳格 |
| 権限モデル | 未確認(本ページ対象外) | 決済区分(case_settlements)はDBレベルでanon/authenticatedに開放。3社間金銭系はservice_role限定+アプリ側ゲートウェイで多重防御 |
差分の全体像はredesign/valueos-gapを参照。
- redesign/valueos-gapの「ValueOSには決済区分でフローを制御するロジックが無い」という記述は、本ページで確認した
WorkflowEngineの実装内容(決済区分別のcanOrder/canInvoice宣言的判定)と齟齬がある。記述の更新要否を確認する create_case_registrationRPC(冪等な案件登録経路)をapp/dealer/orders/newから呼び出す設計へ移行する要否。移行する場合、STEP3の決済区分選択肢(掛売/ローン/現金/カード/その他)をRPCが受理する正式4値へ揃える必要がある。現行はsaveDealerOrder.tsによる直接INSERTで運用されている- 3社間金銭アクション(
finance_receipt/dealer_settlement/supplier_payment)の呼び出し導線(社内のどの画面・どの担当者が実行するか)の確認 - 販売店発注STEP3で「現金」「その他」を選択した案件が発注・請求ともに永続ブロックされる状態を仕様として許容するか、STEP3の選択肢自体をWorkflowEngine正式値(前金/売掛/3社間決済/カード)に合わせて見直すかの確認
case_settlements(決済区分・ローン/カードステータスを含む)がDBレベルでanon/authenticatedに無条件で開放されている点について、アプリケーション側で意図した認可制御(担当者以外は変更不可等)が別途あるのか、それとも運用上のリスクとして許容しているのかの確認- 販売店発注フォームの
resolveDealerIdフォールバック(入力名に一致する販売店が無い場合に別の販売店へ差し替える挙動)を仕様として許容するか、販売店選択UIを設けて明示選択に変える必要があるかの確認
- features/wholesale-settlement — 当方の決済区分・工程順序の正本
- redesign/valueos-gap — ValueOSとの対応・差分の全体像
- valueos/data-model — ValueOSのテーブル定義
- valueos/overview — ValueOSの概要・アーキテクチャ
- glossary