Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
6.3 KiB
6.3 KiB
SCRATCHPAD — QBO Phase 2: Closed-Loop Accounting Sync
Decisions (with rationale)
- 2026-06-10 — QBO is authoritative for payments; Alga pushes both ways (user-confirmed). Bookkeepers live in QBO; payments recorded there flow into Alga as real AR records, and Alga-originated (Stripe portal) payments push out so the books agree. Conflicts resolve in QBO's favor.
- 2026-06-10 — CDC polling through the
IJobRunnerabstraction, no Intuit webhooks this phase (user-confirmed, with correction: "pg-boss is usually not used, usually temporal is, but the abstraction should handle it properly"). On-prem appliances cannot receive Intuit webhooks, so polling must exist regardless; one code path for hosted + appliance. - 2026-06-10 — Credit semantics reshape lands before credit sync
(user-confirmed after impedance-mismatch review).
applyCreditToInvoicedecrementsinvoices.total_amount(creditActions.ts:910) — posted documents must be immutable; application moves derived balance-due. Reshape now while the credit system is barely used; backfill is recoverable (total_amount += credit_applied). - 2026-06-10 — Real void semantics (user-confirmed).
cancelledstatus andinvoice_cancelledtransaction type exist in the schema but are unused; avoidInvoiceaction adopts them. Hard delete blocked for exported invoices. - 2026-06-10 — Architecture B: central sync engine (user-confirmed) over
per-feature jobs (fragmented state) and over resurrecting the event-bus push
model deliberately removed in Oct 2025
(migration
20251031121500_remove_qbo_workflow_automation.cjs). - 2026-06-11 — Engine is adapter-agnostic by construction. Shared tables
carry
adapter_type; noqbo_prefixes. Xero adopts the engine in a later phase ~for free. - 2026-06-11 — Test planning follows the 80/20 rule (user-directed):
automated tests target ~80% confidence; the rest is the live Intuit-sandbox
smoke checklist.
tests.jsonmarks live-smoke entries with"mode": "live-smoke".
Key file paths
- Design (scope authority for architecture):
./design.md - Phase 1 feature commit:
01ff24f662; design commit:91fc97b5b6(branchrelease/qbo-online-integration) - Adapter:
packages/billing/src/adapters/accounting/quickBooksOnlineAdapter.ts - Client service:
packages/integrations/src/lib/qbo/qboClientService.ts - Payment landing (to refactor into shared service):
ee/server/src/lib/payments/PaymentService.ts:598(recordPaymentFromWebhook) - Credit application mutation site:
packages/billing/src/actions/creditActions.ts:910 - Job abstraction:
packages/jobs/src/lib/jobs/interfaces/IJobRunner.ts; startup registrationserver/src/lib/jobs/initializeScheduledJobs.ts - Workflow task inbox:
shared/workflow/persistence/workflowTaskModel.ts:119; unwiredqbo_mapping_errortask type from migration20250511215231_consolidate_qbo_workflow_schema.cjs:126 - Badge precedent:
packages/billing/src/components/invoices/InvoiceTaxSourceBadge.tsx
Gotchas
JobScheduler.scheduleRecurringJob(server/src/lib/jobs/jobScheduler.ts:184) coerces every interval to 24 hours. The 15-minute cycle must use the shorter-interval path (precedent:renew-google-gmail-watchevery 30 min) or fix the coercion.- CDC overlap window (5 min) makes duplicate delivery routine — every inbound applier must be a no-op for already-mapped, unchanged entities.
invoice_paymentsis EE-only today; acceptable because the QBO integration is EE-gated, but the sharedrecordExternalPaymentservice lives in EE.- Pre-existing local test failures unrelated to this work (verified via
git stashon clean tree):xeroAdapter.spec.ts(needs live test DB; local.env.localtestcreds fail),pageTitles.metadata.test.tsT006/T007/T024/T026,XeroIntegrationSettings.contract.test.tsx3/6. - Run package tests from
server/:npx vitest run ../packages/...(rootvitest.config.tsinclude-globs don't match package paths).
Commands
- Targeted suites:
cd server && npx vitest run src/test/unit/api/qboOAuthRoutes.test.ts - Typecheck a package:
cd packages/<pkg> && npx tsc --noEmit -p tsconfig.json - Pseudo-locales after en changes:
node scripts/generate-pseudo-locales.cjs
Implementation notes (slice 1 built 2026-06-11)
- Scheduling deviation (F011): instead of connect/disconnect registration,
accounting-sync-cycleis scheduled for EVERY tenant at startup (initializeScheduledJobs) like the platform's other per-tenant jobs, with a cheap no-op guard for CE/unconnected/auto-sync-off tenants. Cron*/15 * * * *goes throughgetJobRunner()(PgBossJobRunner uses realboss.schedule()per-schedule queues; Temporal in EE) — NOT the legacyinitializeScheduler()JobScheduler, which coerces intervals to 24h. - The auto-sync toggle is the engine's master switch: cycles skip when off;
force(Sync Now) bypasses. First-run cursor = now − 5min (never imports history; the slice-3 wizard is the deliberate path for that). - One job tick per tenant enumerates realms and runs sequential per-realm cycles (multi-realm scheduling needs no extra registration in slice 4).
RefundReceiptadded to the CDC entity set + change-entity union (stats-only).- Health panel =
QboSyncHealthPanelin packages/billing, injected intoQboIntegrationSettingsvia asyncHealthSlotprop threaded fromserver/src/components/settings/SettingsPage.tsx— avoids the billing↔integrations package cycle (nx cycles broke appliance builds before). recordExternalPaymenttransaction metadata is a superset of the old Stripe metadata (addsreference_number); PaymentService webhook path now delegates to it (validation + PAYMENT_RECORDED/APPLIED publication unchanged).- Notifications:
system-announcementinternal template; recipients = active internal users with role_name 'admin'. Token thresholds remembered in tenant_settings.settings.accountingSync.tokenNotices[realm]. - Sync Now runs the cycle inline in the server action (no job round-trip).
- T032 (DB-backed integration cycle test) + T033 (migration round-trip) deferred: local test DB unavailable (pre-existing). Stripe integration tests are DB-skipped locally; T016 verified via typecheck + unchanged validation semantics. Live-smoke T034-T041 pending an Intuit sandbox session.