diff --git a/supabase/functions/buildium-payee-backfill/index.ts b/supabase/functions/buildium-payee-backfill/index.ts index a1d987d..65fafab 100644 --- a/supabase/functions/buildium-payee-backfill/index.ts +++ b/supabase/functions/buildium-payee-backfill/index.ts @@ -298,6 +298,51 @@ Deno.serve(async (req) => { if (name) { payeeById.set(id, name); byType[type].named++; } } + // partymap mode: resolve each Buildium tx to a LOCAL vendor/customer id and + // stage it (accounting.buildium_party_map) so the materialized bank register + // (accounting.transactions) can be backfilled in SQL by bridging tx→JE. + // vendor : Payee.Type=Vendor → match Payee.Name to accounting.vendors.name + // customer : tx.UnitNumber → match to accounting.customers.unit_number + if (mode === "partymap") { + const vendorByName = new Map(); + for (const v of (await supabase.schema("accounting").from("vendors").select("id,name").eq("company_id", companyId)).data ?? []) + if (v.name) vendorByName.set(norm(v.name), v.id); + const customerByUnitNo = new Map(); + for (const c of (await supabase.schema("accounting").from("customers").select("id,unit_number").eq("company_id", companyId)).data ?? []) + if (c.unit_number) customerByUnitNo.set(norm(c.unit_number), c.id); + + const rows: any[] = []; + let vendorHits = 0, customerHits = 0; + for (const tx of txns) { + const id = String(tx?.Id ?? ""); + if (!id) continue; + const name = payeeById.get(id) ?? null; + const payeeType = String(tx?.PaymentDetail?.Payee?.Type ?? ""); + let vendor_id: string | null = null; + let customer_id: string | null = null; + if (payeeType === "Vendor" && name) vendor_id = vendorByName.get(norm(name)) ?? null; + const unitNo = tx?.UnitNumber; + if (!vendor_id && typeof unitNo === "string" && unitNo.trim()) customer_id = customerByUnitNo.get(norm(unitNo)) ?? null; + // owner payment with no UnitNumber: try the payee name against customers + if (!vendor_id && !customer_id && name && payeeType && payeeType !== "Vendor") { + // fall through — name match handled in SQL if needed; leave null here + } + if (vendor_id) vendorHits++; + if (customer_id) customerHits++; + if (name || vendor_id || customer_id) + rows.push({ company_id: companyId, external_id: id, party_name: name, vendor_id, customer_id, party_type: payeeType || null }); + } + // Upsert in batches. + let staged = 0; + for (let i = 0; i < rows.length; i += 500) { + const batch = rows.slice(i, i + 500); + const { error } = await supabase.schema("accounting").from("buildium_party_map").upsert(batch, { onConflict: "company_id,external_id" }); + if (error) throw error; + staged += batch.length; + } + return json({ mode, company: company.name, window: [dateFrom, dateTo], txns: txns.length, staged, vendorHits, customerHits }); + } + // Load this company's imported entries and compute the new description. const entries: { id: string; external_id: string; description: string }[] = []; for (let offset = 0; ; offset += 1000) { diff --git a/supabase/migrations/20260615180000_buildium_party_map_staging.sql b/supabase/migrations/20260615180000_buildium_party_map_staging.sql new file mode 100644 index 0000000..fef92e6 --- /dev/null +++ b/supabase/migrations/20260615180000_buildium_party_map_staging.sql @@ -0,0 +1,19 @@ +-- Staging table for the Buildium payee/payor register backfill. +-- Maps a Buildium GL transaction id to the resolved local vendor/customer and +-- the party name, so the materialized bank register (accounting.transactions) +-- of imported-GL companies can be backfilled by bridging transaction → journal +-- entry (by bank line) → this map. Populated by the buildium-payee-backfill +-- edge function ("partymap" mode). +create table if not exists accounting.buildium_party_map ( + company_id uuid not null, + external_id text not null, + party_name text, + vendor_id uuid, + customer_id uuid, + party_type text, + updated_at timestamptz not null default now(), + primary key (company_id, external_id) +); + +comment on table accounting.buildium_party_map is + 'Staging: Buildium GL transaction id -> resolved local vendor/customer + name, used to backfill the materialized bank register (accounting.transactions) for imported companies.';