Add Balance Reconciliation report (detective control)
Phase 1 of the accounting audit remediation. A read-only diagnostic that surfaces drift in the denormalized balances without changing any posting: - Per account: stored Account.CurrentBalance vs the balance recomputed from source documents (the same LedgerService path RecalculateBalances uses). Drifted rows are highlighted; a difference means the cache is stale and a recalc would fix it. - AR subledger (sum of Customer.CurrentBalance) vs the AR control account, and AP subledger (sum of Vendor.CurrentBalance) vs the AP control account. FinancialReportService now takes ILedgerService to recompute. New GetBalanceReconciliationAsync + BalanceReconciliationDto, a /Reports/Reconciliation action, view, and a card on the reports landing. Build clean; 284 unit tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1250,6 +1250,20 @@ public class ReportsController : Controller
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Balance reconciliation diagnostic: each account's stored CurrentBalance vs its recomputed ledger
|
||||
/// balance, plus AR/AP subledger totals vs their control accounts. Read-only; surfaces drift in the
|
||||
/// denormalized balances without changing any posting. Gated behind <see cref="AllowAccounting"/>.
|
||||
/// </summary>
|
||||
// GET: /Reports/Reconciliation
|
||||
public async Task<IActionResult> Reconciliation()
|
||||
{
|
||||
if (!AllowAccounting()) return RedirectToAction(nameof(Landing));
|
||||
var companyId = int.TryParse(User.FindFirst("CompanyId")?.Value, out var cid) ? cid : 0;
|
||||
var dto = await _financialReports.GetBalanceReconciliationAsync(companyId);
|
||||
return View(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PDF export of the Trial Balance report. Same inline/attachment pattern as other PDF actions.
|
||||
/// Gated behind <see cref="AllowAccounting"/>.
|
||||
|
||||
Reference in New Issue
Block a user