Skip to content

Settlement Data Room Gap Mining

Scope: settlement data-room UX and backend contract for BranchOperator, HeadquartersOperator, and system workflow actors. This is not the generic document room. Settlement data room owns export request state, artifact readiness, download handoff, repair audit, and statement scope visibility. Settlement math remains in pricing/settlement ledgers.

  • Settlement surfaces are different concepts. Status: Contracted as collection scope. Evidence: source-refs/sanmopia-admin/application/views/nav/nav_side.php:374-442 and source-refs/sanmopia-admin/application/views/nav/nav_side.php:483-540. Source behavior: branch document room exposes 정산 자료실(본사), 정산 자료실(단비), and 정산 자료실(단비) - 타 지역; HQ menu exposes 지사별 정산(본사/단비), 정산내역, and 타 지역 서비스 정산. Modern replacement: SettlementDataRoomCollection separates external_counterparty_settlement, branch_to_headquarters_settlement, headquarters_settlement, and inter_office_settlement. Source-era labels stay adapter evidence; they are not one screen with actor if-statements.

  • Settlement statement commands are still too broad. Status: Backend foundation contracted. Evidence: source-refs/sanmopia-admin/application/controllers/Common.php:9-204, source-refs/sanmopia-admin/application/controllers/Calculate.php:150-331, source-refs/sanmopia-admin/static/js/calculate_detail.js:20-323, source-refs/sanmopia-admin/application/models/Payment_model.php:2526-2562, source-refs/sanmopia-admin/application/models/Payment_model.php:2861-2915, source-refs/sanmopia-admin/application/models/Payment_model.php:2977-3025, and source-refs/sanmopia-admin/application/models/Payment_model.php:4382-4400. Source behavior: one detail surface switches by dataType and actor; branch acknowledgement can also update coupon payment state. The source STATUS_FL values mean 0 awaiting branch deposit, 1 awaiting HQ confirmation after branch deposit, 2 settled, 3 awaiting HQ confirmation after arrears deposit, and 4 arrears. Modern replacement: RequestSettlementDeposit, AcknowledgeBranchSettlementDeposit, ApproveHeadquartersSettlement, and RequestSettlementArrearsDeposit are separate commands with actor kind, expected revision, idempotency key, and SettlementTransitionAudit. SettlementActorKind is now a shared settlement vocabulary used by statement and data-room commands. Supabase tokens without an explicit settlement actor role fail closed instead of defaulting to branch manager visibility. Remaining gap: source row command context must be preserved, not flattened. The browser posts selected normal/voucher payment row ids, reservation ids, payment type ids, coupon ids, cancelled gift coupon ids, delivery and membership flags, manual etc content, board id, current branch, actor type, message, memo, and settlement month in one FormData payload. Modern commands need typed command contexts for each source row group so a repair can replay or reject only the affected line without changing unrelated settlement rows.

  • Private memo can leak through actor flip. Status: Backend foundation contracted. Evidence: source-refs/sanmopia-admin/application/views/calculate_detail.php:64-70 source-refs/sanmopia-admin/application/views/calculate_detail.php:188-220, and source-refs/sanmopia-admin/application/views/calculate_detail.php:1404-1405. Source behavior: completed state can flip $type to admin, changing memo visibility. Modern replacement: messageToBranch and headOfficePrivateMemo are separate statement fields. visible_head_office_private_memo() returns the private memo only for HQ or system actors; branch-facing contracts must omit it.

  • Hardcoded amount/date policy. Status: Pending. Evidence: source-refs/sanmopia-admin/application/models/Payment_model.php:892-894, source-refs/sanmopia-admin/application/models/Payment_model.php:918-933, source-refs/sanmopia-admin/application/models/Payment_model.php:2120-2128, source-refs/sanmopia-admin/application/models/Payment_model.php:3910-4045, source-refs/sanmopia-admin/static/js/calculate_detail.js:808-884, source-refs/sanmopia-admin/application/models/Payment_model.php:4312-4324, and source-refs/sanmopia-admin/application/models/Payment_model.php:2452-2458. Source behavior: voucher fee, promotional_coupon_cost, PG fee, default fee, and period join rules are embedded in model/JS. Modern gap: SettlementFeePolicy, PromotionalCouponCostPolicy, and SettlementPeriodPolicy must be versioned policy fixtures before command migration, while deleted product coupon 5,000/10,000 rows stay separate as cancelled_gift_coupon_commission. Voucher fee migration has started: VoucherServiceCommissionCatalog selects an effective-dated policy version and emits the receivable voucher_service_commission line item; source aliases such as voucher_settlement_charge stay adapter-only normalization keys for imported rows. Branch delivery fee migration has started: BranchSettlementDeliveryFeeCatalog selects an effective-dated policy version and emits the receivable delivery_fee line item; source fdelivery-price remains adapter evidence only.

  • Hidden iframe download has no enterprise retry/audit UX. Status: Backend handoff hardened. Evidence: source-refs/sanmopia-admin/application/views/form_reservation_list.php:123-132, source-refs/sanmopia-admin/application/views/form_reservation_list.php:207-208, source-refs/sanmopia-admin/static/js/form_reservation_list.js:225-247, source-refs/sanmopia-admin/static/js/calculate_detail.js:329-337, source-refs/sanmopia-admin/application/controllers/DataRoom.php:218-250, source-refs/sanmopia-admin/application/controllers/DataRoom.php:268-309, and source-refs/sanmopia-admin/application/controllers/DataRoom.php:311-386. Source behavior: generic document download uses hidden iframe POST and settlement detail export uses raw GET navigation by excelType/boardId; branch settlement data-room pages load current board summaries directly from settlement tables. Failure, retry, repair, dataset revision, supersession, and operator audit are weak. Modern replacement: SettlementDataRoomRequest tracks requested, building, ready, failed, superseded, and cancelled; SettlementDataRoomActionEntry records export request, build, ready, repair, and download-link handoff with revision and idempotency key. Artifacts live in business-report-exports with bucket relative keys, hash, file metadata, dataset revision, lineage evidence keys, and supersession history. Backend now rejects ready-artifact handoffs whose datasetRevision differs from the frozen request scope, creates the signed Storage URL before journaling a successful download handoff, and keeps storage object identity in the action metadata while omitting storage keys from the portal handoff payload. Remaining gap: portal must expose repair history and signed-download state from persistence: request status, artifact revision, failed build reason, repair reason, previous artifact supersession, signed URL expiry, and last actor. It must not use raw storage object keys, browser URL board tokens, or direct Excel controller redirects as durable state.

  • Settlement table display still mutates financial meaning in PHP views. Status: Mapped, backend projection gap remains. Evidence: source-refs/sanmopia-admin/application/views/table/calculate/table_local_cal_central.php:42-64, source-refs/sanmopia-admin/application/views/table/calculate/table_local_cal_central.php:76-82, source-refs/sanmopia-admin/application/views/table/data_room/table_calculate_central.php:46-52, and source-refs/sanmopia-admin/application/views/table/data_room/table_calculate_danvi.php:17-41. Source behavior: settlement totals are accumulated in the view, then amount columns are overwritten with display strings such as 입금완료 based on status flags. Modern replacement: SettlementLineProjection and SettlementCollectionSummary must compute line totals, status labels, payable/paid/reconciled state, and display-ready amount text from backend ledger rows. Portal tables render projection fields only; they must not sum, compare, or replace money columns with status text.

  • Refund/adjustment visibility crosses settlement data-room scope. Status: Pending. Evidence: source-refs/sanmopia-admin/application/models/Payment_model.php:3186-3337, source-refs/sanmopia-admin/application/models/Payment_model.php:3484-3563, source-refs/sanmopia-admin/application/models/Payment_model.php:3566-3641, source-refs/sanmopia-admin/application/models/Payment_model.php:1750-1960, source-refs/sanmopia-admin/application/models/Payment_model.php:4265-4310, and source-refs/sanmopia-admin/application/controllers/Calculate.php:151-313. Source behavior: Danbee settlement readers include payment status 1 and 2, so paid and refunded rows can both enter statement/data-room datasets. Central-to-branch readers use board id branches, STATUS_FL row flags, and reservation status filters; completed boards show rows differently than pending boards. Branch acknowledgement flips detail, coupon, cancelled-coupon, delivery, membership, and manual-etc flags in one request. Modern gap: SettlementRefundAdjustmentVisibilityPolicy must decide whether refund, stop, cancellation, payment-change refund, deleted coupon, and reissued coupon facts produce reversal lines, superseded lines, hidden rows, or operator repair tasks. SettlementLineLifecycleState must preserve paid, refunded, reversed, settled, superseded, and adjustment-pending states separately from source PAYMENT_STATUS_LIST_ID_FK and STATUS_FL integers. RefundAdjustedSettlementDatasetRevision must bump data-room request/artifact revision whenever a refund or adjustment changes any included line after board creation.

  • Settlement data-room export grants are not refund-adjustment aware. Status: Pending. Evidence: source-refs/sanmopia-admin/application/controllers/DataRoom.php:215-309, source-refs/sanmopia-admin/application/controllers/DataRoom.php:312-386, source-refs/sanmopia-admin/application/controllers/Calculate.php:569-579, and source-refs/sanmopia-admin/application/models/Payment_model.php:2292-2523. Source behavior: data-room pages scope by logged-in branch id and board lists, other-office rows mark serviceOffice versus otherOffice in controller code, generic form export trusts posted reservation ids, and settlement Excel export trusts raw excelType plus boardId. Modern gap: SettlementDataRoomExportGrant must bind actor, branch side, collection scope, board id, row ids, dataset revision, refund/adjustment watermark, file type, expiry, and download audit. SettlementDataRoomRequest and SettlementDataRoomDownloadHandoff must refuse stale artifacts when refund or payment-change adjustment facts supersede the frozen dataset. Naming follow-up: target ubiquitous language should be SettlementArtifactCollection, SettlementCollectionExportGrant, and BranchSettlementWorkspace because legacy behavior is board/collection download management, not a generic data room.

  • Settlement remittance export formulas not modeled. Status: Pending. Evidence: source-refs/sanmopia-admin/application/config/constants.php:255, source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:3741, source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:3814, source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:3869, and source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:3903. Source behavior: type18.xlsx exports board id, message, memo, settlement completion date, payment id, service type/detail, reservation id, deposited date, mother name, pay method, payment amount, deposit amount, coupon rows, and method-specific fee formulas. Modern target: BranchSettlementRemittanceExport, SettlementRemittanceLine, and PaymentMethodFeeRuleSnapshot must freeze row schema, fee rule id, policy version, and dataset revision before artifact grant.

  • Transition audit misses board id. Status: Backend foundation contracted. Evidence: source-refs/sanmopia-admin/application/models/Payment_model.php:25-32, source-refs/sanmopia-admin/static/js/calculate_detail.js:77-79, source-refs/sanmopia-admin/application/controllers/Calculate.php:187, and source-refs/sanmopia-admin/application/controllers/Calculate.php:257-262. Source behavior: insertLog($branchId,$boardId,...) accepts board id but does not persist it. Branch acknowledgement can log before and after as the same value while the actual transition is computed inside updateBoardId(). Modern replacement: every SettlementTransitionAudit carries command kind, actor, expected revision, committed revision, before status, after status, reason, idempotency key, and metadata for legacy board id/source references.

  • Inter-branch lookup can mix records. Status: Backend and contract invariant added. Evidence: source-refs/sanmopia-admin/application/controllers/Calculate.php:337-430, source-refs/sanmopia-admin/application/controllers/Calculate.php:433-457, source-refs/sanmopia-admin/application/controllers/DataRoom.php:268-309, source-refs/sanmopia-admin/application/models/Payment_model.php:2731-2771, source-refs/sanmopia-admin/application/models/Payment_model.php:2816-2840, and source-refs/sanmopia-admin/application/models/Payment_model.php:2656-2695. Source behavior: other-office detail first loads reservation context, but the board lookup then filters only by BRANCH_TYPE before first_row(). Source insert rows already carry RESERVATION_ID_FK; list queries reveal the intended split between customer relationship branch and service delivery branch. Branch data-room UI marks each row as serviceOffice or otherOffice from the logged-in branch id, while HQ list and branch history have separate Danbee routes. Modern replacement: InterOfficeSettlementLookupKey is the canonical lookup and code source: reservation id, customer relationship branch, service delivery branch, settlement period start, and settlement period end. Branch side/status is line or workflow state, never the sole lookup predicate.

2026-07-05 CaveCrew Explorer A Data-Room Navigation Delta

Section titled “2026-07-05 CaveCrew Explorer A Data-Room Navigation Delta”

These rows avoid duplicating artifact/export rows above. They isolate hidden data-room UI navigation and actor-side decisions that still need backend names.

Missing backend policySource evidenceSource behaviorModern target
Settlement board list navigation projectionsource-refs/sanmopia-admin/application/controllers/DataRoom.php:218-250, source-refs/sanmopia-admin/application/views/table/data_room/table_calculate_central.php:1-69, source-refs/sanmopia-admin/application/views/table/data_room/table_calculate_danvi.php:1-57, source-refs/sanmopia-admin/static/js/data_room/calculate_central.js:1-14, source-refs/sanmopia-admin/static/js/data_room/calculate_danvi.js:1-14Branch settlement data-room pages render current board rows directly, then browser constructs /common/calculate?dataType=...&boardId=... from button attributes. Data-room board open is raw URL navigation, not a backend-issued view grant with request scope.SettlementDataRoomBoardListProjection, SettlementDataRoomBoardAccessDecision, and SettlementCollectionNavigationToken must bind actor, branch, collection, board id, display totals, current status label, dataset revision, and next allowed detail action.
Inter-office branch-side action projectionsource-refs/sanmopia-admin/application/controllers/DataRoom.php:268-308, source-refs/sanmopia-admin/application/views/data_room/calculate_danvi_other_office.php:68-72, source-refs/sanmopia-admin/application/views/table/data_room/table_calculate_danvi_other_office.php:1-70, source-refs/sanmopia-admin/static/js/data_room/calculate_danvi_other_office.js:1-22, source-refs/sanmopia-admin/application/models/Payment_model.php:2684-2704Controller marks each row as serviceOffice or otherOffice by comparing logged-in branch id to service/customer-transfer branch ids; view renders only one side’s action button and JS passes reservation id plus branch-side string. The route parses fyear/fmonth, but period bounds are commented out and the reader is called with NULL, NULL, so the branch data-room list is not a frozen settlement-period dataset. Modern lookup key exists, but action availability and period scope still need their own projection.InterOfficeSettlementSideActionProjection, InterOfficeSettlementBranchSideDecision, and InterOfficeSettlementDataRoomPeriodScope must expose service-side/customer-relationship-side role, row status, allowed detail command, disabled reason, reservation lookup key, canonical settlement period, dataset revision, and branch-side audit scope.

2026-07-06 Explorer A Detail-Journey Delta

Section titled “2026-07-06 Explorer A Detail-Journey Delta”
PriorityStatusModern ubiquitous nameHidden source evidenceFeature gap
P0PartialSettlementDataRoomDetailActionProjectionsource-refs/sanmopia-admin/application/controllers/Common.php:28-204, source-refs/sanmopia-admin/application/views/calculate_detail.php:113-124, source-refs/sanmopia-admin/application/views/calculate_detail.php:1298-1360, source-refs/sanmopia-admin/static/js/calculate_detail.js:22-323, source-refs/sanmopia-admin/static/js/calculate_detail.js:342-380The detail page decides journey state from actor type, dataType, board status flags, and branch-side strings: it changes button text, shows Danbee download only for userToDanbee, hides branch inter-office buttons in comments, hardcodes irreversible confirm copy, and picks post-command return routes in JavaScript. Modern data-room detail reads need a backend-owned action projection with current statement revision, allowed command, disabled reason, irreversible warning copy key, download readiness, return-state token, and actor-specific next action.
flowchart LR
  BranchOperator[BranchOperator / BranchOwner]
  HeadquartersOperator[HeadquartersOperator / HeadOfficeManager]
  Workflow[Export workflow]

  Portal[Operator portal]
  Api[Settlement API]
  Statement[(Settlement statement ledger)]
  DataRoom[(Settlement data-room requests)]
  Storage[(business-report-exports)]

  HeadquartersOperator -->|request deposit, approve HQ settlement| Portal
  BranchOperator -->|acknowledge lines, download, request repair| Portal
  Portal -->|typed commands with expectedRevision| Api
  Api -->|manual rows, line acknowledgements, transition audit| Statement
  Api -->|request/build/ready/repair/download actions| DataRoom
  Workflow -->|build and mark ready| Api
  Workflow -->|write artifact| Storage
  Api -->|signed URL handoff only| Portal

  DataRoom -. collection .-> ExternalCounterpartyScope[external_counterparty_settlement]
  DataRoom -. collection .-> BranchToHeadquartersScope[branch_to_headquarters_settlement]
  DataRoom -. collection .-> HQScope[headquarters_settlement]
  DataRoom -. collection .-> InterOfficeScope[inter_office_settlement]

Design rule: external-counterparty settlement, branch-to-headquarters settlement, headquarters settlement, and inter-office settlement share command/audit vocabulary, but each keeps separate collection scope, dataset revision, lineage evidence, actor visibility, and export reader.

This plan extends the settlement rows above; it does not add duplicate source coverage.

  1. Canonicalize settlement collection vocabulary in the pricing-settlement domain. Use ExternalCounterpartySettlement, BranchToHeadquartersSettlement, HeadquartersSettlement, and InterOfficeSettlement as feature names across commands, query models, archive scopes, and adapter mappings. Keep source/vendor labels as adapter-only evidence. Backend/contract data-room drift retired on 2026-07-06 includes head_office_settlement versus headquarters_settlement, inter_branch_settlement versus inter_office_settlement, and vendor-specific names leaking into domain enum values.
  2. Keep settlement computation in canonical ledger/read-model features, not in archive or portal code. The owning domain vocabulary should expose SettlementStatementLine, SettlementLineLifecycleState, SettlementCollectionSummary, and SettlementLineProjection for branch membership, voucher commission, promotional coupon cost, cancelled benefit commission, delivery fee, inter-office share/service fee, refund adjustment, and manual adjustment lines.
  3. Bind approval flows through command handlers with explicit actor and revision controls: RequestSettlementDeposit, AcknowledgeBranchSettlementDeposit, ApproveHeadquartersSettlement, and RequestSettlementArrearsDeposit. Every command needs actor kind, branch scope, expected revision, idempotency key, line acknowledgement context when applicable, and a persisted SettlementTransitionAudit entry.
  4. Split CQRS surfaces by responsibility. Command handlers mutate SettlementStatement, SettlementDataRoomRequest, and SettlementWorkspace aggregates. Query handlers publish SettlementOperatorBoardProjection, SettlementDataRoomBoardListProjection, SettlementDataRoomDetailActionProjection, and SettlementCollectionSummaryProjection. Portal routes should render these projections without recalculating amounts, dates, row ownership, or status transitions.
  5. Model the settlement archive/material room as a separate workspace feature. SettlementWorkspace, SettlementArtifactRequest, SettlementArtifactVersion, SettlementArtifactGrant, DownloadHandoffAudit, SettlementRepairRequest, and BulkExportTemplatePolicy own export request state, signed download handoff, repair/rebuild state, template selection, dataset revision, and refund/repair watermarks. They must not own ledger math.
  6. Make conflict resolution and audit first-class. Use SettlementWorkspaceRevisionConflict, SettlementWorkspaceConflictResolutionDecision, and a settlement DomainChangeJournal/audit stream for stale operator edits, artifact repair retries, status-transition refusals, and bulk export rebuild decisions.
  7. Implement incrementally: first normalize value vocabulary beside the owning domain enum/value objects; then update application contracts and interface DTOs; then adapt Supabase row mappings; finally refresh targeted domain, adapter, and HTTP tests with uv, ruff, and tach. Avoid full pytest runs for this lane.
  • backend-repo now exposes canonical data-room collection values: external_counterparty_settlement, branch_to_headquarters_settlement, headquarters_settlement, and inter_office_settlement.
  • Supabase migration 20260706021241_canonical_settlement_data_room_collections.sql converts existing request/workspace/conflict rows and JSON payloads from source-era collection names, then adds named check constraints for the canonical set.
  • contract-repo TS client now exposes the same canonical collection union; source/vendor names remain source evidence only.
  • Domain: backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/settlement/settlement_data_room.py and backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/settlement/settlement_statement.py
  • Application handler: backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/settlement_data_room.py and backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/settlement_statement.py
  • Pydantic contract: backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/settlement_data_room_contract.py and backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/settlement_statement_contract.py
  • Other-post resource board foundation: backend-repo/src/sanmopia_modernization/domain/document_reporting/features/branch_resource_post/branch_resource_post.py and backend-repo/src/sanmopia_modernization/application/document_reporting/features/branch_resource_post/branch_resource_post_contract.py
  • Contract repo: contract-repo/packages/ts-client/src/types/settlement-data-room.ts contract-repo/packages/ts-client/src/types/settlement-statement.ts contract-repo/schemas/reservation-settlement/settlement_data_room_request.schema.json, contract-repo/schemas/reservation-settlement/settlement_workspace_revision_conflict.schema.json, contract-repo/schemas/reservation-settlement/settlement_workspace_conflict_resolution_request.schema.json, and contract-repo/schemas/reservation-settlement/settlement_workspace_conflict_resolution_decision.schema.json
  • Supabase persistence and HTTP runtime: backend-repo/src/sanmopia_modernization/adapters/pricing_settlement/features/settlement/supabase_settlement_data_room.py, backend-repo/src/sanmopia_modernization/interfaces/settlement_data_room_api.py, backend-repo/src/sanmopia_modernization/interfaces/settlement_data_room_http.py, and backend-repo/supabase/migrations/20260702211000_settlement_data_room_persistence.sql
  • Workspace runtime: backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/settlement_data_room_workspace/settlement_data_room_workspace.py, backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement_data_room_workspace/settlement_data_room_workspace.py, backend-repo/src/sanmopia_modernization/interfaces/settlement_data_room_workspace_api.py, backend-repo/src/sanmopia_modernization/interfaces/settlement_data_room_workspace_http.py, backend-repo/src/sanmopia_modernization/adapters/pricing_settlement/features/settlement_data_room_workspace/supabase_settlement_data_room_workspace.py, and backend-repo/supabase/migrations/20260703201935_settlement_data_room_workspace_runtime.sql. This runtime adds sanmopia_settlement_data_room_workspaces and sanmopia_settlement_data_room_template_policies as backend-owned Supabase tables with explicit service-role grants, RLS, JSONB aggregate snapshots, and queryable collection/scope/revision columns.
  • Workspace conflict proof: SettlementWorkspaceRevisionConflict exposes stale field diffs with latest actor/audit evidence, while SettlementWorkspaceConflictResolutionRequest and SettlementWorkspaceConflictResolutionDecision force branch/HQ operators to choose keep_current, apply_submitted, or merge_explicit. System workflow cannot silently resolve stale UI writes, and public TS/JSON contracts expose the same shape for frontend conflict UX.
  • Workspace conflict runtime binding: SettlementWorkspaceConflictResolutionHandler, SettlementDataRoomWorkspaceApi.resolve_conflict, POST /settlement-data-room-workspaces/{workspaceCode}/conflict-resolutions, SupabaseSettlementWorkspaceConflictResolutionDecisionStore, and sanmopia_settlement_workspace_conflict_resolution_decisions now persist explicit branch/HQ field-winner decisions by idempotency key. The table is service-role-only, RLS-enabled, and stores queryable workspace/collection, actor, before/after revision, reason, source audit references, field paths, and immutable decision JSONB so later workspace changes do not rewrite the operator-visible resolution.
  • Hardened proof: ready artifact handoff must match the request’s frozen datasetRevision; failed Storage signing does not append issue_download_link; successful download actions journal expiresAt, storageBucket, storageObjectKey, and downloadName while response payload exposes only the signed URL and file/hash metadata.

Current boundary:

  • SettlementDataRoomCollection
  • SettlementDataRoomScope
  • SettlementDataRoomRequest
  • SettlementDataRoomArtifact
  • SettlementDataRoomActionEntry
  • SettlementDataRoomCommandHandler
  • OpenSettlementDataRoomExportRequestCommand
  • StartSettlementDataRoomBuildCommand
  • MarkSettlementDataRoomArtifactReadyCommand
  • RequestSettlementDataRoomRepairCommand
  • IssueSettlementDataRoomDownloadLinkCommand
  • POST /settlement-data-room-export-requests
  • GET /settlement-data-room-requests/{settlementDataRoomRequestId}
  • POST /settlement-data-room-requests/{settlementDataRoomRequestId}/builds
  • POST /settlement-data-room-requests/{settlementDataRoomRequestId}/ready-artifacts
  • POST /settlement-data-room-requests/{settlementDataRoomRequestId}/repair-requests
  • POST /settlement-data-room-requests/{settlementDataRoomRequestId}/download-handoffs
  • SupabaseSettlementDataRoomRequestRepository
  • SettlementDataRoomApi
  • SettlementDataRoomAuthorizationService
  • sanmopia_settlement_data_room_requests
  • sanmopia_settlement_data_room_artifacts
  • sanmopia_settlement_data_room_actions
  • SettlementWorkspace
  • SettlementWorkspaceScope
  • SettlementArtifactRequest
  • SettlementArtifactVersion
  • SettlementArtifactGrant
  • DownloadHandoffAudit
  • SettlementRepairRequest
  • OperatorReturnStateToken
  • BulkExportTemplatePolicy
  • SettlementDataRoomWorkspaceService
  • SettlementDataRoomWorkspaceApi
  • SettlementDataRoomWorkspaceAuthorizationService
  • SupabaseSettlementDataRoomWorkspaceRepository
  • SupabaseSettlementDataRoomWorkspaceTemplatePolicyReader
  • GET /settlement-data-room-workspaces/{workspaceCode}?collection=...
  • POST /settlement-data-room-workspaces/{workspaceCode}/artifact-requests
  • POST /settlement-data-room-workspaces/{workspaceCode}/repair-requests
  • POST /settlement-data-room-workspaces/{workspaceCode}/grant-handoffs
  • POST /settlement-data-room-workspaces/{workspaceCode}/return-state
  • sanmopia_settlement_data_room_workspaces
  • sanmopia_settlement_data_room_template_policies
  • SettlementStatement
  • SettlementRemittanceAccountSnapshot
  • SettlementTransitionAudit
  • SettlementStatementManualRow
  • SettlementStatementCommandHandler
  • RequestSettlementDepositCommand
  • AddManualSettlementRowCommand
  • AcknowledgeBranchSettlementDepositCommand
  • ApproveHeadquartersSettlementCommand
  • RequestSettlementArrearsDepositCommand
  • GET /settlement-statements/{settlementStatementId}
  • POST /settlement-deposit-requests
  • POST /settlement-manual-rows
  • POST /settlement-branch-deposit-acknowledgements
  • POST /settlement-headquarters-settlement-approvals
  • POST /settlement-arrears-deposit-requests
  • sanmopia_settlement_statement_manual_rows
sequenceDiagram
autonumber
actor BranchOperator as BranchOperator
actor HeadquartersOperator as HeadquartersOperator
participant Portal as Operator Portal
participant API as Backend API
participant Workflow as Restate Export Workflow
participant Storage as Supabase Storage
participant DB as Supabase Postgres

BranchOperator->>Portal: Open settlement workspace
Portal->>API: GET /settlement-data-room-workspaces/{code}?collection=...
API->>DB: Read workspace snapshot and active template policy
API-->>Portal: workspace, revision, template policy
HeadquartersOperator->>Portal: Request headquarters settlement export
Portal->>API: POST /settlement-data-room-export-requests
API->>DB: Store request revision 1 and action journal
API-->>Portal: requestId, status=requested
API->>Workflow: Start export build from frozen dataset
Workflow->>API: POST /settlement-data-room-requests/{id}/builds
API->>DB: status=building, revision=2
Workflow->>Storage: Write artifact to business-report-exports
Workflow->>API: POST /settlement-data-room-requests/{id}/ready-artifacts
API->>DB: Attach artifact, status=ready, revision=3
BranchOperator->>Portal: Open settlement data room
Portal->>API: POST /settlement-data-room-workspaces/{code}/artifact-requests
API->>DB: Append artifact request with deterministic idempotency code
API-->>Portal: workspace revision and artifact request
BranchOperator->>Portal: Download
Portal->>API: POST /settlement-data-room-workspaces/{code}/grant-handoffs
API->>DB: Issue grant and journal signed handoff
API-->>Portal: grant reference, audit id, expiry
BranchOperator->>Portal: Report mismatch
Portal->>API: POST /settlement-data-room-workspaces/{code}/repair-requests
API->>DB: Append repair request and preserve artifact history
BranchOperator->>Portal: Return from detail page
Portal->>API: POST /settlement-data-room-workspaces/{code}/return-state
API->>DB: Consume one actor-bound return-state token
HeadquartersOperator->>Portal: Request deposit
Portal->>API: POST /settlement-deposit-requests
API->>DB: Save SettlementStatement revision 1 and request audit
BranchOperator->>Portal: Acknowledge deposit by settlement line
Portal->>API: POST /settlement-branch-deposit-acknowledgements
API->>DB: Save line acknowledgements and revision 2
BranchOperator->>Portal: Retry same acknowledgement
Portal->>API: POST /settlement-branch-deposit-acknowledgements
API-->>Portal: Return committed revision 2 from idempotency key
HeadquartersOperator->>Portal: Approve headquarters settlement
Portal->>API: POST /settlement-headquarters-settlement-approvals
API->>DB: Save approval audit and settled or arrears status

UX requirements:

  • BranchOperator and HeadquartersOperator portals show one settlement statement with actor-specific commands, not separate duplicated screens with hidden conditions.
  • System workflow is the only actor allowed to start builds and mark artifacts ready; BranchOperator and HeadquartersOperator actors request exports, download handoffs, and repair actions through optimistic revisions.
  • Download button shows build status, failure reason, retry/repair action, and last generated artifact; no hidden iframe and no branch/export token in the browser URL.
  • Frontend settlement/export flows use authenticated reads for branch/HQ scope and request short-lived signed download handoffs only when the operator clicks download.
  • Repair never deletes previous artifacts. New artifact supersedes old artifact with audit trail, and repair invalidation revokes active grants for the stale artifact version.
  • BranchOperator never sees HeadOfficePrivateMemo; status changes cannot change memo visibility.
  • Headquarters settlement approval is separate from branch deposit acknowledgement. Branch acknowledgement records SettlementStatementLineAcknowledgement per settlement line so missing fees, membership dues, delivery fees, and adjustments are not hidden in one boolean.
  • Headquarters manual etc rows are now AddManualSettlementRow commands with SettlementStatementManualRow, actor kind, idempotency key, expected revision, and before/after SettlementTransitionAudit; they are only mutable before branch acknowledgement. Remaining slice: generated contract handoff and operator UI for pending manual settlement rows. Source context that must remain visible in UI/API: manual row name, count, unit price, source board id, statement line target, HQ message, private memo, actor, reason, expected revision, and command idempotency key.
  • Retried statement commands reuse SettlementTransitionAudit.idempotencyKey and never append duplicate audits; reusing the same key for a different command kind is rejected.
  • External-counterparty export requires explicit external recipient code. Inter-office export requires both branch sides in lookup scope.
  • Source DANBEE_STATUS_FL is not current-row truth in the modern model. ReservationExternalCounterpartySettlementStatus stores reservation-scoped completion state, statement reference, board status, lineage evidence keys, and dataset revision in a service-role-only Supabase projection. Branch/HQ reads use GET /reservation-external-counterparty-settlement-statuses/{reservationId} with branch-scope authorization. Internal workflow updates use POST /internal/reservation-external-counterparty-settlement-status-updates and reject stale dataset revisions before upserting the projection.

No new SFC ids. These refine existing refund, branch/HQ settlement, data-room artifact, mailbox grant, and approval-conflict rows.

  • Refund-triggered board invalidation must refresh branch/HQ operator projections. Status: Backend partial. Evidence: source-refs/sanmopia_web/application/models/Payment_model.php:416-430 calls provider refund, then only flips the normal/voucher paid row to payment status 2; source-refs/sanmopia-admin/application/models/Payment_model.php:3315 still includes Danbee settlement rows whose payment status is 1 or 2; source-refs/sanmopia-admin/application/models/Payment_model.php:3504-3516 reads branch/HQ board detail rows by board id and admin-branch payment status; source-refs/sanmopia-admin/application/models/Payment_model.php:3579-3594 reads coupon board rows with the source payment-status guard commented out; and source-refs/sanmopia-admin/application/models/Payment_model.php:1086-1307 derives branch/HQ operator labels, component flags, completed money, and unpaid money from current board/component flags. Modern gap: RefundAdjustedSettlementBoardInvalidation, BranchSettlementOperatorBoardRefresh, HeadquartersSettlementOperatorBoardRefresh, and SettlementArtifactGrantRevocationAfterRefund must consume refund transition facts, mark affected statements stale, bump dataset revision, revoke active download grants, and enqueue reissue/repair tasks. Source payment status flips, commented filters, and board flag summaries are evidence only. Backend d478bdb now routes provider-refund reporting queue work to branch/HQ settlement statement and operator-board read-model refresh validation. Latest settled-refund-impact stage proof booking 9700b2e4-0b25-4978-9477-2c864c3f29c1 reached settled, then provider refund event stage-provider-refund-result-20260709201933-65135721 produced paid-row mutation, settlement reversal, and reporting refresh fanout. Dataset revision bump, stage proof for grant revocation, and artifact reissue history remain pending. Supabase adapter persistence for workspace grant revocation is now regression-covered: saving/loading SettlementWorkspacePayload preserves grant revoked_by_repair_request_code, revoked_reason, repair revoked_grant_ids, and replacement_artifact_request_code. Backend repair planning/persistence is partial: PaymentRefundRepairPlan and SupabasePaymentRefundRepairPlanStore persist repair cases, permissions, gateway-command payload, and settlement_invalidation_key. Backend follow-up binds PaymentRefundRepairHandler into main.py and exposes internal POST /internal/payment-refund-repairs/provider-refund-plans, so repair planning now has a runtime HTTP boundary. The internal POST /internal/payment-refund-repairs/provider-refund-executions route now calls the configured billing gateway only for new idempotency keys and persists execution attempts with idempotency and source evidence. Matching stored attempts are replayed into the ledger-backed provider-result handler without a second provider call; mismatched replays are rejected. That handler feeds paid-row transition, settlement reversal, and reporting invalidation logic. Retry backoff/operator result-history projection, stage webhook proof, and downstream reporting plus artifact/grant invalidation fanout from executed repair results remain unproven.

  • Data-room artifact reissue/revocation needs explicit invalidation lineage. Status: Backend partial. Evidence: source-refs/sanmopia-admin/application/controllers/Calculate.php:568-578 exports by raw excelType and boardId; source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:3741-3911 reloads mutable board rows, recalculates fees, streams the workbook, and exits; source-refs/sanmopia-admin/application/controllers/DataRoom.php:85-94 exports documents by raw excelType plus reservation ids; and source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:3914-4198 builds XLSX/PDF/ZIP artifacts from current rows and temporary files. Modern gap: SettlementArtifactRevocation, SettlementArtifactReissueRequest, SettlementArtifactSupersessionLink, SettlementArtifactReplacementRequestLink, and DocumentExportArtifactGrantRevoked must persist invalidation reason, refund/provider-result id, affected statement revision, previous artifact id, revoked grant ids, replacement request id, and actor/system initiator. Streaming from current rows cannot prove stale artifact refusal or reissue history. Backend partial: data-room artifacts persist dataset revision and superseded_at, workspace artifact requests preserve version history, and artifact grants carry revoked_at. Backend 3e6d83f proves the workspace-domain and contract-level refund/repair command behavior that marks active grants revoked and records the replacement artifact request code. Missing slice is persistence/stage proof plus operator-visible lineage from superseded artifact id to replacement request. Backend c1e0904 tightens the core repair transition: request_repair now supersedes the active data-room artifact immediately, journals the superseded artifact id plus supersession reason, and persists that lineage through the Supabase data-room request RPC. Supabase/stage proof for grant revocation, durable SettlementArtifactReplacementRequestLink, and operator-visible SettlementArtifactReissueLineageProjection remain pending. 2026-07-10 workspace-domain follow-up: backend 3e6d83f SettlementWorkspace.request_repair now revokes active artifact grants for the invalidated artifact version, records revoked_by_repair_request_code and revoked_reason on each grant, and stores revoked_grant_ids plus replacement_artifact_request_code on the repair request payload. 2026-07-10 Supabase adapter follow-up adds round-trip proof that the workspace JSON row persists that revocation and repair lineage. Remaining work is stage proof and operator-visible reissue lineage projection.

  • Document mailbox downloads need grant-use audit and revocation state. Status: Pending. Evidence: source-refs/sanmopia-admin/application/models/Data_room_model.php:85-99 loads attachment URL values; source-refs/sanmopia-admin/application/views/data_room/other_post_view.php:78-84 renders direct <a href=... download> links; source-refs/sanmopia-admin/static/js/data_room/other_post_write.js:11-63 posts attachment uploads and deleted file ids; source-refs/sanmopia-admin/static/js/data_room/other_post_write.js:97-108 marks file ids for deletion in browser state; and source-refs/sanmopia-admin/application/models/Data_room_model.php:163-179 deletes attachment DB rows and physical files. Modern gap: DocumentMailboxAttachmentDownloadGrant, DocumentMailboxAttachmentGrantUseAudit, and DocumentMailboxAttachmentRevocation must bind actor, post revision, participant branch scope, attachment version, expiry, use timestamp, revoke reason, and retained storage object key. Direct file URLs and physical delete side effects are evidence only.

  • Branch/HQ approval conflicts need command-level conflict logs, not only workspace field-winner decisions. Status: Pending. Evidence: source-refs/sanmopia-admin/application/controllers/Calculate.php:219-276 changes HQ-side branch-settlement state based on current board status; source-refs/sanmopia-admin/application/controllers/Calculate.php:279-309 lets branch acknowledgement mutate board/component flags; source-refs/sanmopia-admin/application/models/Payment_model.php:25-32 logs before/after values but drops the passed board id; source-refs/sanmopia-admin/application/models/Payment_model.php:2977-3030 computes board status without expected revision; and source-refs/sanmopia-admin/application/models/Payment_model.php:4382-4419 derives completion from current component counts. Modern gap: SettlementActorApprovalAttempt, SettlementApprovalConflictLog, and SettlementApprovalConflictDecision must record rejected stale attempts, expected revision, latest revision, actor grant, idempotency key, before/after projection hashes, conflicting field paths, selected resolution, and reason. Control-lane refinement: insertLog($branchId, $boardId, ...) receives a board id but does not persist it, so refund/repair impact cannot reliably link a stale branch/HQ approval attempt to the exact statement revision, artifact request, or later grant revocation. Modern commands must emit SettlementTransitionAudit, SettlementApprovalProvenanceSnapshot, and RefundRepairSettlementImpactJournal in the same transaction that mutates the settlement statement. Approval command conflicts are separate from settlement workspace edit conflicts; both must remain queryable for audit.

  • Branch, headquarters, Danbee, and inter-office settlement ownership. Status: Partial. Evidence: source-refs/sanmopia-admin/application/views/nav/nav_side.php:425-439, source-refs/sanmopia-admin/application/views/nav/nav_side.php:501-529, source-refs/sanmopia-admin/application/controllers/Calculate.php:70-150, source-refs/sanmopia-admin/application/models/Payment_model.php:2657-2814, source-refs/sanmopia-admin/application/models/Payment_model.php:2916-2972, and source-refs/sanmopia-admin/application/models/Payment_model.php:4225-4238. Source behavior: branch menus, HQ menus, Danbee boards, and other-office boards are separate screens, but detail logic still depends on DANBEE_STATUS_FL, OTHER_OFFICE_STATUS_FL, BRANCH_TYPE, actor type, and current branch id. Other-office rows split service office and customer handoff office, while HQ and Danbee status are read from different payment tables. Modern gap: SettlementStatementKind, SettlementDataRoomCollection, ReservationExternalCounterpartySettlementStatus, and InterOfficeSettlementLookupKey must keep these concepts separate through command, read model, export, and repair workflows. Branch acknowledgement, HQ approval, external-counterparty export readiness, and inter-office service/customer-relationship branch acknowledgement must not share one boolean, one board id, or one mutable source flag.

  • Settlement statement typed command context split. Status: Backend/contract foundation. Evidence: source-refs/sanmopia-admin/application/controllers/Calculate.php:70-150, source-refs/sanmopia-admin/application/controllers/Calculate.php:337-430, and source-refs/sanmopia-admin/static/js/calculate_detail.js:276-285. Source behavior: one request payload mixes branch id, board id, actor type, line ids, coupon ids, membership/delivery booleans, manual etc rows, public branch message, private headquarters memo, and caller-provided total amounts. Modern replacement: SettlementStatementCommandContext in application/pricing_settlement/features/settlement as a typed context that carries statement id, statement kind, branch profile id, actor, expected revision, idempotency key, and selected settlement line references. It should build existing settlement commands such as AcknowledgeBranchSettlementDepositCommand without trusting caller totals. messageToBranch and headOfficePrivateMemo stay separate; branch acknowledgement cannot carry the private memo. Selected line refs must be non-empty for branch acknowledgement and must use settlement line kinds instead of source booleans such as coupon/cancel/member flags. Each SettlementStatementLineAcknowledgement may now carry sourceEvidenceKey, and Supabase persists it on sanmopia_settlement_statement_line_acknowledgements.

  • Collection-specific export reader parity. Status: Pending. Evidence: source-refs/sanmopia-admin/application/controllers/DataRoom.php:218-250, source-refs/sanmopia-admin/application/controllers/DataRoom.php:268-309, source-refs/sanmopia-admin/application/controllers/Calculate.php:568-578, and source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:3741-3914. Source behavior: branch data room loads calculate_central, calculate_danvi, and calculate_danvi_other_office board summaries directly, while export uses raw excelType/boardId GET. The other-office data-room route reads fyear/fmonth but comments out the period bounds and calls the reader with NULL, NULL, while the reader only applies date filters when explicit bounds are supplied (source-refs/sanmopia-admin/application/controllers/DataRoom.php:279-293; source-refs/sanmopia-admin/application/models/Payment_model.php:2684-2704). Modern gap: each SettlementDataRoomCollection needs its own export reader, frozen dataset revision, canonical period scope, column contract, artifact request id, and signed download handoff. HQ-to-branch export must not accidentally read Danbee board tables or branch-wide unbounded other-office rows.

  • Reservation document export worklist parity. Status: Pending. Evidence: source-refs/sanmopia-admin/application/controllers/DataRoom.php:30-94, source-refs/sanmopia-admin/application/controllers/ajax/DataRoom.php:17-240, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:1-80, source-refs/sanmopia-admin/application/models/Statistics_model.php:873-895, and source-refs/sanmopia-admin/application/models/Payment_model.php:228-231. Source behavior: the browser asks for a DataTables server-side list, but the controller loads reservation rows first, then searches, sorts, and slices in PHP memory. Document eligibility also lives in a controller switch by form id, service type, voucher end marker, and report type. Modern gap: ReservationDocumentExportWorklist must push branch/HQ visibility, template eligibility, search, sort, pagination, status filters, and report kind into a database-backed read model with an expected revision and selection token. The portal should submit an immutable export selection, not a mutable list of checked reservation ids rebuilt from current rows. Status filters must keep 12 stop-requested and 13 stopped distinct, and stopped voucher rows must use the source-mined >=3 elapsed-service-span eligibility rule instead of UI-side status filters. Backend now owns the rule in ReservationSettlementEligibilityRule: source status 13 is eligible only when SettlementServicePeriod.elapsed_days() is at least 3, matching MySQL DATEDIFF(end, start) semantics. is_voucher_settlement_worklist_eligible keeps the Statistics-model worklist selector separate from headquarters settlement, because source worklist status inclusion differs. Remaining acceptance: settlement/export readers must call the shared backend rule and include stopped voucher reservations only when status is 13 and DATEDIFF(SERVICE_END_DT, SERVICE_START_DT) >= 3, matching source-refs/sanmopia-admin/application/models/Statistics_model.php:875-893 and source-refs/sanmopia-admin/application/models/Payment_model.php:228-231. Do not reinterpret this as a count of attendance or daily-report rows unless a new domain policy version explicitly changes the source rule.

  • Branch resource post storage and audit parity. Status: Backend foundation contracted. Evidence: source-refs/sanmopia-admin/application/controllers/DataRoom.php:119-212 and source-refs/sanmopia-admin/application/models/Data_room_model.php:25-203. Source behavior: other-post routing depends on null branch ids and the headquarters branch id; create/update uploads files during the same request, deletes selected files immediately, and board removal hard-deletes rows plus filesystem paths. Modern replacement: BranchResourcePost, BranchResourceAttachmentVersion, and BranchResourceGrant use explicit headquarters, branch, and all-branches sender/recipient scopes instead of null-routing or a fixed headquarters branch id. Commands now require actor, idempotency key, caller-supplied time, and expected post revision for mutations. Attachment versions supersede instead of replacing files in place; revoked attachments and retained/revoked posts deny signed download grants. Retention keeps business records addressable for audit, and application contracts are Pydantic-only at the boundary. Remaining proof: Supabase tables/storage policies, signed URL adapter, HTTP API, generated clients, and portal UI.

  • Inter-office branch-side acknowledgement. Status: Backend/API contract foundation. Evidence: source-refs/sanmopia-admin/application/controllers/Calculate.php:337-369, source-refs/sanmopia-admin/application/controllers/Calculate.php:376-430, source-refs/sanmopia-admin/application/controllers/Common.php:156-198, and source-refs/sanmopia-admin/static/js/calculate_detail.js:276-285. Source behavior: one otherOffice detail page changes behavior by actor and fbranch-type; branch users update only board status, while HQ first updates reservation other-office settlement status and inserts detail. Modern gap: InterOfficeSettlement now has branch-facing line projection for inter_office_branch_share and inter_office_service_fee, and service-fee projection requires explicit charged-branch evidence. Branch acknowledgement now accepts typed InterOfficeSettlementAcknowledgement evidence through the application and HTTP boundary, validates service-delivery versus customer-relationship side, actor, expected revision, dataset revision, and projected line ids, then writes that evidence into transition-audit metadata. Remaining proof: bind live Danbee/operator boards and portal actions to this command path instead of source fbranch-type / OTHER_OFFICE_STATUS_FL.

  • Settlement detail navigation state. Status: Pending. Evidence: source-refs/sanmopia-admin/static/js/calculate_detail.js:297-310, source-refs/sanmopia-admin/static/js/calculate_detail.js:329-337, and source-refs/sanmopia-admin/application/controllers/Calculate.php:460-498. Source behavior: successful settlement command redirects to document.referrer when present, and history/export pages read GET filters. Modern gap: settlement command responses should include return-state token, list filters, current statement revision, and next visible action. Browser referrer cannot be source of record for branch/HQ settlement operations.

  • Data-room document bundle export selection. Status: Pending. Evidence: source-refs/sanmopia-admin/application/views/data_room/form_reservation_list.php:131, source-refs/sanmopia-admin/application/views/data_room/form_reservation_list.php:207, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:62, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:219, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:225, source-refs/sanmopia-admin/application/controllers/DataRoom.php:312-332, source-refs/sanmopia-admin/application/controllers/DataRoom.php:354, source-refs/sanmopia-admin/application/controllers/DataRoom.php:368, source-refs/sanmopia-admin/application/config/constants.php:217, and source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:4117. Source behavior: selected reservation ids are posted through a hidden iframe and streamed immediately. index == 16 expands into a document bundle by source service labels such as 바우처 and 일반, with controller-side document id switches. The source posts keyed hidden reservation[service_type][id], enforces a single selected row in JavaScript, and deletes temp XLSX/PDF files after ZIP creation. Voucher bundles use source document ids 1,3,4,5,6,20,7,8,9,10,11,15,17,21; normal bundles repeat the same six ids twice because raw ids and constants are both appended. Unknown service type returns 존재하지 않는 서비스 유형입니다.. Modern gap: DocumentBundleExportRequest must become an immutable backend request with explicit reservation selection token, service-kind decision, template/catalog version, artifact build id, and signed grant. The portal must not pick document ids, trust hidden service-type inputs, duplicate bundle membership, or use hidden POST-download frames.

  • Settlement/data-room export UX workflow authority. Status: Pending. Evidence: source-refs/sanmopia-admin/application/controllers/DataRoom.php:30-94, source-refs/sanmopia-admin/application/controllers/DataRoom.php:218-250, source-refs/sanmopia-admin/application/controllers/DataRoom.php:268-386, source-refs/sanmopia-admin/application/controllers/ajax/DataRoom.php:17-240, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:1-260, source-refs/sanmopia-admin/application/views/table/data_room/table_calculate_central.php:1-69, and source-refs/sanmopia-admin/application/views/table/data_room/table_calculate_danvi.php:1-57. Source behavior: reservation document worklists are filtered, searched, sorted, and paginated after the controller loads candidate rows; the branch filter lives in the URL hash; selected rows are posted through a hidden iframe; settlement data-room pages read current board summaries directly and expose raw export parameters. Modern gap: ReservationDocumentExportWorklist, SettlementDataRoomCollectionExportReader, SettlementArtifactVersion, and DownloadHandoffAudit need to own query scope, selection token, dataset revision, bundle/template policy, artifact build state, signed grant, failed-build repair history, return-state token, and next visible action. UI cannot be source of record for filters, checked ids, board ids, or download success.

    2026-07-05 persistent-worker tightening:

    Missing backend policySource evidenceSource behaviorModern target
    Worklist query authoritysource-refs/sanmopia-admin/application/controllers/DataRoom.php:30-94, source-refs/sanmopia-admin/application/controllers/ajax/DataRoom.php:14-240, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:26-162Controller receives DataTables params but loads candidate rows, then searches, sorts, and slices in PHP memory. Browser sends branch/date/form ids and maps raw row ids into checkboxes and hidden service-type inputs.ReservationDocumentExportWorklistQuery must own branch/HQ scope, template eligibility, search, sort, pagination, status filters, result revision, and row selection ids in a DB-backed read model.
    Export selection and bundle membershipsource-refs/sanmopia-admin/application/views/data_room/form_reservation_list.php:123-208, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:62-80, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:200-261, source-refs/sanmopia-admin/application/controllers/DataRoom.php:312-385UI posts mutable checked reservation ids through a hidden iframe; index == 16 trusts hidden service type and expands source document ids in controller code.Backend foundation exists: ReservationDocumentExportWorklist, ReservationDocumentExportWorklistCriteria, ReservationDocumentExportSelectionToken, and ReservationDocumentExportArtifactRequest freeze reservation ids, document definition keys, actor scope, source collection, period/filter keys, and artifact request idempotency behind a SHA-256 criteria fingerprint. Remaining work: DB-backed worklist query authority, service-kind/template eligibility, bundle membership policy, worker handoff, and signed grant lifecycle.
    Download authority and artifact lifecyclesource-refs/sanmopia-admin/application/controllers/DataRoom.php:85-94, source-refs/sanmopia-admin/application/controllers/DataRoom.php:388-418, source-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:224-261Raw GET export params and POST iframe submissions stream files directly from current DB rows with no durable artifact, retry, expiry, use audit, or stale dataset rejection.SettlementArtifactVersion, DocumentExportArtifactVersion, SettlementArtifactGrant, and DownloadHandoffAudit must issue signed expiring grants against immutable artifacts and record build, ready, failure, retry, supersession, and grant-use events.
    Return-state and filter authoritysource-refs/sanmopia-admin/static/js/data_room/form_reservation_list.js:163-195, source-refs/sanmopia-admin/application/controllers/DataRoom.php:47-69, source-refs/sanmopia-admin/application/controllers/DataRoom.php:268-308Browser rebuilds query strings for branch/date filters and settlement collection pages read current branch/month board rows directly. One JS path even rebuilds end_date query text without a stable token.OperatorReturnStateToken and SettlementDataRoomCollectionWorklistQuery must preserve filters, collection, branch scope, month/date window, current dataset revision, and next visible action after export/repair/download.
  • Settlement board role split and completion gate. Status: Partial. Evidence: source-refs/sanmopia-admin/application/controllers/Calculate.php:126, source-refs/sanmopia-admin/application/models/Payment_model.php:554, source-refs/sanmopia-admin/application/models/Payment_model.php:2731, source-refs/sanmopia-admin/application/models/Payment_model.php:2917, and source-refs/sanmopia-admin/application/models/Payment_model.php:4265. Source behavior: Danbee writes BOARD_PAYMENT_DANBEE_TB and flips reservation/coupon DANBEE_STATUS_FL; branch-to-HQ settlement writes board/detail/coupon/etc tables; inter-office service-side and customer-relationship side use separate DANBEE_STATUS_FL and OTHER_OFFICE_STATUS_FL flags. Full completion may depend on all three payment rows carrying both flags. Modern gap: ExternalCounterpartySettlementBatch, HeadquartersBranchSettlementStatement, and InterOfficeSettlementCase must keep separate statement revisions and completion gates. Do not collapse these source flags into one board status. Additional source dig: Payment_model::updateBoardId starts branch-to-HQ board status at 1, may move to 3 for arrears, and only moves to 2 when getCompleteFlagCountFromBoard() returns 0. That counter filters detail rows where PAYMENT_TYPE_LIST_ID_FK = 0, but current source writers insert selected UI payment-type ids and no writer for type 0 was found. Modern gate should prefer the canonical getFlagCountFromBoard(..., 'is_complete') all-flags-and-no-open-detail behavior while keeping type-0 as adapter-only legacy evidence.

  • Caregiver service-balance approval route parity. Status: Partial. Evidence: source-refs/sanmopia-admin/static/js/manager/manager_pay_inquiry.js:254, source-refs/sanmopia-admin/application/controllers/Manager.php:1057, and source-refs/sanmopia-admin/application/models/Reservation_model.php:2453. Source behavior: manager service-balance approval has two axes: PAYMENT_STATUS_ID_FK/SUB_PAYMENT_STATUS_ID_FK and BALANCE_PRICE_PAYMENT_FL/SUB_BALANCE_PRICE_PAYMENT_FL. The UI shows require, check, and complete, while backend can move reservation status between 5 and 6. Modern gap: CaregiverServiceBalanceApproval must preserve main/sub caregiver role, payout statement reference, expected revision, approval actor, and resulting reservation-status side effect. Additional source dig: /reservation/serviceBalanceConfirm is stale JavaScript path with no verified route alias; actual reachable path is /reservation/confirmServiceBalance, which sets SERVICE_BALANCE_PRICE, SERVICE_BALANCE_CONFIRM_FL = 1, and SERVICE_BALANCE_PRICE_USE_FL = 1. The stale model method only flips SERVICE_BALANCE_CONFIRM_FL. Modern command must preserve the actual three-field closeout and mark the stale path as migration compatibility evidence, not product API.