mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Accounting reports: page through all GL lines (fix 1000-row truncation)
PostgREST caps each response at 1000 rows. The P&L and Balance Sheet fetch every journal_entry_line for the company and aggregate client-side, so any association with >1000 GL lines (e.g. an imported Buildium GL) only saw the first 1000 — accounts whose activity fell past that point read as $0 and were hidden, making reports look empty/out-of-balance. Fetch the GL in 1000-row pages ordered by a stable key so every line is included. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -87,6 +87,30 @@ function shiftBack(from: string, to: string) {
|
|||||||
return { from: pFrom.toISOString().slice(0, 10), to: pTo.toISOString().slice(0, 10) };
|
return { from: pFrom.toISOString().slice(0, 10), to: pTo.toISOString().slice(0, 10) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostgREST caps each response at 1000 rows. Reports aggregate GL lines
|
||||||
|
// client-side, so for companies with >1000 journal lines we must page through
|
||||||
|
// all of them — otherwise balances are truncated (accounts whose activity
|
||||||
|
// falls past row 1000 silently read as $0). Order by a stable key (id) so the
|
||||||
|
// pages don't overlap or skip rows.
|
||||||
|
const GL_PAGE = 1000;
|
||||||
|
async function fetchAllGLLines(cid: string, to: string, select: string, from?: string): Promise<any[]> {
|
||||||
|
const out: any[] = [];
|
||||||
|
for (let offset = 0; ; offset += GL_PAGE) {
|
||||||
|
let q = accounting
|
||||||
|
.from("journal_entry_lines")
|
||||||
|
.select(select)
|
||||||
|
.eq("journal_entries.company_id", cid)
|
||||||
|
.lte("journal_entries.date", to);
|
||||||
|
if (from) q = q.gte("journal_entries.date", from);
|
||||||
|
const { data, error } = await q.order("id", { ascending: true }).range(offset, offset + GL_PAGE - 1);
|
||||||
|
if (error) throw error;
|
||||||
|
const rows = (data ?? []) as any[];
|
||||||
|
out.push(...rows);
|
||||||
|
if (rows.length < GL_PAGE) break;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
function useReportData(cid: string, from: string, to: string) {
|
function useReportData(cid: string, from: string, to: string) {
|
||||||
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
@@ -109,16 +133,9 @@ function useReportData(cid: string, from: string, to: string) {
|
|||||||
// All bills (not date-filtered) for AP aging
|
// All bills (not date-filtered) for AP aging
|
||||||
accounting.from("bills").select("id,vendor_id,total,paid_amount,status,due_date,issue_date,vendors(id,name)").eq("company_id", cid),
|
accounting.from("bills").select("id,vendor_id,total,paid_amount,status,due_date,issue_date,vendors(id,name)").eq("company_id", cid),
|
||||||
// General-ledger lines in period — P&L is built from these, grouped by account
|
// General-ledger lines in period — P&L is built from these, grouped by account
|
||||||
accounting.from("journal_entry_lines")
|
fetchAllGLLines(cid, to, "id,debit,credit,accounts!inner(id,name,code,type,parent_account_id),journal_entries!inner(company_id,date)", from),
|
||||||
.select("debit,credit,accounts!inner(id,name,code,type,parent_account_id),journal_entries!inner(company_id,date)")
|
|
||||||
.eq("journal_entries.company_id", cid)
|
|
||||||
.gte("journal_entries.date", from)
|
|
||||||
.lte("journal_entries.date", to),
|
|
||||||
// Cumulative GL through `to` — Balance Sheet is built from these (as-of balances)
|
// Cumulative GL through `to` — Balance Sheet is built from these (as-of balances)
|
||||||
accounting.from("journal_entry_lines")
|
fetchAllGLLines(cid, to, "id,debit,credit,account_id,accounts!inner(type),journal_entries!inner(company_id,date)"),
|
||||||
.select("debit,credit,account_id,accounts!inner(type),journal_entries!inner(company_id,date)")
|
|
||||||
.eq("journal_entries.company_id", cid)
|
|
||||||
.lte("journal_entries.date", to),
|
|
||||||
// All invoices (not date-filtered) — Accounts Receivable = unpaid invoices
|
// All invoices (not date-filtered) — Accounts Receivable = unpaid invoices
|
||||||
accounting.from("invoices").select("total,paid_amount,status").eq("company_id", cid),
|
accounting.from("invoices").select("total,paid_amount,status").eq("company_id", cid),
|
||||||
// Whether the platform manages this company's GL (A/R-A/P sub-ledgers tie to the GL).
|
// Whether the platform manages this company's GL (A/R-A/P sub-ledgers tie to the GL).
|
||||||
@@ -132,8 +149,8 @@ function useReportData(cid: string, from: string, to: string) {
|
|||||||
openingBalances: ob.data ?? [],
|
openingBalances: ob.data ?? [],
|
||||||
ytdInvoices: ytdInv.data ?? [], ytdExpenses: ytdExp.data ?? [], ytdBills: ytdBills.data ?? [],
|
ytdInvoices: ytdInv.data ?? [], ytdExpenses: ytdExp.data ?? [], ytdBills: ytdBills.data ?? [],
|
||||||
allBills: allBills.data ?? [],
|
allBills: allBills.data ?? [],
|
||||||
glLines: glRes.data ?? [],
|
glLines: glRes ?? [],
|
||||||
glCumulative: glCumRes.data ?? [],
|
glCumulative: glCumRes ?? [],
|
||||||
allInvoices: allInvRes.data ?? [],
|
allInvoices: allInvRes.data ?? [],
|
||||||
glManaged: companyRes.data ? companyRes.data.gl_auto_post !== false : true,
|
glManaged: companyRes.data ? companyRes.data.gl_auto_post !== false : true,
|
||||||
from, asOf: to,
|
from, asOf: to,
|
||||||
|
|||||||
Reference in New Issue
Block a user