Skip to content

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.

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_entries with actor, field diff, revision, idempotency, workspace, and correlation facts; Postgres pgaudit is 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 in d5b9c80; operator report tables replace DataTables draw/order/search/page authority with typed column catalogs, sort/filter/page intents, totals-row policy, and Pydantic payloads in d95fdd7; settlement statement exports freeze line snapshots and formula-free totals in e67350c; service-balance exception metrics freeze semantic status policy, daily zero-filled buckets, dataset revision key, and checksum in 2fadc96; 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 in 550f3c7; 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 in f5e24a1. 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-string reservationArray is replaced by ReservationHistoryExportSelectionCriteria, branch/operator ReservationHistoryExportGrant, 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 for service_offering.offerings and service_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 direct service_role DML and remain service_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. Migration 20260715090000_customer_reservation_step_catalog_exact_set_materializer.sql freezes 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 without consume_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 no latest fallback 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 SpiceDB manage on hq_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 report 193 passed; a narrower integrated materializer run reports 38 passed, and migration/SSOT contracts report 14 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:

  1. 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 ConflictResolutionDecision instead of silently overwriting mother-visible facts.
  2. 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, and petAllergySpeciesCodes, 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.
  3. 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.
  4. Customer signature is captured daily or per record and cannot be reused in bulk across records.
  5. 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 PaymentRefundIntent plus adjustment catalog versions.
  6. Settlement and compensation derive from finalized payment, service delivery, caregiver assignment, tax/insurance choice, and branch/HQ settlement ledgers instead of hand-edited totals.
  7. Member withdrawal and sensitive data correction preserve legal/settlement evidence while applying Korean statutory retention, masking, deletion, role authorization, reason capture, and audit logs. MemberPrivacyLifecycleWorkflow runs withdrawal and privacy retention through Restate; Supabase stores the ledger/read/RLS facts; SpiceDB checks ReBAC; OPA evaluates law/status/context policy.
  8. Repeated branch/HQ/developer requests become office operations, scheduled exports, or CMS-authored content instead of DB/manual developer tickets.

Purpose: tighten pricing_settlement DDD language before more reservation, caregiver, branch, payment, and settlement flows import current names as stable API.

  • 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, and sanmopia_modernization.{domain,application}.pricing_settlement.features.payment.

  • Ubiquitous names to promote: PaymentLifecycleStatus, PaymentMethodCode, CustomerPaymentDisplayState, PaymentProviderEventStatusAcl.

  • Replace/rename: PaymentStatus -> PaymentLifecycleStatus; ReservationPaymentRecordStatus string literal -> application alias around PaymentLifecycleStatus; ReservationPaymentPublicState and CustomerPaymentPublicState -> CustomerPaymentDisplayState; provider method codes such as bank and vcnt -> adapter mapping into PaymentMethodCode.BANK_TRANSFER and PaymentMethodCode.VIRTUAL_ACCOUNT.

  • 2026-07-05 backend foundation: payment.py now promotes PaymentLifecycleStatus and PaymentMethodCode as canonical domain names while keeping PaymentStatus and PaymentMethod as compatibility aliases. Internal payment/product-plan/fee/adapters/main imports now use canonical names; enum values remain card, bank, and vcnt to preserve DB/API compatibility. Remaining steps: move ReservationPaymentRecordStatus and 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, and payment_lifecycle_status_value() now live beside PaymentLifecycleStatus. ReservationPaymentRecordStatus is 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, and payment_method_code_value() now live beside PaymentMethodCode. ReservationPaymentMethod and RESERVATION_PAYMENT_METHODS in payment pre-registration now alias the canonical domain value tuple instead of repeating card/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 PaymentMethodCodeValue for payment-method fields, so card/bank/vcnt is 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 beside ReservationPaymentPurpose. Public payment HTTP DTOs now reuse those domain aliases for paymentPurpose, including the narrower manual receipt and service-balance closeout subsets, instead of repeating customer_share_* / service_balance 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: PaymentMethodFeeBucketValue, PAYMENT_METHOD_FEE_BUCKET_VALUES, PaymentMethodDisplayNameValue, and PAYMENT_METHOD_DISPLAY_NAME_VALUES now live beside PaymentMethodFeeBucket. 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, and payment_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 repeating pre_registered / already_pre_registered literals 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, and payment_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:

    1. Add new domain enum names as value-compatible aliases; no value changes.
    2. Add provider ACL functions for Kill Bill event and method-code mapping.
    3. Move application literals to conversion helpers returning canonical enum values or .value strings for current contracts.
    4. Rename public read-model display state last; keep deprecated aliases for one release.
    5. Add Tach rules and run uv run tach check --dependencies --interfaces --exact plus uv run tach check-external.
  • 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 matching adapters/pricing_settlement/features/*data_room* modules.

  • Ubiquitous names to promote: SettlementArtifactCollection, SettlementArtifactRequest, SettlementArtifactGrant, SettlementDownloadHandoffAudit.

  • Replace/rename: old SettlementDataRoomRequest -> SettlementArtifactRequestLegacy during migration only; SettlementDataRoomWorkspace -> application workspace/read surface over SettlementArtifactCollection; 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:

    1. Mark old settlement data-room module as legacy in docstrings and public interface only; no behavior change.
    2. Add converter from old request payloads to new artifact request payloads.
    3. Move active interfaces/adapters to workspace module.
    4. Delete old contract and adapter after callers stop importing it.
    5. Split 2500-line workspace domain into collections.py, artifact_request.py, grant.py, handoff_audit.py, and repair.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, and sanmopia_modernization.application.pricing_settlement.features.caregiver_compensation.

  • Ubiquitous names to promote: CareTeamCompensationSource, CareTeamCompensationMember, QuoteCareTeamCompensationCommand.

  • Replace/rename: QuoteReservationCaregiverCompensationCommand -> legacy adapter only; ReservationCaregiverAssignment input -> CareTeamCompensationSource; CaregiverCompensationSeed import from reservation -> pricing-owned CareTeamCompensationSeed or 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:

    1. Route new callers through existing CareTeamCompensationSource.
    2. Keep old reservation aggregate command as adapter wrapper inside same file.
    3. Move compensation seed construction into pricing application mapper.
    4. Remove arbitrary_types_allowed=True once commands contain DTOs and domain pricing value objects only.
    5. Add Tach rule and delete reservation aggregate import.
  • 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, and sanmopia_modernization.application.pricing_settlement.features.settlement.

  • Ubiquitous names to promote: BranchSettlementCommand, BranchSettlementBoardQuery, BranchSettlementOperatorBoardReadModel.

  • Replace/rename: settlement/cqrs package -> concrete settlement/commands and settlement/queries packages, or delete cqrs package 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:

    1. Move command DTOs and command handler together.
    2. Move query DTOs and operator-board read model together.
    3. Update parent settlement/__init__.py re-exports as compatibility aliases.
    4. Add Tach rules.
    5. 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 under application/pricing_settlement/features/settlement.

  • Ubiquitous names to promote: branch_profile_id for modern branch identity, source_branch_id for legacy numeric source id, branch_settlement_board_id for settlement aggregate id, customer_relationship_branch_profile_id and service_delivery_branch_profile_id only for inter-office roles.

  • Replace/rename: settlement command branch_id: UUID -> branch_profile_id; regional benefit branch_id: int -> source_branch_id; ambiguous branch_scope strings in pricing outputs -> explicit branch_profile_id or source_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:

    1. Add compatibility properties for current branch_id fields.
    2. Rename constructor/command fields to branch_profile_id where value is UUID/profile identity.
    3. Rename legacy int fields to source_branch_id.
    4. Update settlement read models and contract payloads.
    5. Add Tach rules to prevent pricing domain from importing branch context instead of using adapter-supplied branch identity facts.

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, and source-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, and source-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-431 and source-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, and source-refs/sanmopia-admin/application/controllers/Reservation.php:632-639.

Backend/contract expectation:

  • expose one stable WorkflowStatusProjection endpoint 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.

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 Starlight astro 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.

Use this as the next AgentSquad handoff. Do not implement every slice in one PR.

featureSlice: renewal-operating-core
handoffFrom: planner
handoffTo: curator
sourceRoot: source-refs
operationalEvidence:
- assembly-repo/apps/docs/src/content/docs/migration-source-map/kanboard-renewal-feature-improvements.md
assemblyRepo: yamonco/sanmopia-modernization-assembly
targetRepos:
contract: yamonco/sanmopia-modernization-contract
backend: yamonco/sanmopia-modernization-backend
frontend: yamonco/sanmopia-modernization-frontend
requiredAction:
- 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 evidence
wallFallback:
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 report

First acceptance batch:

  1. BranchSettlementBoard: admin 입금요청 and branch 입금확인 are distinct commands. First PR should cover only the central-admin request/close path from Calculate.php::insertPaymentCentralToBranch().
  2. BranchSettlementStatus: 0=waiting_for_branch_deposit, 1=branch_deposited, 2=settled, 3=branch_paid_arrears, 4=arrears_waiting.
  3. PriceCatalogVersionPolicy: PRICE_VERSION_YEAR has voucher direct year versions and general effective-date tuples; migrate as policy/fixture before quote math.
  4. ReservationPriceQuote: voucher Yeongdeungpo discount, voucher in-home/special surcharge, rental delivery fee, coupon discount, user-fee deposit/balance must be separate facts.
  5. PaymentMethodFeePolicy: card/bank/virtual-account fee rules belong to billing policy, not settlement board opening.
  6. BranchOfficeScope: branch user sees own reservation/settlement rows; central role can filter any branch.

Next source-mined batch:

  1. SettlementActionJournal: HQ 입금요청, 지사 입금확인, 타지사 정산확인, 정산자료실 export/repair actions are separate commands with expected revision, idempotency key, actor, frozen input rows, and branch/HQ scope. Source priority is HeadOfficeSettlement first, then ExternalCounterpartySettlement, then InterBranchSettlement: head-office flow has the broadest status, amount, data-room, and export blast radius. Source anchors include Calculate.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, and Spreadsheet_model.php:3741-3911.
  2. 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.
  3. PaymentChangeAdjustment: changed-payment completion must freeze customer delta, provider context, coupon adjustment, and post-payment amount snapshot before settlement continuation can read it.
  4. PriceCatalogVersionPolicy plus ServiceBalanceAdjustment: 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-62 and source-refs/sanmopia-admin/application/models/Reservation_model.php:431-460. Ubiquitous payment purposes are customer_share_deposit, customer_share_balance, service_balance, and pre_reservation_deposit; source flags are wrapped as SourceReservationPaymentTypeId adapter 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 displayed customerPayableKrw. Source evidence: source-refs/sanmopia-admin/application/models/Payment_model.php:53-62, source-refs/sanmopia-admin/application/models/Reservation_model.php:431-460, and source-refs/sanmopia_web/application/controllers/MyReservation.php:194-326. Backend exposes a GetMotherVisibleChargeSummaryQuery read model over sanmopia_finalized_charge_snapshots and sanmopia_finalized_charge_lines; contract exposes MotherVisibleChargeSummary with 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, and source-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, and source-refs/sanmopia-price-table/*. Backend domain owns PriceCatalogEntry, PriceCatalogComponent, import issues, and duplicate checks; the OSS openpyxl parser is isolated in the adapter layer with defusedxml installed for XML hardening. Current workbook audit results are 2025_voucher_250121 = 190 entries / 8 issues, 2025_voucher_250827 = 190 / 8, 2026_voucher_251211 = 154 / 0, and 2026_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. Backend 7a9d26e supplies the guarded approval path, but no source/version was selected, approved, or invoked; 154 / 0 is not authority. Supabase stores private workbook artifacts in price-catalog-imports, import lifecycle rows in sanmopia_price_catalog_import_batches, and immutable componentized entries in sanmopia_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 returns PublishedPriceCatalogEntry values to the application handler. PriceCatalogQuotePolicy returns componentized quote lines with componentName, basis, amountKrw, and quantity, while gross_service_price and customer_share stay 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 into sanmopia_finalized_charge_snapshots and sanmopia_finalized_charge_lines; reference direction 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, and source-refs/sanmopia-admin/application/models/Payment_model.php:3880-4055. Supabase persists these facts in sanmopia_branch_settlement_lines with line_kind, direction, amount_krw, policy_version_label, source_reference_key, and line_order; branch membership dues are frozen in sanmopia_branch_membership_due_snapshots from the monthly override/base branch fee source before settlement board opening; PromotionalCouponCost source parity is frozen in sanmopia_branch_promotional_coupon_cost_snapshots and read as PromotionalCouponCost / promotional_coupon_cost calculation source lines; deleted product coupon 5,000/10,000 source rows remain cancelled_gift_coupon_commission. Branch RLS follows the parent settlement branch membership, while HQ retains central override. The backend now exposes BranchSettlementOperatorBoard through GET /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 use GET /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. settlementStatusCounts replaces source dashboard subquery counters such as detailStatusZero and couponStatusZero with domain status names. Branch inclusion now reads BranchProfile branch_settlement_participation plus is_active; source internal branch ids stay traceable as source_branch_id and are not used as hardcoded filters in the query adapter. Reconciliation export datasets now use GET /branch-settlement-reconciliation-exports/{branchProfileId} with csv, xlsx, or json format intent, returning stable columns, rows, totals, and the private business-report-exports artifact bucket target. Stored artifact commands now use POST /branch-settlement-reconciliation-export-artifacts/{branchProfileId} to render CSV, XLSX, or JSON, write a frozen sanmopia_business_report_snapshots row, upsert a rendered sanmopia_business_report_exports row, and upload the private Supabase Storage object. Async export starts now use POST /branch-settlement-reconciliation-export-workflow-starts/{branchProfileId} to authorize the branch operator, freeze the export dataset at request time, and send that dataset to BranchSettlementReconciliationExportWorkflow in Restate for delayed rendering without recalculating changed settlement rows. Backend b3d3dc1 preserves that frozen dataset lineage when the branch reconciliation adapter persists and rehydrates the business-report snapshot: dataset_revision_key and 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 source PRODUCT1, PRODUCT2, GIFT, and SHOPPINGMALL values 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, and source-refs/sanmopia-admin/application/models/Coupon_model.php:255-319,888-927. Backend domain exposes versioned PromotionalEntitlementRule, ServiceGiftEntitlementPolicy, PromotionalEntitlement, and GiftFulfillmentPolicy; 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-52 and source-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, and source-refs/sanmopia-admin/application/controllers/Manager.php:1845-1884. Supabase persists immutable per-caregiver quote lines in sanmopia_caregiver_compensation_quote_lines so assigned caregivers, branch staff, mothers, and HQ can read the same captured result through RLS. Quote lines now snapshot contract_assignment_key, rate_rule_id, policy_version_label, rate_effective_from, and rate_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 to employment_insurance; customer-funded reservations default to business_income_withholding; saved caregiver terms override defaults per caregiver. IssueReservationCaregiverPayoutInstructionsHandler turns frozen quote lines into payout instructions, and SupabaseReservationCaregiverPayoutInstructionStore upserts quote lines before instructions so FK-backed payout facts stay idempotent. Settlement continuation reads sanmopia_caregiver_payout_instructions, not raw quote lines, so payout cannot advance without bank-account and tax-evidence facts. Supabase persists payout instructions in sanmopia_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 preserve caregiver_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-bounded PrimaryCaregiverTerm inside 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 expose grade_code, and grade-sensitive booking policies can filter candidates before ranking. Supabase reservation rows use assigned_caregiver_user_id, and profile role values use caregiver. 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 uses service_day_attendance_ledger as 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 same careEnvironment, acceptsPetHousehold, and petAllergySpeciesCodes snapshot 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, and source-refs/sanmopia-admin/application/models/Reservation_model.php:4471-4480. CaregiverAssignmentOptimizer is the domain port for branch-wide assignment plans. OrToolsCaregiverAssignmentOptimizer lives 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 as care_environment_not_supported or capacity_conflict. OptimizeCaregiverAssignmentsHandler is 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. CaregiverAssignmentOptimizerInputs now 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. ReservationOperationCommandRuleCatalog owns 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 as awaiting_deposit, change_requested, and cancellation_requested from forcing controller or UI rewrites whenever branch policy changes. ReservationOperationCommandHandler is 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 in sanmopia_reservation_operation_contexts and sanmopia_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, and source-refs/sanmopia-admin/application/models/Reservation_manager_salary_model.php:21-124. Supabase CLI is pinned in the backend repo with supabase@2.109.0; pnpm run supabase:db:push:stage, pnpm run supabase:db:lint:stage, and pnpm run supabase:types:stage are the stage schema gates. Generated supabase/generated/database.types.ts is 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 through application.platform.supabase_queue_contract and the Supabase CLI migration sanmopia_queue_workload_contracts. Backend worker dispatch now has a shared SupabaseQueueWorkloadBatchDispatcher and SupabasePgmqQueueAdapter read/archive contract, so feature workers inherit one success, retry, max-attempt dead-letter, and source-archive policy instead of copy-pasting pgmq calls. SpiceDBRelationshipSyncProcessor now binds the first workload processor: branch membership and branch hierarchy relationship payloads are tuple-validated before SpiceDBAuthorizationPort.write_relationships(...), with corrupt payloads routed to the shared dead-letter path. Accepted plans with follow-up actions now start ReservationOperationFollowUpWorkflow through the neutral workflow engine port. Supabase stores service-role-only request and action-event ledgers in sanmopia_reservation_operation_follow_up_workflow_requests and sanmopia_reservation_operation_follow_up_action_events; the shared WorkflowStatusProjection exposes them as reservation_operation_follow_up. Workflow row status/json/date parsing is promoted into application.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, and source-refs/sanmopia-admin/static/js/reservation_status/reservation_history_new.js:224-230. Backend domain and contract now expose ReservationCollaborationCommand, ReservationCollaborationPolicy, ReservationCollaborationConflict, ReservationChangeJournalEntry, ReservationRealtimeProjectionEvent, and ReservationCollaborationCommitPlan. The command requires expected_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_intent and return_state_token replace history.back() style flow control. Supabase now stores revision_number, append-only sanmopia_reservation_change_journal_entries, and sanmopia_reservation_realtime_projection_events; the sanmopia_commit_reservation_collaboration RPC commits the revision bump, idempotency replay, journal row, and private Supabase Broadcast in one service-role call. CommitReservationCollaborationHandler now reads the current Supabase booking revision, rejects stale writes before persistence, and exposes POST /reservation-collaboration-commits/{reservationId} with a 409 conflict detail payload for operator UX. OpenReservationCollaborationWorkspaceHandler exposes POST /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. Supabase realtime.messages RLS now authorizes reservation Presence on the same branch/HQ topic boundary as Broadcast. ListReservationCollaborationJournalHandler exposes POST /reservation-collaboration-journal/{reservationId} for the recent operator-facing audit timeline. Generated database.types.ts includes both tables. pgAudit remains a compliance/troubleshooting log, not the operator-facing history source. Branch handoff is now wired as transfer_reservation_branch: branch_handoff.branch_profile_id updates 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_on and service_schedule.end_on update service_start_at and service_end_at inside the same revision-checked commit with range validation. service_schedule.extra_service_days replaces extra service-day child rows after the same revision commit succeeds. caregiver_assignment.roster replaces sanmopia_reservation_caregiver_assignments rows after the same revision commit, preserving primary/assistant role, Supabase user id, grade, planned days, term dates, and contract_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: ReservationCareDelivery owns ServicePeriod, CareTeam, and ServiceDayAttendance ledger 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-79 and source-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. ReservationBirthProfile stores unbounded ChildBirthRecord arrays, expected child count, recorder role, actual/expected birth dates, and policy version. BirthInformationReminderPolicy plans idempotent reminders per reservation/caregiver/date with configurable service-window offsets, repeat interval, local dispatch time, and timezone. Supabase persists profiles in sanmopia_reservation_birth_profiles and due reminders in sanmopia_birth_information_reminder_tasks; Novu sends the birth_information_requested workflow 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, and source-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. ServiceCalendarPolicy plans 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 in sanmopia_service_calendar_holidays, selected reservation extra days in sanmopia_reservation_extra_service_days, and captured plan snapshots in sanmopia_reservation_service_calendar_plans. National Korean public holidays can be generated through the OSS holidays package; branch closures stay explicit and RLS-scoped. Calendar impact facts now include customer_benefit_entitlement_review_required for 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, and source-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. DailyCareReportDefinition groups lower_snake_case fields into sections with value kinds, choice options, required fields, and policy version. DailyCareReportSubmission records 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 in sanmopia_daily_care_report_definitions and submitted answers in sanmopia_daily_care_report_submissions; RLS limits reads/writes to mothers, assigned caregivers, branch operators, and HQ. Document reporting consumes submissions through DailyCareReportPrintPayload for 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, and source-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 one record_kind and record_reference_code to one signature_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 in sanmopia_customer_record_confirmations; authenticated users can select participant rows, mothers can insert their own confirmations, and service role retains administrative repair authority. Contract helpers expose buildCustomerRecordConfirmationCode and findCustomerRecordConfirmationProblems for frontend task flows. Kanboard evidence: #2491, #2625, #2731.
  • ReservationAuthorization: reservation workflow actors now use one branch_operator role instead of duplicated branch staff/leader capability matrices. Supabase profile and branch membership rows accept mother, caregiver, branch_operator, and hq_admin; SpiceDB branch relations use branch_operator, caregiver, hq_admin, and parent_hq. Reservation capabilities remain stage-bound in domain policy, then relationship permission is checked through SpiceDB. Booking submission uses submit_booking_request at draft stage and branch manage permission before a Restate reservation workflow is started. Supabase role claims stay in app_metadata, and Data API exposure stays explicit-grant plus RLS. Branch membership and branch-to-headquarters changes enqueue idempotent sanmopia_spicedb_relationships messages 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 returns reservationStatus and reservationCapabilityAuthorization for 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 use definitionKey values such as service_use_contract and mother_daily_report; source document keys stay inside optional sourceReference values for migration/audit mapping only. Supabase sanmopia_document_definitions rows 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 choose html or pdf, jinja_weasyprint or browser_print, a4, and orientation. Printable templates use repeat template blocks with flow_to_next_page overflow and unbounded capacity 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 rejects repeat_sections that omit those keys, use hyphen/camel section or item template keys, use non-flow overflow, or reintroduce fixed-slot fields such as maxItemsPerPage. Render request identity is an idempotent renderRequestCode composed 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, builds PrintableDocumentTemplate, then renders HTML/PDF. The rendered artifact is uploaded to the private reservation-documents bucket and upserted into sanmopia_document_render_artifacts on request_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 logical renderRequestCode, requires VIEW_RESERVATION through reservation stage policy plus SpiceDB, and returns only signed URL metadata instead of storage object internals. Bundle handoff validates requested members against DocumentCatalog.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 carry mother or caregiver audience, 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, and source-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 bucket document-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, and source-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. ContentPublication covers 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. CampaignPlacement covers home popups, banners, community hero slots, event lists, caregiver-news lists, media references, dismiss keys, CTA values, and display priority. CustomerInquiry covers 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 Storage content-assets; app-facing contracts stay in customer-engagement and Supabase table types. CommunicationDelivery adds 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, and source-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. BranchOfficePublication covers branch/HQ notices, caregiver instructions, caregiver event notices, branch news, audience windows, pinning, and attachment keys. BranchOfficeLearningMaterial covers global or branch-scoped caregiver education with online/offline/hybrid delivery. BranchOfficeBrandAsset covers 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, and source-refs/sanmopia-admin/application/models/Branch_model.php:476-557.
  • MotherLeadIntake: partner-benefit application sources now map into MotherLeadProgramPolicy, 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 uses breast_milk_nutrition_analysis, postpartum_gift_box, and home_sanitizing. Supabase owns versioned program policy, active request uniqueness, export lifecycle, private mother-lead-results storage, 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, and source-refs/sanmopia-admin/application/models/Momfirst_model.php:12-272.
  • CaregiverPerformanceRecognition: yearly caregiver and branch award logic now maps into CaregiverPerformanceSeason, 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 as opened_this_season and award_paused instead 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, and source-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. BusinessReportSnapshot captures report kind, audience scope, branch scope, period, calculation version, columns, rows, and totals. Supabase stores snapshots in sanmopia_business_report_snapshots, stores export request state in sanmopia_business_report_exports, and keeps rendered files in the private business-report-exports bucket. 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, and source-refs/sanmopia-admin/static/js/statistics/performance.js.

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 sliceTarget feature folderSource evidenceFirst proof
BranchSettlementCalculationReaderpricing_settlement/features/settlement/branch_settlement_calculationCalculate.php:15-66, Payment_model.php:828-980, Payment_model.php:1124-1298, Payment_model.php:4335-4400Backend 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.
InterOfficeSettlementpricing_settlement/features/settlementCalculate.php:337-430, Payment_model.php:2657-2814, Payment_model.php:2816-2935, Payment_model.php:2977-3031Backend 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.
PaymentChangeAdjustmentpricing_settlement/features/payment now, later API surface can move under payment_change_flow if it growsajax/Reservation.php:68-178, Payment_model.php:4423-4521, Payment.php:658-764, web Payment_model.php:857-900Backend 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.
FamilySponsoredReservationmember_management/features/family_account, reservation_operations/features/reservation_bookingweb 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-704Backend/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.
ReservationPartyAuthoritySnapshotmember_management/features/family_accountweb 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-704Backend/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.
PostpartumCareCenterStayPlanreservation_operations/features/postpartum_care_center_stayweb 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-3562Backend 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.
OfflineReservationDuplicateGuardreservation_operations/features/offline_reservation_intakeReservation.php:1490-1518, Reservation_model.php:6100-6113, Reservation.php:452, Reservation.php:632-640Backend 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.
SettlementManualAdjustmentCatalogpricing_settlement/features/settlement/manual_adjustment_catalog.pyCalculate.php:504-548, Service_model.php:274-319, other_service_management.js:1-89, calculate_detail.php:1214-1265Backend 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.
VoucherEndNoticeReadinessdocument_reporting/features/document_deliveryReservationDocument.php:254-621, Reservation_model.php:4388-4459, Reservation_model.php:6117-6127, Spreadsheet_model.php:4104-4113, Spreadsheet_model.php:4328-4338Backend 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.
ReservationDocumentExportWorklistdocument_reporting/features/reservation_document_export_worklistDataRoom.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-4198Backend/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.
ReservationPriceVersionAndSubsidyAdjustmentpricing_settlement/features/reservation_price_version_adjustmentconstants.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-1068Backend 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.
CaregiverAssignmentOptimizerInputscaregiver_assignment/features/assignment_optimizer_inputsMatching_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-629Backend 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.
CaregiverPayoutReadModelAndAcknowledgementpricing_settlement/features/caregiver_compensationmanager 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-270Backend 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.
BranchOfficeProfileAndMembershipFactsbranch_operations/features/office_profileBranch_model.php:157-169, Branch_model.php:186-285, Branch_model.php:293-425, OfficeManagement.php:399-429Branch 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.

Feature sliceDomain ownerSource evidenceRequired proof
pricing-settlement-corepricing_catalog, settlement_accountingPayment_model.php, Reservation_model.php, Calculate.php, voucher spreadsheetDomain tests for fee/eligibility/buckets, API contract, admin UI preview
price-catalog-importpricing_catalogcli/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-quotepricing_catalogReservation_model.php, voucher spreadsheetPublished/effective catalog read model, componentized quote contract, accepted quote snapshot capture, no mother payable double-counting, no raw mutable amount input
branch-operations-corebranch_networkBranch_model.php, OfficeManagement.php, branch login/session codeBranch-scoped row visibility and central-role override proof
caregiver-assignmentbranch_networkManager_model.php, Matching_model.php, Reservation.php, ajax caregiver selectionCandidate card shows score, preference match, distance, transport time, grade code, and handoff order
caregiver-compensationsettlement_accountingReservation_manager_salary_model.php, source salary helperCaregiver compensation facts separate from branch settlement facts; quote lines snapshot contract assignment key, rate rule id, policy version, and effective dates
caregiver-performance-recognitioncare_deliveryWintherace.php, Win_the_race_model.php, Manager_score_model.php, Manager_class_model.phpStrapi-authored seasons/metric rules/grade thresholds, Supabase RLS, competition ranking, manual adjustment evidence, branch tag eligibility
service-calendarreservation_operationsManageSystem.php, Manage_system_model.php, Reservation_model.php, DATE_LIST_VW, HOLIDAY_TB, RESERVATION_ADD_DATEVersioned 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-attendancecare_deliverysanmopia-manager/application/controllers/Attendance.php, sanmopia-manager/application/controllers/Schedule.phpReservation care delivery aggregate records one attendance ledger entry per reservation/caregiver/service date and emits mother safety notice
notification-obligation-dispatchcare_deliverysanmopia-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.phpBackend-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-intakecustomer_engagementMomfirst.php, Clean.php, Momfirst_model.php, Clean_model.php, momfirst admin/promotion viewsLead 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-entitlementspricing_catalog, settlement_accountingShop.php, api/Shop.php, Product_model.php, Coupon_model.php, coupon payment callbackVersioned entitlement rules, application-only Pydantic contracts, Supabase RLS, fulfillment request lifecycle
voucher-coupon-creditspricing_catalogCoupon_model.php, voucher selection/payment viewsCoupon discount, active coupon amount buckets, and cancelled_gift_coupon_commission handoff to settlement
daily-care-reportcare_deliveryReport.php, Report_model.php, Service.php, service/service.phpStrapi-authored dynamic form definitions, keyed answer submissions, Supabase RLS, multiple caregivers through role and handoff order, no source view field names in contracts
document-reportingdocument_reportingDataRoom.php, ReservationDocument.php, MyReservation.php, report controllers, reservation history exportContract 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-roomdocument_reportingDataRoom.php, Data_room_model.phpStrapi-authored branch/HQ posts, direction and target-branch RLS, private Supabase document-room-attachments, file content-type allowlist, retention, no open upload bucket
business-reportingbusiness_reportingStatistics.php, Datatable.php, Statistics_model.php, Datatable_model.php, statistics JS filesImmutable 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-contentcustomer_engagementBoard.php, Clinic.php, Notice.php, Qna.php, Faq_model.php, Notification_model.php, CommonBoard.phpStrapi-backed CMS authoring, ContentPublication, health-education taxonomy, CustomerInquiry, and MemberNotification contracts with Supabase RLS
campaign-placementcustomer_engagementEvent.php, modal_start_popup.php, event image directoriesStrapi-backed popup/banner/event placement, Supabase RLS, active windows, dismiss keys, CTA metadata, no hardcoded campaign arrays
communication-deliverycustomer_engagementFcm_model.php, Notify_model.php, Notification_model.php, cli/Reservation.php, management/Push.phpNovu-backed OSS notification workflows, Strapi-authored workflow rules, user preferences, dispatch idempotency, provider audit events, no provider names in domain
workflow-state-orchestrationreservation_orchestration, pricing_settlement, member_managementKanboard renewal reservation/settlement/withdrawal repeated-operation cardsRestate 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-lifecyclereservation_operationsconstants.php, Reservation_model.php, reservation status views, Report_model.php:312-350, Report_model.php:445-477Status 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-flowcustomer_status_billingajax/Reservation.php, Payment_model.phpBackend 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-feessettlement_accountingBranch_model.php, Calculate.php, Payment_model.phpMembership due, delivery fee, caregiver compensation, promotional_coupon_cost, cancelled_gift_coupon_commission, and adjustments are versioned directional settlement line items
member-privacy-lifecyclecustomer_status_billing, document_reporting, settlement_accountingKanboard #2253 and repeated withdrawal/privacy correction cardsMemberPrivacyLifecycleWorkflow, 개인정보 보호법 제21조, 전자상거래법 시행령 제6조, 국세기본법 제85조의3, 사회서비스 제공자료 5년 retention basis, OPA policy gates, SpiceDB actor checks, Supabase retained-data ledger, OpenFGA not required
  • 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, and close_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 tables sanmopia_reservation_history_export_*. Public API route POST /reservation-history-export-requests builds the frozen dataset from a backend source-row reader and SpiceDB-scoped grant, not from browser raw reservationArray query strings. Next work: renderer, signed Storage download handoff, artifact audit ledger, and admin UI contract consumption.

Planner/curator must hand off one feature slice at a time with:

  • featureSlice
  • sourcePath
  • sourceSymbol
  • sourceTerm
  • ubiquitousName
  • acceptanceCase
  • workflowName
  • retentionBasis
  • targetRepos
  • requiredPreviewPath

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.

EvidenceSource pathRule extraction
Reservation statusessanmopia-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-280Convert 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 versionsanmopia-admin/application/config/constants.phpConvert PRICE_VERSION_YEAR to PriceCatalogVersionPolicy; do not hard-code current year in handlers.
Price catalog importsanmopia-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 actionsanmopia-admin/application/controllers/Calculate.phpSplit admin request and branch confirmation into CQRS commands.
Settlement persistencesanmopia-admin/application/models/Payment_model.phpConvert board/detail/coupon updates into settlement aggregate events.
Reservation payment summarysanmopia-admin/application/models/Payment_model.php, sanmopia-admin/application/models/Reservation_model.phpConvert source payment type ids into immutable recorded payment summary purposes.
Mother-visible finalized charge summarysanmopia-admin/application/models/Payment_model.php, sanmopia-admin/application/models/Reservation_model.php, sanmopia_web/application/controllers/MyReservation.phpConvert post-payment mother display into a read model sourced from finalized charge snapshot rows, not current price rules.
Payment method feesanmopia-admin/application/models/Payment_model.phpConvert getFeeData and related helpers into named payment-method fee buckets and basis-point rates.
Branch settlement line itemssanmopia-admin/application/controllers/Calculate.php, sanmopia-admin/application/models/Payment_model.phpConvert 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 entitlementssanmopia_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.phpConvert source coupon/gift codes into versioned promotional entitlements; keep Pydantic at application boundary and block domain imports with Tach.
Reservation quotesanmopia-admin/application/models/Reservation_model.php, voucher spreadsheetConvert 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 scopesanmopia-admin/application/models/Branch_model.phpConvert branch token/login/scope to access policy and branch-office aggregate.
Document catalog eligibilitysanmopia-admin/application/controllers/ajax/DataRoom.php, sanmopia-admin/application/models/Reservation_model.phpConvert form list filters, report joins, branch scope, reservation statuses, and paid-reservation checks into key-driven document catalog eligibility policy.
Printable document renderingsanmopia_web/application/controllers/api/ReservationDocument.php, sanmopia-admin/application/controllers/cli/ReservationDocument.phpConvert token-checked downloads and scheduled document sends into versioned render request and artifact contracts.
Printable template paginationsanmopia-admin/application/models/Spreadsheet_model.phpReplace 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 exportsanmopia_web/application/controllers/MyReservation.php, sanmopia-manager/application/controllers/Report.phpConvert first/middle/final service-day report selection into audience/stage/date export items for report-series generation.
Daily care report submissionsanmopia-manager/application/controllers/Report.php, sanmopia-manager/application/models/Report_model.php, sanmopia-manager/application/controllers/Service.phpReplace question-list/report-type branches and hardcoded service record form fields with Strapi-authored definitions plus Supabase submitted answer rows.
Customer engagement contentsanmopia_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.phpConvert board/clinic/common-board/notice/QnA/FAQ/notification logic into Strapi-backed content publications, health-education taxonomy, customer inquiries, and member notifications.
Campaign placementsanmopia_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 deliverysanmopia_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.phpConvert 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 calendarsanmopia-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.phpConvert 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 compensationsanmopia-admin/application/models/Reservation_manager_salary_model.php, sanmopia-manager/application/helpers/salary_helper.phpKeep 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 recognitionsanmopia-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.phpConvert 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 reportingsanmopia-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.