• DevPilot's avatar
    fix(accounting): make the accounting fundamentals actually work · 33c76389
    DevPilot authored
    Auditing the basics against the live books turned up four things that could
    never have worked, plus a fifth that worked too well.
    
    CLOSING A FISCAL YEAR WAS IMPOSSIBLE — three separate faults stacked:
    
      1. `Database::execute()` did not exist. Six call sites used it — closing a
         period, setting the current fiscal year, setting the default bank account,
         rebuilding an account balance, deleting a facility blackout — and every one
         was a fatal the moment it ran. Added to Database rather than patched at the
         call sites. This alone is why period_closings was empty.
      2. Closing a year requires every month closed first, then dates its entry the
         last day of the year — inside a month it just required to be closed. The
         two rules deadlocked. The closing entry, and only it, may now post there.
      3. The same entry has to zero accounts retired during the year, which the
         inactive-account guard refused. Closing an account is exactly when you must
         be able to clear it.
      4. And `period_closings.period` is varchar(7) while the year-end key is
         `YYYY-MM-YE` — ten characters — so the entry posted and the record failed,
         leaving the year half-closed.
    
      It now runs end to end: 19 lines, revenue and expenses zeroed to retained
      earnings, year marked closed, balance sheet still balanced.
    
    PAYROLL POSTED TWICE. Salaries are the largest single expense the club books and
    nothing guarded the reference, so a retried event or a second click doubled the
    expense, the withheld tax and the insurance liability. Every other posting in
    that service guards; this one did not.
    
    FIXED ASSETS COULD NOT BE CREATED AT ALL. The club carries 4,705,686 of
    machinery and 41,703,867 of construction in progress, and `asset_register` is
    empty — so nothing has ever depreciated. Not because depreciation is unwired, it
    posts correctly, but because there was no create route, no create method, and
    nothing anywhere that inserted an asset. `item_id` was NOT NULL against
    inventory_items, so registering a building meant inventing a stock row for it.
    
      Now: item_id and warehouse_id are nullable, the eight asset categories the
      chart already defines are seeded with their three GL accounts each, and there
      is a form. It distinguishes a purchase (posts Dr Asset / Cr Cash-or-Payable)
      from an opening-balance asset (posts nothing — the cost is already in the
      ledger, and posting it again would double the balance sheet).
    
      Asset disposal never worked either: `AccountCodes` was not imported, so the
      handler threw a fatal that the bootstrap's catch swallowed. Assets came off
      the register and stayed on the balance sheet.
    
      The register's own queries inner-joined inventory_items and warehouses, which
      would have hidden every real fixed asset behind an empty list and a "not
      found" page. LEFT joins, and the category name where there is no stock item.
    
      The depreciation month picker posted `month` while the controller read
      `period_month`, so whichever month the accountant chose was dropped and the
      current one ran instead.
    
    Overlapping fiscal years are now shown on the fiscal-years screen. The club
    carries calendar years alongside a July–June year, so entries in the overlap
    belong to both and closing one leaves the other open over the same
    transactions. Only the club can say which convention is real, so it is surfaced,
    not guessed.
    
    Verified on a clone of production with all 409 foreign keys: full asset cycle
    posts (purchase, depreciation by category, disposal with its 5,000 loss), all
    1,489 routes resolve, 252 controllers instantiate, 241 services load, trial
    balance diff 0.00, balance sheet balanced.
    Co-Authored-By: 's avatarClaude Opus 5 <noreply@anthropic.com>
    33c76389
Phase_113_002_period_closing_year_end.php 1.15 KB