Skip to content

ADR 016 Branch Settlement Board Projection

The migrated reservation flow could advance a real booking through service delivery, caregiver payout, branch settlement, HQ settlement, and settlement completion, but the branch settlement operator dashboard still showed no rows. The source problem was not UI layout: Supabase had sanmopia_reservation_financial_ledger_lines with ledger_bucket = branch_settlement, while sanmopia_branch_settlements and sanmopia_branch_settlement_lines had no producer.

Letting Astro total ledger rows or invent a dashboard row would repeat the legacy PHP failure mode where screens owned money facts.

SupabaseReservationFinancialLifecycleStore.save_financial_lifecycle() now materializes deterministic branch settlement board projections whenever a financial lifecycle contains branch-settlement ledger lines.

The projection rules are:

  1. branch_settlement_id is deterministic per reservation booking.
  2. Header period comes from care delivery dates, then recognized ledger dates.
  3. Line kind is mapped from the versioned ledger line_type_code.
  4. Direction comes from payer/payee party roles, not UI text.
  5. Net balance uses central-office perspective: receivable is positive, payable is negative.
  6. Supabase storage uses the actual source_reference_key column; application contracts may expose the clearer lineageReferenceKey.
  7. Financial lifecycle adapters must not import settlement-domain aggregates directly. They may depend on settlement-domain vocabulary codecs when they write branch-settlement projection rows. Tach declares that boundary explicitly.

Frontend pages may only consume branch dashboard/detail contracts. They must not calculate branch settlement totals or synthesize line items from raw rows.

Historical stage evidence recorded 2026-07-09:

  • pnpm run -s supabase:smoke:stage:reservation-to-settlement-completion
  • Booking 038a8647-01c2-4a8a-930c-1db29636e9a8
  • Payment workflow RPW-STAGE-CONT-20260709034950
  • Service balance closeout event e6953362-25ad-40a2-aeb3-3a95be457f3a
  • Branch settlement dashboard rows: 3
  • Branch settlement board line items: 1

The same proof now fails if the dashboard has only an empty header row.

This audit did not rerun stage proof. Repository evidence confirms projection code, not current runtime health.

The board command path now carries settlement_period_start from CQRS command to BranchSettlementBoard, while settlement_date remains the period end. SupabaseBranchSettlementBoardRepository persists and reloads board headers and line items through sanmopia_branch_settlements and sanmopia_branch_settlement_lines. The migration adds settlement_scope so existing per-booking projections remain booking_projection, while monthly board openings are stored as period_statement. The unique branch-period index applies only to period_statement rows, so per-booking demo projections cannot block each other.

Line-kind mapping is now fail-closed through the shared BranchSettlementLineKindCodec. Unknown source buckets raise before projection write instead of silently becoming branch_adjustment.

Remaining work is full monthly/periodic branch settlement opening policy: fact link-back, source revision tracking, component acknowledgement projection, and repair/recalculation audit. The current state is strong enough for a backend-owned reservation-to-settlement demo because totals and line kinds are frozen in Supabase instead of recomputed by UI.

2026-07-09 operator UI polish keeps the same boundary. The branch settlement surface now hides branch/export/workflow/storage ids behind collapsed operator details and keeps the visible dashboard focused on backend projection counts, status labels, and returned KRW values. The page still consumes backend contracts only; it does not calculate branch settlement money in Astro.

2026-07-09 source-backed payout smoke booking d33201b3-d98c-494c-b674-91775a79dd8a returned branch settlement dashboard rows 26 and board line items 1 after freezing caregiver payout from the latest bank-account revision source. The same smoke still reported settlement continuation waiting_for_financial_facts with reason branch settlement ledger lines are not ready; therefore board projection availability is not the same as full branch settlement ledger readiness. The next backend gate is a branch-settlement ledger producer/source reader that can feed settlement continuation without UI-computed totals.

2026-07-09 backend follow-up closes that narrow readiness gap for the stage reservation-to-settlement path. IssueBranchSettlementAfterCaregiverPayout lives in the financial-lifecycle application slice, reads the frozen lifecycle, finalized charge snapshot, caregiver payout ledger, and closeout references, then emits a canonical inter_office_branch_share branch-settlement ledger line through PrepareReservationBranchSettlementCommand. The stage payment continuation script no longer calculates the branch settlement amount or builds the branch-settlement line payload. Stage proof: pnpm run supabase:smoke:stage:reservation-to-settlement-completion, booking b0060d1a-7bcd-42a5-aaa3-866042644318, payout instruction cee6722b-7df4-56b2-b9a5-cb9a5f7e9f7d, branch settlement dashboard rows 29, board line items 1.

2026-07-09 HQ follow-up applies the same source-authority rule to the next settlement hop. IssueHqSettlementAfterBranchSettlement derives HQ settlement from the finalized charge snapshot’s settlement-deduction facts after branch settlement readiness; smoke scripts no longer build the HQ amount or line. Latest stage proof booking 521a5051-9825-44e9-bb5a-fd7d84d3981e produced branch settlement 100000, HQ settlement 50000, dashboard rows 32, and board line items 1. Direct browser backend driver proof booking 27f69a4c-7600-4de1-b611-839d8e757b94 now uses the same source-owned branch/HQ line producers and returns account-disclosure readiness not_required instead of stale instruction_unavailable for zero service-balance due. Remaining board risk is broader period statement, component-acknowledgement parity, and catalog-owned fixture replacement, not the narrow per-booking branch/HQ source lines.

2026-07-12 runtime recheck passed both narrow stage paths. Standard pnpm supabase:smoke:stage:reservation-to-settlement-completion created booking c4540bf8-7432-456e-93a2-f5a5d843e110, proved family payment delegation, payment continuation, service delivery, caregiver payout, branch/HQ settlement completion, branch dashboard rows 63, board line items 1, zero service balance readiness not_required, and registered-batch cleanup with SpiceDB cleanup verification. Positive-balance pnpm supabase:smoke:stage:reservation-to-settlement-positive-balance created booking 3aedc22f-72e2-4e6b-8fc8-c33e2458a016, proved service-balance amount 25000, customer disclosure readiness ready, rawAccountFieldsVisible=false, caregiver payout 50000, branch settlement 100000, HQ settlement 50000, financial stage settled, ledger line count 3, and registered-batch cleanup. The positive-balance wrapper now uses the same cleanup reaper as the standard flow, so stage data and SpiceDB grants are not left behind after proof.

  • 2026-07-09: accepted backend-owned branch settlement projection.
  • 2026-07-10: marked runtime snapshots historical and implementation partial pending period-statement completion.
  • 2026-07-12: refreshed standard and positive-balance reservation-to-settlement stage proof; fixed positive-balance smoke cleanup.