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
40 KiB
40 KiB
Accounting Export Abstraction Plan
Purpose & Overview
Deliver a reusable accounting export layer that transforms Alga PSA invoices into accounting-ready payloads and adapters for QuickBooks (Online/Desktop) and Xero. The solution must support configurable GL/item/tax mappings, batchable exports with auditability, and integration points for both file-based and API-driven handoffs.
Primary outcomes:
- Canonical “accounting export” schema that captures invoice headers, line items, tax, service-period metadata, and mapping resolutions in tenant scope.
- Mapping management UX so finance teams align PSA services, tax regions, payment terms, and tracking dimensions with accounting identifiers without engineering changes.
- Export engine that validates invoices, resolves mappings, produces adapter-ready DTOs, and records export batches/status for reconciliation.
- QuickBooks adapter(s) (Online API, Desktop IIF/CSV) and Xero adapter (API or CSV) that translate canonical payloads into package-specific formats while reusing credentials and throttling services.
Discovery Highlights (Completed)
- Billing data richness: Current
invoice_items/invoice_item_detailstables—slated to becomeinvoice_charges/invoice_charge_details—already store service, contract, tax, and service-period metadata (server/migrations/20250412214804_add_invoice_item_details_tables.cjs,docs/billing.md). Amounts are persisted in cents (integers) whiletransactions.amountstays decimal, so the export layer must normalize currency precision. - Mappings backbone:
tenant_external_entity_mappingstracks per-tenant relationships between Alga entities and external IDs with optional metadata (server/migrations/20250502173321_create_tenant_external_entity_mappings.cjs). UI scaffolding exists underserver/src/components/integrations/qbo/*, and server actions (server/src/lib/actions/externalMappingActions.ts) expose CRUD, but lookups inside QBO workflows still rely on placeholders. - Event-driven QBO sync (retired): Legacy workflows (
server/src/lib/workflows/qboInvoiceSyncWorkflow.ts,qboCustomerSyncWorkflow.ts) have been removed in favor of the shared accounting export abstraction.QboClientServicenow serves only the adapter. - Client + tax metadata:
clients,client_locations,client_tax_rates, andtax_ratestables hold billing contacts, addresses, tax regions, and default tax codes (server/migrations/20251003000001_company_to_client_migration.cjs). Invoice generation already callsTaxServiceand storestax_rateandtax_regionon each line. - Gaps identified:
- No canonical export tables or status tracking exist today; reconciliations rely on
transactions. - Invoice schema lacks currency code/precision fields despite multi-currency goals noted in
docs/overview.md. - Mapping UIs retrieve QBO catalog data but assume a single realm and do not support fallback mapping by contract line/service category.
- Xero integrations are absent; only QuickBooks Online scaffolding exists.
- No canonical export tables or status tracking exist today; reconciliations rely on
Scope & Deliverables
- Canonical accounting export data model and services covering invoices, line items, taxes, and mapping resolutions.
- Mapping UX enhancements (service category fallbacks, tax code alignment, GL account mapping) leveraging
tenant_external_entity_mappings. - Export orchestration service with batch creation, validation, error reporting, and rerun controls.
- Adapter implementations:
- QuickBooks Online API export (leveraging
QboClientService). - QuickBooks Desktop GL export (IIF or CSV).
- Xero API/CSV export with OAuth connection management.
- QuickBooks Online API export (leveraging
- Operations UI for listing export batches, downloading files, inspecting errors, and marking batches as posted.
- Automated tests (unit/integration) and sandbox verification flows for each adapter.
Out of scope for this iteration: automatic payment imports, two-way sync of journal entries, and generalized ERP integrations beyond QuickBooks/Xero.
Phase 1 – Data Model & Canonical Schema
- Terminology alignment (Charges)
- Rename
invoice_itemstable toinvoice_charges; create compatibility viewinvoice_itemsfor interim references and update ORM/Knex bindings. - Rename supporting detail tables (
invoice_item_details→invoice_charge_details,invoice_item_fixed_details→invoice_charge_fixed_details) and adjust foreign keys. - Update TypeScript interfaces (
IInvoiceItem→IInvoiceCharge), billing engine services, and API serializers to use “charge” terminology while maintaining backward-compatible DTO aliases where external integrations rely on the old name. - Migrate tests, seeds, and documentation to the new naming; publish release notes and migration guidance for self-hosted tenants.
- Rename
- Add export tables
accounting_export_batches(tenant, adapter, target_company/realm, export_type, filters, triggered_by, status timestamps, checksum).accounting_export_lines(batch_id, tenant, invoice_id, invoice_charge_id, canonical amounts, currency, service dates, mapping references, tax breakdown, export payload snapshot).accounting_export_errors(batch_id, line_id nullable, code, message, resolution_status).
- Normalize currency handling
- Introduce
currency_code+exchange_ratecolumns oninvoices(defaulting to tenant currency) and extend billing engine to populate them. - Convert
transactions.amountto an integer column (representing cents) via staged migration: add temp integer column, backfill by multiplying existing decimals, swap, and remove the legacy decimal. - Ensure
invoice_charges.unit_price/total_priceandtransactions.amountshare the same integer-based representation; add helper utilities when interfacing with external APIs that expect decimals.
- Introduce
- Canonical DTOs
- Create TypeScript interfaces (e.g.,
AccountingInvoiceExport,AccountingLineExport) underserver/src/interfaces/accountingExport.interfaces.ts. - Implement a builder (e.g.,
AccountingExportAssembler) that reads invoices, joinsinvoice_charge_details,client_contract_lines,services,tax_rates, and emits canonical DTOs.
- Create TypeScript interfaces (e.g.,
- Audit trails
- Store rendered adapter payload (JSON blob / file path) per line or batch for traceability.
- Add relationship from
transactionstoaccounting_export_batches(nullable FK) for reconciliation reporting.
Schema Overview (ASCII)
+-------------------------------+
| accounting_export_batches |
|-------------------------------|
| batch_id (PK) |
| tenant |
| adapter_type |
| target_realm |
| export_type |
| filters_json |
| status |
| started_at / completed_at |
| checksum |
+---------------+---------------+
|
1 batch : N lines
v
+-----------------------------+-----------------------------+
| accounting_export_lines |
|----------------------------------------------------------|
| line_id (PK) |
| batch_id (FK -> accounting_export_batches.batch_id) |
| tenant |
| invoice_id (FK -> invoices) |
| invoice_charge_id (FK -> invoice_charges) |
| canonical_amount_cents |
| currency_code / exchange_rate |
| service_period_start / service_period_end |
| mapping_resolution_json |
| delivery_status |
| external_document_ref |
+-----------------------------+----------------------------+
|
1 line : N errors
v
+----------------------------------------------------------+
| accounting_export_errors |
|----------------------------------------------------------|
| error_id (PK) |
| batch_id (FK -> accounting_export_batches.batch_id) |
| line_id NULLABLE (FK -> accounting_export_lines.line_id) |
| code |
| message |
| resolution_state |
| created_at / resolved_at |
+----------------------------------------------------------+
^
|
nullable link from transactions ledger
existing tables:
invoices ---------+
|
invoice_charges --+--> accounting_export_lines (line-level joins)
tenant_external_entity_mappings --(
referenced via mapping_resolution_json lookup metadata
transactions -----(
optional FK -> accounting_export_batches for reconciliation
Phase 2 – Mapping & Configuration Enhancements
- Extend mapping schema
- Allow hierarchical keys (service_id, contract_line_id, service_category) with priority ordering; store in
metadataor add dedicated columns. - Introduce mapping types for GL account (
gl_account), revenue class (class/department), and tax code fallback. - Track effective dates / versioning where needed (e.g., mapping changes mid-year).
- Allow hierarchical keys (service_id, contract_line_id, service_category) with priority ordering; store in
- Mapping UI upgrades
- Update QBO mapping tables to support fallback resolution order and surface validation warnings for unmapped entities.
- Add Xero mapping pages (items/accounts, tax rates, tracking categories) reusing the same
externalMappingActions.
- Lookup services
- Replace placeholder lookup actions with shared resolver (
AccountingMappingResolver) that queriestenant_external_entity_mappings, supports fallback rules, and caches results during batch export.
- Replace placeholder lookup actions with shared resolver (
- Validation hooks
- Add pre-export validation step that flags invoices/lines missing required mappings (service, tax code, payment term) and records them in the error table.
- API append-line path now triggers validation immediately to transition batches to
readyorneeds_attention.
Phase 3 – Export Engine & Workflow Integration
- Batch orchestration
- Service API (
createAccountingExportBatch,executeAccountingExportBatch) handling filters (date range, invoice status, tenant, integration target). - Option to schedule recurring exports via Automation Hub or cron jobs; record source trigger.
- Service API (
- API surface
- Expose REST endpoints under
/api/accounting/exportsfor batch CRUD, line/error append, and status updates to support UI and automation integrations. - Added
POST /api/accounting/exports/{batchId}/executeroute (Next controller delegates toAccountingExportService.executeBatch).
- Expose REST endpoints under
- Adapter interface
- New
AccountingExportAdaptercontract underserver/src/lib/adapters/accounting/accountingExportAdapter.tscoverscapabilities,transform,deliver, optionalpostProcessplus unified payload/result types. AccountingAdapterRegistrynow bootstraps QuickBooks Online/Desktop and Xero stubs (server/src/lib/adapters/accounting/*Adapter.ts) so integrations resolve byadapter_type.
- New
- Workflow alignment
AccountingExportService.executeBatchemitsACCOUNTING_EXPORT_COMPLETED/ACCOUNTING_EXPORT_FAILEDvia event bus once delivery succeeds or fails; events added toserver/src/lib/eventBus/events.tsand shared workflow schemas.- Execution API path above allows workflows/Automation Hub to invoke the canonical exporter in place of bespoke scripts (hook-up pending but interface ready).
- Registered workflow actions (
accounting_export.create_batch,accounting_export.execute_batch) so Automation Hub/typed workflows can orchestrate new exporter without legacy QBO scripts.
- Status tracking
- Repository
updateBatchStatusnow preserves timestamps unless explicitly overwritten; service recordsvalidating,ready,delivered,failedtransitions withvalidated_at/delivered_atstamps. - Delivery loop updates
accounting_export_lines.status+external_document_refand keeps batch notes for failure messaging.
- Repository
Phase 4 – QuickBooks Adapter Implementation
- QuickBooks Online
- Build adapter transform/deliver pipeline that assembles invoices from canonical export lines, hydrates service/customer mappings, and calls
QboClientServicefor create/update with mapping persistence. - Map tax codes and payment terms via extended resolver helpers; persist SyncToken +
last_exported_atmetadata (tenant_external_entity_mappings.metadata). - Implement rate limiting, partial failure retries per invoice, and richer status propagation back to batch tables.
- Retire placeholder workflow actions (
lookup_qbo_item_id,create_qbo_invoice) in favor of adapter orchestration; route workflows through export service (actions now emit deprecation failures that instruct callers to useaccounting_export.*). - Update
qboInvoiceSyncWorkflowto seed accounting export batches and callAccountingExportServiceinstead of invoking legacy QBO invoice actions directly.
- Build adapter transform/deliver pipeline that assembles invoices from canonical export lines, hydrates service/customer mappings, and calls
- QuickBooks Desktop GL Export
- Define IIF (or CSV) writer that translates canonical batches into
TRNS/SPLrows with account codes, classes, terms, and due dates. - Store generated file path in
accounting_export_batchesand expose Download action in UI. - Provide mapping for GL account codes (income, AR, tax liabilities) via new mapping types.
- Define IIF (or CSV) writer that translates canonical batches into
Phase 5 – Xero Adapter Implementation
- Connectivity
- Introduce
XeroClientServicescaffold with OAuth placeholders and logging hooks for future API calls. - Implement retrieval of Xero items, accounts, tax rates, and tracking categories for mapping UI (
listAccounts,listItems,listTaxRates,listTrackingCategories).
- Introduce
- Invoice payload
- Group canonical export lines into per-invoice payload drafts and prime connection metadata for future API mapping.
- Handle multi-tax lines (GST/VAT) per Xero requirements and map contact references via resolver lookups (service + tax resolver integration, tracking normalization).
- Error handling
- Normalize Xero API errors into export error records and flag failed lines for targeted retries via batch metadata. (UI trigger for manual retry remains to be implemented.)
Phase 6 – UI & Operational Tooling
- Export dashboard
- Add “Accounting Exports” tab under billing settings showing batches, status, adapter, created by, exported totals, and links to download payload or view errors.
- Invoice detail integration
- Surface export status per invoice (e.g., “Exported to QuickBooks on 2025-11-02”) with link to batch record.
- Allow manual re-export for a single invoice after correcting mappings (creates new batch or appends to existing pending batch).
- Notifications
- Trigger email/task notifications for failed batches or unmapped line items to assigned finance role.
Phase 7 – Testing, QA, and Rollout
- Automated tests
- Unit tests for mapping resolver, canonical assembler, adapter transformations.
- Integration tests using sqlite/pg tenant fixtures to verify batch creation and error paths.
- Sandbox tests hitting QuickBooks Online sandbox and Xero demo company; capture sample payloads for regression fixtures.
- Data migration & backfill
- Backfill currency codes and service-period data for historical invoices before enabling exports.
- Migrate existing QBO mapping data into new hierarchy/metadata structure.
- Documentation & training
- Author admin guide covering mapping maintenance, export workflows, retries, and reconciliation steps.
- Publish release notes and upgrade checklist for tenants (include mapping prerequisites).
- Launch plan
- Enable feature flag for internal tenant; run pilot exports with finance team.
- Roll out QuickBooks Online first, follow with Desktop and Xero after validation.
Dependencies & Integration Points
tenant_external_entity_mappings(extend schema and UI) and associated RLS policies (server/migrations/20250512135501_update_constrains_and_fks.cjs).- Billing engine outputs (
server/src/lib/billing/billingEngine.ts,server/src/lib/services/invoiceService.ts) for canonical data assembly. - Event bus + Automation Hub if exports are scheduled or triggered by workflows (
server/src/lib/eventBus,shared/workflow/init/registerWorkflowActions.ts). - Event bus now exposes
ACCOUNTING_EXPORT_COMPLETED/ACCOUNTING_EXPORT_FAILEDtypes; downstream consumers must update catalogs (shared/workflow/streams/eventBusSchema.ts,shared/workflow/types/eventCatalog.ts). - Secret management (
@alga-psa/coresecret provider) for QuickBooks/Xero credentials. - QuickBooks Online adapter now persists invoice-level mappings in
tenant_external_entity_mappings(alga_entity_type='invoice'); ensure migrations/cleanup scripts account for the new entity type.
Risks & Mitigations
- Mapping drift / missing references: Mitigate with validation gates, batch-level blockers, and proactive UI warnings; consider nightly report of unmapped services.
- Currency inconsistencies: Normalize to cents in canonical DTOs, store currency code on invoices, and add regression tests around rounding.
- Adapter throttling / rate limits: Use per-tenant throttling queues and exponential backoff (reuse existing QBO throttling helpers).
- Workflow overlap: Coordinate with existing QBO workflow owners to deprecate placeholder logic and avoid duplicate pushes.
- Tenant isolation in new tables: Apply RLS and composite keys mirroring invoice tables to preserve tenancy guarantees.
Open Questions
- Do we need to support QuickBooks Desktop first (file-based) or prioritize QuickBooks Online? Outputs differ (IIF vs. API).
- Should payments posted in accounting flow back into PSA (
transactions) as part of this initiative or a follow-up? - How many revenue recognition dimensions (department/class/location) must be supported at launch? Do we need additional PSA metadata to map them?
- What is the minimal viable currency story (single base currency vs. true multi-currency)? Do we require historical exchange rates per invoice?
- Are there regulatory requirements (e.g., VAT digital links) that dictate export format or audit storage beyond current design?
Screen Architecture & User Flows
Accounting Settings ▸ Accounting Integrations (/msp/settings/accounting)
- Layout: Two-column settings shell. Left nav highlights “Accounting”. Primary content area renders accounting integration cards and mapping workspace.
- Adapter Tabs: Tab bar (
id="accounting-integration-tabs") with entries for each connected adapter (QuickBooks Online, QuickBooks Desktop, Xero). Selecting a tab loads adapter-specific mappings panel. - Mappings Panel
- Sections for Service Items, Tax Codes, Payment Terms, and optional GL/Class Mappings rendered as cards with tables (Radix
Tablecomponent). - Each table header includes
Add Mappingbutton (id="add-{entity}-mapping-button") that opensQboMappingFormDialog/XeroMappingFormDialog. - Table rows provide
Edit(id="edit-{entity}-mapping-{rowId}") andRemoveactions in dropdown menus ({entity}-mapping-actions-menu). - Fallback order modal triggered by
Configure Fallbackchip; modal lists overrides (contract line, service, category) with drag handles to reorder and checkbox to enable fallback. - CSV import available via
Import CSVbutton which opens upload dialog; download template link included.
- Sections for Service Items, Tax Codes, Payment Terms, and optional GL/Class Mappings rendered as cards with tables (Radix
- Bulk Operations: Toolbar (
id="mapping-toolbar") with adapter realm selector,Refresh from Adapter,Import CSV, andExport CSV. - Permissions: Non-finance roles see tables in read-only state (buttons disabled, actions hidden) with tooltip “Finance role required”.
Accounting Exports Dashboard (/msp/billing/accounting-exports)
- Filters Bar: Date range picker (
id="export-date-filter"), status multi-select, adapter select, client search.Reset Filterschip clears selections. - Batch Table: Columns for Batch ID, Created At, Adapter, Invoice Count, Amount, Status, Created By. Row click opens side drawer.
- Create Batch CTA:
New Exportbutton triggers modal with filter form (date range, invoice status, adapter target, optional client filter).Preview Invoicesstep shows resolved invoice list before confirming. - Side Drawer (Batch Detail):
- Tabs for Overview (summary, totals, download links) and Line Items (table with invoice, amount, status, message).
Mark as Posted,Cancel Batch,Retry Failed Linesbuttons shown based on status.- Activity log timeline showing state transitions and actor.
Invoice Detail Augmentation (/msp/billing/invoices/[invoiceId])
- Export History Card: Within right-hand sidebar. Lists export events (adapter, status, batch ID link, timestamp, actor).
Re-export Invoicebutton shown when invoice eligible; opens modal referencing batch creation wizard with invoice preselected. - Status Chips: When exported, invoice header shows chip “Exported to {Adapter} on {date}”.
Notifications & Task Inbox
- Toast/Email: Failed batches raise toast with link to batch drawer and send email to finance distribution list.
- Task Inbox Card: Workflow-generated tasks for failures display
Review Accounting Exporttemplate embedded with summary and quick actions (Acknowledge,Open Batch).
Permissions
- Navigation and API routes are always available; access remains gated by finance-centric roles.
- Role matrix:
finance_adminfull access;billing_managercan run exports but not edit mappings; others view-only.
Acceptance Tests
- Mapping Management
MappingCRUD#L1- Log in as
finance_admin; navigate to/msp/settings/accounting. - Select “QuickBooks Online” tab (
accounting-integration-tabs). - In Service Items section, click
Add Mappingbutton; complete dialog (select PSA servicesvc-001, select QBO itemConsulting, save). - Verify new row shows PSA Service Name and QBO Item Name.
- Open row actions (
service-mapping-actions-menu-{rowId}) → Edit; change QBO item toConsulting - Premium; save; verify table updates. - Open row actions → Delete; confirm removal; entry disappears.
- Open Audit Log (existing system) and confirm create/update/delete entries referencing mapping ID.
- Integration coverage:
server/src/test/integration/accounting/mappingCrud.integration.test.tsexercises create/list/update/delete flows via server actions. - Playwright coverage:
ee/server/src/__tests__/integration/mapping-crud.playwright.test.tsnow drives the real/msp/settingsscreen with Playwright-provided mocks to verify mapping CRUD.
- Log in as
MappingFallback#L1- From same screen, click
Configure Fallbackchip in Service Items card. - Reorder fallback list to
Contract Line,Service,Categoryby dragging handles. - Enable category fallback checkbox and select category
Managed Services. - Save; run test export (see
ValidationUnmapped#L1steps) with invoice covering contract line, confirm exported payload uses contracted mapping. - Move category fallback to top; run batch preview again; verify preview now shows category-based mapping.
- Remove category fallback, save; attempt batch creation; ensure validation error highlights unmapped line with instruction to add mapping.
- Integration coverage:
server/src/test/integration/accounting/accountingMappingResolver.integration.test.tsvalidates service vs. category fallback and realm-specific resolution behaviour. - Playwright coverage:
ee/server/src/__tests__/integration/mapping-fallback.playwright.test.tsexercises the UI harness for fallback ordering, category overrides, and validation feedback.
- From same screen, click
XeroMappings#L1- Navigate to Xero tab; click
Refresh from Adapterto pull latest accounts/tax/tracking; ensure spinner resolves without error. - Add Service mapping selecting PSA service and Xero revenue account; ensure
AccountCodecolumn populated. - Click
Import CSV, upload sample mapping file; verify rows added without duplicates. - Mark Xero account inactive externally; click
Refresh from Adapter; verify affected row shows warning badge and validation message.
- Navigate to Xero tab; click
MappingPermissions#L1- Log in as
billing_manager; confirm Add/Edit/Delete buttons enabled. - Log in as
support_agent; navigate to same screen; verify controls disabled and tooltip text “Finance role required”. - Attempt to POST via API as support agent; expect 403 and audit log entry for denied attempt.
- Integration coverage:
server/src/test/integration/accounting/mappingPermissions.integration.test.tsverifies mapping CRUD is permitted for finance users (billing_settingsupdate) and denied for read-only users.
- Log in as
- Export Validation & Execution
ValidationUnmapped#L1- Create invoice with service lacking mapping.
- Go to
/msp/billing/accounting-exports; clickNew Export. - Select adapter, date range covering invoice, proceed to Preview; expect preview table shows red badge “Mapping Required”.
- Attempt to confirm; modal blocks with error banner referencing missing mapping.
- After adding mapping, reopen modal, preview shows green check, confirmation succeeds.
- Integration coverage:
server/src/test/integration/accounting/validationUnmapped.integration.test.tsseeds an invoice with no service mapping, verifiesensureMappingsForBatchrecords the error/status, then confirms the batch flips toreadyonce the mapping exists. - Playwright coverage:
ee/server/src/__tests__/integration/mapping-validation-unmapped.playwright.test.tsexercises the export wizard harness, ensuring unmapped services block confirmation until a mapping is added.
ValidationCurrency#L1- Create invoice in EUR with stored exchange rate on invoice record.
- Run export wizard; on preview confirm displayed home currency totals match converted values.
- After batch delivery, inspect batch detail drawer to verify captured
currency_code,exchange_rate, converted amount.
BatchLifecycle#L1- Create batch via wizard and confirm; batch row shows
pending. - Trigger worker to process; observe status transitions to
validating,ready,delivered. - From drawer, click
Mark as Posted; status updates. - Attempt to re-run same filter range; wizard displays warning “Batch already exists”; prevents duplicate creation.
- Create second batch, cancel from drawer before delivery; status becomes
cancelled; confirm actions disabled thereafter.
- Integration coverage:
server/src/test/integration/accounting/batchLifecycle.integration.test.tsexercises lifecycle transitions, duplicate guard, and cancellation execution blocks. - Playwright coverage:
ee/server/src/__tests__/integration/batch-lifecycle.playwright.test.tsdrives the lifecycle harness (worker simulation, duplicate guard, posting, and cancellation).
- Create batch via wizard and confirm; batch row shows
InvoiceSelection#L1- Use filters: set date range, choose invoice statuses, select specific client.
- Click
Preview Invoicesto ensure only matching invoices appear. - Verify manual invoices, multi-period items, credit memos, zero-dollar lines display with correct metadata.
- On confirm, ensure resulting batch references proper
transaction_idlinks.
- Integration coverage:
server/src/test/integration/accounting/invoiceSelection.integration.test.tsvalidates filtered previews, metadata flags, and transaction linkage on batch creation.
Concurrency#L1- Start batch creation for Tenant A; simultaneously attempt same range for Tenant B; both succeed.
- Attempt to create overlapping batch for Tenant A before first finishes; wizard shows blocking message referencing existing batch.
- Verify same invoice ID cannot enter two
pendingbatches by inspecting batch detail data.
AuditTrail#L1- Open delivered batch drawer; download canonical JSON snapshot via link.
- Confirm
transactionsrow now stores integeramountmatching invoice charge totals (in cents) and containsaccounting_export_batch_idreferencing batch. - Run reporting query to ensure batch ID appears in finance audit view.
- Integration coverage:
server/src/test/integration/accounting/auditTrail.integration.test.tsexercises batch delivery, transaction linkage, and repository CRUD paths (status updates, line/error management).
- Adapters – QuickBooks Online
QBOInvoiceCreate#L1- Ensure tenant connected to QBO via OAuth and mappings configured.
- Run export with new invoices; monitor job logs for create API call payload; confirm includes Line ServiceDate per invoice item.
- After success, open mapping table and confirm metadata column shows QBO invoice ID + SyncToken.
- In QBO sandbox, verify invoice exists with matching totals and tax.
- Inspect payload/assertions to ensure
Line[].SalesItemLineDetail.TaxCodeRefmatches mapped tax code andSalesTermRefreflects mapped payment term. - Check
tenant_external_entity_mappingshasalga_entity_type='invoice'row for exported invoice withsync_tokenmetadata set.
QBOInvoiceUpdate#L1- Modify exported invoice in PSA (e.g., adjust quantity).
- Initiate re-export via Invoice detail
Re-exportbutton. - Confirm system performs update call (sparse, includes SyncToken).
- Validate QBO invoice reflects change and mapping metadata updated with new SyncToken timestamp.
QBOErrorHandling#L1- Configure mock to respond 429; execute export; ensure adapter retries with exponential backoff and records attempt log.
- Force 401 (expire token); verify adapter refreshes token and retries once; if refresh fails, batch flagged failed with reconnect prompt.
- Create invoice with missing mapping to cause validation error; ensure batch line enters
failedwith message referencing missing mapping.
QBOPermissions#L1- Revoke OAuth consent from Intuit portal.
- Trigger export; verify batch stops with status
failed, UI shows “Reconnect QuickBooks” CTA linking to OAuth flow. - After reconnection, rerun export and confirm success.
QBOClassTracking#L1- Add class/location mapping in settings.
- Export invoice; inspect QBO payload includes
ClassRef/DepartmentRef. - Remove mapping; export again; confirm fields omitted.
- Adapters – QuickBooks Desktop
QBDFileGeneration#L1- On batch drawer, click
Download IIF; verify file includes TRNS/SPL rows with invoice numbers. - Import into QuickBooks Desktop sample company; ensure no import errors and totals match.
- Confirm batch record captures checksum and download timestamp.
- On batch drawer, click
QBDAccountMapping#L1- Remove GL account mapping; attempt export; wizard blocks with error referencing missing account.
- Add mapping; export again; ensure generated file uses mapped account codes.
QBDMultiInvoice#L1- Create batch covering >1 invoice; after delivery confirm single artifact produced.
- Download twice; ensure each download logged in batch activity without duplicating batch.
QBDRetry#L1- After correcting mapping, click
Retry Failed Lines; confirm new file generated and appended to activity log. - Verify previous failed lines now marked delivered.
- After correcting mapping, click
- Adapters – Xero
- Spec compliance:
server/src/test/unit/accounting/xeroAdapter.spec.tsasserts invoice payload construction and delivery stubs align with https://developer.xero.com/documentation/accounting/invoices (required fields, line structure, date formatting). - Spec coverage (API client):
server/src/test/unit/accounting/xeroClientService.spec.tsverifies POST/Invoicespayload/response handling and OAuth token refresh headers follow Xero API documentation. XeroConnectivity#L0- Provision demo credentials; call
listAccounts,listItems,listTaxRates, andlistTrackingCategories; verify non-empty payloads cached per tenant. - Force access-token expiry; re-run
listAccounts; confirm refresh token flow executes once and persistence updates secret store. - Inspect structured logs to ensure tenant/connection metadata logged without leaking secrets.
- Unit coverage:
server/src/test/unit/accounting/xeroClientService.spec.tsstubs/Accounts,/Items,/TaxRates,/TrackingCategories, enforces refresh-on-401, and asserts logging/secret persistence behavior.
- Provision demo credentials; call
XeroInvoiceCreate#L1- Connect tenant to Xero; ensure client/service/tax mappings exist.
- Run export; capture payload ensuring
AccountCode,TaxType, and tracking categories align with mapping metadata. - Verify in Xero sandbox invoice posts successfully with correct totals, currency, and reference number.
- Unit coverage:
server/src/test/unit/accounting/xeroAdapter.spec.ts+xeroClientService.spec.tsvalidate payload composition, mapping metadata, and successful API response handling.
XeroMultipleTax#L1- Prepare invoice mixing GST/PST components; export.
- Confirm adapter assembles
taxComponentsmetadata and resulting Xero invoice splits tax detail correctly.
- Unit coverage:
server/src/test/unit/accounting/xeroAdapter.spec.tsensures multi-component tax metadata flows into invoice payload before delivery.
XeroErrorHandling#L1- Disable mapped tax rate in Xero; execute export; expect service to raise
XERO_VALIDATION_ERRORwith per-document detail. - After remapping, rerun export and ensure success path clears prior validation error (batch currently fails fast pending error persistence automation).
- Unit coverage:
server/src/test/unit/accounting/xeroClientService.spec.tsexercises validation error normalization and subsequent success retry.
- Disable mapped tax rate in Xero; execute export; expect service to raise
XeroCreditNote#L1- Create PSA credit memo linked to original invoice; export via Xero adapter.
- Validate Xero credit note references source invoice and issues negative line totals with matching tracking metadata.
- Status: Credit-note export path not yet implemented; add adapter support before finalizing tests.
- Spec compliance:
- User Interface & Operations
ExportDashboard#L1- Navigate to dashboard; apply filters; confirm table updates.
- Use pagination to view additional batches; ensure data persists.
- As finance admin, download payload from drawer; as support agent, verify download button hidden/disabled.
- Integration coverage:
server/src/test/integration/accounting/exportDashboard.integration.test.tsvalidates dashboard filters, batch detail retrieval, and re-export dataset preparation.
InvoiceDetail#L1- Open exported invoice; view Export History card listing batch links.
- Click batch link to open drawer; verify context slides in.
- Use
Re-export Invoicebutton to start guided wizard prefilled with invoice; complete run and confirm new batch appended to history.
- Integration coverage:
server/src/test/integration/accounting/exportDashboard.integration.test.tsbuilds drawer detail data and exercises invoice-selector driven re-export batch creation.
Notifications#L1- Cause batch failure; ensure toast displays within 5 seconds with “View Batch” CTA.
- Check finance inbox for email summary containing batch ID.
- Open Task Inbox, locate “Review Accounting Export” task, mark as resolved; confirm toast disappears and task closed.
FeatureFlag#L1- Sign in as a non-finance role; confirm Accounting Exports navigation is visible but actions are disabled with “Finance role required” tooltips.
- Re-enable; ensure dashboard reappears with prior data intact.
- Security & Isolation
TenantIsolation#L1- Log in as Tenant A user; ensure only Tenant A batches visible.
- Switch to Tenant B; confirm Tenant A data not present.
- Execute API call for foreign batch ID; expect 404/403.
SecretManagement#L1- Rotate OAuth secret via secret provider; execute export; confirm new token used.
- Attempt to read secret via unauthorized user; expect denial with audit entry.
LoggingPII#L1- Enable debug logging; run successful export; inspect logs for absence of tokens.
- Force failure; confirm error logs scrub sensitive fields.
- Regression & Legacy Compatibility
LegacyReexport#L1- Select invoice created prior to feature rollout; add necessary mappings.
- Use invoice
Re-exportbutton; ensure service period defaults to invoice header and export succeeds.
BillingEngineCompat#L1- Export batch; trigger next billing run generating new invoice.
- Verify original invoice still marked exported; new invoice listed as unexported until next batch.
ArrearsAdvanceMix#L1- Invoice with both advance and arrears lines; export.
- In accounting payload, confirm service dates reflect respective periods; downstream system displays two distinct service windows.
- Performance & Resilience
LargeBatch#L1- Seed 500+ invoices; run export; capture processing time (< threshold) and monitor resource metrics.
- Verify adapter paginates API calls to stay within vendor limits.
RetryPolicy#L1- Configure retry count to 2; induce transient failures; ensure system retries twice then fails with aggregated reason message.
- Update policy to 5; confirm new limit applied on next run.
SystemRestart#L1- Launch export; midway, restart worker service.
- Confirm batch resumes automatically and delivered payload contains no duplicates.
- API & Tooling
APIExports#L1- Call
POST /api/accounting/exportsto create a batch and verify response schema matches DTO definitions. - Call
GET /api/accounting/exportswith filters to confirm listings respect status/adapter parameters.
- Call
APIExportDetails#L1- Append lines/errors via
POST /api/accounting/exports/{batchId}/linesand/errors; ensure subsequent batch fetch returns appended data. - Update batch status using
PATCH /api/accounting/exports/{batchId}and verify state transitions recorded.
- Append lines/errors via
ValidationMissingMapping#L1- Create batch, append a line lacking service mapping, and confirm validation marks batch
needs_attentionwith error record. - Add mapping and re-run validation (re-append or trigger) to see batch flip to
ready.
- Create batch, append a line lacking service mapping, and confirm validation marks batch
APIExecute#L1- Call
POST /api/accounting/exports/{batchId}/execute; expect 200 with delivery summary containing delivered line IDs. - Retrieve batch and lines; confirm statuses updated to
deliveredandexternal_document_refpopulated with adapter stub values. - Repeat call on already delivered batch; expect idempotent failure response (status remains
failedordeliveredwithout duplicate lines).
- Call
ExportEvents#L1- Subscribe test consumer to event bus stream.
- Execute export via API; inspect emitted event includes
ACCOUNTING_EXPORT_COMPLETED, correct tenant, adapter type, and delivered line IDs. - Force adapter error (throw in stub) to confirm
ACCOUNTING_EXPORT_FAILEDfires with error payload and batch marked failed.
CLITrigger#L1- Run
scripts/trigger-accounting-export.tsand confirm it creates a placeholder batch/line. - Validate seeded data appears through the API and can be managed alongside UI-driven batches.
- Run
WorkflowActions#L0- Open Automation Hub template builder; search action registry for
accounting_export.create_batchandaccounting_export.execute_batch. - Add each action to a draft workflow and verify parameter prompts align with adapter, dates, and batch id expectations.
- Open Automation Hub template builder; search action registry for