mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Per-association chart of accounts
Each association now owns an independent set of chart_of_accounts rows. Two associations can both have a "5000" meaning different things, edited independently — previously buildium rows were shared across associations via the association_ids[] array, so editing one association's number edited it for all. - Data migration: split each shared buildium row into one row per association in its association_ids (excluding nulls and ids of deleted associations). The original row stays as the per-association row for its own association_id; clones are added for the others — nothing is deleted, so no FK dangles. 94 -> 370 buildium rows. References repointed by each record's association (bills, budgets, owner_ledger_entries, vendor_coa_mappings, units, vendors, journal_entries; budget_actuals_monthly is a view). parent_account_id remapped same-association; orphan-parent children become top-level. Pristine backup in public._coa_perassoc_backup. Ran in one transaction with in-line verification. - Uniqueness: drop (account_number, accounting_system); add UNIQUE(association_id, account_number) WHERE accounting_system <> 'platform' (platform rows mirror accounting.accounts and carry blank/dup codes). This also finally backs the buildium importers' existing onConflict target. - Keep association_ids as a single-element mirror of association_id during the transition so the admin COA page and direct array-contains callers keep working. - App: fetchChartOfAccounts scopes buildium/zoho by association_id when an association is given (was system-wide). Importers and sync_account_to_public_coa were already per-association; no change needed. Verified on live data: 370 singleton-array rows across 12 associations, zero intra-association duplicates, zero cross-association references or parents, and a live two-association "5000" independence test (create/rename/isolate) passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,10 @@ function fromPlatform(row: any, associationId: string): NormalizedAccount {
|
||||
* - `platform` → the Accounting module's `accounting.accounts` (single source of
|
||||
* truth once an association is on the platform). Returns [] if the association
|
||||
* has no `accounting.companies` row yet.
|
||||
* - `zoho` / `buildium` → the public `chart_of_accounts`, scoped by system.
|
||||
* - `zoho` / `buildium` → the public `chart_of_accounts`, scoped by system AND, when
|
||||
* an association is given, by that association. Each association owns an independent
|
||||
* set of accounts (one row per association — see the per-association COA migration),
|
||||
* so two associations can both have a "5000" meaning different things.
|
||||
*
|
||||
* Returned rows are normalized to {@link NormalizedAccount} so callers never
|
||||
* branch on the source.
|
||||
@@ -81,11 +84,14 @@ export async function fetchChartOfAccounts(
|
||||
return (data ?? []).map((row) => fromPlatform(row, associationId));
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
let query = supabase
|
||||
.from("chart_of_accounts")
|
||||
.select("*")
|
||||
.eq("accounting_system", system)
|
||||
.order("account_number", { ascending: true });
|
||||
.eq("accounting_system", system);
|
||||
// Scope to the current association so each one sees only its own accounts. When no
|
||||
// association is supplied, fall back to the whole system set (back-compat).
|
||||
if (associationId) query = query.eq("association_id", associationId);
|
||||
const { data, error } = await query.order("account_number", { ascending: true });
|
||||
if (error) throw error;
|
||||
return (data ?? []).map(fromPublic);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user