diff --git a/src/pages/accounting/AccountingReportsPage.tsx b/src/pages/accounting/AccountingReportsPage.tsx index a06aa01..56b423b 100644 --- a/src/pages/accounting/AccountingReportsPage.tsx +++ b/src/pages/accounting/AccountingReportsPage.tsx @@ -131,7 +131,7 @@ export async function fetchReportData(cid: string, from: string, to: string) { const [inv, bills, accs, exp, custs, vends, ob, ytdInv, ytdExp, ytdBills, allBills, glRes, glCumRes, allInvRes, companyRes, periodBillItemsRes] = await Promise.all([ accounting.from("invoices").select("number,total,paid_amount,status,issue_date,customers(name)").eq("company_id", cid).gte("issue_date", from).lte("issue_date", to), accounting.from("bills").select("number,total,paid_amount,status,issue_date,due_date,vendors(name)").eq("company_id", cid).gte("issue_date", from).lte("issue_date", to), - accounting.from("accounts").select("id,name,code,type,subtype,balance,is_bank").eq("company_id", cid).eq("is_archived", false), + accounting.from("accounts").select("id,name,code,type,subtype,balance,is_bank,parent_account_id").eq("company_id", cid).eq("is_archived", false), accounting.from("expenses").select("date,category,amount,vendor_name,vendors(name)").eq("company_id", cid).gte("date", from).lte("date", to), accounting.from("customers").select("id,name,balance,email,phone,property_address,lot_number").eq("company_id", cid).order("name"), accounting.from("vendors").select("id,name").eq("company_id", cid), @@ -1667,12 +1667,27 @@ function buildBalanceSheet(d: any, p?: any, useCompare?: boolean): StructuredRep const sumBal = (rows: any[]) => rows.reduce((s, a) => s + balOf(a), 0); const sumBalP = (rows: any[]) => (prev ? rows.reduce((s, a) => s + (prev.glByAcct.get(a.id) ?? 0), 0) : undefined); + // Account-number + parent-category labels (Buildium style: + // "3011 SIRS Reserves - 3066 Painting/Waterproofing"). Codes are baked into the + // label so they always show, and rows are sorted so siblings group by parent. + const acctMeta = new Map(allAccounts.map((a: any) => [a.id, { code: a.code ?? null, name: a.name }])); + const codeName = (code: any, name: any) => `${code != null && String(code).trim() !== "" ? String(code) + " " : ""}${name ?? ""}`.trim(); + const bsLabel = (a: any) => { + const p = a.parent_account_id ? acctMeta.get(a.parent_account_id) : null; + return p ? `${codeName(p.code, p.name)} - ${codeName(a.code, a.name)}` : codeName(a.code, a.name); + }; + const bsKey = (a: any) => { + const p = a.parent_account_id ? acctMeta.get(a.parent_account_id) : null; + return `${p ? String(p.code ?? "") : String(a.code ?? "")}|${String(a.code ?? "")}`; + }; + const bsSort = (arr: any[]) => [...arr].sort((x, y) => bsKey(x).localeCompare(bsKey(y), undefined, { numeric: true })); + const rows: StructuredRow[] = []; // Assets rows.push({ kind: "section", label: "Assets" }); const assets = byType("asset"); - for (const a of assets) rows.push({ kind: "sub", label: a.name, code: a.code ?? undefined, amount: balOf(a), compare: cmp(balOfP(a)), accountId: a.id }); + for (const a of bsSort(assets)) rows.push({ kind: "sub", label: bsLabel(a), amount: balOf(a), compare: cmp(balOfP(a)), accountId: a.id }); const totalA = sumBal(assets); rows.push({ kind: "grand", label: "TOTAL ASSETS", amount: totalA, compare: cmp(sumBalP(assets)) }); rows.push({ kind: "spacer", label: "" }); @@ -1680,7 +1695,7 @@ function buildBalanceSheet(d: any, p?: any, useCompare?: boolean): StructuredRep // Liabilities rows.push({ kind: "section", label: "Liabilities" }); const liabs = byType("liability"); - for (const a of liabs) rows.push({ kind: "sub", label: a.name, code: a.code ?? undefined, amount: balOf(a), compare: cmp(balOfP(a)), accountId: a.id }); + for (const a of bsSort(liabs)) rows.push({ kind: "sub", label: bsLabel(a), amount: balOf(a), compare: cmp(balOfP(a)), accountId: a.id }); const totalL = sumBal(liabs); rows.push({ kind: "total", label: "Total Liabilities", amount: totalL, compare: cmp(sumBalP(liabs)) }); rows.push({ kind: "spacer", label: "" }); @@ -1703,7 +1718,7 @@ function buildBalanceSheet(d: any, p?: any, useCompare?: boolean): StructuredRep 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 }); + for (const a of bsSort(otherEquity)) rows.push({ kind: "sub", label: bsLabel(a), amount: balOf(a), compare: cmp(balOfP(a)), accountId: a.id }); const rePosted = reAccts.reduce((s, a) => s + balOf(a), 0); const rePostedP = prev ? reAccts.reduce((s, a) => s + (prev.glByAcct.get(a.id) ?? 0), 0) : undefined; const cyePosted = cyeAccts.reduce((s, a) => s + balOf(a), 0);