• Mahmoud Aglan's avatar
    fix(training): stop a deleted programme from silently unbilling its players · a6451e4e
    Mahmoud Aglan authored
    Deleting a programme or a group called `$group->enrollments()->forceDelete()`
    on both paths — hard delete, no soft delete, no audit row, nothing to restore
    from. `enrollments` is the only table GenerateRenewalInvoices bills from, so
    the players were not merely losing history: they stopped existing as far as
    billing was concerned while still training, still members, still owing money.
    
    OC-Sport reorganised its season on 28 August 2026 by deleting the programmes
    and recreating them under new names. 181 enrolments went with them. On
    2 September the renewal run raised 229 invoices, reported a clean success, and
    95 paying players were not among them. Nobody found out until a parent asked
    why no bill had come for his son.
    
    Two halves, because neither alone is enough:
    
    - A destructive delete is refused while anything is enrolled — active or
      cancelled, since a cancelled enrolment is still the only record of what an
      issued invoice bought. Archive the programme, or transfer the players and
      close the group. The count drops the branch scope: an enrolment hidden by
      the active branch is still an enrolment, and reading zero because of it is
      how a guard like this fails open.
    - The renewal command now names every paying player who has invoice history
      and no enrolment at all, and logs them. No guard recovers the rows already
      lost, or catches the next way somebody finds to lose them. An empty enrolment
      set with such players left over is a FAILURE exit — "nothing to bill" and
      "every enrolment was destroyed" produce the same empty set, and the players
      left over are the only thing that tells them apart.
    
    Group deletion moves into TrainingGroupService so the guard sits on every path
    to it; GroupList duplicating the cleanup inline is how one path ended up
    without it.
    Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    a6451e4e
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...