Skip to content

Domain Pricing Snapshot

Guard duplicated from ARCHITECTURE §14.7 + §15 — ARCHITECTURE wins on conflict.

WARNING — SILENT DATA CORRUPTION: Reading live pricing at billing time silently corrupts the economic record the moment anyone edits a pricing row.

Pricing is resolved via a 4-branch fallback (resolveStudentPrice / resolveTutorPrice):

  1. Applicability gate — if subject not sold at this tingkat → null (not sold)
  2. Override row exists (student_price_overrides / tutor_price_overrides) → use override amount_idr
  3. Base row exists (student_base_prices / tutor_base_prices) → use base amount_idr
  4. Else → null (unpriced)

The snapshot captures the output of resolvePrice — the resolved amount plus a source+ref pair for traceability. There are no student_pricing_matrix / tutor_pricing_matrix tables (old flat model dropped per ADR-001).

At REQUESTED → APPROVED transition (mandatory)

Section titled “At REQUESTED → APPROVED transition (mandatory)”

Capture all of these on lesson_sessions in the same tx as the status update:

FieldSource
amount_student_idrSnapshot — output of resolveStudentPrice() at approval time
student_price_source'OVERRIDE' or 'BASE' — which branch resolved
student_price_ref_idFK → student_price_overrides.id OR student_base_prices.id (traceability)
amount_tutor_idrSnapshot — output of resolveTutorPrice() at approval time
tutor_price_source'OVERRIDE' or 'BASE'
tutor_price_ref_idFK → tutor_price_overrides.id OR tutor_base_prices.id
mode_surcharge_idrOptional per-session override (nullable escape hatch)

Read lesson_sessions.amount_student_final_idr / amount_tutor_final_idr. NEVER re-read from student_base_prices, tutor_base_prices, student_price_overrides, or tutor_price_overrides — those may have changed.

Pricing lookup (reference — resolvePrice inputs)

Section titled “Pricing lookup (reference — resolvePrice inputs)”
  • Student: (academic_year_id, subject_id, segmentasi_id, tingkat_id) → resolved amount
  • Tutor: (academic_year_id, subject_id, segmentasi_id, tingkat_id, golongan_id) → resolved amount
  • subject_level_id is NOT a pricing axis (dropped per ADR-001)
  • Mid-year pricing row edits are allowed — snapshot immunity means existing sessions are unaffected

resolveStudentPrice / resolveTutorPrice live in: packages/service/src/pricing/resolve-price.ts

Unit tests (all 4 branches): packages/service/src/pricing/__tests__/resolve-price.test.ts

Full spec: plans/ARCHITECTURE.md §14.4 (resolvePrice) and §14.7 (snapshot fields) and §15 (sesi state machine + billing implications).