Accounting: fix cross-page account-picker cache collisions + allow equity on bills

Root cause of filters conflicting between pages: many accounting pages shared
the same React Query keys (['accounts',cid], ['bank-accounts',cid],
['deposit-accounts',cid]) while running DIFFERENT queries, so whichever page
loaded first poisoned the others' cache (e.g. bill payment picker showing
banks-only with no equity; archived accounts leaking from the CoA list).

- Give every account read query a unique key discriminator per page/purpose;
  ['accounts',cid] / ['bank-accounts',cid] / ['deposit-accounts',cid] stay as
  invalidation prefixes (React Query partial match) so cross-page refresh still works
- Bill line-item category picker now includes expense + asset + equity + liability
  (was expense-only) — fixes 11 bills/13 lines categorized to equity (reserve-funded)
  that showed blank/—; and lets you assign an equity account to a bill
- Payment account picker (bills + expenses) reliably shows banks + equity now that
  it no longer collides with the banks-only deposit/receive-payment pickers

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 23:54:38 -04:00
parent f518f0b8f4
commit fbc5019730
14 changed files with 23 additions and 16 deletions
@@ -140,7 +140,7 @@ export default function AccountingBankingPage() {
const [selected, setSelected] = useState<Set<string>>(new Set());
const { data: accounts = [] } = useQuery({
queryKey: ["accounts", cid],
queryKey: ["accounts", cid, "banking"],
enabled: !!cid,
queryFn: async () =>
// Archived accounts are hidden from Banking (cards + category pickers)
+8 -3
View File
@@ -85,11 +85,16 @@ export default function AccountingBillsPage() {
.or(`association_id.eq.${associationId},association_ids.cs.{${associationId}}`)
.order("name")).data ?? [],
});
// Bill line categories: expenses plus asset/equity/liability accounts
// (e.g. prepaid insurance, reserve-funded repairs to an equity component) —
// not just expense accounts. Bank accounts are excluded.
const { data: expenseAccounts = [] } = useQuery({
queryKey: ["expense-accounts", cid],
queryKey: ["bill-category-accounts", cid],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("id,name,code,type").eq("company_id", cid).eq("type", "expense").eq("is_archived", false).order("code")).data ?? [],
(await accounting.from("accounts").select("id,name,code,type")
.eq("company_id", cid).in("type", ["expense", "asset", "equity", "liability"])
.eq("is_bank", false).eq("is_archived", false).order("type").order("code")).data ?? [],
});
const enriched = useMemo(
@@ -351,7 +356,7 @@ export default function AccountingBillsPage() {
const [paying, setPaying] = useState(false);
const { data: bankAccounts = [] } = useQuery({
queryKey: ["bank-accounts", cid],
queryKey: ["bank-accounts", cid, "bills-payment"],
enabled: !!cid,
queryFn: async () =>
// Payment accounts: banks plus equity accounts (e.g. reserve components
@@ -50,7 +50,7 @@ export default function AccountingBudgetDetailPage({ basePath = "/dashboard/acco
});
const { data: accounts = [] } = useQuery({
queryKey: ["accounts", cid],
queryKey: ["accounts", cid, "budget"],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("*").eq("company_id", cid).eq("is_archived", false).order("code")).data ?? [],
@@ -79,7 +79,9 @@ export default function AccountingChartOfAccountsPage() {
// ── Queries ──
const { data: accounts = [] } = useQuery({
queryKey: ["accounts", cid],
// Distinct key: this is the only list that INCLUDES archived accounts, so
// it must not share a cache with the active-only pickers on other pages.
queryKey: ["accounts", cid, "coa-all"],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("*").eq("company_id", cid).order("code", { ascending: true })).data ?? [],
@@ -46,7 +46,7 @@ export default function AccountingDepositsPage() {
}, [cid]);
const { data: bankAccounts = [] } = useQuery({
queryKey: ["bank-accounts", cid],
queryKey: ["bank-accounts", cid, "deposits"],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("id,name,code,balance").eq("company_id", cid).eq("is_bank", true).eq("is_archived", false).order("code")).data ?? [],
@@ -68,7 +68,7 @@ export default function AccountingExpensesPage() {
});
const { data: accounts = [] } = useQuery({
queryKey: ["bank-accounts", cid],
queryKey: ["bank-accounts", cid, "expenses-payment"],
enabled: !!cid,
// Paid-through accounts: banks plus equity accounts; archived hidden.
queryFn: async () => (await accounting.from("accounts").select("id,name").eq("company_id", cid).or("is_bank.eq.true,type.eq.equity").eq("is_archived", false).order("name")).data ?? [],
@@ -54,7 +54,7 @@ export default function AccountingGeneralLedgerPage() {
});
const { data: accounts = [] } = useQuery({
queryKey: ["accounts", cid],
queryKey: ["accounts", cid, "gl"],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("id, code, name, type").eq("company_id", cid).order("code")).data ?? [],
@@ -123,7 +123,7 @@ export default function AccountingInvoicesPage() {
const [paying, setPaying] = useState(false);
const { data: depositAccounts = [] } = useQuery({
queryKey: ["deposit-accounts", cid],
queryKey: ["deposit-accounts", cid, "invoices"],
enabled: !!cid,
queryFn: async () => {
const { data } = await accounting.from("accounts")
@@ -53,7 +53,7 @@ export default function AccountingJournalEntriesPage() {
});
const { data: accounts = [] } = useQuery({
queryKey: ["accounts", cid],
queryKey: ["accounts", cid, "je"],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("*").eq("company_id", cid).eq("is_archived", false).order("code")).data ?? [],
@@ -35,7 +35,7 @@ export default function AccountingOpeningBalancesPage() {
const qc = useQueryClient();
const { data: accounts = [] } = useQuery({
queryKey: ["accounts", cid],
queryKey: ["accounts", cid, "ob"],
enabled: !!cid,
queryFn: async () => (await accounting.from("accounts").select("id,name,code,type").eq("company_id", cid).eq("is_archived", false).order("code", { ascending: true })).data ?? [],
});
@@ -39,7 +39,7 @@ export default function AccountingReceivePaymentsPage() {
const [initialized, setInitialized] = useState(false);
const { data: bankAccounts = [] } = useQuery({
queryKey: ["bank-accounts", cid],
queryKey: ["bank-accounts", cid, "receive-payments"],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("id,name,code,balance").eq("company_id", cid).eq("is_bank", true).eq("is_archived", false).order("name")).data ?? [],
@@ -122,7 +122,7 @@ export default function AccountingReconcileDetailPage() {
const priorReconDate: string | null = lastCompleted?.statement_end_date ?? null;
const { data: allAccounts = [] } = useQuery({
queryKey: ["accounts", cid],
queryKey: ["accounts", cid, "recon"],
enabled: !!cid,
queryFn: async () =>
(await accounting.from("accounts").select("id,name,type,is_bank").eq("company_id", cid).eq("is_archived", false).order("name")).data ?? [],
@@ -1920,7 +1920,7 @@ function BudgetVsActuals({ companyId, from, to, currency, companyName, rangeLabe
const actualsLabel = `${actFrom} to ${actTo}`;
const { data: accounts = [] } = useQuery({
queryKey: ["accounts", companyId],
queryKey: ["accounts", companyId, "budget-actuals"],
enabled: !!companyId,
queryFn: async () => (await accounting.from("accounts").select("*").eq("company_id", companyId).eq("is_archived", false).order("code")).data ?? [],
});
@@ -63,7 +63,7 @@ export default function AccountingSalesReceiptsPage() {
});
const { data: depositAccounts = [] } = useQuery({
queryKey: ["deposit-accounts", cid],
queryKey: ["deposit-accounts", cid, "sales-receipts"],
enabled: !!cid,
queryFn: async () => {
const { data } = await accounting