Task 08's pricing chain was reachable only through the API, so a clinic could
not define a price list or see what a booked appointment was actually charged.
Price lists
- Draft / active / expired are shown as three states because they mean three
different things operationally: a draft has no effect on today's price at all
- Activation is a separate action rather than a checkbox in the form, matching
the backend rule that creating a list must not change anything
- "Copy" seeds a new list from an existing one starting the day the old one
ends, since most lists are last quarter's with a few numbers moved
- "All branches" is an explicit option, not an empty field
Invoice card
- Renders the recorded chain down to the final amount, hiding zero rows so the
card stays readable
- A missing invoice renders as a normal state, not an error: an appointment
that was never confirmed has no invoice
- Says outright that the numbers are from the appointment's own date and later
tariff changes do not move them — otherwise someone who edited a price
yesterday reads today's older number as a bug
Also corrects task 08's checklist: its test section carried a copy-pasted "no
UI was built" note against rows whose tests have existed since the task
shipped. Replaced with the real test names and the two that genuinely are not
covered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Section 12 and the fifth closing rule: changing a price never changes an
already-booked appointment.
The pricing chain already existed and worked. Two things were missing. Tariff only
carries a year, so a rate change starting in Mehr could not be expressed — PriceList
now takes an explicit date range and Tariff remains the layer beneath it. And an
appointment stored a single number, so after a price change or a discount nobody
could say what those 2,400,000 rials were made of.
Price resolution walks four layers per service and takes the first hit: branch
override, then the covering price list, then the yearly tariff, then the service's own
price. The last one is the guarantee that a date no list covers still returns a price
rather than zero or an exception. breakdown.sources reports which layer answered, so a
surprising number can be traced instead of guessed at.
Two calculation decisions worth stating. Tax is computed on the patient's share, not
the gross — a patient does not pay tax on the portion the insurer covers. And a
discount larger than the amount floors the total at zero rather than going negative,
because a negative balance would mean the clinic owes the patient money, which nothing
downstream is built to mean.
A branch-specific list deliberately does not count as overlapping a general one; it
takes precedence instead. Treating them as a conflict would have made per-branch
exceptions impossible to express. Lists have no effect until activated, so drafting
next quarter's prices cannot disturb today's.
PriceSnapshot has no setters and a unique key on appointment_id: a snapshot that can
be edited is not a snapshot, and two invoices for one appointment would be two truths.
Corrections are a new row plus voiding the old one. Invoices are written during
confirm with the prices of that moment — computing later would let a rate change
between booking and invoicing produce a different number, which is exactly what rule
five forbids.
12 tests. The one that matters is
testBookedAppointmentKeepsItsOriginalInvoiceAfterAPriceChange: book, double the
service price, watch quote return the new number while the appointment's invoice
returns the old one. Without it rule five is only a claim.
1220 tests / 3551 assertions. phpstan back at its 14-error baseline. Frozen slot
contract green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Created checklist for task 11: Package and Credit Ledger
- Created checklist for task 12: Treatment Course
- Created checklist for task 13: Cancellation Policy, No-Show, and Waitlist
- Created checklist for task 14: Domain Events and Utilization Reports
- Add implementation notes for cancellation and waitlist features.
- Create task documentation outlining goals, current status, and acceptance criteria for cancellation policy and resource utilization reporting.
- Establish architecture for domain events and outbox pattern to ensure reliable event publishing.
- Define database schema for domain events and necessary queries for resource utilization and plan accuracy reports.
- Implement detailed implementation notes covering edge cases, testing strategies, and documentation requirements.