-
Mahmoud Aglan authored
enrollments is the only table GenerateRenewalInvoices bills from, so a player whose enrolment row was destroyed is not "skipped" — that command never sees them, every counter in its summary reads clean, and they train unbilled indefinitely. GuardsEnrolmentDeletion refuses the delete that causes it and GenerateRenewalInvoices names the players already affected. Neither puts anybody back. The rebuild is from evidence, never inference: audit_logs holds the created and updated entries for the destroyed enrolment, and those name the group and programme the player was actually in. Where that programme survives the player goes straight back into it. Where it was deleted — the usual case, since deleting the programme is what destroyed the enrolment — the command refuses to pick a replacement by name similarity and demands an explicit --map old:new. A player put in the wrong programme is billed the wrong price, and a wrong invoice is worse than a missing one because it looks right. next_billing_date is set to the cycle after the player's last invoice, capped at the current one, so the renewal run raises the months they actually owe, each dated the 1st of the cycle it buys — never a month nobody ever billed them for. Dry run by default; this exists to be pointed at production. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
190c0ccf