• Mahmoud Aglan's avatar
    feat(registration): ask a product's own questions before selling it, and hold names to رباعي · d3113b08
    Mahmoud Aglan authored
    Two things the desk could not previously be made to capture.
    
    A product now carries the details it cannot be sold without — a size, a
    colour, the name printed on the back. The admin declares them per product
    as either free text or a list of predefined answers; the registration
    wizard renders them inline on the cart row and refuses to move to payment
    while a required one is blank.
    
    None of this is a separate product, and none of it is reconstructable
    after the fact from an invoice line that says "قميص تدريب" — which is why
    the answers freeze onto invoice_items.metadata and into the line
    description at sale time. Renaming "لارج" to "L" next season must not
    rewrite what a player ordered last season, exactly as prices freeze.
    
    hotbuyCustomizations is browser-writable by necessity — it is what the
    receptionist is typing — so nothing downstream trusts it. Both the step
    guard and confirm() re-read the questions through Eloquent (Product
    carries BranchScope, so another branch's product resolves to nothing) and
    check every answer against ProductCustomization::accepts(). A select must
    match its own list: the dropdown is a convenience, not the check.
    
    Separately, a person entered into the system must now carry a four-part
    name. Egyptian records are keyed on it — national ID, birth certificate,
    federation card, school file — and a player registered as "محمد أحمد"
    matches none of them; two players sharing a first and father's name are
    common enough that parts three and four are what tell them apart. Applied
    where people are created at the desk and in the portal: the registration
    wizard (player and guardian), retroactive enrolment, the participant form,
    and portal sign-up. Four is a floor, not a target.
    
    FullName splits on Unicode whitespace explicitly. \s under /u still only
    means ASCII whitespace, and an Arabic keyboard produces U+00A0 — glued
    together, a perfectly valid four-part name would have been rejected.
    
    Deliberately left alone:
    - ParticipantImport is exempt. Enforcing the rule on a bulk import would
      block loading an existing roster, which is the one case where the short
      names are already a fact.
    - Records already in the database are untouched. This validates at entry.
    - The POS terminal, settlement wizard, group screen and essential-
      deliveries screen sell these same products and do not yet ask for the
      customizations.
    - Kits carry no customizations, only products.
    
    Migration is additive and guarded; CHECK constraint matches the enum
    character for character. 382 passed / 99 skipped on the restored Postgres
    tenant, 303 passed / 178 skipped on SQLite.
    Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    d3113b08
Name
Last commit
Last update
..
components Loading commit data...
errors Loading commit data...
layouts Loading commit data...
livewire Loading commit data...
portal Loading commit data...
print Loading commit data...
public Loading commit data...
receipts Loading commit data...
reports Loading commit data...
website Loading commit data...
welcome.blade.php Loading commit data...