AR Aging (Property): label each unit with its current owner

The per-unit aging picked an arbitrary (often moved-out) owner for the
unit label. Fetch owner status/is_primary and prefer the active, primary
owner per unit so balances are never tagged to a former owner. Aging is
already keyed by unit (funds attach to the unit, not the owner).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 00:17:10 -04:00
parent 4ffe17cd7f
commit 7b9bc2d4c7
2 changed files with 9 additions and 2 deletions
@@ -83,8 +83,13 @@ export function ARAgingPropertyReport({ companyId, companyName, logoUrl, to: pro
for (const u of units) unitById.set(u.id, u); for (const u of units) unitById.set(u.id, u);
const ownerById = new Map<string, OwnerInfo>(); const ownerById = new Map<string, OwnerInfo>();
for (const o of owners) ownerById.set(o.id, o); for (const o of owners) ownerById.set(o.id, o);
// Show the CURRENT owner per unit (active + primary first) so a unit's
// balance is never labeled with a moved-out owner. Funds attach to the unit.
const ownerByUnit = new Map<string, OwnerInfo>(); const ownerByUnit = new Map<string, OwnerInfo>();
for (const o of owners) if (o.unit_id && !ownerByUnit.has(o.unit_id)) ownerByUnit.set(o.unit_id, o); const ownerRank = (o: OwnerInfo) =>
((o.status == null || o.status === "active") ? 0 : 2) + (o.is_primary ? 0 : 1);
for (const o of [...owners].sort((a, b) => ownerRank(a) - ownerRank(b)))
if (o.unit_id && !ownerByUnit.has(o.unit_id)) ownerByUnit.set(o.unit_id, o);
// Latest collection status per unit (rows came back newest-first) // Latest collection status per unit (rows came back newest-first)
const collByUnit = new Map<string, string>(); const collByUnit = new Map<string, string>();
+3 -1
View File
@@ -29,6 +29,8 @@ export type OwnerInfo = {
first_name: string | null; first_name: string | null;
last_name: string | null; last_name: string | null;
unit_id: string | null; unit_id: string | null;
status: string | null;
is_primary: boolean | null;
}; };
export async function fetchAssociationId(companyId: string): Promise<string | null> { export async function fetchAssociationId(companyId: string): Promise<string | null> {
@@ -73,7 +75,7 @@ export async function fetchOwnerLedger(associationId: string, asOf: string): Pro
export async function fetchUnitsAndOwners(associationId: string): Promise<{ units: UnitInfo[]; owners: OwnerInfo[] }> { export async function fetchUnitsAndOwners(associationId: string): Promise<{ units: UnitInfo[]; owners: OwnerInfo[] }> {
const [unitsRes, ownersRes] = await Promise.all([ const [unitsRes, ownersRes] = await Promise.all([
supabase.from("units").select("id, unit_number, address, account_number").eq("association_id", associationId), supabase.from("units").select("id, unit_number, address, account_number").eq("association_id", associationId),
supabase.from("owners").select("id, first_name, last_name, unit_id").eq("association_id", associationId), supabase.from("owners").select("id, first_name, last_name, unit_id, status, is_primary").eq("association_id", associationId),
]); ]);
return { return {
units: (unitsRes.data ?? []) as UnitInfo[], units: (unitsRes.data ?? []) as UnitInfo[],