Direct Buildium A/P import: bills, payments, one-off checks via GL Account Map

- public.bills.line_items + line-aware accounting mirror (one bill_item per
  Buildium line with its mapped platform account)
- buildium-sync bills: strict per-line resolution through
  buildium_gl_account_links (unmapped -> bill held + flagged); pulls
  /bills/{id}/payments (check#, bank, date) and /bankaccounts/{id}/checks
  (one-off checks become paid bill+payment pairs)
- import-mode companies get direct JEs (buildium_bill Dr expense/Cr AP,
  buildium_billpay Dr AP/Cr mapped bank) + cleared register rows; sets
  buildium_gl.exclude_ap so the nightly GL pull skips Bill/Bill Payment/Check
- buildium-gl-sync: exclude_ap transaction-type filter; preserve buildium_gl
  config keys when advancing the watermark
- Settings: Pull Bills & Expenses card with held/unmapped reporting

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 18:43:58 -04:00
parent 4e77098f88
commit 25064d8418
4 changed files with 376 additions and 30 deletions
@@ -273,6 +273,23 @@ Deno.serve(async (req) => {
}
companyResult.pulled = txById.size;
// Direct A/P import (buildium-sync "bills") posts Bill / Bill Payment /
// Check journal entries itself for this company — skip those
// transaction types here so they aren't double counted.
if (cfg?.buildium_gl?.exclude_ap) {
// NOTE: owner "Refund" transactions stay in the GL pull — they are
// not returned by the /checks endpoint the direct import reads.
const AP_TYPES = new Set(["bill", "bill payment", "billpayment", "check", "bill credit", "vendor credit", "applied vendor credit"]);
let excludedAp = 0;
for (const [txId, tx] of [...txById.entries()]) {
if (AP_TYPES.has(String(tx.transactionType || "").toLowerCase())) {
txById.delete(txId);
excludedAp++;
}
}
companyResult.excluded_ap = excludedAp;
}
// ---- Already-imported transaction ids for this company ----
const existingIds = new Set<string>();
for (let offset = 0; ; offset += 1000) {
@@ -432,6 +449,7 @@ Deno.serve(async (req) => {
const nextCfg = {
...cfg,
buildium_gl: {
...(cfg.buildium_gl ?? {}),
last_synced_date: until,
last_run_at: new Date().toISOString(),
last_result: {