fix(pos): charge the member rate, and sell on the plan that was configured
A product carries a member rate, a non-member rate, and instalment plans
open to one tier or the other. Only the registration wizard ever read any
of it. The till resolved prices through the pricing engine, the engine
knew only about base_prices, and nothing writes a product's member rate
there — on OC-Sport there is not one base_prices row for any product — so
every calculate() threw, the terminal fell back to selling_price, and
every member buying at reception paid the walk-in price with nothing on
the receipt to say so. The 8,000 card costs members 6,000; they were
charged 8,000. The plans were invisible too: the only partial payment the
terminal offered was a free-typed عربون, so a receptionist taking the
first instalment of an agreed schedule typed it into a manual line, and
the sale left no plan behind for anything to track.
The precedence now lives in the engine, once, so every caller gets it:
a base price tagged with this membership type wins, then the product's
own column for this tier, then any other base price. selling_price stays
out of it — it is the catalogue's advertised number, not a configured
price, and admitting it would make "nobody set a price" undetectable,
which is the hard fail the pricing rules require. The terminal falls back
to priceForTier() only when the engine has nothing at all, and says on
screen which price list is in force.
Plans reach the terminal as a per-line picker: pick a schedule, choose
how many instalments are being paid today, see the rest. Everything is
re-resolved in POSService from the database — the cart is a public
property, so a plan id in it is a number the browser chose, and a plan
belonging to another product or to the other tier is refused rather than
ignored. participantId is #[Locked] for the same reason: it now decides
which price list the sale is quoted from.
buildSchedule() rounded every auto slot up, so three instalments of an
8,000.00 card came to 8,000.01 — a plan asking for a piaster the invoice
never billed, which could never reach `completed`. Piasters split the way
they do everywhere else here: floor each share, last slot takes the
remainder.
Verified against the restored OC-Sport tenant: member #18 prices at
6,000.00 on the 2,000x3 members' plan, non-member #257 at 8,000.00 on the
2,500/2,500/3,000 plan; both schedules sum exactly. Full suite green on
SQLite and on Postgres, POS terminal renders.
Co-Authored-By:
Claude Opus 5 (1M context) <noreply@anthropic.com>
Showing
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Please register or sign in to comment