• Mahmoud Aglan's avatar
    feat(pricing): let a sibling discount apply to one membership tier only · c1c1e9bc
    Mahmoud Aglan authored
    A compound charges a resident 2,200 and a non-resident 2,800, and the second
    child of a non-resident 2,400 — while a resident's second child stays at 2,200,
    because the resident price is already the discounted one. A sibling_order rule
    could say "second child" but not "and only for non-residents", and the
    arithmetic has no shortcut: a flat 400 off takes the resident to 1,800, and a
    percentage landing on 2,400 from 2,800 lands on 1,886 from 2,200.
    
    The config-only alternative was a second stackable rule holding
    fixed_price 220000 to push residents back up. That works arithmetically and is
    the wrong answer: it stores the tier's base price in a second table. The day
    fees rise and only base_prices is edited — the obvious place — that rule
    silently forces every resident back to the old number, with no error and no
    missing-price failure to notice. It also prints a +200 EGP "discount" line on a
    resident sibling's invoice, because appliedRules is stored verbatim on it.
    
    So sibling_order and family_size gain an optional membership_type list in their
    conditions, evaluated as an AND alongside the existing range.
    
    conditions is jsonb with no CHECK constraint, so no migration is needed. The key
    is absent from every rule authored before now, isset() is false, and evaluation
    is bit-identical for every existing rule on every tenant — additive in the same
    sense a nullable column is. It is accepted only where the schema declares it;
    normalize() strips it from an age or loyalty rule rather than storing a
    condition the engine will never consult. describe() appends the tier to the
    existing clause so the picker's rejection reason stays a sentence.
    
    Pinned by 13 tests, the first of which is the one that matters to the other
    tenants: absent key, unchanged behaviour.
    Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    c1c1e9bc
Name
Last commit
Last update
Screenshots Loading commit data...
app Loading commit data...
bootstrap Loading commit data...
config Loading commit data...
database Loading commit data...
docker Loading commit data...
docs Loading commit data...
docs-site Loading commit data...
flutter_shell Loading commit data...
gap-filling-plans Loading commit data...
lang Loading commit data...
marketing-site Loading commit data...
plans Loading commit data...
public Loading commit data...
reference/oc-sport Loading commit data...
resources Loading commit data...
routes Loading commit data...
scripts Loading commit data...
storage Loading commit data...
tests Loading commit data...
.dockerignore Loading commit data...
.editorconfig Loading commit data...
.env.example Loading commit data...
.gitattributes Loading commit data...
.gitignore Loading commit data...
.npmrc Loading commit data...
Beanding Guide.txt Loading commit data...
CLAUDE.md Loading commit data...
Dockerfile Loading commit data...
README.md Loading commit data...
artisan Loading commit data...
captain-definition Loading commit data...
composer.json Loading commit data...
composer.lock Loading commit data...
deploy.sh Loading commit data...
elcaptain-sportsonly-db.md Loading commit data...
one-click-app.yml Loading commit data...
package-lock.json Loading commit data...
package.json Loading commit data...
phpunit.xml Loading commit data...
system info.txt Loading commit data...
vite.config.js Loading commit data...
نادى الشيخ زايد كشف بيانات اللاعبين (2).html Loading commit data...