• Mahmoud Aglan's avatar
    feat(settlements): let the desk put a player back in a programme · 2b6e6997
    Mahmoud Aglan authored
    The console could tell an operator that a player had no enrolment and nothing
    in the system was billing him. It could not do anything about it: all ten
    settlement actions move money on an account that already exists, so the one
    account that had stopped existing was the one the wizard could not fix. The
    operator read the diagnosis and had no button.
    
    `restore_enrollment` is that button. It delegates to EnrollmentService rather
    than inserting a row, so a restored enrolment passes the same checks a new one
    does — capacity, group status, one-group-per-programme, the branch rule — and
    lands identical to every other row in the table. A repair that produces a
    slightly different shape of row is a second bug waiting.
    
    It raises no invoice. `next_billing_date` comes out as the 1st of next month,
    so the cycle resumes on its own and the current month stays a deliberate act:
    the operator adds `bill_month` for it if it is owed, with the amount in front
    of them. Restoring a subscription and charging for it are two decisions and the
    second is not ours to assume.
    
    The group is suggested, not chosen. The enrolment that knew the programme is
    gone; the only surviving record is the name on the player's last subscription
    invoice, and that programme was deleted and recreated under a new one — so it
    cannot be looked up, only matched. Matching is on the years in the name,
    because these programmes are birth-year cohorts: «اكاديمية 2017 -2018» and
    «أكاديمية (2017-2018)» share nothing as strings and are obviously one cohort to
    a human. Cohorts written in two digits match nothing and a retired squad has no
    successor, which is exactly where the person at the desk knows and this screen
    does not — so the suggestion is labelled as a guess and the full list is always
    there.
    
    Two bugs found while proving it works, both real rather than test-only:
    
    - EnrollmentService left `academy_id` to BelongsToAcademy, which fills it from
      the `current_academy` container binding — bound only inside a web request. An
      enrolment created from a command, a queued job or a service died on a NOT
      NULL violation. It is now stated from the participant, which is true under
      every caller and which the trait leaves alone.
    - The group list sorted with `sortBy([fn, fn])`. sortBy reads an array as
      [column, direction] pairs, so it sorted by neither and buried the suggestion
      mid-list, where an operator in a hurry never sees it.
    
    Verified end to end against the restored tenant: stage the restore, apply it,
    and the player is enrolled in the suggested group with next month's billing
    date and not one new invoice. 498 tests, 389 pass / 109 skip on Postgres and
    313 / 185 on SQLite.
    Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    2b6e6997
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...