-
Mahmoud Aglan authored
Carbon 3 signs its differences: `$a->diffIn*($b)` answers `$b − $a`. The pricing engine asked it the other way round — `now()->diffInYears($birthday)` — so every participant it priced arrived at the rules with a negative age and a negative membership duration. The damage ran in both directions at once. A rule with a `min` never matched anyone again: the loyalty and annual recipes both ask for twelve months, and −25 is not twelve. A rule with a `max` matched the entire academy: the juniors recipe is `max: 6`, and −34 is comfortably under six, so one click in the rule builder would have taken 10% off every price in the club. The registration wizard computed age correctly in its own provisional context, which is why the desk saw one price at registration and another at renewal. Age and tenure now read from the older moment forward, through two named helpers that say why, and a date in the future is no age rather than a negative one. Alongside it, the discount picker: `selectedDiscountIds` is a public Livewire property, so it is a list the browser sends, and the total was summed from whatever ids arrived. applyDiscount() refuses a blocked rule and diverts an above-ceiling one into an approval request; neither guard survived to where the money was worked out. The engine's verdict is re-read there now, the academy's global discount ceiling applies to a hand-assembled total exactly as it does at step 8, a manual discount above the actor's cap reaches neither the total nor the invoice snapshot, and the picker's state is #[Locked] — it is driven entirely by wire:click, so nothing needed to arrive from the browser at all. Also here, found while reading for the above: - POSTerminal::updateQuantity() did not check the index exists, so an invented one wrote a cart line made of a quantity and nothing else. - The same reversed diff in four other places: overdue invoices and renewals reported negative days on the dashboard and in reminder messages, expiring memberships reported negative days remaining, and a product's months-active pinned to 1, inflating its average monthly movement to its entire lifetime sales. - validateCoupon() still carried a comment promising academy-wide coupons, three commits after branch_owns_the_catalogue removed them. Verified: 379 tests green on SQLite and against the restored OC-Sport tenant. That tenant carries one pricing rule (sibling_order, 4 EGP) and no invoice with a discount snapshot, so there is no historical billing to correct — the bug was waiting on the first age or loyalty rule. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
54df3e88