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
475 lines
40 KiB
Markdown
475 lines
40 KiB
Markdown
# 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_details` tables—slated to become `invoice_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) while `transactions.amount` stays decimal, so the export layer must normalize currency precision.
|
||
- **Mappings backbone:** `tenant_external_entity_mappings` tracks per-tenant relationships between Alga entities and external IDs with optional metadata (`server/migrations/20250502173321_create_tenant_external_entity_mappings.cjs`). UI scaffolding exists under `server/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. `QboClientService` now serves only the adapter.
|
||
- **Client + tax metadata:** `clients`, `client_locations`, `client_tax_rates`, and `tax_rates` tables hold billing contacts, addresses, tax regions, and default tax codes (`server/migrations/20251003000001_company_to_client_migration.cjs`). Invoice generation already calls `TaxService` and stores `tax_rate` and `tax_region` on 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.
|
||
|
||
---
|
||
|
||
## 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.
|
||
- 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_items` table to `invoice_charges`; create compatibility view `invoice_items` for 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.
|
||
- [x] **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_rate` columns on `invoices` (defaulting to tenant currency) and extend billing engine to populate them.
|
||
- Convert `transactions.amount` to 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_price` and `transactions.amount` share the same integer-based representation; add helper utilities when interfacing with external APIs that expect decimals.
|
||
- [x] **Canonical DTOs**
|
||
- Create TypeScript interfaces (e.g., `AccountingInvoiceExport`, `AccountingLineExport`) under `server/src/interfaces/accountingExport.interfaces.ts`.
|
||
- Implement a builder (e.g., `AccountingExportAssembler`) that reads invoices, joins `invoice_charge_details`, `client_contract_lines`, `services`, `tax_rates`, and emits canonical DTOs.
|
||
- [ ] **Audit trails**
|
||
- Store rendered adapter payload (JSON blob / file path) per line or batch for traceability.
|
||
- Add relationship from `transactions` to `accounting_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 `metadata` or 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).
|
||
- [ ] **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`.
|
||
- [x] **Lookup services**
|
||
- Replace placeholder lookup actions with shared resolver (`AccountingMappingResolver`) that queries `tenant_external_entity_mappings`, supports fallback rules, and caches results during batch export.
|
||
- [x] **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 `ready` or `needs_attention`.
|
||
|
||
## Phase 3 – Export Engine & Workflow Integration
|
||
- [x] **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.
|
||
- [x] **API surface**
|
||
- Expose REST endpoints under `/api/accounting/exports` for batch CRUD, line/error append, and status updates to support UI and automation integrations.
|
||
- Added `POST /api/accounting/exports/{batchId}/execute` route (Next controller delegates to `AccountingExportService.executeBatch`).
|
||
- [x] **Adapter interface**
|
||
- New `AccountingExportAdapter` contract under `server/src/lib/adapters/accounting/accountingExportAdapter.ts` covers `capabilities`, `transform`, `deliver`, optional `postProcess` plus unified payload/result types.
|
||
- `AccountingAdapterRegistry` now bootstraps QuickBooks Online/Desktop and Xero stubs (`server/src/lib/adapters/accounting/*Adapter.ts`) so integrations resolve by `adapter_type`.
|
||
- [x] **Workflow alignment**
|
||
- `AccountingExportService.executeBatch` emits `ACCOUNTING_EXPORT_COMPLETED` / `ACCOUNTING_EXPORT_FAILED` via event bus once delivery succeeds or fails; events added to `server/src/lib/eventBus/events.ts` and 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.
|
||
- [x] **Status tracking**
|
||
- Repository `updateBatchStatus` now preserves timestamps unless explicitly overwritten; service records `validating`, `ready`, `delivered`, `failed` transitions with `validated_at`/`delivered_at` stamps.
|
||
- Delivery loop updates `accounting_export_lines.status` + `external_document_ref` and keeps batch notes for failure messaging.
|
||
|
||
## Phase 4 – QuickBooks Adapter Implementation
|
||
- [ ] **QuickBooks Online**
|
||
- [x] Build adapter transform/deliver pipeline that assembles invoices from canonical export lines, hydrates service/customer mappings, and calls `QboClientService` for create/update with mapping persistence.
|
||
- [x] Map tax codes and payment terms via extended resolver helpers; persist SyncToken + `last_exported_at` metadata (`tenant_external_entity_mappings.metadata`).
|
||
- [ ] Implement rate limiting, partial failure retries per invoice, and richer status propagation back to batch tables.
|
||
- [x] 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 use `accounting_export.*`).
|
||
- [x] Update `qboInvoiceSyncWorkflow` to seed accounting export batches and call `AccountingExportService` instead of invoking legacy QBO invoice actions directly.
|
||
- [ ] **QuickBooks Desktop GL Export**
|
||
- Define IIF (or CSV) writer that translates canonical batches into `TRNS`/`SPL` rows with account codes, classes, terms, and due dates.
|
||
- Store generated file path in `accounting_export_batches` and expose Download action in UI.
|
||
- Provide mapping for GL account codes (income, AR, tax liabilities) via new mapping types.
|
||
|
||
## Phase 5 – Xero Adapter Implementation
|
||
- [ ] **Connectivity**
|
||
- [x] Introduce `XeroClientService` scaffold with OAuth placeholders and logging hooks for future API calls.
|
||
- [x] Implement retrieval of Xero items, accounts, tax rates, and tracking categories for mapping UI (`listAccounts`, `listItems`, `listTaxRates`, `listTrackingCategories`).
|
||
- [ ] **Invoice payload**
|
||
- [x] Group canonical export lines into per-invoice payload drafts and prime connection metadata for future API mapping.
|
||
- [x] Handle multi-tax lines (GST/VAT) per Xero requirements and map contact references via resolver lookups (service + tax resolver integration, tracking normalization).
|
||
- [ ] **Error handling**
|
||
- [x] 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_FAILED` types; downstream consumers must update catalogs (`shared/workflow/streams/eventBusSchema.ts`, `shared/workflow/types/eventCatalog.ts`).
|
||
- Secret management (`@alga-psa/core` secret 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**
|
||
1. Sections for **Service Items**, **Tax Codes**, **Payment Terms**, and optional **GL/Class Mappings** rendered as cards with tables (Radix `Table` component).
|
||
2. Each table header includes `Add Mapping` button (`id="add-{entity}-mapping-button"`) that opens `QboMappingFormDialog`/`XeroMappingFormDialog`.
|
||
3. Table rows provide `Edit` (`id="edit-{entity}-mapping-{rowId}"`) and `Remove` actions in dropdown menus (`{entity}-mapping-actions-menu`).
|
||
4. Fallback order modal triggered by `Configure Fallback` chip; modal lists overrides (contract line, service, category) with drag handles to reorder and checkbox to enable fallback.
|
||
5. CSV import available via `Import CSV` button which opens upload dialog; download template link included.
|
||
- **Bulk Operations:** Toolbar (`id="mapping-toolbar"`) with adapter realm selector, `Refresh from Adapter`, `Import CSV`, and `Export 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 Filters` chip 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 Export` button triggers modal with filter form (date range, invoice status, adapter target, optional client filter). `Preview Invoices` step 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 Lines` buttons 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 Invoice` button 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 Export` template 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_admin` full access; `billing_manager` can run exports but not edit mappings; others view-only.
|
||
|
||
---
|
||
|
||
## Acceptance Tests
|
||
- **Mapping Management**
|
||
- `MappingCRUD#L1`
|
||
1. Log in as `finance_admin`; navigate to `/msp/settings/accounting`.
|
||
2. Select “QuickBooks Online” tab (`accounting-integration-tabs`).
|
||
3. In Service Items section, click `Add Mapping` button; complete dialog (select PSA service `svc-001`, select QBO item `Consulting`, save).
|
||
4. Verify new row shows PSA Service Name and QBO Item Name.
|
||
5. Open row actions (`service-mapping-actions-menu-{rowId}`) → Edit; change QBO item to `Consulting - Premium`; save; verify table updates.
|
||
6. Open row actions → Delete; confirm removal; entry disappears.
|
||
7. Open Audit Log (existing system) and confirm create/update/delete entries referencing mapping ID.
|
||
- *Integration coverage:* `server/src/test/integration/accounting/mappingCrud.integration.test.ts` exercises create/list/update/delete flows via server actions.
|
||
- *Playwright coverage:* `ee/server/src/__tests__/integration/mapping-crud.playwright.test.ts` now drives the real `/msp/settings` screen with Playwright-provided mocks to verify mapping CRUD.
|
||
- `MappingFallback#L1`
|
||
1. From same screen, click `Configure Fallback` chip in Service Items card.
|
||
2. Reorder fallback list to `Contract Line`, `Service`, `Category` by dragging handles.
|
||
3. Enable category fallback checkbox and select category `Managed Services`.
|
||
4. Save; run test export (see `ValidationUnmapped#L1` steps) with invoice covering contract line, confirm exported payload uses contracted mapping.
|
||
5. Move category fallback to top; run batch preview again; verify preview now shows category-based mapping.
|
||
6. 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.ts` validates service vs. category fallback and realm-specific resolution behaviour.
|
||
- *Playwright coverage:* `ee/server/src/__tests__/integration/mapping-fallback.playwright.test.ts` exercises the UI harness for fallback ordering, category overrides, and validation feedback.
|
||
- `XeroMappings#L1`
|
||
1. Navigate to Xero tab; click `Refresh from Adapter` to pull latest accounts/tax/tracking; ensure spinner resolves without error.
|
||
2. Add Service mapping selecting PSA service and Xero revenue account; ensure `AccountCode` column populated.
|
||
3. Click `Import CSV`, upload sample mapping file; verify rows added without duplicates.
|
||
4. Mark Xero account inactive externally; click `Refresh from Adapter`; verify affected row shows warning badge and validation message.
|
||
- `MappingPermissions#L1`
|
||
1. Log in as `billing_manager`; confirm Add/Edit/Delete buttons enabled.
|
||
2. Log in as `support_agent`; navigate to same screen; verify controls disabled and tooltip text “Finance role required”.
|
||
3. 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.ts` verifies mapping CRUD is permitted for finance users (`billing_settings` update) and denied for read-only users.
|
||
- **Export Validation & Execution**
|
||
- `ValidationUnmapped#L1`
|
||
1. Create invoice with service lacking mapping.
|
||
2. Go to `/msp/billing/accounting-exports`; click `New Export`.
|
||
3. Select adapter, date range covering invoice, proceed to Preview; expect preview table shows red badge “Mapping Required”.
|
||
4. Attempt to confirm; modal blocks with error banner referencing missing mapping.
|
||
5. After adding mapping, reopen modal, preview shows green check, confirmation succeeds.
|
||
- *Integration coverage:* `server/src/test/integration/accounting/validationUnmapped.integration.test.ts` seeds an invoice with no service mapping, verifies `ensureMappingsForBatch` records the error/status, then confirms the batch flips to `ready` once the mapping exists.
|
||
- *Playwright coverage:* `ee/server/src/__tests__/integration/mapping-validation-unmapped.playwright.test.ts` exercises the export wizard harness, ensuring unmapped services block confirmation until a mapping is added.
|
||
- `ValidationCurrency#L1`
|
||
1. Create invoice in EUR with stored exchange rate on invoice record.
|
||
2. Run export wizard; on preview confirm displayed home currency totals match converted values.
|
||
3. After batch delivery, inspect batch detail drawer to verify captured `currency_code`, `exchange_rate`, converted amount.
|
||
- `BatchLifecycle#L1`
|
||
1. Create batch via wizard and confirm; batch row shows `pending`.
|
||
2. Trigger worker to process; observe status transitions to `validating`, `ready`, `delivered`.
|
||
3. From drawer, click `Mark as Posted`; status updates.
|
||
4. Attempt to re-run same filter range; wizard displays warning “Batch already exists”; prevents duplicate creation.
|
||
5. 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.ts` exercises lifecycle transitions, duplicate guard, and cancellation execution blocks.
|
||
- *Playwright coverage:* `ee/server/src/__tests__/integration/batch-lifecycle.playwright.test.ts` drives the lifecycle harness (worker simulation, duplicate guard, posting, and cancellation).
|
||
- `InvoiceSelection#L1`
|
||
1. Use filters: set date range, choose invoice statuses, select specific client.
|
||
2. Click `Preview Invoices` to ensure only matching invoices appear.
|
||
3. Verify manual invoices, multi-period items, credit memos, zero-dollar lines display with correct metadata.
|
||
4. On confirm, ensure resulting batch references proper `transaction_id` links.
|
||
- *Integration coverage:* `server/src/test/integration/accounting/invoiceSelection.integration.test.ts` validates filtered previews, metadata flags, and transaction linkage on batch creation.
|
||
- `Concurrency#L1`
|
||
1. Start batch creation for Tenant A; simultaneously attempt same range for Tenant B; both succeed.
|
||
2. Attempt to create overlapping batch for Tenant A before first finishes; wizard shows blocking message referencing existing batch.
|
||
3. Verify same invoice ID cannot enter two `pending` batches by inspecting batch detail data.
|
||
- `AuditTrail#L1`
|
||
1. Open delivered batch drawer; download canonical JSON snapshot via link.
|
||
2. Confirm `transactions` row now stores integer `amount` matching invoice charge totals (in cents) and contains `accounting_export_batch_id` referencing batch.
|
||
3. Run reporting query to ensure batch ID appears in finance audit view.
|
||
- *Integration coverage:* `server/src/test/integration/accounting/auditTrail.integration.test.ts` exercises batch delivery, transaction linkage, and repository CRUD paths (status updates, line/error management).
|
||
- **Adapters – QuickBooks Online**
|
||
- `QBOInvoiceCreate#L1`
|
||
1. Ensure tenant connected to QBO via OAuth and mappings configured.
|
||
2. Run export with new invoices; monitor job logs for create API call payload; confirm includes Line ServiceDate per invoice item.
|
||
3. After success, open mapping table and confirm metadata column shows QBO invoice ID + SyncToken.
|
||
4. In QBO sandbox, verify invoice exists with matching totals and tax.
|
||
5. Inspect payload/assertions to ensure `Line[].SalesItemLineDetail.TaxCodeRef` matches mapped tax code and `SalesTermRef` reflects mapped payment term.
|
||
6. Check `tenant_external_entity_mappings` has `alga_entity_type='invoice'` row for exported invoice with `sync_token` metadata set.
|
||
- `QBOInvoiceUpdate#L1`
|
||
1. Modify exported invoice in PSA (e.g., adjust quantity).
|
||
2. Initiate re-export via Invoice detail `Re-export` button.
|
||
3. Confirm system performs update call (sparse, includes SyncToken).
|
||
4. Validate QBO invoice reflects change and mapping metadata updated with new SyncToken timestamp.
|
||
- `QBOErrorHandling#L1`
|
||
1. Configure mock to respond 429; execute export; ensure adapter retries with exponential backoff and records attempt log.
|
||
2. Force 401 (expire token); verify adapter refreshes token and retries once; if refresh fails, batch flagged failed with reconnect prompt.
|
||
3. Create invoice with missing mapping to cause validation error; ensure batch line enters `failed` with message referencing missing mapping.
|
||
- `QBOPermissions#L1`
|
||
1. Revoke OAuth consent from Intuit portal.
|
||
2. Trigger export; verify batch stops with status `failed`, UI shows “Reconnect QuickBooks” CTA linking to OAuth flow.
|
||
3. After reconnection, rerun export and confirm success.
|
||
- `QBOClassTracking#L1`
|
||
1. Add class/location mapping in settings.
|
||
2. Export invoice; inspect QBO payload includes `ClassRef`/`DepartmentRef`.
|
||
3. Remove mapping; export again; confirm fields omitted.
|
||
- **Adapters – QuickBooks Desktop**
|
||
- `QBDFileGeneration#L1`
|
||
1. On batch drawer, click `Download IIF`; verify file includes TRNS/SPL rows with invoice numbers.
|
||
2. Import into QuickBooks Desktop sample company; ensure no import errors and totals match.
|
||
3. Confirm batch record captures checksum and download timestamp.
|
||
- `QBDAccountMapping#L1`
|
||
1. Remove GL account mapping; attempt export; wizard blocks with error referencing missing account.
|
||
2. Add mapping; export again; ensure generated file uses mapped account codes.
|
||
- `QBDMultiInvoice#L1`
|
||
1. Create batch covering >1 invoice; after delivery confirm single artifact produced.
|
||
2. Download twice; ensure each download logged in batch activity without duplicating batch.
|
||
- `QBDRetry#L1`
|
||
1. After correcting mapping, click `Retry Failed Lines`; confirm new file generated and appended to activity log.
|
||
2. Verify previous failed lines now marked delivered.
|
||
- **Adapters – Xero**
|
||
- *Spec compliance:* `server/src/test/unit/accounting/xeroAdapter.spec.ts` asserts 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.ts` verifies POST `/Invoices` payload/response handling and OAuth token refresh headers follow Xero API documentation.
|
||
- `XeroConnectivity#L0`
|
||
1. Provision demo credentials; call `listAccounts`, `listItems`, `listTaxRates`, and `listTrackingCategories`; verify non-empty payloads cached per tenant.
|
||
2. Force access-token expiry; re-run `listAccounts`; confirm refresh token flow executes once and persistence updates secret store.
|
||
3. Inspect structured logs to ensure tenant/connection metadata logged without leaking secrets.
|
||
- *Unit coverage:* `server/src/test/unit/accounting/xeroClientService.spec.ts` stubs `/Accounts`, `/Items`, `/TaxRates`, `/TrackingCategories`, enforces refresh-on-401, and asserts logging/secret persistence behavior.
|
||
- `XeroInvoiceCreate#L1`
|
||
1. Connect tenant to Xero; ensure client/service/tax mappings exist.
|
||
2. Run export; capture payload ensuring `AccountCode`, `TaxType`, and tracking categories align with mapping metadata.
|
||
3. 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.ts` validate payload composition, mapping metadata, and successful API response handling.
|
||
- `XeroMultipleTax#L1`
|
||
1. Prepare invoice mixing GST/PST components; export.
|
||
2. Confirm adapter assembles `taxComponents` metadata and resulting Xero invoice splits tax detail correctly.
|
||
- *Unit coverage:* `server/src/test/unit/accounting/xeroAdapter.spec.ts` ensures multi-component tax metadata flows into invoice payload before delivery.
|
||
- `XeroErrorHandling#L1`
|
||
1. Disable mapped tax rate in Xero; execute export; expect service to raise `XERO_VALIDATION_ERROR` with per-document detail.
|
||
2. 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.ts` exercises validation error normalization and subsequent success retry.
|
||
- `XeroCreditNote#L1`
|
||
1. Create PSA credit memo linked to original invoice; export via Xero adapter.
|
||
2. 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.
|
||
- **User Interface & Operations**
|
||
- `ExportDashboard#L1`
|
||
1. Navigate to dashboard; apply filters; confirm table updates.
|
||
2. Use pagination to view additional batches; ensure data persists.
|
||
3. 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.ts` validates dashboard filters, batch detail retrieval, and re-export dataset preparation.
|
||
- `InvoiceDetail#L1`
|
||
1. Open exported invoice; view Export History card listing batch links.
|
||
2. Click batch link to open drawer; verify context slides in.
|
||
3. Use `Re-export Invoice` button 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.ts` builds drawer detail data and exercises invoice-selector driven re-export batch creation.
|
||
- `Notifications#L1`
|
||
1. Cause batch failure; ensure toast displays within 5 seconds with “View Batch” CTA.
|
||
2. Check finance inbox for email summary containing batch ID.
|
||
3. Open Task Inbox, locate “Review Accounting Export” task, mark as resolved; confirm toast disappears and task closed.
|
||
- `FeatureFlag#L1`
|
||
1. Sign in as a non-finance role; confirm Accounting Exports navigation is visible but actions are disabled with “Finance role required” tooltips.
|
||
2. Re-enable; ensure dashboard reappears with prior data intact.
|
||
- **Security & Isolation**
|
||
- `TenantIsolation#L1`
|
||
1. Log in as Tenant A user; ensure only Tenant A batches visible.
|
||
2. Switch to Tenant B; confirm Tenant A data not present.
|
||
3. Execute API call for foreign batch ID; expect 404/403.
|
||
- `SecretManagement#L1`
|
||
1. Rotate OAuth secret via secret provider; execute export; confirm new token used.
|
||
2. Attempt to read secret via unauthorized user; expect denial with audit entry.
|
||
- `LoggingPII#L1`
|
||
1. Enable debug logging; run successful export; inspect logs for absence of tokens.
|
||
2. Force failure; confirm error logs scrub sensitive fields.
|
||
- **Regression & Legacy Compatibility**
|
||
- `LegacyReexport#L1`
|
||
1. Select invoice created prior to feature rollout; add necessary mappings.
|
||
2. Use invoice `Re-export` button; ensure service period defaults to invoice header and export succeeds.
|
||
- `BillingEngineCompat#L1`
|
||
1. Export batch; trigger next billing run generating new invoice.
|
||
2. Verify original invoice still marked exported; new invoice listed as unexported until next batch.
|
||
- `ArrearsAdvanceMix#L1`
|
||
1. Invoice with both advance and arrears lines; export.
|
||
2. In accounting payload, confirm service dates reflect respective periods; downstream system displays two distinct service windows.
|
||
- **Performance & Resilience**
|
||
- `LargeBatch#L1`
|
||
1. Seed 500+ invoices; run export; capture processing time (< threshold) and monitor resource metrics.
|
||
2. Verify adapter paginates API calls to stay within vendor limits.
|
||
- `RetryPolicy#L1`
|
||
1. Configure retry count to 2; induce transient failures; ensure system retries twice then fails with aggregated reason message.
|
||
2. Update policy to 5; confirm new limit applied on next run.
|
||
- `SystemRestart#L1`
|
||
1. Launch export; midway, restart worker service.
|
||
2. Confirm batch resumes automatically and delivered payload contains no duplicates.
|
||
- **API & Tooling**
|
||
- `APIExports#L1`
|
||
1. Call `POST /api/accounting/exports` to create a batch and verify response schema matches DTO definitions.
|
||
2. Call `GET /api/accounting/exports` with filters to confirm listings respect status/adapter parameters.
|
||
- `APIExportDetails#L1`
|
||
1. Append lines/errors via `POST /api/accounting/exports/{batchId}/lines` and `/errors`; ensure subsequent batch fetch returns appended data.
|
||
2. Update batch status using `PATCH /api/accounting/exports/{batchId}` and verify state transitions recorded.
|
||
- `ValidationMissingMapping#L1`
|
||
1. Create batch, append a line lacking service mapping, and confirm validation marks batch `needs_attention` with error record.
|
||
2. Add mapping and re-run validation (re-append or trigger) to see batch flip to `ready`.
|
||
- `APIExecute#L1`
|
||
1. Call `POST /api/accounting/exports/{batchId}/execute`; expect 200 with delivery summary containing delivered line IDs.
|
||
2. Retrieve batch and lines; confirm statuses updated to `delivered` and `external_document_ref` populated with adapter stub values.
|
||
3. Repeat call on already delivered batch; expect idempotent failure response (status remains `failed` or `delivered` without duplicate lines).
|
||
- `ExportEvents#L1`
|
||
1. Subscribe test consumer to event bus stream.
|
||
2. Execute export via API; inspect emitted event includes `ACCOUNTING_EXPORT_COMPLETED`, correct tenant, adapter type, and delivered line IDs.
|
||
3. Force adapter error (throw in stub) to confirm `ACCOUNTING_EXPORT_FAILED` fires with error payload and batch marked failed.
|
||
- `CLITrigger#L1`
|
||
1. Run `scripts/trigger-accounting-export.ts` and confirm it creates a placeholder batch/line.
|
||
2. Validate seeded data appears through the API and can be managed alongside UI-driven batches.
|
||
- `WorkflowActions#L0`
|
||
1. Open Automation Hub template builder; search action registry for `accounting_export.create_batch` and `accounting_export.execute_batch`.
|
||
2. Add each action to a draft workflow and verify parameter prompts align with adapter, dates, and batch id expectations.
|