• Mahmoud Aglan's avatar
    feat(financial): let the academy decide where every collected pound lands · e0b69a71
    Mahmoud Aglan authored
    Where a collection went in the ledger was a `match` statement: five itemable
    classes hardcoded to five revenue codes, identical for every client, and no way
    to express tax at all. An accountant who said "no, five pounds of that is a
    registration fee and the rest is training, and the kit carries 14% VAT" had no
    answer but a deploy.
    
    Revenue routing makes that answer data. Every kind of money the ERP can take is
    a source with one rule: a tax treatment, and destination lines that take a flat
    amount, a percentage, or the remainder. A rule can be narrowed to a single
    product, programme or event, which beats the academy default for that item
    alone. financial.revenue-routing edits them, with a preview that runs the
    unsaved rule through the real allocation engine rather than a second
    implementation that agrees with it until it doesn't.
    
    Three things it will not do:
    
    - Book tax as revenue. VAT collected is owed to the Authority, so it comes out
      first and credits a liability; the service refuses a tax account that is not
      one. Egypt's 14% is a per-source setting because some services are 10% and
      some are exempt.
    - Write an entry that does not balance. Integer piasters throughout, floors
      everywhere, one line closes the rounding, and assertBalanced() throws before a
      single row is written — a transaction is immutable, so a wrong one can only be
      reversed, never corrected.
    - Change anything on the day it ships. The migration seeds each academy the rule
      that reproduces its current behaviour exactly, so nothing moves until someone
      asks it to. The platform fee ships switched off for the same reason.
    
    Refunds now reverse the collection's own ledger rows in proportion rather than
    re-running today's rules — a rule edited last week would otherwise unwind money
    into accounts it never touched.
    
    LedgerAccountResolver::splitRevenue() and revenueCodeForItemable() are deleted
    rather than deprecated: a second implementation of "where does this money go" is
    one a caller would eventually reach for, and it would bypass every rule the
    academy wrote.
    
    Verified on a restored oc_sport tenant copy and on a database built from zero:
    521 tests, 0 failures on both the Postgres and SQLite suites. PaymentLedgerTest
    builds a schema without the routing tables, which pins the other half of the
    safety property — an installation that has not received the migration keeps
    taking money on the built-in behaviour instead of failing at the till.
    Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
    e0b69a71
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...