Hermes 284313f908
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
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

105 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.