Sanmopia Feature Migration Backlog
Source archive is read-only evidence. Current local evidence root is
source-refs/; see migration-source-map/original-sources.
Do not copy CodeIgniter controllers, models, database table names, or UI folders
into modern repos. Agents must convert rules into DDD feature slices, contract
changes, backend CQRS handlers, frontend task flows, and preview proof.
Kanboard renewal requirements are now first-class migration evidence; see Kanboard Renewal Feature Improvements. Use the Kanboard P0/P1/P2 split to prevent source-era manual-operation tickets from being hidden behind narrow source-code migration.
Full source coverage and completion status are tracked in Source Feature Checklist.
Current Priority Slice
Section titled “Current Priority Slice”renewal-operating-core
- Contract: reservation lifecycle, care-team assignment, service record, customer signature, payment adjustment, financial ledger, settlement summary, privacy retention, workflow state, office operation action.
- Backend: pure domain policies for reservation transition, payment/adjustment
ledger, reward ledger, care-team assignment, service-day record, document
issuance, customer signature, caregiver compensation, branch/HQ settlement,
privacy retention, Restate workflow orchestration, OPA policy gates, and
branch office operation authority. Cross-cutting writes must emit
sanmopia_domain_change_journal_entrieswith actor, field diff, revision, idempotency, workspace, and correlation facts; Postgrespgauditis the self-host DB-level OSS audit layer, not the business timeline. - Frontend: task flows for mother, caregiver, branch operator, and HQ operator are delegated to frontend subagents, with this backlog as contract source. Each portal must render workflow progress from backend read models, not from raw Restate internals or copied source status ids.
- Tester: Dokploy/Monstore preview plus Playwright evidence, not script-only proof.
- Business-reporting migration proof now covers seven backend foundations:
sales revenue dashboards freeze chart buckets, table rows, row counts,
dataset revision, stable drilldown references, and Pydantic read payloads in
bbb6455; reporting drilldowns replace raw reservation-id lists with scoped, expiring, HMAC-signed selection tokens and Pydantic handoff payloads ind5b9c80; operator report tables replace DataTables draw/order/search/page authority with typed column catalogs, sort/filter/page intents, totals-row policy, and Pydantic payloads ind95fdd7; settlement statement exports freeze line snapshots and formula-free totals ine67350c; service-balance exception metrics freeze semantic status policy, daily zero-filled buckets, dataset revision key, and checksum in2fadc96; performance funnel metrics freeze normalized reservation facts, daily zero-filled metric buckets, backend rebooking numerator/denominator/ratio, date-basis code, dataset revision, checksum, and strict Pydantic payloads in550f3c7; engagement analytics freeze age-band catalog, source-device normalization, pet taxonomy mapping, duplicate-member counting, branch exclusion, backend ratio policy, dataset revision, checksum, and Pydantic payloads inf5e24a1. Remaining work stays in Supabase persistence, API routes, SpiceDB grants, artifacts, and frontend read-model rendering; UI must not pass raw reservation ids, DataTables column indexes, sum chart/export totals, relabel browser/pet metrics, or infer settlement state from raw source status ids. - Reservation-history export proof now has backend foundation
483593d: browser query-stringreservationArrayis replaced byReservationHistoryExportSelectionCriteria, branch/operatorReservationHistoryExportGrant, frozen masked line snapshots, dataset checksum, private storage artifact request, retention policy, and strict Pydantic payloads. Remaining work: Supabase persistence, source-row adapter, signed download grant, async renderer, export audit, API route, and admin UI migration away from raw checkbox id lists. - Customer reservation catalog proof now reaches backend
20a79e3: producer boundaries exist forservice_offering.offeringsandservice_schedule.voucher_service_days, while the consumer inbox no longer loses revisioned authority reference/revision facts. Source writes share one owner/draft/revision lock, snapshot tables reject directservice_roleDML and remainservice_role-read-only in the SSOT, and the old contribution RPC forwards to the expanded form for a database-first rollout. The exact-26 materializer RPC is now their sole snapshot writer. Its application handler, strict Supabase current-set source and CAS sink, and production runtime composition require one complete current ordered set, then recheck lineage, payload, scope, window, authoritative fingerprint, and contribution-set fingerprint before persisting the snapshot. Migration20260715090000_customer_reservation_step_catalog_exact_set_materializer.sqlfreezes 26 contribution-lineage rows, supports exact idempotent replay, and makes the loader reject stale lineage. The voucher-day producer reads an exact immutable normalized price revision, preserves legacy slash-token order, and crosses contexts only through named orchestration. Pricing Settlement now also owns completed source revisions/slices, explicit service-detail/baby-type/delivery-count/benefit-band/day axis binding withoutconsume_type_prefix inference, atomic full-population publication, and exact selection from committed populations only. Callers cannot nominate revision, import-batch, or price-version keys and nolatestfallback exists. The reservation bridge now accepts canonical axes/date only, resolves the exact selection through the Pricing Settlement decision port, and rejects decision drift before revision read. The protected HQ caller now requires an active bearer session actor and SpiceDBmanageonhq_settlement:price_catalog_management. PostgreSQL owns first approval time and replay, derives canonical manifest SHA-256 from frozen header, ordered slices, batch/workbook, and normalized entries, and checks their exact equality. Caller actor/time/manifest injection fails closed. Unapproved completion/publication service-role execution is revoked; existing unapproved populations are preserved but quarantined, with immutability, insert, and deferred linkage guards. No real workbook/source/version was selected, approved, or invoked. The materializer remains deliberately uninvoked until canonical draft-axis resolution and a trusted draft prepare/PATCH trigger can collect all 26 current contributions and refresh one complete snapshot. Remaining work is: business selection/approval/invocation of an exact source/version, trusted draft prepare invocation, 24 real source producers, a complete production contribution set, final-review materializers, and integrated stage/browser proof. Focused application/adapter/runtime tests report193 passed; a narrower integrated materializer run reports38 passed, and migration/SSOT contracts report14 passed. A rollback-scoped isolated Supabase PostgreSQL 17 smoke proves exact-25/26, replay, stale CAS, lineageless and invalid/expired latest rejection, hashes, and RPC-only ACL. This is backend-only evidence: no screenshot or WebP was created or reused. No placeholder publisher or partial readable snapshot is permitted.
Kanboard P0 acceptance batch:
- Reservation lifecycle supports consultation, pre-reservation, conversion,
active reservation, completion, cancellation, extension, suspension, payment
before edit, and payment-after adjustment without losing immutable price
snapshots.
Stale branch/HQ saves must return a conflict candidate; branch operator or
HQ admin chooses field winners through a
ConflictResolutionDecisioninstead of silently overwriting mother-visible facts. - Care-team assignment keeps one reservation contract while handling requested
caregiver, smart matching rerun, admin final assignment, caregiver
replacement, assistant caregivers, pet constraints, and multi-child cases.
Current migration adds versioned matching decisions with rejection reasons,
care-environment pet constraints, selected-caregiver policy validation,
workflow-contract snapshots for
careEnvironment,acceptsPetHousehold, andpetAllergySpeciesCodes, plus an OR-Tools CP-SAT adapter for branch-wide assignment optimization when capacity and overlapping schedules must be solved together. Assignment optimizer inputs now make cached travel-time freshness, fairness/rotation workload, replacement continuity, branch scope, and assistant slot constraints backend-owned facts before solver execution. - Service records are date-driven and written at submission time, not pre-generated as fixed database rows; replacement, multiple caregivers, and twins or higher-order births create separate report obligations.
- Customer signature is captured daily or per record and cannot be reused in bulk across records.
- Payment uses an auditable ledger for Kill Bill-ready provider flows,
conversion payment, partial cancellation, virtual-account limits, coupons,
points, refund/partial refund, custom discounts, additional burden lines,
and post-payment correction. Kill Bill REST cancellation commands are derived
from immutable
PaymentRefundIntentplus adjustment catalog versions. - Settlement and compensation derive from finalized payment, service delivery, caregiver assignment, tax/insurance choice, and branch/HQ settlement ledgers instead of hand-edited totals.
- Member withdrawal and sensitive data correction preserve legal/settlement
evidence while applying Korean statutory retention, masking, deletion, role
authorization, reason capture, and audit logs.
MemberPrivacyLifecycleWorkflowruns withdrawal and privacy retention through Restate; Supabase stores the ledger/read/RLS facts; SpiceDB checks ReBAC; OPA evaluates law/status/context policy. - Repeated branch/HQ/developer requests become office operations, scheduled exports, or CMS-authored content instead of DB/manual developer tickets.
Pricing Settlement Refactor Backlog
Section titled “Pricing Settlement Refactor Backlog”Purpose: tighten pricing_settlement DDD language before more reservation,
caregiver, branch, payment, and settlement flows import current names as stable
API.
P0. Payment Vocabulary Canonicalization
Section titled “P0. Payment Vocabulary Canonicalization”-
Target files/modules:
backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/payment/payment.py,backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/payment/reservation_payment_summary.py,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/reservation_payment_record.py,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/payment/killbill_payment_event.py, andsanmopia_modernization.{domain,application}.pricing_settlement.features.payment. -
Ubiquitous names to promote:
PaymentLifecycleStatus,PaymentMethodCode,CustomerPaymentDisplayState,PaymentProviderEventStatusAcl. -
Replace/rename:
PaymentStatus->PaymentLifecycleStatus;ReservationPaymentRecordStatusstring literal -> application alias aroundPaymentLifecycleStatus;ReservationPaymentPublicStateandCustomerPaymentPublicState->CustomerPaymentDisplayState; provider method codes such asbankandvcnt-> adapter mapping intoPaymentMethodCode.BANK_TRANSFERandPaymentMethodCode.VIRTUAL_ACCOUNT. -
2026-07-05 backend foundation:
payment.pynow promotesPaymentLifecycleStatusandPaymentMethodCodeas canonical domain names while keepingPaymentStatusandPaymentMethodas compatibility aliases. Internal payment/product-plan/fee/adapters/main imports now use canonical names; enum values remaincard,bank, andvcntto preserve DB/API compatibility. Remaining steps: moveReservationPaymentRecordStatusand provider webhook status mapping onto the canonical lifecycle vocabulary, converge mother/customer public display state names, then add exact Tach rules. -
2026-07-05 backend follow-up:
PaymentLifecycleStatusValue,PAYMENT_LIFECYCLE_STATUS_VALUES, andpayment_lifecycle_status_value()now live besidePaymentLifecycleStatus.ReservationPaymentRecordStatusis an application alias to that domain value type, and Kill Bill webhook status mapping now derives synchronized payment record statuses through the canonical helper instead of repeating raw lifecycle strings. Remaining steps: pre-registration payment-method value alias, customer/mother display-state convergence, and Tach rule enforcement. -
2026-07-05 backend follow-up:
PaymentMethodCodeValue,PAYMENT_METHOD_CODE_VALUES, andpayment_method_code_value()now live besidePaymentMethodCode.ReservationPaymentMethodandRESERVATION_PAYMENT_METHODSin payment pre-registration now alias the canonical domain value tuple instead of repeatingcard/bank/vcnt.payment_method_values()also reads the canonical tuple. Remaining steps: keep public HTTP literals until contract versioning, converge customer/mother display-state names, and add exact Tach rules. -
2026-07-05 backend follow-up: public reservation payment HTTP contracts and mother booking operational context now use
PaymentMethodCodeValuefor payment-method fields, socard/bank/vcntis sourced from the payment domain instead of repeated in interface/application DTOs. Remaining steps: converge customer/mother display-state names, keep generated contracts synchronized, and add exact Tach rules. -
2026-07-05 backend follow-up:
ReservationPaymentPurposeValue,RESERVATION_PAYMENT_PURPOSE_VALUES, and receipt-recordable payment-purpose value helpers now live besideReservationPaymentPurpose. Public payment HTTP DTOs now reuse those domain aliases forpaymentPurpose, including the narrower manual receipt and service-balance closeout subsets, instead of repeatingcustomer_share_*/service_balanceliterals in interface code. Remaining steps: converge customer/mother display-state names, keep generated contracts synchronized, and add exact Tach rules. -
2026-07-05 backend follow-up:
PaymentMethodFeeBucketValue,PAYMENT_METHOD_FEE_BUCKET_VALUES,PaymentMethodDisplayNameValue, andPAYMENT_METHOD_DISPLAY_NAME_VALUESnow live besidePaymentMethodFeeBucket. Public payment HTTP fee quote DTOs now reuse those domain aliases for fee buckets and customer-visible payment method labels instead of repeating fee bucket/display literals in interface code. Remaining steps: converge customer/mother display-state names, keep generated contracts synchronized, and add exact Tach rules. -
2026-07-05 backend follow-up:
PaymentPreRegistrationStatusValue,PAYMENT_PRE_REGISTRATION_STATUS_VALUES, andpayment_pre_registration_status_value()now live beside payment domain method/status values. Pre-registration application results and public payment HTTP DTOs now reuse that domain alias instead of repeatingpre_registered/already_pre_registeredliterals in application or interface code. Remaining steps: converge customer/mother display-state names, keep generated contracts synchronized, and add exact Tach rules. -
2026-07-05 backend follow-up:
PaymentCloseoutKindValue,PAYMENT_CLOSEOUT_KIND_VALUES, andpayment_closeout_kind_value()now live beside payment domain method/status values. Reservation payment workflow, Kill Bill ingestion, and Supabase webhook persistence now reuse that domain alias instead of defining separate closeout-kind literals for instant payment and virtual-account deposit. Remaining steps: converge customer/mother display-state names, keep generated contracts synchronized, and add exact Tach rules. -
Tach rule to add after imports are clean:
[[modules]]path = "sanmopia_modernization.domain.pricing_settlement.features.payment"cannot_depend_on = ["sanmopia_modernization.application","sanmopia_modernization.adapters","sanmopia_modernization.interfaces","sanmopia_modernization.domain.reservation_operations","sanmopia_modernization.domain.branch_operations",]cannot_depend_on_external = ["fastapi", "pydantic", "sqlalchemy"][[modules]]path = "sanmopia_modernization.application.pricing_settlement.features.payment"depends_on = ["sanmopia_modernization.application.platform","sanmopia_modernization.application.shared_kernel","sanmopia_modernization.domain.pricing_settlement.features.payment","sanmopia_modernization.domain.pricing_settlement.features.settlement",]cannot_depend_on = ["sanmopia_modernization.domain.reservation_operations","sanmopia_modernization.domain.branch_operations","sanmopia_modernization.adapters","sanmopia_modernization.interfaces",]cannot_depend_on_external = ["fastapi", "sqlalchemy"] -
Smallest safe migration order:
- Add new domain enum names as value-compatible aliases; no value changes.
- Add provider ACL functions for Kill Bill event and method-code mapping.
- Move application literals to conversion helpers returning canonical enum
values or
.valuestrings for current contracts. - Rename public read-model display state last; keep deprecated aliases for one release.
- Add Tach rules and run
uv run tach check --dependencies --interfaces --exactplusuv run tach check-external.
P0. Settlement Data-Room Duplication
Section titled “P0. Settlement Data-Room Duplication”-
Target files/modules:
backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/settlement/settlement_data_room.py,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/settlement_data_room_contract.py,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_contract.py, and matchingadapters/pricing_settlement/features/*data_room*modules. -
Ubiquitous names to promote:
SettlementArtifactCollection,SettlementArtifactRequest,SettlementArtifactGrant,SettlementDownloadHandoffAudit. -
Replace/rename: old
SettlementDataRoomRequest->SettlementArtifactRequestLegacyduring migration only;SettlementDataRoomWorkspace-> application workspace/read surface overSettlementArtifactCollection;ExternalCounterpartySettlement*public names ->ExternalSettlementCounterparty*unless source evidence requires Danbee as trace metadata. -
Tach rule to add:
[[modules]]path = "sanmopia_modernization.domain.pricing_settlement.features.settlement_data_room_workspace"depends_on = []cannot_depend_on = ["sanmopia_modernization.domain.pricing_settlement.features.settlement","sanmopia_modernization.application","sanmopia_modernization.adapters","sanmopia_modernization.interfaces",]cannot_depend_on_external = ["fastapi", "pydantic", "sqlalchemy"][[modules]]path = "sanmopia_modernization.application.pricing_settlement.features.settlement_data_room_workspace"depends_on = ["sanmopia_modernization.domain.pricing_settlement.features.settlement_data_room_workspace",]cannot_depend_on = ["sanmopia_modernization.application.pricing_settlement.features.settlement","sanmopia_modernization.domain.pricing_settlement.features.settlement","sanmopia_modernization.adapters","sanmopia_modernization.interfaces",]cannot_depend_on_external = ["fastapi", "sqlalchemy"] -
Smallest safe migration order:
- Mark old settlement data-room module as legacy in docstrings and public interface only; no behavior change.
- Add converter from old request payloads to new artifact request payloads.
- Move active interfaces/adapters to workspace module.
- Delete old contract and adapter after callers stop importing it.
- Split 2500-line workspace domain into
collections.py,artifact_request.py,grant.py,handoff_audit.py, andrepair.py; then add Tach rules.
P0. Caregiver Compensation Aggregate Leakage
Section titled “P0. Caregiver Compensation Aggregate Leakage”-
Target files/modules:
backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/caregiver_compensation/reservation_caregiver_compensation_quote.py,backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/caregiver_compensation/caregiver_compensation.py, andsanmopia_modernization.application.pricing_settlement.features.caregiver_compensation. -
Ubiquitous names to promote:
CareTeamCompensationSource,CareTeamCompensationMember,QuoteCareTeamCompensationCommand. -
Replace/rename:
QuoteReservationCaregiverCompensationCommand-> legacy adapter only;ReservationCaregiverAssignmentinput ->CareTeamCompensationSource;CaregiverCompensationSeedimport from reservation -> pricing-ownedCareTeamCompensationSeedor internal mapper result. -
Tach rule to add:
[[modules]]path = "sanmopia_modernization.application.pricing_settlement.features.caregiver_compensation"depends_on = ["sanmopia_modernization.domain.pricing_settlement.features.caregiver_compensation",]cannot_depend_on = ["sanmopia_modernization.domain.reservation_operations","sanmopia_modernization.domain.care_delivery","sanmopia_modernization.domain.caregiver_assignment","sanmopia_modernization.adapters","sanmopia_modernization.interfaces",]cannot_depend_on_external = ["fastapi", "sqlalchemy"] -
Smallest safe migration order:
- Route new callers through existing
CareTeamCompensationSource. - Keep old reservation aggregate command as adapter wrapper inside same file.
- Move compensation seed construction into pricing application mapper.
- Remove
arbitrary_types_allowed=Trueonce commands contain DTOs and domain pricing value objects only. - Add Tach rule and delete reservation aggregate import.
- Route new callers through existing
P1. Settlement CQRS Boundary Cleanup
Section titled “P1. Settlement CQRS Boundary Cleanup”-
Target files/modules:
backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/cqrs/commands.py,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/cqrs/queries.py,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/branch_settlement_command_handler.py,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/get_branch_settlement_operator_board.py, andsanmopia_modernization.application.pricing_settlement.features.settlement. -
Ubiquitous names to promote:
BranchSettlementCommand,BranchSettlementBoardQuery,BranchSettlementOperatorBoardReadModel. -
Replace/rename:
settlement/cqrspackage -> concretesettlement/commandsandsettlement/queriespackages, or deletecqrspackage if no separate rule will be enforced. -
Tach rule to add:
[[modules]]path = "sanmopia_modernization.application.pricing_settlement.features.settlement.commands"depends_on = ["sanmopia_modernization.domain.pricing_settlement.features.settlement",]cannot_depend_on = ["sanmopia_modernization.application.pricing_settlement.features.settlement.queries","sanmopia_modernization.adapters","sanmopia_modernization.interfaces",]cannot_depend_on_external = ["fastapi", "sqlalchemy"][[modules]]path = "sanmopia_modernization.application.pricing_settlement.features.settlement.queries"depends_on = ["sanmopia_modernization.domain.pricing_settlement.features.settlement",]cannot_depend_on = ["sanmopia_modernization.application.pricing_settlement.features.settlement.commands","sanmopia_modernization.adapters","sanmopia_modernization.interfaces",]cannot_depend_on_external = ["fastapi", "sqlalchemy"] -
Smallest safe migration order:
- Move command DTOs and command handler together.
- Move query DTOs and operator-board read model together.
- Update parent
settlement/__init__.pyre-exports as compatibility aliases. - Add Tach rules.
- Remove aliases after adapters/interfaces import concrete command/query packages.
P1. Branch Identity Names Inside Pricing Settlement
Section titled “P1. Branch Identity Names Inside Pricing Settlement”-
Target files/modules:
backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/settlement/branch_settlement_board.py,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/branch_settlement_command_handler.py,backend-repo/src/sanmopia_modernization/application/pricing_settlement/features/settlement/get_branch_settlement_operator_board.py,backend-repo/src/sanmopia_modernization/domain/pricing_settlement/features/pricing/regional_benefit.py, and settlement statement/inter-office contracts underapplication/pricing_settlement/features/settlement. -
Ubiquitous names to promote:
branch_profile_idfor modern branch identity,source_branch_idfor legacy numeric source id,branch_settlement_board_idfor settlement aggregate id,customer_relationship_branch_profile_idandservice_delivery_branch_profile_idonly for inter-office roles. -
Replace/rename: settlement command
branch_id: UUID->branch_profile_id; regional benefitbranch_id: int->source_branch_id; ambiguousbranch_scopestrings in pricing outputs -> explicitbranch_profile_idorsource_branch_id. -
Tach rule to add:
[[modules]]path = "sanmopia_modernization.domain.pricing_settlement.features.settlement"cannot_depend_on = ["sanmopia_modernization.domain.branch_operations","sanmopia_modernization.application","sanmopia_modernization.adapters","sanmopia_modernization.interfaces",]cannot_depend_on_external = ["fastapi", "pydantic", "sqlalchemy"][[modules]]path = "sanmopia_modernization.domain.pricing_settlement.features.pricing"cannot_depend_on = ["sanmopia_modernization.domain.branch_operations","sanmopia_modernization.application","sanmopia_modernization.adapters","sanmopia_modernization.interfaces",]cannot_depend_on_external = ["fastapi", "pydantic", "sqlalchemy"] -
Smallest safe migration order:
- Add compatibility properties for current
branch_idfields. - Rename constructor/command fields to
branch_profile_idwhere value is UUID/profile identity. - Rename legacy int fields to
source_branch_id. - Update settlement read models and contract payloads.
- Add Tach rules to prevent pricing domain from importing branch context instead of using adapter-supplied branch identity facts.
- Add compatibility properties for current
Workflow Status And Portal Surface
Section titled “Workflow Status And Portal Surface”Source evidence splits one real reservation lifecycle across mother web, caregiver web, branch admin, and HQ admin screens. Migration should preserve capabilities, not source coupling:
- Mother portal: show reservation/pre-reservation status, payment progress,
selected caregiver visibility, service-period dates, pending daily report or
signature work, document/receipt readiness, and safe next actions such as
reservation conversion or cancel request. Source anchors:
source-refs/sanmopia-user-old/src/design-system/ui-kit/organisms/ReservationCard.astro:21-80,source-refs/sanmopia-user-old/src/design-system/ui-kit/organisms/PreReservationActionButtons.astro:21-42, andsource-refs/sanmopia-user-old/src/design-system/ui-kit/organisms/ReservationManagerInfo.astro:21-80. - Care-manager portal: show assigned proceeding/completed schedules, 90-day
post-service visibility limit, main/sub caregiver role, attendance status,
daily care report obligations, payout/payment acknowledgement, and blocked
reasons when a workflow stage needs branch/HQ action. Source anchors:
source-refs/sanmopia-manager/application/controllers/Schedule.php:14-24,source-refs/sanmopia-manager/application/controllers/Schedule.php:33-49,source-refs/sanmopia-manager/application/controllers/Attendance.php:19-79, andsource-refs/sanmopia-manager/application/controllers/Reservation.php:36-76. - Branch operator portal: show branch-scoped reservations, editable commands
before policy lockout, assigned caregiver actions, payment/settlement
handoff status, and workflow retry/block state. Branch commands must come
from actor-stage policy plus SpiceDB/OPA checks. Source anchors:
source-refs/sanmopia-admin/application/controllers/Reservation.php:415-431andsource-refs/sanmopia-admin/application/controllers/Reservation.php:650-689. - HQ admin portal: show all branch workflow requests, manual/offline cases,
override-only commands, final failure reasons, retry controls, and settlement
closeout state. Source anchors:
source-refs/sanmopia-admin/application/config/constants.php:116-160,source-refs/sanmopia-admin/application/views/reservation_status/reservation_detail.php:81-169, andsource-refs/sanmopia-admin/application/controllers/Reservation.php:632-639.
Backend/contract expectation:
- expose one stable
WorkflowStatusProjectionendpoint across reservation booking, reservation operation follow-up, payment, financial lifecycle, privacy, and later care-delivery workflows; - include request id, workflow kind, source portal, actor role/id, business entity id, command name, durable step, public status, blocked reason, retryable/nonretryable failure, timestamps, resulting read-model ids, reservation status when the workflow is a booking, and the actor-stage capability authorization snapshot that is currently available;
- make portals poll this backend read model for display and refresh state instead of calling workflow engine APIs or unauthenticated raw workflow-row readers;
- filter visible fields and allowed commands per mother, caregiver, branch_operator, and hq_admin through actor/stage capability, SpiceDB relation, and OPA policy checks instead of leaking Restate service internals;
- keep source numeric status ids only as adapter/evidence references. Modern display status and actor-stage capability names must use bounded-context language.
Development Doctor
Section titled “Development Doctor”assembly-repo owns the workspace doctor commands so backend, contract,
frontend, and Starlight checks stay reproducible from one entry point.
pnpm doctor: fast developer diagnosis. Checks required tools, Serena Python config, forbidden npm/pip lockfiles, git dirty snapshots, backend Ruff/Tach, expired stage test-data batches, source refs, stage config, Supabase CLI version, and Starlightastro check.pnpm doctor:backend: backend-focused diagnosis while feature workers are editing frontend/contract repos.pnpm doctor:full: merge gate. Adds backend pytest, Vulture high-confidence scan, Supabase local DB lint, contract check, frontend check/lint/test, and Starlight build.
Doctor is read-only by design. It must not generate Supabase types, rewrite
formatting, start dev servers, mutate stage data, or hide dirty subagent work.
The test-data reaper runs in dry-run mode inside doctor: unreachable local stage
is skipped, but reachable expired fixture batches fail the gate. Actual cleanup
requires the explicit confirmation env on pnpm test-data:reap:apply.
Warnings are allowed for optional local tooling such as Docker or Mutagen;
failures mean the migration batch cannot be committed.
Planner/Curator Task Card
Section titled “Planner/Curator Task Card”Use this as the next AgentSquad handoff. Do not implement every slice in one PR.
featureSlice: renewal-operating-corehandoffFrom: plannerhandoffTo: curatorsourceRoot: source-refsoperationalEvidence: - assembly-repo/apps/docs/src/content/docs/migration-source-map/kanboard-renewal-feature-improvements.mdassemblyRepo: yamonco/sanmopia-modernization-assemblytargetRepos: contract: yamonco/sanmopia-modernization-contract backend: yamonco/sanmopia-modernization-backend frontend: yamonco/sanmopia-modernization-frontendrequiredAction: - continue from existing branch or PR if present - extract one acceptance case from source evidence - create or update contract first - hand off to backend/frontend only after contract PR evidence exists - tester must attach Dokploy/Monstore preview evidencewallFallback: ifFirstTurnPlanOnly: continue work, create real diff, run gates, submit PR ifAmbiguousSourceRule: ask sender a question card; do not invent rule ifNoToolEvidence: call required evidence tool before terminal reportFirst acceptance batch:
BranchSettlementBoard: admin 입금요청 and branch 입금확인 are distinct commands. First PR should cover only the central-admin request/close path fromCalculate.php::insertPaymentCentralToBranch().BranchSettlementStatus:0=waiting_for_branch_deposit,1=branch_deposited,2=settled,3=branch_paid_arrears,4=arrears_waiting.PriceCatalogVersionPolicy:PRICE_VERSION_YEARhas voucher direct year versions and general effective-date tuples; migrate as policy/fixture before quote math.ReservationPriceQuote: voucher Yeongdeungpo discount, voucher in-home/special surcharge, rental delivery fee, coupon discount, user-fee deposit/balance must be separate facts.PaymentMethodFeePolicy: card/bank/virtual-account fee rules belong to billing policy, not settlement board opening.BranchOfficeScope: branch user sees own reservation/settlement rows; central role can filter any branch.
Next source-mined batch:
SettlementActionJournal: HQ 입금요청, 지사 입금확인, 타지사 정산확인, 정산자료실 export/repair actions are separate commands with expected revision, idempotency key, actor, frozen input rows, and branch/HQ scope. Source priority isHeadOfficeSettlementfirst, thenExternalCounterpartySettlement, thenInterBranchSettlement: head-office flow has the broadest status, amount, data-room, and export blast radius. Source anchors includeCalculate.php:15-66,Calculate.php:151-312,Payment_model.php:827-1351,DataRoom.php:218-250,Payment_model.php:1750-1960,calculate_detail.js:81-310, andSpreadsheet_model.php:3741-3911.CaregiverAvailabilityWindow: caregiver unavailable days and branch matching conflict checks use append-only change events, realtime invalidation, and a derived conflict projection instead of direct insert/delete schedule rows.PaymentChangeAdjustment: changed-payment completion must freeze customer delta, provider context, coupon adjustment, and post-payment amount snapshot before settlement continuation can read it.PriceCatalogVersionPolicyplusServiceBalanceAdjustment: voucher year, effective-date price versions, reservation deposit/balance, subsidies, support funds, and service-balance corrections must be versioned facts, not recalculated mutable amounts.
Migrated acceptance cases:
ReservationPaymentSummary: source payment rows now project to immutable recorded payment facts instead of recalculating from mutable price policy. Source evidence:source-refs/sanmopia-admin/application/models/Payment_model.php:53-62andsource-refs/sanmopia-admin/application/models/Reservation_model.php:431-460. Ubiquitous payment purposes arecustomer_share_deposit,customer_share_balance,service_balance, andpre_reservation_deposit; source flags are wrapped asSourceReservationPaymentTypeIdadapter values.MotherVisibleChargeSummary: mother-facing payable amount now reads only from finalized reservation charge snapshots after payment close. Current pricing rules, downstream caregiver payout, branch settlement, HQ settlement, and payment-provider fee recalculation cannot change the displayedcustomerPayableKrw. Source evidence:source-refs/sanmopia-admin/application/models/Payment_model.php:53-62,source-refs/sanmopia-admin/application/models/Reservation_model.php:431-460, andsource-refs/sanmopia_web/application/controllers/MyReservation.php:194-326. Backend exposes aGetMotherVisibleChargeSummaryQueryread model oversanmopia_finalized_charge_snapshotsandsanmopia_finalized_charge_lines; contract exposesMotherVisibleChargeSummarywith customer-facing line directions only.PaymentMethodFeePolicy: PG fee now uses a named basis-point rate catalog. Source evidence:source-refs/sanmopia-admin/application/models/Payment_model.php:3923-3970,source-refs/sanmopia-admin/application/models/Payment_model.php:3985-4009, andsource-refs/sanmopia-admin/application/models/Payment_model.php:4019-4042. Deposit-balance flat-fee rows, missing gateway context, card, bank transfer, and virtual account are separate fee buckets.PriceCatalogImport: price workbook ingestion now uses a componentized catalog import model instead of fixed spreadsheet coordinates. Source evidence:source-refs/sanmopia-admin/application/controllers/cli/Price.php,source-refs/sanmopia-admin/application/models/Spreadsheet_model.php:4807-5395, andsource-refs/sanmopia-price-table/*. Backend domain ownsPriceCatalogEntry,PriceCatalogComponent, import issues, and duplicate checks; the OSSopenpyxlparser is isolated in the adapter layer withdefusedxmlinstalled for XML hardening. Current workbook audit results are2025_voucher_250121= 190 entries / 8 issues,2025_voucher_250827= 190 / 8,2026_voucher_251211= 154 / 0, and2026_voucher_260119= 143 / 55. Issue-bearing workbooks are blocked, while the older issue-free workbook is never auto-selected merely because it has zero issues. Backend7a9d26esupplies the guarded approval path, but no source/version was selected, approved, or invoked;154 / 0is not authority. Supabase stores private workbook artifacts inprice-catalog-imports, import lifecycle rows insanmopia_price_catalog_import_batches, and immutable componentized entries insanmopia_price_catalog_entries.PriceCatalogQuote: published catalog entries now feed a mother-facing quote read model instead of accepting raw mutable amount fields from UI or source-shaped handlers. The Supabase repository reads only published and effective catalog rows, filters by catalog, program, service tier, service day count, and criteria, and returnsPublishedPriceCatalogEntryvalues to the application handler.PriceCatalogQuotePolicyreturns componentized quote lines withcomponentName,basis,amountKrw, andquantity, whilegross_service_priceandcustomer_sharestay separate so the customer payable amount is not double-counted after subsidies are already represented in the workbook. Contract validators mirror the same totals rule. Accepted quotes can now be finalized intosanmopia_finalized_charge_snapshotsandsanmopia_finalized_charge_lines;referencedirection lines preserve catalog gross/subsidy facts without changing mother payable or settlement deduction totals.BranchSettlementLineItems: branch settlement opening now records explicit directional line items instead of hiding branch due, delivery fee, caregiver compensation, voucher service commission,promotional_coupon_cost,cancelled_gift_coupon_commission, and branch adjustment in one mutable total. Source evidence:source-refs/sanmopia-admin/application/controllers/Calculate.php:166-235,source-refs/sanmopia-admin/application/controllers/Calculate.php:290-299, andsource-refs/sanmopia-admin/application/models/Payment_model.php:3880-4055. Supabase persists these facts insanmopia_branch_settlement_lineswithline_kind,direction,amount_krw,policy_version_label,source_reference_key, andline_order; branch membership dues are frozen insanmopia_branch_membership_due_snapshotsfrom the monthly override/base branch fee source before settlement board opening;PromotionalCouponCostsource parity is frozen insanmopia_branch_promotional_coupon_cost_snapshotsand read asPromotionalCouponCost/promotional_coupon_costcalculation source lines; deleted product coupon 5,000/10,000 source rows remaincancelled_gift_coupon_commission. Branch RLS follows the parent settlement branch membership, while HQ retains central override. The backend now exposesBranchSettlementOperatorBoardthroughGET /branch-settlement-operator-boards/{branchSettlementId}so operator screens receive line-derived receivable/payable totals, stored net balance, and reconciliation delta without reading Supabase tables directly. Branch dashboard reads now useGET /branch-settlement-operator-dashboards/{branchProfileId}to return branch-scoped settlement counts,settlementStatusCounts, aggregate receivable/payable/net/delta totals, item rows, and public Restate financial lifecycle workflow status.settlementStatusCountsreplaces source dashboard subquery counters such asdetailStatusZeroandcouponStatusZerowith domain status names. Branch inclusion now readsBranchProfilebranch_settlement_participationplusis_active; source internal branch ids stay traceable assource_branch_idand are not used as hardcoded filters in the query adapter. Reconciliation export datasets now useGET /branch-settlement-reconciliation-exports/{branchProfileId}withcsv,xlsx, orjsonformat intent, returning stable columns, rows, totals, and the privatebusiness-report-exportsartifact bucket target. Stored artifact commands now usePOST /branch-settlement-reconciliation-export-artifacts/{branchProfileId}to render CSV, XLSX, or JSON, write a frozensanmopia_business_report_snapshotsrow, upsert a renderedsanmopia_business_report_exportsrow, and upload the private Supabase Storage object. Async export starts now usePOST /branch-settlement-reconciliation-export-workflow-starts/{branchProfileId}to authorize the branch operator, freeze the export dataset at request time, and send that dataset toBranchSettlementReconciliationExportWorkflowin Restate for delayed rendering without recalculating changed settlement rows. Backendb3d3dc1preserves that frozen dataset lineage when the branch reconciliation adapter persists and rehydrates the business-report snapshot:dataset_revision_keyand its SHA-256 fingerprint round-trip as one nullable pair. This is adapter-specific lineage repair; it does not claim every report snapshot has non-null lineage or close export grant, revocation, Storage, and UI evidence gaps.PromotionEntitlements: coupon/gift shop and promotion admin now map sourcePRODUCT1,PRODUCT2,GIFT, andSHOPPINGMALLvalues at the application boundary only. Source evidence:source-refs/sanmopia_web/application/controllers/Shop.php:21-148,source-refs/sanmopia_web/application/controllers/api/Shop.php:10-238,source-refs/sanmopia_web/application/models/Product_model.php:8-76,source-refs/sanmopia_web/application/controllers/api/Payment.php:223-310, andsource-refs/sanmopia-admin/application/models/Coupon_model.php:255-319,888-927. Backend domain exposes versionedPromotionalEntitlementRule,ServiceGiftEntitlementPolicy,PromotionalEntitlement, andGiftFulfillmentPolicy; Pydantic request models live only in application contracts. Supabase persists entitlement rules, issued entitlements, fulfillment products, and fulfillment requests with explicit RLS.CaregiverCompensation: caregiver compensation terms now cover primary and assistant caregivers with source tax ids isolated in adapters. Source evidence:source-refs/sanmopia-admin/application/models/Reservation_manager_salary_model.php:21-52andsource-refs/sanmopia-admin/application/models/Reservation_manager_salary_model.php:112-180. Per-caregiver compensation quotes now price each assigned caregiver by role, grade code, and compensated days, with explicit adjustment overrides replacing hidden custom totals. Source evidence:source-refs/sanmopia-admin/application/models/Reservation_model.php:2699-2804,source-refs/sanmopia-admin/application/models/Reservation_model.php:3150-3204, andsource-refs/sanmopia-admin/application/controllers/Manager.php:1845-1884. Supabase persists immutable per-caregiver quote lines insanmopia_caregiver_compensation_quote_linesso assigned caregivers, branch staff, mothers, and HQ can read the same captured result through RLS. Quote lines now snapshotcontract_assignment_key,rate_rule_id,policy_version_label,rate_effective_from, andrate_effective_until. Supabase uniqueness is based on reservation booking, contract assignment key, and policy version so changing handoff order or future grade-rate policies cannot rewrite already captured payout facts. Voucher reservations default toemployment_insurance; customer-funded reservations default tobusiness_income_withholding; saved caregiver terms override defaults per caregiver.IssueReservationCaregiverPayoutInstructionsHandlerturns frozen quote lines into payout instructions, andSupabaseReservationCaregiverPayoutInstructionStoreupserts quote lines before instructions so FK-backed payout facts stay idempotent. Settlement continuation readssanmopia_caregiver_payout_instructions, not raw quote lines, so payout cannot advance without bank-account and tax-evidence facts. Supabase persists payout instructions insanmopia_caregiver_payout_instructions; instructions carry payable amount, withholding amount, tax category, bank-account holder/number, tax evidence reference, rate rule id, and policy version label before payout can be marked paid.ReservationCaregiverAssignment: one reservation contract can hold a primary caregiver plus ordered assistant caregivers. Assignment facts now preservecaregiver_id, stable contract assignment key, role, handoff order, grade code, and planned service days so compensation can quote each caregiver independently by grade and day count. Primary caregiver replacement is a date-boundedPrimaryCaregiverTerminside the same reservation contract, so replacement does not create a fake re-reservation. Each primary term has its own stable contract assignment key, grade code, and inclusive planned service day count, allowing caregiver compensation and service attendance to split facts by worked period. Matching candidates exposegrade_code, and grade-sensitive booking policies can filter candidates before ranking. Supabase reservation rows useassigned_caregiver_user_id, and profile role values usecaregiver. Application quote handling now converts reservation caregiver rosters into caregiver compensation assignments; missing grade codes or planned service days fail fast instead of falling back to hidden amounts. When recorded care delivery facts are supplied, the quote usesservice_day_attendance_ledgeras the compensated-day source, quotes only caregivers with recorded service delivery, and preserves the source on each quote line.CaregiverMatchingPolicy: matching now evaluates each candidate through a versioned policy and returns explicit rejection reasons before ranking. Care-environment constraints are carried as structured facts, not source-era pet flags: each pet has species, count, size, and newborn-isolation fields; candidates declare whether they accept pet households and which species allergies block assignment. Requested or manually selected caregivers are no longer blindly accepted; they must pass the same policy, including availability, distance, grade, rating, and care-environment constraints. The mother booking context, booking workflow contract, JSON schema, TS client, and Python Pydantic contracts all carry the samecareEnvironment,acceptsPetHousehold, andpetAllergySpeciesCodessnapshot so Restate replay and frontend submission use one immutable matching input. Source evidence:source-refs/sanmopia-admin/application/models/Matching_model.php:83-95,source-refs/sanmopia-admin/application/models/Matching_model.php:237-310,source-refs/sanmopia_web/static/js/smart_matching_1.js:57-89, andsource-refs/sanmopia-admin/application/models/Reservation_model.php:4471-4480.CaregiverAssignmentOptimizeris the domain port for branch-wide assignment plans.OrToolsCaregiverAssignmentOptimizerlives in adapters, imports OR-Tools CP-SAT, and optimizes multiple booking demands at once while domain matching remains dependency-free. It assigns one primary caregiver per demand, avoids overlapping double-booking by caregiver capacity, preserves policy version labels on assignments, and returns unassigned demand reasons such ascare_environment_not_supportedorcapacity_conflict.OptimizeCaregiverAssignmentsHandleris the application command boundary for branch/HQ planning: it validates branch actor scope, rejects branch-operator out-of-scope candidates before solver execution, and delegates only valid demands to the optimizer port.CaregiverAssignmentOptimizerInputsnow prepares travel-time penalties, fairness/rotation weights, replacement continuity, and assistant caregiver slot constraints as backend-owned inputs; next proof is Supabase persistence plus adapter/API ingestion.ReservationOperationCommand: branch/HQ reservation operation boards now have a modular domain command policy instead of exposing source numeric status ids.ReservationOperationCommandRuleCatalogowns the changing stage/condition matrix, while the policy maps actor, current reservation status, and command to an operation plan with target status, explicit missing facts, and follow-up actions. This keeps new statuses such asawaiting_deposit,change_requested, andcancellation_requestedfrom forcing controller or UI rewrites whenever branch policy changes.ReservationOperationCommandHandleris the application boundary: it validates branch actor scope, loads the command context through a port, records accepted plans, and refuses rejected decisions without mutating state. Supabase now stores backend-only operation contexts and plan ledgers insanmopia_reservation_operation_contextsandsanmopia_reservation_operation_plans; branch/HQ HTTP routes expose command boards and apply commands through Supabase Auth plus SpiceDB permissions. The migrated command catalog covers change request, care stop request, cancellation approval, virtual-account expiry/deposit closeout, primary and assistant caregiver assignment, attendance/care delivery/report recording, caregiver service-balance receipt, and price-version change. Source evidence:source-refs/sanmopia-admin/application/config/constants.php:116-160,source-refs/sanmopia-admin/application/models/Reservation_model.php:1768-2224,source-refs/sanmopia_web/application/models/Payment_model.php:198-226,source-refs/sanmopia-manager/application/models/Attendance_model.php:18-52, andsource-refs/sanmopia-admin/application/models/Reservation_manager_salary_model.php:21-124. Supabase CLI is pinned in the backend repo withsupabase@2.109.0;pnpm run supabase:db:push:stage,pnpm run supabase:db:lint:stage, andpnpm run supabase:types:stageare the stage schema gates. Generatedsupabase/generated/database.types.tsis the Supabase table-shape source for frontend and contract work, preventing handwritten table contracts from drifting away from self-hosted Supabase. Queue workload names, priorities, visibility timeout, max attempts, worker-concurrency env names, and dead-letter queues are also backend-owned throughapplication.platform.supabase_queue_contractand the Supabase CLI migrationsanmopia_queue_workload_contracts. Backend worker dispatch now has a sharedSupabaseQueueWorkloadBatchDispatcherandSupabasePgmqQueueAdapterread/archive contract, so feature workers inherit one success, retry, max-attempt dead-letter, and source-archive policy instead of copy-pasting pgmq calls.SpiceDBRelationshipSyncProcessornow binds the first workload processor: branch membership and branch hierarchy relationship payloads are tuple-validated beforeSpiceDBAuthorizationPort.write_relationships(...), with corrupt payloads routed to the shared dead-letter path. Accepted plans with follow-up actions now startReservationOperationFollowUpWorkflowthrough the neutral workflow engine port. Supabase stores service-role-only request and action-event ledgers insanmopia_reservation_operation_follow_up_workflow_requestsandsanmopia_reservation_operation_follow_up_action_events; the sharedWorkflowStatusProjectionexposes them asreservation_operation_follow_up. Workflow row status/json/date parsing is promoted intoapplication.platform.supabase_workflow_ledger, so booking, payment, financial lifecycle, operation follow-up, and projection adapters do not carry copy-pasted parsing rules. Remaining gap: expose the new command strings in frontend contract consumers, implement each follow-up effect in its owning feature slice, and keep pricing/payment policy readers feeding the operation context instead of hardcoded flags.ReservationCollaboration: branch/HQ reservation writes now have a shared optimistic-concurrency and realtime-invalidation foundation. Source evidence:source-refs/sanmopia-admin/static/js/reservation_status/reservation_detail.js:271-336,source-refs/sanmopia-admin/application/controllers/Reservation.php:843-932,source-refs/sanmopia-admin/application/models/Reservation_model.php:3731-3739,source-refs/sanmopia-admin/application/models/Reservation_model.php:1349-1456,source-refs/sanmopia-admin/application/models/Branch_model.php:23-73, andsource-refs/sanmopia-admin/static/js/reservation_status/reservation_history_new.js:224-230. Backend domain and contract now exposeReservationCollaborationCommand,ReservationCollaborationPolicy,ReservationCollaborationConflict,ReservationChangeJournalEntry,ReservationRealtimeProjectionEvent, andReservationCollaborationCommitPlan. The command requiresexpected_revision,idempotency_key, actor, source workspace, mutation-kind list, and lower-snake field paths; stale saves return a conflict diff instead of overwriting newer branch/HQ edits.navigation_intentandreturn_state_tokenreplacehistory.back()style flow control. Supabase now storesrevision_number, append-onlysanmopia_reservation_change_journal_entries, andsanmopia_reservation_realtime_projection_events; thesanmopia_commit_reservation_collaborationRPC commits the revision bump, idempotency replay, journal row, and private Supabase Broadcast in one service-role call.CommitReservationCollaborationHandlernow reads the current Supabase booking revision, rejects stale writes before persistence, and exposesPOST /reservation-collaboration-commits/{reservationId}with a409conflict detail payload for operator UX.OpenReservationCollaborationWorkspaceHandlerexposesPOST /reservation-collaboration-workspaces/{reservationId}for current revision, Supabase Broadcast/Presence topic, event name, current field values, latest audit event id, last return-state token, and a coordination policy. The policy names private Broadcast+Presence, expected-revision-only commits, save/discard/draft dirty navigation, and domain-journal audit as the UX contract. Supabaserealtime.messagesRLS now authorizes reservation Presence on the same branch/HQ topic boundary as Broadcast.ListReservationCollaborationJournalHandlerexposesPOST /reservation-collaboration-journal/{reservationId}for the recent operator-facing audit timeline. Generateddatabase.types.tsincludes both tables.pgAuditremains a compliance/troubleshooting log, not the operator-facing history source. Branch handoff is now wired astransfer_reservation_branch:branch_handoff.branch_profile_idupdates booking branch ownership inside the same revision-checked RPC that writes the journal and private Broadcast event. Booking service dates are also wired:service_schedule.start_onandservice_schedule.end_onupdateservice_start_atandservice_end_atinside the same revision-checked commit with range validation.service_schedule.extra_service_daysreplaces extra service-day child rows after the same revision commit succeeds.caregiver_assignment.rosterreplacessanmopia_reservation_caregiver_assignmentsrows after the same revision commit, preserving primary/assistant role, Supabase user id, grade, planned days, term dates, andcontract_assignment_key, while updating the booking primary caregiver user for compatibility. Remaining work: bind service term, payment fact, address, and memo mutation paths to this handler before their owning persistence commands, expose the collaboration routes/types from contract repo, wire frontend Nanostores stale-state/conflict/Presence UX, backfill initial revisions, and run two-workspace stage smoke.ServiceDayAttendance: reservation care delivery now has its own aggregate:ReservationCareDeliveryownsServicePeriod,CareTeam, andServiceDayAttendanceledger entries. One reservation can record attendance for multiple caregivers on the same service date, while still enforcing one ledger entry per reservation/caregiver/service date. Service-day recording now resolves the active primary caregiver term for the requested service date, so an original primary caregiver cannot record attendance after a replacement term has started. Attendance carries role, handoff order, grade code, and credited day unit so caregiver compensation can use recorded service delivery facts instead of mutable UI totals. Recording is blocked outside the service period or after the 90-day post-service window, and a mother safety attendance notice event is emitted from the aggregate. Source evidence:source-refs/sanmopia-manager/application/controllers/Attendance.php:19-79andsource-refs/sanmopia-manager/application/controllers/Schedule.php:33-49.ReservationBirthInformation: caregiver service-record birth fields and the daily birth-information push cron now migrate into a reservation birth-information feature slice instead of fixed source slots.ReservationBirthProfilestores unboundedChildBirthRecordarrays, expected child count, recorder role, actual/expected birth dates, and policy version.BirthInformationReminderPolicyplans idempotent reminders per reservation/caregiver/date with configurable service-window offsets, repeat interval, local dispatch time, and timezone. Supabase persists profiles insanmopia_reservation_birth_profilesand due reminders insanmopia_birth_information_reminder_tasks; Novu sends thebirth_information_requestedworkflow and Strapi authors workflow rules. Source evidence:source-refs/sanmopia-manager/application/controllers/cli/push/Reservation_baby.php:5-45,source-refs/sanmopia-manager/application/models/Reservation_baby_model.php:4-110, andsource-refs/sanmopia-manager/application/controllers/Service.php:46-235.ServiceCalendar: reservation end-date calculation, holiday management, and selected extra Saturday/holiday service dates now map into a versioned service-calendar feature instead of system-management rows and duplicated reservation helper methods.ServiceCalendarPolicyplans workdays from a national/branch holiday catalog, preserves warning messages when selected extra-service counts do not match registered dates, and emits immutable service-calendar plan results. Supabase stores configurable holiday rows insanmopia_service_calendar_holidays, selected reservation extra days insanmopia_reservation_extra_service_days, and captured plan snapshots insanmopia_reservation_service_calendar_plans. National Korean public holidays can be generated through the OSSholidayspackage; branch closures stay explicit and RLS-scoped. Calendar impact facts now includecustomer_benefit_entitlement_review_requiredfor selected extra service days, leaving coupon or compensation realization to promotion/benefit consumers instead of service-calendar or frontend amount code. Source evidence:source-refs/sanmopia-admin/application/models/Manage_system_model.php:9-172,source-refs/sanmopia-admin/application/controllers/ManageSystem.php:12-68,source-refs/sanmopia-admin/application/models/Reservation_model.php:4009-4262, andsource-refs/sanmopia_web/application/models/Reservation_model.php:2709-2768.CareDeliveryDailyCareReport: caregiver service-day reports now use a Strapi-authored form definition and keyed answer submission model instead of PHP view fields and report-type branches.DailyCareReportDefinitiongroups lower_snake_case fields into sections with value kinds, choice options, required fields, and policy version.DailyCareReportSubmissionrecords reservation, mother, branch, caregiver id, caregiver role, handoff order, service date, service-day sequence, submitted-by user, submitted-at time, and keyed answers. Supabase stores definitions insanmopia_daily_care_report_definitionsand submitted answers insanmopia_daily_care_report_submissions; RLS limits reads/writes to mothers, assigned caregivers, branch operators, and HQ. Document reporting consumes submissions throughDailyCareReportPrintPayloadfor A4 report-series rendering, so printable output does not own care-delivery form rules or source Excel coordinates. Source evidence:source-refs/sanmopia-manager/application/controllers/Report.php:5-68,source-refs/sanmopia-manager/application/models/Report_model.php:4-80,source-refs/sanmopia-manager/application/controllers/Service.php:46-235, andsource-refs/sanmopia-manager/application/views/service/service.php.CustomerRecordConfirmation: mother confirmation for customer-facing care records now has its own care-delivery feature. A confirmation binds onerecord_kindandrecord_reference_codeto onesignature_capture_id, with both domain policy and Supabase uniqueness preventing the old bulk reuse of one electronic signature across multiple service records. The domain rejects non-mother signers, confirmation before signature capture, duplicate record confirmation, reused signature capture ids, and raw signature image payloads. Supabase stores immutable rows insanmopia_customer_record_confirmations; authenticated users can select participant rows, mothers can insert their own confirmations, and service role retains administrative repair authority. Contract helpers exposebuildCustomerRecordConfirmationCodeandfindCustomerRecordConfirmationProblemsfor frontend task flows. Kanboard evidence: #2491, #2625, #2731.ReservationAuthorization: reservation workflow actors now use onebranch_operatorrole instead of duplicated branch staff/leader capability matrices. Supabase profile and branch membership rows acceptmother,caregiver,branch_operator, andhq_admin; SpiceDB branch relations usebranch_operator,caregiver,hq_admin, andparent_hq. Reservation capabilities remain stage-bound in domain policy, then relationship permission is checked through SpiceDB. Booking submission usessubmit_booking_requestat draft stage and branchmanagepermission before a Restate reservation workflow is started. Supabase role claims stay inapp_metadata, and Data API exposure stays explicit-grant plus RLS. Branch membership and branch-to-headquarters changes enqueue idempotentsanmopia_spicedb_relationshipsmessages so Supabase remains the membership source of record while SpiceDB owns permission evaluation. Backend authorization now exposes a capability snapshot with allowed and denied reservation actions plus stage/relationship reasons, and the contract package includes the matching snapshot type and stage-only helper for frontend fallback/pre-rendering. Workflow status projection now returnsreservationStatusandreservationCapabilityAuthorizationfor booking workflows so UI command rendering can use backend authorization as the SSOT.DocumentReporting: printable documents now separate key-driven catalog selection, template rendering, and stored artifacts. Catalog entries usedefinitionKeyvalues such asservice_use_contractandmother_daily_report; source document keys stay inside optionalsourceReferencevalues for migration/audit mapping only. Supabasesanmopia_document_definitionsrows are projected into domain catalog entries through an adapter, so document types, service-kind eligibility, normal-catalog visibility, download availability, and template versions can change without adding source-coordinate branches to handlers. Catalog entries classify family and generation strategy, carry template and stylesheet versions, and expose normal-catalog visibility separately from download availability. Render requests choosehtmlorpdf,jinja_weasyprintorbrowser_print,a4, and orientation. Printable templates use repeat template blocks withflow_to_next_pageoverflow andunboundedcapacity so service rows, report answers, and contract line items are not capped by spreadsheet coordinates; long content flows into additional A4 pages. Each repeat block names lower_snake_case section key, data path, item template key, and item template fields. Supabase rejectsrepeat_sectionsthat omit those keys, use hyphen/camel section or item template keys, use non-flow overflow, or reintroduce fixed-slot fields such asmaxItemsPerPage. Render request identity is an idempotentrenderRequestCodecomposed from reservation, definition key, template version, and output format instead of an opaque UI request id. Runtime rendering loads the enabled document definition through Supabase Data API, downloads the HTML template from Supabase Storage, buildsPrintableDocumentTemplate, then renders HTML/PDF. The rendered artifact is uploaded to the privatereservation-documentsbucket and upserted intosanmopia_document_render_artifactsonrequest_code, preserving stable download metadata after price or template-adjacent policy changes. The platform Storage adapter can create short-lived signed URLs for private artifact download handoff without exposing service credentials to the frontend. Download handoff now loads by logicalrenderRequestCode, requiresVIEW_RESERVATIONthrough reservation stage policy plus SpiceDB, and returns only signed URL metadata instead of storage object internals. Bundle handoff validates requested members againstDocumentCatalog.bundle_members(serviceKind), signs each ready artifact, and returns missing definition keys so UI can show incomplete bundle state without constructing Supabase paths. Daily report export items carrymotherorcaregiveraudience, first/middle/final service-day stage, service date, and optional caregiver assignment facts. Daily care report printable payloads carry ordered caregiver submissions, sections, answers, display values, caregiver role, handoff order, and policy version for Jinja/WeasyPrint or browser-print HTML templates. Source evidence:source-refs/sanmopia-admin/application/controllers/ajax/DataRoom.php:16-129,source-refs/sanmopia_web/application/controllers/MyReservation.php:194-326,source-refs/sanmopia_web/application/controllers/api/ReservationDocument.php:9-51, andsource-refs/sanmopia-admin/application/controllers/cli/ReservationDocument.php:19-124.DocumentRoom: branch/HQ 자료실 behavior now maps into a document-room collaboration feature instead of a generic source controller name. Posts carry direction (headquarters_to_branch,branch_to_headquarters,headquarters_internal), target branch, author branch, audience, visibility windows, pinning, revision, and Strapi authoring metadata. Attachments live in private Supabase Storage bucketdocument-room-attachments, use content-type allowlists instead of open uploads, and are readable only through parent-post RLS. Settlement-room exports and promotion application exports remain separate feature slices because they are business reports, not document-room posts. Source evidence:source-refs/sanmopia-admin/application/controllers/DataRoom.php:119-202,source-refs/sanmopia-admin/application/models/Data_room_model.php:25-199, andsource-refs/sanmopia-admin/application/controllers/DataRoom.php:389-417.CustomerEngagementContent: board/notice/QnA/FAQ/notification sources now map into modern engagement concepts instead of source table names.ContentPublicationcovers community articles, public notices, knowledge-base answers, service campaigns, alliance benefits, caregiver news, and health-education profiles, articles, videos, FAQ entries, and visit classes with schedule, pinning, revision, audience, and view-count rules.CampaignPlacementcovers home popups, banners, community hero slots, event lists, caregiver-news lists, media references, dismiss keys, CTA values, and display priority.CustomerInquirycovers mother-submitted 1:1 inquiries with branch/HQ answer lifecycle. Member notifications carry visible windows, action payloads, and read state. Strapi is adopted as the OSS CMS/admin surface over Supabase Postgres and Supabase Storagecontent-assets; app-facing contracts stay incustomer-engagementand Supabase table types.CommunicationDeliveryadds Novu-backed workflow rules, subscriber preferences, dispatch idempotency, and delivery events for coupon, virtual-account, service-start, daily-report, and campaign notification flows. Source evidence:source-refs/sanmopia_web/application/models/Board_model.php:9-44,source-refs/sanmopia_web/application/models/Notice_model.php:8-27,source-refs/sanmopia_web/application/models/Qna_model.php:8-23,source-refs/sanmopia-admin/application/controllers/Clinic.php:17-60,source-refs/sanmopia_web/application/models/Faq_model.php:8-68,source-refs/sanmopia_web/application/models/Notification_model.php:11-68, andsource-refs/sanmopia-admin/application/controllers/ajax/CommonBoard.php:18-86.BranchOfficeContent: OfficeManagement notice, education, event notice, branch news, and stamp/profile image sources now map into branch operations instead of handmade admin CRUD.BranchOfficePublicationcovers branch/HQ notices, caregiver instructions, caregiver event notices, branch news, audience windows, pinning, and attachment keys.BranchOfficeLearningMaterialcovers global or branch-scoped caregiver education with online/offline/hybrid delivery.BranchOfficeBrandAssetcovers official stamps, profile images, logos, and attachments as private Supabase Storage objects. Strapi provides OSS authoring, while backend contracts and Supabase RLS own target branch visibility. Source evidence:source-refs/sanmopia-admin/application/controllers/OfficeManagement.php:454-1150,source-refs/sanmopia-admin/application/models/Office_management_model.php:153-497, andsource-refs/sanmopia-admin/application/models/Branch_model.php:476-557.MotherLeadIntake: partner-benefit application sources now map intoMotherLeadProgramPolicy,MotherLeadRequest, eligibility, cancellation, export readiness, export batches, export items, and result-file attachment. The old source programs for breast-milk analysis, gift box, and home sanitizing are absorbed at application/contract boundaries; domain naming usesbreast_milk_nutrition_analysis,postpartum_gift_box, andhome_sanitizing. Supabase owns versioned program policy, active request uniqueness, export lifecycle, privatemother-lead-resultsstorage, and RLS by mother, branch participant, and HQ role. Source evidence:source-refs/sanmopia_web/application/controllers/Momfirst.php:10-146,source-refs/sanmopia_web/application/controllers/Clean.php:10-126,source-refs/sanmopia_web/application/controllers/api/Momfirst.php:9-137,source-refs/sanmopia_web/application/controllers/api/Clean.php:9-128,source-refs/sanmopia_web/application/models/Momfirst_model.php:53-230,source-refs/sanmopia_web/application/models/Momfirst_model.php:280-774,source-refs/sanmopia_web/application/models/Clean_model.php:49-219,source-refs/sanmopia_web/application/models/Clean_model.php:242-496,source-refs/sanmopia_web/application/controllers/cli/Momfirst.php:21-168,source-refs/sanmopia_web/application/controllers/cli/Clean.php:20-127, andsource-refs/sanmopia-admin/application/models/Momfirst_model.php:12-272.CaregiverPerformanceRecognition: yearly caregiver and branch award logic now maps intoCaregiverPerformanceSeason, metric rules, grade rules, scorecards, contribution facts, manual adjustments, competition ranking, and branch performance scorecards. Strapi authors seasons, metric rules, and grade thresholds over Supabase Postgres; generated scorecards remain backend facts with RLS for caregivers, branch members, and HQ. Source metric fields map only at application/contract boundaries; source total fields are not accepted as contribution metrics. Branch eligibility uses tags such asopened_this_seasonandaward_pausedinstead of hard-coded branch id lists. Source evidence:source-refs/sanmopia-admin/application/controllers/Wintherace.php:19-540,source-refs/sanmopia-admin/application/models/Win_the_race_model.php:10-740,source-refs/sanmopia-manager/application/controllers/WinTheRace.php:7-23,source-refs/sanmopia-manager/application/models/Win_the_race_model.php:28-110,source-refs/sanmopia-admin/application/models/Manager_score_model.php:31-260, andsource-refs/sanmopia-admin/application/models/Manager_class_model.php:15-40.BusinessReporting: statistics, charts, tabular report data, and export requests now map into immutable business-report snapshots instead of source controller names or jQuery DataTables request shapes.BusinessReportSnapshotcaptures report kind, audience scope, branch scope, period, calculation version, columns, rows, and totals. Supabase stores snapshots insanmopia_business_report_snapshots, stores export request state insanmopia_business_report_exports, and keeps rendered files in the privatebusiness-report-exportsbucket. Operator access is RLS-scoped by headquarters role or branch membership. The UI/export plan should use OSS table/export tooling, such as TanStack Table and SheetJS when a browser-side XLSX writer is needed, instead of rebuilding source table plugins or mutable spreadsheet controller code. Source evidence:source-refs/sanmopia-admin/application/controllers/Statistics.php:19-542,source-refs/sanmopia-admin/application/controllers/Datatable.php:4-28,source-refs/sanmopia-admin/application/models/Statistics_model.php:75-2451,source-refs/sanmopia-admin/application/models/Datatable_model.php:4-150, andsource-refs/sanmopia-admin/static/js/statistics/performance.js.
Next Source-Verified Slices
Section titled “Next Source-Verified Slices”Use this list before launching another legacy-source collection agent. These items were selected from the current docs and source map to avoid repeating already migrated booking/payment/document scans.
| Next slice | Target feature folder | Source evidence | First proof |
|---|---|---|---|
BranchSettlementCalculationReader | pricing_settlement/features/settlement/branch_settlement_calculation | Calculate.php:15-66, Payment_model.php:828-980, Payment_model.php:1124-1298, Payment_model.php:4335-4400 | Backend foundation migrated: branch settlement opening now reads unsettled Supabase financial lifecycle rows, branch-settlement ledger lines, branch membership due snapshots, and PromotionalCouponCost snapshots into a source-ID-preserving BranchSettlementCalculationSnapshot, then freezes those lines when a board opens. PromotionalCouponCost / promotional_coupon_cost, cancelled_gift_coupon_commission, and voucher_service_commission are now first-class receivable line kinds with Supabase source-alias normalization and separate board/API read fields; VoucherServiceCommission now owns policy version, effective period, eligible-reservation count, and branch settlement line-item materialization instead of source UI/model formulas; BranchSettlementDeliveryFee now owns effective-dated delivery fee selection and line-item materialization while rental-equipment quote delivery remains separate pricing lifecycle; branch membership due freezes legacy monthly override/base fee sources before board opening; cancelled gift coupon completion uses cancelled_gift_coupon_commission_paid; branch operator dashboard responses expose settlementStatusCounts for status-count UX; HQ/internal branch exclusion uses branch_settlement_participation instead of source id filters; SettlementCompletionReview consolidates membership due, delivery fee, cancelled gift coupon commission, unpaid detail rows, and unpaid coupon rows before central review closes as settled or arrears. Remaining proof: broader public API wiring. |
InterOfficeSettlement | pricing_settlement/features/settlement | Calculate.php:337-430, Payment_model.php:2657-2814, Payment_model.php:2816-2935, Payment_model.php:2977-3031 | Backend foundation migrated: service-delivery/customer-relationship branch split, headquarters fee, coupon deduction, misc adjustment, frozen charge snapshot, export row-shape validation, canonical lookup key, Supabase aggregate/line persistence, command/API handlers, lookup-key CQRS reads, and branch-period history reads through GET /inter-office-settlement-histories/{branchProfileId} plus interOfficeSettlementHistoryPath. Settlement statement command replay covers branch-side acknowledgement idempotency. Branch-facing projection now emits inter_office_branch_share and inter_office_service_fee line items, requires fee charged-branch evidence, extends Supabase line-kind checks, and allows signed net_balance so payable-heavy other-area settlement does not get forced into UI arithmetic. Branch-side acknowledgement now has typed InterOfficeSettlementAcknowledgement evidence at domain/application/HTTP/TS contract boundaries and stores service/customer branch side, actor, expected revision, dataset revision, and projected line ids in transition-audit metadata. ReservationExternalCounterpartySettlementStatus now replaces source DANBEE_STATUS_FL with a Supabase projection, backend query API, TS contract helpers, and internal workflow updater with source dataset revision conflict checks. Next proof: bind projection and acknowledgement into live Danbee/operator board persistence and UI. |
PaymentChangeAdjustment | pricing_settlement/features/payment now, later API surface can move under payment_change_flow if it grows | ajax/Reservation.php:68-178, Payment_model.php:4423-4521, Payment.php:658-764, web Payment_model.php:857-900 | Backend foundation migrated: latest awaiting adjustment is owner-payable only; delta, coupon, and pure amount stay separate; pre-registration freezes payment id/provider/timestamp before payment UI; server-side Kill Bill paid verification freezes provider context and deposit time; superseded unpaid rows are cancelled, not deleted; public FastAPI/TS contract handoff now requires Supabase bearer auth, mother user ownership for pre-registration/payment verification, SpiceDB booking permission for operator request/completion, protected read projection by reservation/latest or durable adjustment id, PaymentChangeOperatorBoard for paid/completed rows with operator-only access and booking-scoped visibility, board enrichment for human reservation id, branch profile id, mother display name, reservation charge amount, purpose-tagged payment deposit milestones, and backend/contract-owned manual receipt command drafts for customer-share-balance payment-change rows with stable command draft id and deposited-at defaults (4bd81e1, 455f74a, 858fa24, 87a6e61), frontend reservation-operation board consumes the backend receipt draft instead of local payment-method/purpose/amount/date/idempotency defaults (d28ff5f), payment pre-registration/direct booking/Kill Bill status writers now fill purpose/deposit facts for new records, append-only PaymentChangeOperatorAudit for completion and amount-correction commands without rewriting paid totals, stale amount-correction rejection through expectedAdjustmentUpdatedAt (f9ce5dc, 05059a5), zero mother-payable closeout now has CloseLatestZeroPayablePaymentChangeAdjustmentCommand, POST /reservation-payment-change-adjustments/{reservationBookingId}/zero-payable-closeouts, backend-owned zero_payable_closeout receipt facts, negative-refund guard, settlement continuation through the same financial lifecycle workflow source-id convention, refund-eligible adjustment selection through PaymentChangeRefundEligibleAdjustmentSelectionPolicy, and adjustment-id workflow polling through GET /reservation-payment-change-adjustments/{paymentChangeAdjustmentId}/settlement-continuation-workflow-status. Next proof: broader paid-row board/search/status UI, existing payment row backfill, provider/refund workflow execution for credit cases, manager/admin manual service-balance receipt commands, status projection invalidation, and mother payment-change UI. |
FamilySponsoredReservation | member_management/features/family_account, reservation_operations/features/reservation_booking | web step1.php:91-213, web Reservation_model.php:314-374, web api/Service.php:75-81, web api/Payment.php:91-92, web Payment_model.php:570-704 | Backend/contract foundation migrated: FamilyAccountAuthority reads Supabase family-account grants and exposes sponsor booking, delegated payment, and care-history access decisions without accepting sponsor/payer/grantee ids from client payloads. Supabase schema separates family members, reservation sponsors, payment delegations, and care-history access grants with explicit grants and RLS. Payment closeout recipient resolution now derives delegated reservation payer users from active family payment-delegation facts and keeps ambiguous grants unresolved; catalog v4 adds payer payment-complete workflow routes for delegated reservation payment products. Gift-coupon receiver routing now has sanmopia_payment_coupon_receiver_snapshots plus a coupon-fulfillment dispatch resolver path for user-backed receivers; phone-only gift receiver contacts route through sanmopia_payment_direct_recipient_contact_snapshots and contact-only sanmopia_direct_contact_communication_dispatch_requests without fake user ids; and primary/secondary branch payment notices use versioned sanmopia_branch_payment_notice_contacts instead of source branch-id hardcoding or sorted membership selection. Remaining proof: booker/beneficiary snapshot in booking workflow, emergency-contact contact-only projection, consent authority, payment liability, document visibility, refund recipient, and full booking/payment/document UI wiring without inferring authority from emergency-contact relation text. |
ReservationPartyAuthoritySnapshot | member_management/features/family_account | web step1.php:91-213, web Reservation_model.php:314-374, web api/Service.php:70-86, web api/Payment.php:91-92, web Payment_model.php:570-704 | Backend/contract foundation migrated: ReservationPartyAuthoritySnapshot freezes mother beneficiary, booker, payer, emergency contact, document viewer, refund recipient, and notification recipient roles through explicit ReservationPartyAuthorityEvidence. ReservationPartyAuthoritySnapshotPayload exposes the same vocabulary through strict Pydantic contracts and rejects unknown source relation text as role/authority. Contact-only emergency parties and relation labels are preserved as evidence but do not imply document, payment, or refund authority. Remaining proof: Supabase persistence, booking/payment/refund/document command binding, consent authority, route wiring, and UI removal of phone/relation-text authority inference. |
PostpartumCareCenterStayPlan | reservation_operations/features/postpartum_care_center_stay | web Reservation_model.php:264-306, web api/Reservation.php:363-405, admin Reservation.php:699-897, admin Reservation_model.php:6016-6032, manager schedule_info.php:557-562, Spreadsheet_model.php:3558-3562 | Backend foundation migrated: versioned care-center stay catalog, delivery-method due-date rules, customer/operator delivery-profile command, expected revision, continuation next-weekday policy, service due-date recalculation, downstream invalidation targets, caregiver schedule fields, and document projection are now domain/application-owned. Source care-center ids stay out of contracts. Remaining proof: Supabase persistence, public/internal API handlers, actor authorization, and UI wiring. |
OfflineReservationDuplicateGuard | reservation_operations/features/offline_reservation_intake | Reservation.php:1490-1518, Reservation_model.php:6100-6113, Reservation.php:452, Reservation.php:632-640 | Backend foundation migrated: offline/manual intake now has a domain duplicate guard and application contract that use deterministic idempotency, normalized customer identity, branch/source channel, service-period overlap checks, conflict response, and duplicate-decision audit instead of source status-id queries by email/start/end date. Remaining proof: Supabase persistence, API command handler, actor authorization, and operator UI wiring. |
SettlementManualAdjustmentCatalog | pricing_settlement/features/settlement/manual_adjustment_catalog.py | Calculate.php:504-548, Service_model.php:274-319, other_service_management.js:1-89, calculate_detail.php:1214-1265 | Backend foundation migrated: other-service settlement items are now stable-code, revision-guarded, versioned catalog entries with headquarters actor/reason/idempotency audit; used entries deprecate instead of hard-deleting; branch, Danbee, and inter-office statement lines freeze catalog version, item name, unit price, count, direction, actor, statement revision, lineage evidence, and total amount in backend-owned snapshots. Branch-scope frozen manual lines now materialize bidirectional branch_adjustment line items so receivable/payable manual adjustments flow through central net-balance math instead of view-side PRICE * COUNT. This replaces source numeric row ids, rendered-row modal state, and client/view total authority. Remaining proof: Supabase persistence, command/query APIs, SpiceDB/RLS, and operator UI wiring. |
VoucherEndNoticeReadiness | document_reporting/features/document_delivery | ReservationDocument.php:254-621, Reservation_model.php:4388-4459, Reservation_model.php:6117-6127, Spreadsheet_model.php:4104-4113, Spreadsheet_model.php:4328-4338 | Backend foundation expanded: voucher end notice readiness now uses backend-owned normal/stopped batch kinds, voucher eligibility, reservation-status skip, customer-deposit payment confirmation, artifact readiness, signed grant state, sent-attempt duplicate suppression, failed-attempt retry planning, and customer inbox action payloads. Source doc id 21, mail flags 3/4, END_DOCUMENT text search, and /myReservation/document/{id} URL are not contract values. Remaining proof: Supabase persistence, render worker/provider handoff, route binding, delivery provider adapter, revoke/resend UI, and access-history UI. |
ReservationDocumentExportWorklist | document_reporting/features/reservation_document_export_worklist | DataRoom.php:119-212, DataRoom.php:312-385, form_reservation_list.php:123-208, form_reservation_list.js:62-80, form_reservation_list.js:200-261, Spreadsheet_model.php:3741-4198 | Backend/contract foundation migrated: export worklists now freeze selected reservation ids, document definition keys, source collection, actor scope, period/filter keys, and artifact request idempotency through a backend SHA-256 criteria fingerprint and Pydantic payloads. Numeric source document ids are rejected as document definition keys; duplicate mutable row selections are rejected; token actor/expiry/fingerprint checks replace hidden iframe and raw GET export authority. Remaining proof: DB-backed worklist query/read model, service-kind/template eligibility and bundle membership policy, async render worker handoff, Supabase Storage signed grant lifecycle, and UI removal of iframe/raw-param export paths. |
ReservationPriceVersionAndSubsidyAdjustment | pricing_settlement/features/reservation_price_version_adjustment | constants.php:291-313, Reservation_model.php:646-923, Reservation_model.php:3560-3642, Reservation_model.php:3667-3685, Reservation_model.php:5608-5867, Reservation.php:461-511, Reservation.php:1819-1855, reservation_price_table.php:1-120, reservation_detail.js:390-410, reservation_detail.js:760-832, reservation_detail.js:1031-1068 | Backend foundation migrated: PRICE_VERSION_YEAR is now dated PriceCatalogVersionPolicy; reservation price-version changes emit immutable before/after facts for catalog version, catalog entry, voucher consume type, additional service options, customer share, subsidy, and regional support instead of rewriting old reservation/payment rows; service-day mismatch blocks the decision; service-balance override freezes default/override amounts with actor/reason/idempotency audit. Backend naming follow-up: PricingPlan lookup now exposes catalog_source_code (standard_price_catalog / voucher_price_catalog) instead of leaking source table names. Remaining proof: Supabase persistence, command/query APIs, SpiceDB/RLS, price catalog read-model wiring, backend-owned price-table/service-balance preview projections, voucher pure-additional service-balance calculation, and operator UI without client-side recalculation. |
CaregiverAssignmentOptimizerInputs | caregiver_assignment/features/assignment_optimizer_inputs | Matching_model.php:44-177, Matching_model.php:720-862, Reservation_manager_trans_info_model.php:13-87, Reservation.php:538-577, Reservation_model.php:2166-2303, Manager_model.php:2039-2088, Manager_model.php:2146-2180, ajax/Reservation.php:34-64, ajax/Reservation.php:180-208, reservation_detail.js:586-629 | Backend foundation migrated: demand slots model primary/assistant roles without fixed slot ceilings; cached travel-time freshness, fairness/rotation workload, replacement continuity, assistant slot handoff order, same-caregiver cross-slot exclusion, branch scope, and backend-owned score components are prepared before solver execution. Shared-kernel follow-up: caregiver matching, matching profile, care team contract, and optimizer input stable-code predicates now reuse shared_kernel / stable_identity instead of local regex copies. Remaining proof: Supabase persistence, OR-Tools adapter ingestion, server-owned slot-count/class eligibility, primary/assistant duplicate prevention across all slots, public branch/HQ planning API, generated clients, and operator UI. |
CaregiverPayoutReadModelAndAcknowledgement | pricing_settlement/features/caregiver_compensation | manager salary_helper.php:8-227, Reservation_manager_salary_model.php:21-52, Reservation_manager_salary_model.php:112-180, manager Other.php:223-294, manager Reservation_model.php:13-78, manager Reservation_model.php:352-410, manager Reservation_model.php:488-516, admin Manager.php:1133-1236, admin Manager.php:1810-1945, admin Manager_model.php:377-590, admin Reservation_model.php:2945-3036, manager_pay_detail.js:1-260, pay_info_detail*.php:165-270 | Backend foundation migrated: CaregiverPayoutReadModel projects caregiver-facing rows from frozen payout statements plus branch deposit acknowledgement and receipt state; account display is masked; available receipt actions are backend-owned; query scope rejects caregiver mismatch. Supabase persists revisioned receipt-state events/current projections, append-only payout adjustment audits, and append-only payout destination change audits with participant RLS, target-level expected revision, idempotency keys, full previous/adjusted amount, withholding, tax, memo, bank account, and tax-evidence snapshots. FastAPI now binds branch deposit acknowledgement, caregiver receipt acknowledgement/exception/repair/read-state routes, payout adjustment audit recording, and payout destination change audit recording to Supabase stores, derives actor identity from Supabase Auth, and checks SpiceDB reservation-booking record_caregiver_payout or caregiver self-view permission. Public TS/OpenAPI contracts expose matching paths, schemas, command kinds, changed-field vocabularies, and Supabase row types. Assistant payout acknowledgements are now matched by payout-slot identity including role and handoff order, covering source SORT > 0 row collision risk. Backend commit 81719dc adds admin/manager shared payout projection foundation: AdminCaregiverPayoutStatementProjection, CaregiverPayoutSlot, CaregiverPayoutOperatorActionAvailability, CaregiverPayoutServiceBalanceState, CaregiverPayoutCompletionDecision, and Pydantic projection payloads. Backend commit 292f736 adds Supabase-backed admin projection persistence with projection/row tables, participant RLS, realtime publication, deterministic latest-revision reader, row upsert store, and JSONB display/action roundtrip for multiple caregiver slots. Backend commit 33db343 and contract commit 1082f66 bind the admin projection to authenticated FastAPI and public TS contract via GET /admin-caregiver-payout-statement-projections/{reservationBookingId} and AdminCaregiverPayoutStatementProjectionResponse. Backend commit e973097 queues admin payout projection refresh requests from branch deposit, receipt state, payout adjustment, and destination-change mutations through Supabase PGMQ queue sanmopia_admin_payout_projection_refresh; backend commit 6e3bdfc executes the rebuild worker; backend commit 39a8eba adds care-team assignment change as a refresh trigger; backend commit 6758b30 binds protected assignment/assistant-clear HTTP commands to that invalidation bridge and persists document-projection invalidation receipts; backend commit 5dd338b queues service-use contract document render requests to sanmopia_document_rendering after assignment changes; backend commit 3cdc86a consumes document render queue messages, waits for frozen render-source rows, renders printable artifacts, and stores the result; contract commit 495d5cc exposes the command schemas. Remaining proof: frontend wiring without PHP/JS salary recalculation, live E2E reservation payout refresh proof through assignment changes, render-source producer binding, and access-grant issuance. |
BranchOfficeProfileAndMembershipFacts | branch_operations/features/office_profile | Branch_model.php:157-169, Branch_model.php:186-285, Branch_model.php:293-425, OfficeManagement.php:399-429 | Branch address/profile/membership facts become versioned office profile rows and scoped admin operations, not source-field CRUD. |
2026-07-11 Reservation Payment Authority Runtime Delta
Section titled “2026-07-11 Reservation Payment Authority Runtime Delta”Backend c5c69c8 and contract c8e1016 replace browser-created reservation
payment facts with a backend-owned waiting obligation. Booking now remains
scheduled; pre-registration accepts only paymentId, bearer identity, and
Idempotency-Key. Family payer authority consumes the exact reservation,
purpose-derived scope, delegation revision/effective interval, and amount
ceiling, then freezes an immutable ReservationPaymentActorSnapshot before
Kill Bill handoff. The bounded stage chain passed booking, family delegation,
pre-registration/replay/conflict, payment verification, service delivery,
caregiver payout, branch settlement, headquarters settlement, and final
settled. 2026-07-12 checkout handoff proof added backend-owned
PaymentProviderHandoffSession persistence to
POST /reservation-payment-pre-registrations, with exact replay and stage
cleanup verified by pnpm supabase:smoke:stage:reservation-payment-handoff-session.
2026-07-12 callback proof added
pnpm supabase:smoke:stage:killbill-payment-event-callback, which posts an
official-shape Kill Bill ExtBus payment callback to the hosted backend, verifies
one persisted inbox row, one queued payment-webhook message, duplicate replay
without requeue, worker archive, completed Restate payment workflow, approved
payment record, and stage batch reaping.
2026-07-12 integrated proof added
pnpm supabase:smoke:stage:killbill-callback-to-settlement-completion, which
uses the callback-approved payment workflow as the settlement continuation
source and proves service delivery, caregiver payout, branch settlement,
headquarters settlement, final settled, branch settlement board visibility,
and stage cleanup. Latest run used booking
b681a97a-3bb9-4e00-887d-c50d3799ef23, payment workflow
killbill:PAYMENT_SUCCESS:b681a97a3bb94e00887dc50d3799ef23:stage-killbill-tx-20260712141353-8b4ed89b,
caregiver payout instruction ec4f5565-8a01-57bf-b830-35e1e0a193b6, and
service-balance closeout event 9f805567-b079-484e-ad60-054a53d6a9fd.
Remaining family work is sponsor contract acceptance, historical
payment/refund/receipt recipient projections, and the same actor-snapshot
binding for balance, change-adjustment, and refund flows.
Functional Backlog
Section titled “Functional Backlog”| Feature slice | Domain owner | Source evidence | Required proof |
|---|---|---|---|
pricing-settlement-core | pricing_catalog, settlement_accounting | Payment_model.php, Reservation_model.php, Calculate.php, voucher spreadsheet | Domain tests for fee/eligibility/buckets, API contract, admin UI preview |
price-catalog-import | pricing_catalog | cli/Price.php, Spreadsheet_model.php, sanmopia-price-table/* | OSS workbook parser, componentized amount rows, Supabase private workbook bucket, import issue contract, no fixed cell-coordinate import API |
price-catalog-quote | pricing_catalog | Reservation_model.php, voucher spreadsheet | Published/effective catalog read model, componentized quote contract, accepted quote snapshot capture, no mother payable double-counting, no raw mutable amount input |
branch-operations-core | branch_network | Branch_model.php, OfficeManagement.php, branch login/session code | Branch-scoped row visibility and central-role override proof |
caregiver-assignment | branch_network | Manager_model.php, Matching_model.php, Reservation.php, ajax caregiver selection | Candidate card shows score, preference match, distance, transport time, grade code, and handoff order |
caregiver-compensation | settlement_accounting | Reservation_manager_salary_model.php, source salary helper | Caregiver compensation facts separate from branch settlement facts; quote lines snapshot contract assignment key, rate rule id, policy version, and effective dates |
caregiver-performance-recognition | care_delivery | Wintherace.php, Win_the_race_model.php, Manager_score_model.php, Manager_class_model.php | Strapi-authored seasons/metric rules/grade thresholds, Supabase RLS, competition ranking, manual adjustment evidence, branch tag eligibility |
service-calendar | reservation_operations | ManageSystem.php, Manage_system_model.php, Reservation_model.php, DATE_LIST_VW, HOLIDAY_TB, RESERVATION_ADD_DATE | Versioned national/substitute/company/branch holiday catalog, substitute-of relation, announcement/supersession audit, OSS Korean public holiday seed provider, extra-service-day selections, immutable plan snapshots, Supabase RLS, no hidden end-date recalculation |
service-day-attendance | care_delivery | sanmopia-manager/application/controllers/Attendance.php, sanmopia-manager/application/controllers/Schedule.php | Reservation care delivery aggregate records one attendance ledger entry per reservation/caregiver/service date and emits mother safety notice |
notification-obligation-dispatch | care_delivery | sanmopia-manager/application/controllers/cli/Fcm.php, sanmopia-manager/application/models/Notify_model.php, sanmopia-manager/application/models/Fcm_model.php, sanmopia-manager/application/models/User_model.php | Backend-owned report/service notify obligations, scheduled/manual dispatch decisions, retry/dead-letter/suppress/repair work-queue actions, provider attempt audit vocabulary, no UI-side due-date/token/filter math |
mother-lead-intake | customer_engagement | Momfirst.php, Clean.php, Momfirst_model.php, Clean_model.php, momfirst admin/promotion views | Lead lifecycle separate from paid reservation lifecycle; source program codes map only at contract boundary; Supabase tables cover policy, request, export, and result file lifecycle |
promotion-entitlements | pricing_catalog, settlement_accounting | Shop.php, api/Shop.php, Product_model.php, Coupon_model.php, coupon payment callback | Versioned entitlement rules, application-only Pydantic contracts, Supabase RLS, fulfillment request lifecycle |
voucher-coupon-credits | pricing_catalog | Coupon_model.php, voucher selection/payment views | Coupon discount, active coupon amount buckets, and cancelled_gift_coupon_commission handoff to settlement |
daily-care-report | care_delivery | Report.php, Report_model.php, Service.php, service/service.php | Strapi-authored dynamic form definitions, keyed answer submissions, Supabase RLS, multiple caregivers through role and handoff order, no source view field names in contracts |
document-reporting | document_reporting | DataRoom.php, ReservationDocument.php, MyReservation.php, report controllers, reservation history export | Contract coverage for key-driven catalog entries, render requests, artifacts, daily report export items, daily care report print payloads, and reservation-history export datasets; source document keys/raw selected reservation ids remain internal migration references only |
document-room | document_reporting | DataRoom.php, Data_room_model.php | Strapi-authored branch/HQ posts, direction and target-branch RLS, private Supabase document-room-attachments, file content-type allowlist, retention, no open upload bucket |
business-reporting | business_reporting | Statistics.php, Datatable.php, Statistics_model.php, Datatable_model.php, statistics JS files | Immutable report snapshots, scoped Supabase RLS, private export artifacts, contract validators, OSS table/export UI, no jQuery DataTables request contract; performance funnel and engagement analytics now have backend snapshot foundations for chart/table metric authority |
customer-engagement-content | customer_engagement | Board.php, Clinic.php, Notice.php, Qna.php, Faq_model.php, Notification_model.php, CommonBoard.php | Strapi-backed CMS authoring, ContentPublication, health-education taxonomy, CustomerInquiry, and MemberNotification contracts with Supabase RLS |
campaign-placement | customer_engagement | Event.php, modal_start_popup.php, event image directories | Strapi-backed popup/banner/event placement, Supabase RLS, active windows, dismiss keys, CTA metadata, no hardcoded campaign arrays |
communication-delivery | customer_engagement | Fcm_model.php, Notify_model.php, Notification_model.php, cli/Reservation.php, management/Push.php | Novu-backed OSS notification workflows, Strapi-authored workflow rules, user preferences, dispatch idempotency, provider audit events, no provider names in domain |
workflow-state-orchestration | reservation_orchestration, pricing_settlement, member_management | Kanboard renewal reservation/settlement/withdrawal repeated-operation cards | Restate owns long-running state transitions, retry, and idempotency; MemberPrivacyLifecycleWorkflow advances withdrawal rows through running, operator-review, failed, and completed states, separates retained-record snapshots, schedules delayed purge tasks, and masks the Supabase Auth account plus member profile when review is not required; MemberPrivacyPurgeWorkflow claims due purge tasks, redacts retained snapshot payloads, and marks purge ledgers completed or failed with execution summaries; due purge workflow triggering is exposed through internal token-guarded POST /internal/member-privacy-purge-workflow-triggers and bound by Supabase Cron through pg_cron plus pg_net; stage runtime settings are configured by pnpm supabase:cron:configure; already-issued access JWTs are rejected by backend Auth metadata checks and a restrictive Supabase RLS guard after withdrawal masking lands; ReservationBookingWorkflow uses stable booking request keys, public POST /reservation-booking-workflow-starts, backend SDK handler mount /restate/v1, stage deployment registration, accepted price catalog quote input, and finalized charge snapshot id progress; ReservationFinancialLifecycleWorkflow advances service-delivered, caregiver payout, branch settlement, HQ settlement, and settled transitions, exposes public GET /reservation-financial-lifecycle-workflow-requests/{financialLifecycleWorkflowRequestId}, and writes service-role-only Supabase ledger rows; Kill Bill payment pre-registration freezes payment amount through public POST /reservation-payment-pre-registrations before payment UI entry; ReservationPaymentWorkflow runs Kill Bill paid-payment verification, cancel, refund, approved charge finalization handoff, virtual-account deposit closeout classification, reservation status-at-closeout freezing, automatic settlement continuation requests, typed payment communication dispatch planning, and automatic paid-payment dispatch command resolution for mother/branch-operator/delegated-payer routes through the same deployment, public POST /reservation-payment-workflow-starts, public GET /reservation-payment-workflow-requests/{paymentWorkflowRequestId}, and service-role-only Supabase ledger rows; Supabase persists booking, financial lifecycle, payment, and member privacy workflow ledger/read facts including last_transition_at, completed reservation id, financial stage totals, gateway result, approved charge finalization status, finalized charge snapshot id, payment closeout kind, reservation booking/status closeout snapshot, settlement continuation status/workflow id, withdrawal status, retained-record snapshot, purge schedule, purge workflow invocation id, purge execution summary, and failure reason; settlement continuation source readers include service delivery, caregiver payout, branch settlement, and HQ settlement ledger lines; branch settlement operator dashboards expose branch-scoped aggregate totals plus public financial lifecycle workflow status through GET /branch-settlement-operator-dashboards/{branchProfileId}, export-ready reconciliation datasets through GET /branch-settlement-reconciliation-exports/{branchProfileId}, frozen Supabase business-report export artifacts through POST /branch-settlement-reconciliation-export-artifacts/{branchProfileId}, Restate-delayed frozen export rendering through POST /branch-settlement-reconciliation-export-workflow-starts/{branchProfileId} plus BranchSettlementReconciliationExportWorkflow, and backend repair commands through export workflow retry/cancellation endpoints; export workflow requests are persisted through sanmopia_business_report_exports workflow and repair-audit columns and exposed by WorkflowStatusProjection kind branch_settlement_reconciliation_export; next required gap is frontend repair controls, Cron request-history smoke for due-purge workflow triggering, payment closeout state-transition policy application, refund entitlement math, and live stale-token rejection smoke after withdrawal; no Supabase-only orchestration loop |
reservation-status-lifecycle | reservation_operations | constants.php, Reservation_model.php, reservation status views, Report_model.php:312-350, Report_model.php:445-477 | Status transition contract covers reserve, confirmed, process, calculate, done, cancel, continue, stop, pre-reservation. ServiceSuspension backend foundation now replaces log-derived stop intervals with durable request, approval, resume boundary, affected dates, and impact commands. Remaining proof: persistence/API binding, revision-checked stop/resume collaboration mutations, realtime conflict logging, stop-day final report, suspended-day report exclusion, coupon reset, stopped-voucher >=3-day settlement rule, and customer/branch notifications. |
payment-change-flow | customer_status_billing | ajax/Reservation.php, Payment_model.php | Backend ledger, protected public command/read API, server-side Kill Bill paid verification, paid/completed operator-board projection, append-only operator audit, paid-adjustment settlement continuation, shared financial-lifecycle workflow status lookup, reservation/profile enrichment, and purpose-tagged payment deposit milestone read model exist for sanmopia_payment_change_adjustments; next work wires frontend operator paid-row status UX, payment command purpose capture, and status projection invalidation while preserving finalized charge snapshots |
branch-membership-fees | settlement_accounting | Branch_model.php, Calculate.php, Payment_model.php | Membership due, delivery fee, caregiver compensation, promotional_coupon_cost, cancelled_gift_coupon_commission, and adjustments are versioned directional settlement line items |
member-privacy-lifecycle | customer_status_billing, document_reporting, settlement_accounting | Kanboard #2253 and repeated withdrawal/privacy correction cards | MemberPrivacyLifecycleWorkflow, 개인정보 보호법 제21조, 전자상거래법 시행령 제6조, 국세기본법 제85조의3, 사회서비스 제공자료 5년 retention basis, OPA policy gates, SpiceDB actor checks, Supabase retained-data ledger, OpenFGA not required |
Runtime Implementation Notes
Section titled “Runtime Implementation Notes”-
ReservationFinancialLifecycle: public financial lifecycle query/command routes now take Supabase bearer identity only as actor identity and delegate action authorization to SpiceDB reservation-booking permissions. Stage mapping is:view,finalize_charge,record_care_delivery,record_caregiver_payout,prepare_branch_settlement,review_hq_settlement, andclose_reservation_finance. Supabase RLS remains a persistence safety rail, not the application RBAC source. -
SFC-20260706-CUR-028/ReservationHistoryExport: backend runtime now owns reservation-history export selection, source-row projection, frozen dataset, and artifact-request persistence through Supabase tablessanmopia_reservation_history_export_*. Public API routePOST /reservation-history-export-requestsbuilds the frozen dataset from a backend source-row reader and SpiceDB-scoped grant, not from browser rawreservationArrayquery strings. Next work: renderer, signed Storage download handoff, artifact audit ledger, and admin UI contract consumption.
Handoff Contract
Section titled “Handoff Contract”Planner/curator must hand off one feature slice at a time with:
featureSlicesourcePathsourceSymbolsourceTermubiquitousNameacceptanceCaseworkflowNameretentionBasistargetReposrequiredPreviewPath
Backend/frontend/tester must continue existing PRs when present. If first turn only produced a plan, the wall fallback prompt must say: continue work, produce real diff, run gates, publish PR handoff evidence, then request tester proof.
Source Evidence Index
Section titled “Source Evidence Index”| Evidence | Source path | Rule extraction |
|---|---|---|
| Reservation statuses | sanmopia-admin/application/config/constants.php:115-160, sanmopia_web/application/config/constants.php:47-92, sanmopia-manager/application/config/constants.php:110-155, sanmopia_web/application/views/mypage/item/reservation_func_btn.php:1-190, sanmopia_web/application/views/mypage/item/reservation_detail_func_btn.php:1-210, sanmopia_web/application/controllers/MyReservation.php:21-113, sanmopia_web/application/views/mypage/reservation.php:41-88, sanmopia_web/application/models/Reservation_model.php:806-827, sanmopia_web/application/controllers/api/Reservation.php:86-130, sanmopia_web/application/models/Reservation_model.php:1091-1111, sanmopia_web/application/models/Report_model.php:312-350, sanmopia_web/application/models/Report_model.php:445-477, sanmopia_web/application/controllers/api/Service.php:24-33, sanmopia_web/application/controllers/cli/Reservation.php:37-280 | Convert source constants and UI status switches into a versioned CustomerReservationStateMappingCatalog plus bounded-context lifecycle language. Source codes include -2/-1/0..19 such as 0 예약, 2 일정미확정, 4 진행, 5 정산, 6 완료, 7 변경요청, 8 취소요청, 9 취소, 11 연장, 12 중단요청, 13 중단, 14 입금대기, 15 사전예약, 16 사전예약대기, 17 입금대기취소, 18 사전예약취소, 19 오프라인; keep these as adapter/catalog data only. Old reservations must carry source catalog version/effective date and compatibility flags (PRERESERVATION_FL, PRE_CONVERT_FL, offline marker) so later status changes do not break action availability, active/history tab placement, shared CustomerDetailSurfaceKind, CustomerNewReservationEligibilityPolicy blocking, pre-reservation card selection, or cron/payment transition interpretation. Backend foundation now adds ReservationStateTransitionPolicy / contract rules for source-mined customer cancel/change/stop/extension, payment callback, virtual-account expiry, cron auto-progress, and approval transitions. ReservationOperationCommandHandler now uses those rules for command paths with enough guard facts, stop_requested is a first-class status before stopped, and ServiceSuspensionImpactPlan captures stop intervals that legacy reports inferred from RESERVATION_CHANGE_LOG. Contract state fields are stable codes such as waiting_deposit, not raw status numbers. |
| Year price version | sanmopia-admin/application/config/constants.php | Convert PRICE_VERSION_YEAR to PriceCatalogVersionPolicy; do not hard-code current year in handlers. |
| Price catalog import | sanmopia-admin/application/controllers/cli/Price.php, sanmopia-admin/application/models/Spreadsheet_model.php, sanmopia-price-table/* | Convert workbook ingestion into componentized PriceCatalogImport batches and entries. Keep worksheet coordinates at adapter boundary only; preserve imported workbook artifacts in private Supabase Storage. |
| Branch settlement action | sanmopia-admin/application/controllers/Calculate.php | Split admin request and branch confirmation into CQRS commands. |
| Settlement persistence | sanmopia-admin/application/models/Payment_model.php | Convert board/detail/coupon updates into settlement aggregate events. |
| Reservation payment summary | sanmopia-admin/application/models/Payment_model.php, sanmopia-admin/application/models/Reservation_model.php | Convert source payment type ids into immutable recorded payment summary purposes. |
| Mother-visible finalized charge summary | sanmopia-admin/application/models/Payment_model.php, sanmopia-admin/application/models/Reservation_model.php, sanmopia_web/application/controllers/MyReservation.php | Convert post-payment mother display into a read model sourced from finalized charge snapshot rows, not current price rules. |
| Payment method fee | sanmopia-admin/application/models/Payment_model.php | Convert getFeeData and related helpers into named payment-method fee buckets and basis-point rates. |
| Branch settlement line items | sanmopia-admin/application/controllers/Calculate.php, sanmopia-admin/application/models/Payment_model.php | Convert branch membership due, delivery fee, voucher service commission, promotional_coupon_cost, cancelled_gift_coupon_commission, and caregiver compensation into versioned directional line items persisted under the branch settlement. |
| Promotion entitlements | sanmopia_web/application/controllers/Shop.php, sanmopia_web/application/controllers/api/Shop.php, sanmopia_web/application/models/Product_model.php, sanmopia-admin/application/models/Coupon_model.php | Convert source coupon/gift codes into versioned promotional entitlements; keep Pydantic at application boundary and block domain imports with Tach. |
| Reservation quote | sanmopia-admin/application/models/Reservation_model.php, voucher spreadsheet | Convert price table composition into PriceCatalogQuotePolicy plus line-item projection from published catalog entries. Preserve gross_service_price and customer_share as separate facts so mother payable totals do not change when later catalog rows are edited. |
| Branch office scope | sanmopia-admin/application/models/Branch_model.php | Convert branch token/login/scope to access policy and branch-office aggregate. |
| Document catalog eligibility | sanmopia-admin/application/controllers/ajax/DataRoom.php, sanmopia-admin/application/models/Reservation_model.php | Convert form list filters, report joins, branch scope, reservation statuses, and paid-reservation checks into key-driven document catalog eligibility policy. |
| Printable document rendering | sanmopia_web/application/controllers/api/ReservationDocument.php, sanmopia-admin/application/controllers/cli/ReservationDocument.php | Convert token-checked downloads and scheduled document sends into versioned render request and artifact contracts. |
| Printable template pagination | sanmopia-admin/application/models/Spreadsheet_model.php | Replace fixed spreadsheet coordinates with A4 HTML repeat template blocks that flow to the next page for unbounded service rows and report answers. Persist section key, data path, item template key, and item field bindings; reject fixed per-page slot fields. Backend domain now enforces unbounded flow-to-next-page repeat sections, and the Jinja print renderer expands item templates from metadata-defined bindings with required-field failures instead of silent blank output. Daily report print payloads now provide flattened answer rows so templates can repeat daily_care_report_answer_rows instead of rebuilding nested loops. |
| Daily report export | sanmopia_web/application/controllers/MyReservation.php, sanmopia-manager/application/controllers/Report.php | Convert first/middle/final service-day report selection into audience/stage/date export items for report-series generation. |
| Daily care report submission | sanmopia-manager/application/controllers/Report.php, sanmopia-manager/application/models/Report_model.php, sanmopia-manager/application/controllers/Service.php | Replace question-list/report-type branches and hardcoded service record form fields with Strapi-authored definitions plus Supabase submitted answer rows. |
| Customer engagement content | sanmopia_web/application/models/Board_model.php, sanmopia-admin/application/controllers/Clinic.php, sanmopia-admin/application/models/Board_model.php, sanmopia_web/application/models/Notice_model.php, sanmopia_web/application/models/Qna_model.php, sanmopia_web/application/models/Faq_model.php, sanmopia_web/application/models/Notification_model.php, sanmopia-admin/application/controllers/ajax/CommonBoard.php | Convert board/clinic/common-board/notice/QnA/FAQ/notification logic into Strapi-backed content publications, health-education taxonomy, customer inquiries, and member notifications. |
| Campaign placement | sanmopia_web/application/controllers/Event.php, sanmopia_web/application/views/modal/modal_start_popup.php, sanmopia-user-old/public/image/event/** | Convert static event route switches, popup arrays, campaign banners, alliance benefits, and review-result placements into Strapi-authored campaign placements linked to content publications. |
| Communication delivery | sanmopia_web/application/models/Fcm_model.php, sanmopia-admin/application/models/Fcm_model.php, sanmopia-manager/application/models/Fcm_model.php, sanmopia-manager/application/models/Notify_model.php, sanmopia_web/application/controllers/cli/Reservation.php, sanmopia_web/application/controllers/management/Push.php | Convert provider-specific push/SMS/Alimtalk and manager notification jobs into Novu-backed communication workflows with Strapi-authored rules, Supabase preferences, dispatch requests, and delivery events. |
| Service calendar | sanmopia-admin/application/controllers/ManageSystem.php, sanmopia-admin/application/models/Manage_system_model.php, sanmopia-admin/application/models/Reservation_model.php, sanmopia_web/application/models/Reservation_model.php | Convert HOLIDAY_TB, LIST_HOLIDAY_TYPE_TB, and RESERVATION_ADD_DATE into service-calendar holidays, selected extra-service days, and captured plan snapshots with branch-scoped RLS. |
| Caregiver compensation | sanmopia-admin/application/models/Reservation_manager_salary_model.php, sanmopia-manager/application/helpers/salary_helper.php | Keep caregiver compensation facts out of branch settlement aggregate, freeze per-caregiver quote lines by contract assignment key, rate rule id, policy version, and effective date range, then issue payout instructions with bank account and tax evidence references before marking payouts paid. |
| Caregiver performance recognition | sanmopia-admin/application/controllers/Wintherace.php, sanmopia-admin/application/models/Win_the_race_model.php, sanmopia-manager/application/controllers/WinTheRace.php, sanmopia-manager/application/models/Win_the_race_model.php, sanmopia-admin/application/models/Manager_score_model.php, sanmopia-admin/application/models/Manager_class_model.php | Convert yearly award and caregiver grade logic into versioned performance seasons, metric rules, grade thresholds, scorecards, contribution facts, manual adjustments, competition ranking, and branch tag eligibility. |
| Business reporting | sanmopia-admin/application/controllers/Statistics.php, sanmopia-admin/application/controllers/Datatable.php, sanmopia-admin/application/models/Statistics_model.php, sanmopia-admin/application/models/Datatable_model.php, sanmopia-admin/static/js/statistics/* | Convert statistics/chart/table/export flows into immutable report snapshots, scoped export requests, private Supabase artifacts, and OSS table/export UI components. |