-
Mahmoud Aglan authored
The opening figures live in TWO places in this ledger: the chart_of_accounts.opening_balance column AND 24 posted journal entries dated 2024-07-01 with reference_type='opening', totalling 90,601,962.36. Three reports read the column and then also summed the ledger movement that already contained those same entries, counting the opening twice: - Trial balance (LedgerService::getTrialBalance) - General ledger (LedgerService::getAccountLedger) - Balance sheet (FinancialReportService::getBalanceSheet, and the consolidated sheet which delegates to it) Measured on live data, trial balance over FY 2024/2025: 1103 مشروعات تحت التنفيذ reported 85,627,410.75 actual 43,923,543.75 210201 أرباح مرحلة reported -146,645,270 actual -73,322,635 i.e. exactly double on every account carrying an opening balance. The report still footed, because opening balances net to zero across debit and credit — so it looked right and every line was wrong. Only periods containing 2024-07-01 were affected; a 2026 trial balance was already correct. The opening column is now derived as cumulative posted movement BEFORE the period start, which is the standard definition, removes the double count structurally, and works for any period rather than only a year boundary. The trial balance query is also restructured into two independent aggregates so no row multiplication is possible and an account whose only movement predates the period still appears. Income statement was already correct and is unchanged. Also in this commit: - LedgerService::rebuildBalances() + a seed that runs it. The opening import wrote journal rows without going through JournalService, so 24 accounts had a cached current_balance disagreeing with the ledger — retained earnings cached 0.00 against an actual 73,322,635.00. The reports read the ledger and were fine, but the Chart of Accounts screen and the bank-reconciliation opening figure read the cache, which is precisely where an accountant would find a number contradicting the trial balance. - Carnet guest entry never posted. Accounting listened on 'carnet.guest_entry_recorded'; GuestEntryService dispatches 'carnet_guest.entry_recorded' (underscore, not dot). Notifications listens on the correct name, which is why notifications worked and the ledger entry never appeared. Fees were recorded in carnet_guest_entries.amount_paid and posted nowhere. - 'tournament.fee_collected' has no dispatcher anywhere. Documented as dead rather than left looking wired. - Two fiscal years were flagged is_current; the seed leaves exactly the one containing today. FiscalYear::findByDate now resolves overlapping years deterministically (open first, then narrowest range) instead of taking whatever the database returned — this chart has calendar years overlapping a July-June year, so Jul-Dec 2024 matches two. No entry is reassigned; all 795 are already inside their assigned year. - PostingRouter and postViaRule now probe App::db() with try/catch. It is typed `: Database` and throws when unbound rather than returning null, so the previous null guards could never fire. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
2f77d3e1
| Name |
Last commit
|
Last update |
|---|---|---|
| .. | ||
| PostingRouter.php | ||
| RevenueAllocator.php | ||
| RevenuePostingEngine.php | ||
| RevenueRecognitionService.php | ||
| RevenueStreamRegistry.php |