mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Reconciliation PDF: include outstanding items in prior-period reports; Balance Sheet: stop folding distinctly-named equity accounts
- Prior-reconciliation 'View Report' PDF was hard-coding empty uncleared lists, so outstanding items never appeared. It now reconstructs the items outstanding as of that statement (dated on/before the statement date, not voided, and not cleared in that or an earlier reconciliation) and includes them, with the book balance reflecting them. - Balance Sheet equity folding matched any name containing 'retained earnings' (or 'current year earnings'), so a distinct account like 'Retained Earnings Savings' was swallowed into the calculated line and never shown. Now only the exact standard accounts fold; other equity accounts render on their own line. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -609,16 +609,35 @@ export default function AccountingReconcileDetailPage() {
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Button size="sm" variant="ghost" onClick={async () => {
|
||||
const { data: rows } = await accounting
|
||||
const mapRow = (r: any) => ({ name: r.description ?? "", date: r.date, number: r.reference ?? "", memo: "", amount: Number(r.amount) });
|
||||
// Items cleared IN this reconciliation
|
||||
const { data: clearedRows } = await accounting
|
||||
.from("transactions")
|
||||
.select("date,description,reference,amount,type")
|
||||
.eq("reconciliation_id", h.id);
|
||||
const mapRow = (r: any) => ({ name: r.description ?? "", date: r.date, number: r.reference ?? "", memo: "", amount: Number(r.amount) });
|
||||
const clearedDeposits = (rows ?? []).filter((r: any) => r.type === "credit").map(mapRow);
|
||||
const clearedWithdrawals = (rows ?? []).filter((r: any) => r.type === "debit").map(mapRow);
|
||||
// Outstanding AS OF this statement: dated on/before the
|
||||
// statement date and not cleared in this or any earlier
|
||||
// reconciliation — i.e. still uncleared, or cleared only
|
||||
// in a LATER statement period. (Excludes voided.)
|
||||
const laterReconIds = (history as any[])
|
||||
.filter((r: any) => r.id !== h.id && String(r.statement_end_date) > String(h.statement_end_date))
|
||||
.map((r: any) => r.id);
|
||||
const { data: priorRows } = await accounting
|
||||
.from("transactions")
|
||||
.select("date,description,reference,amount,type,reconciliation_id,voided")
|
||||
.eq("account_id", accountId)
|
||||
.lte("date", h.statement_end_date);
|
||||
const outstanding = (priorRows ?? []).filter((r: any) =>
|
||||
!r.voided && r.reconciliation_id !== h.id &&
|
||||
(r.reconciliation_id == null || laterReconIds.includes(r.reconciliation_id)));
|
||||
const clearedDeposits = (clearedRows ?? []).filter((r: any) => r.type === "credit").map(mapRow);
|
||||
const clearedWithdrawals = (clearedRows ?? []).filter((r: any) => r.type === "debit").map(mapRow);
|
||||
const unclearedDeposits = outstanding.filter((r: any) => r.type === "credit").map(mapRow);
|
||||
const unclearedWithdrawals = outstanding.filter((r: any) => r.type === "debit").map(mapRow);
|
||||
const sumAmt = (rs: { amount: number }[]) => rs.reduce((s, r) => s + r.amount, 0);
|
||||
const begin = Number(h.opening_balance);
|
||||
const ending = begin + sumAmt(clearedDeposits) - sumAmt(clearedWithdrawals);
|
||||
const book = ending + sumAmt(unclearedDeposits) - sumAmt(unclearedWithdrawals);
|
||||
const acctLabel = (account as any)?.code ? `${(account as any).code} ${(account as any).name}` : ((account as any)?.name ?? "Account");
|
||||
renderReconciliationPdf({
|
||||
companyName: associationName ?? "Company",
|
||||
@@ -626,8 +645,8 @@ export default function AccountingReconcileDetailPage() {
|
||||
statementEndDate: h.statement_end_date,
|
||||
beginningBalance: begin,
|
||||
endingBalance: ending,
|
||||
bookBalance: ending,
|
||||
clearedDeposits, clearedWithdrawals, unclearedDeposits: [], unclearedWithdrawals: [],
|
||||
bookBalance: book,
|
||||
clearedDeposits, clearedWithdrawals, unclearedDeposits, unclearedWithdrawals,
|
||||
preparedBy: user?.email ?? "—",
|
||||
currency: cur,
|
||||
completedAt: h.completed_at ?? new Date().toISOString(),
|
||||
|
||||
@@ -1656,8 +1656,12 @@ function buildBalanceSheet(d: any, p?: any, useCompare?: boolean): StructuredRep
|
||||
// postable via journal entries / opening balances. Fold their balances into the
|
||||
// calculated prior-years and Net Income lines (instead of separate lines) so the
|
||||
// figures the user enters in Opening Balances show up where expected.
|
||||
const reAccts = equityAccs.filter((a) => /retained\s+earnings/i.test(String(a.name || "")));
|
||||
const cyeAccts = equityAccs.filter((a) => /current\s*year\s*(earnings|income)/i.test(String(a.name || "")) || /^\s*net\s*income\s*$/i.test(String(a.name || "")));
|
||||
// Fold ONLY the standard "Retained Earnings" / "Current Year Earnings" accounts
|
||||
// into the calculated lines. Distinctly-named equity accounts (e.g. "Retained
|
||||
// Earnings Savings", a reserve-equity account) must NOT be swallowed — they
|
||||
// render as their own equity line. Match the full name, not a substring.
|
||||
const reAccts = equityAccs.filter((a) => /^\s*retained\s+earnings(\s*\(.*\))?\s*$/i.test(String(a.name || "")));
|
||||
const cyeAccts = equityAccs.filter((a) => /^\s*current\s*year\s*(earnings|income)(\s*\(.*\))?\s*$/i.test(String(a.name || "")) || /^\s*net\s*income\s*$/i.test(String(a.name || "")));
|
||||
const foldedIds = new Set([...reAccts, ...cyeAccts].map((a) => a.id));
|
||||
const otherEquity = equityAccs.filter((a) => !foldedIds.has(a.id));
|
||||
for (const a of otherEquity) rows.push({ kind: "sub", label: a.name, code: a.code ?? undefined, amount: balOf(a), compare: cmp(balOfP(a)), accountId: a.id });
|
||||
|
||||
Reference in New Issue
Block a user