• Mahmoud Aglan's avatar
    fix(pos): charge the member rate, and sell on the plan that was configured · 22d43e2b
    Mahmoud Aglan authored
    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: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    22d43e2b
Name
Last commit
Last update
..
Activities Loading commit data...
Admin Loading commit data...
Assignments Loading commit data...
Attendance Loading commit data...
Audit Loading commit data...
Auth Loading commit data...
Branches Loading commit data...
CashSessions Loading commit data...
Components Loading commit data...
Concerns Loading commit data...
Dashboard Loading commit data...
Documents Loading commit data...
Enrollments Loading commit data...
Evaluations Loading commit data...
Events Loading commit data...
Facilities Loading commit data...
Financial Loading commit data...
Groups Loading commit data...
Guardian Loading commit data...
HR Loading commit data...
Inventory Loading commit data...
Invoices Loading commit data...
Messaging Loading commit data...
Notifications Loading commit data...
POS Loading commit data...
Parent Loading commit data...
Participants Loading commit data...
People Loading commit data...
Portal Loading commit data...
Pricing Loading commit data...
Profile Loading commit data...
Programs Loading commit data...
Public Loading commit data...
Receptionist Loading commit data...
Reports Loading commit data...
Requests Loading commit data...
Roles Loading commit data...
Schedule Loading commit data...
Settings Loading commit data...
Trainer Loading commit data...
Training Loading commit data...
Users Loading commit data...
Wallets Loading commit data...
Website Loading commit data...
WhatsApp Loading commit data...
Wizards Loading commit data...
BranchSwitcher.php Loading commit data...
Dashboard.php Loading commit data...
GlobalSearch.php Loading commit data...