mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Accounting platform: remove Zoho, unify reports, board access, vendor sharing
- Remove the Zoho Books integration (edge functions, sync libs, settings, reports/overview, banking links, fees tab, import dialog); preserve fee rules as a standalone FeesTab and the COA accounting_system classification. - Financial Overview/Reports (staff + board) render the Accounting dashboard and reports; board reports mirror the rich Accounting Reports. - New Reserve Fund Schedule report + an is_reserve flag on accounts. - Unify all report exports to a branded format (logo + centered header + footer): shared ReportSheet (on-screen) and reportHeader (PDF). Budget vs Actuals and Bank Reconciliation PDFs now match the reference layout. - Render financial reports inline (no preview pop-up). - Budget Management mirrors Accounting Budgeting (staff-accessible) with SPA navigation; editable bills in the Accounting Bills page. - Negative opening balances flow through to the GL and reports (allow negative input; keep non-zero on save; signed CSV import). - Upload a per-account trial balance via CSV on Opening Balances. - Board members: read-only RLS access to their association's accounting ledger; editable board-members panel on the association page; share vendor contacts with the board (toggle + directory section). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -197,20 +197,28 @@ export default function AccountingReconcileDetailPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const deposits = (txs as Tx[]).filter((t) => checked.has(t.id) && t.type === "credit")
|
||||
.map((t) => ({ date: t.date, description: t.description, amount: Number(t.amount) }));
|
||||
const withdrawals = (txs as Tx[]).filter((t) => checked.has(t.id) && t.type === "debit")
|
||||
.map((t) => ({ date: t.date, description: t.description, amount: Number(t.amount) }));
|
||||
const mapRow = (t: Tx) => ({ name: t.description ?? "", date: t.date, number: t.reference ?? "", memo: "", amount: Number(t.amount) });
|
||||
const clearedDepRows = (txs as Tx[]).filter((t) => checked.has(t.id) && t.type === "credit").map(mapRow);
|
||||
const clearedWdRows = (txs as Tx[]).filter((t) => checked.has(t.id) && t.type === "debit").map(mapRow);
|
||||
const unclearedDepRows = (txs as Tx[]).filter((t) => !checked.has(t.id) && t.type === "credit").map(mapRow);
|
||||
const unclearedWdRows = (txs as Tx[]).filter((t) => !checked.has(t.id) && t.type === "debit").map(mapRow);
|
||||
const sumAmt = (rs: { amount: number }[]) => rs.reduce((s, r) => s + r.amount, 0);
|
||||
const begin = Number(active.opening_balance);
|
||||
const ending = begin + sumAmt(clearedDepRows) - sumAmt(clearedWdRows);
|
||||
const book = ending + sumAmt(unclearedDepRows) - sumAmt(unclearedWdRows);
|
||||
const acctLabel = (account as any)?.code ? `${(account as any).code} ${(account as any).name}` : ((account as any)?.name ?? "Account");
|
||||
|
||||
setSuccessData({
|
||||
companyName: associationName ?? "Company",
|
||||
accountName: (account as any)?.name ?? "Account",
|
||||
accountName: acctLabel,
|
||||
statementEndDate: active.statement_end_date,
|
||||
openingBalance: active.opening_balance,
|
||||
statementBalance: active.statement_balance,
|
||||
difference: 0,
|
||||
deposits,
|
||||
withdrawals,
|
||||
beginningBalance: begin,
|
||||
endingBalance: ending,
|
||||
bookBalance: book,
|
||||
clearedDeposits: clearedDepRows,
|
||||
clearedWithdrawals: clearedWdRows,
|
||||
unclearedDeposits: unclearedDepRows,
|
||||
unclearedWithdrawals: unclearedWdRows,
|
||||
preparedBy: user?.email ?? "—",
|
||||
currency: cur,
|
||||
completedAt: new Date().toISOString(),
|
||||
@@ -465,20 +473,23 @@ export default function AccountingReconcileDetailPage() {
|
||||
<Button size="sm" variant="ghost" onClick={async () => {
|
||||
const { data: rows } = await accounting
|
||||
.from("transactions")
|
||||
.select("date,description,amount,type")
|
||||
.select("date,description,reference,amount,type")
|
||||
.eq("reconciliation_id", h.id);
|
||||
const deposits = (rows ?? []).filter((r: any) => r.type === "credit")
|
||||
.map((r: any) => ({ date: r.date, description: r.description, amount: Number(r.amount) }));
|
||||
const withdrawals = (rows ?? []).filter((r: any) => r.type === "debit")
|
||||
.map((r: any) => ({ date: r.date, description: r.description, amount: Number(r.amount) }));
|
||||
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);
|
||||
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 acctLabel = (account as any)?.code ? `${(account as any).code} ${(account as any).name}` : ((account as any)?.name ?? "Account");
|
||||
renderReconciliationPdf({
|
||||
companyName: associationName ?? "Company",
|
||||
accountName: (account as any)?.name ?? "Account",
|
||||
accountName: acctLabel,
|
||||
statementEndDate: h.statement_end_date,
|
||||
openingBalance: Number(h.opening_balance),
|
||||
statementBalance: Number(h.statement_balance),
|
||||
difference: 0,
|
||||
deposits, withdrawals,
|
||||
beginningBalance: begin,
|
||||
endingBalance: ending,
|
||||
bookBalance: ending,
|
||||
clearedDeposits, clearedWithdrawals, unclearedDeposits: [], unclearedWithdrawals: [],
|
||||
preparedBy: user?.email ?? "—",
|
||||
currency: cur,
|
||||
completedAt: h.completed_at ?? new Date().toISOString(),
|
||||
@@ -579,8 +590,8 @@ export default function AccountingReconcileDetailPage() {
|
||||
{successData && (
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between"><span className="text-muted-foreground">Period reconciled</span><span>{fmtDate(successData.statementEndDate)}</span></div>
|
||||
<div className="flex justify-between"><span className="text-muted-foreground">Statement balance</span><span>{money(successData.statementBalance, successData.currency)}</span></div>
|
||||
<div className="flex justify-between"><span className="text-muted-foreground">Cleared transactions</span><span>{successData.deposits.length + successData.withdrawals.length}</span></div>
|
||||
<div className="flex justify-between"><span className="text-muted-foreground">Ending balance</span><span>{money(successData.endingBalance, successData.currency)}</span></div>
|
||||
<div className="flex justify-between"><span className="text-muted-foreground">Cleared transactions</span><span>{successData.clearedDeposits.length + successData.clearedWithdrawals.length}</span></div>
|
||||
<div className="flex justify-between"><span className="text-muted-foreground">Date completed</span><span>{fmtDate(successData.completedAt)}</span></div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user