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
105 lines
6.3 KiB
Markdown
105 lines
6.3 KiB
Markdown
# 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 `IJobRunner` abstraction, 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). `applyCreditToInvoice`
|
||
decrements `invoices.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). `cancelled` status and
|
||
`invoice_cancelled` transaction type exist in the schema but are unused; a
|
||
`voidInvoice` action 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`; no `qbo_` 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.json` marks 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`
|
||
(branch `release/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 registration `server/src/lib/jobs/initializeScheduledJobs.ts`
|
||
- Workflow task inbox: `shared/workflow/persistence/workflowTaskModel.ts:119`;
|
||
unwired `qbo_mapping_error` task type from migration
|
||
`20250511215231_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-watch` every 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_payments` is EE-only today; acceptable because the QBO integration
|
||
is EE-gated, but the shared `recordExternalPayment` service lives in EE.
|
||
- Pre-existing local test failures unrelated to this work (verified via
|
||
`git stash` on clean tree): `xeroAdapter.spec.ts` (needs live test DB; local
|
||
`.env.localtest` creds fail), `pageTitles.metadata.test.ts` T006/T007/T024/T026,
|
||
`XeroIntegrationSettings.contract.test.tsx` 3/6.
|
||
- Run package tests from `server/`: `npx vitest run ../packages/...`
|
||
(root `vitest.config.ts` include-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-cycle` is 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 through `getJobRunner()` (PgBossJobRunner uses real `boss.schedule()`
|
||
per-schedule queues; Temporal in EE) — NOT the legacy `initializeScheduler()`
|
||
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).
|
||
- `RefundReceipt` added to the CDC entity set + change-entity union (stats-only).
|
||
- Health panel = `QboSyncHealthPanel` in packages/billing, injected into
|
||
`QboIntegrationSettings` via a `syncHealthSlot` prop threaded from
|
||
`server/src/components/settings/SettingsPage.tsx` — avoids the
|
||
billing↔integrations package cycle (nx cycles broke appliance builds before).
|
||
- `recordExternalPayment` transaction metadata is a superset of the old Stripe
|
||
metadata (adds `reference_number`); PaymentService webhook path now delegates
|
||
to it (validation + PAYMENT_RECORDED/APPLIED publication unchanged).
|
||
- Notifications: `system-announcement` internal 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.
|