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
39 lines
2.3 KiB
Markdown
39 lines
2.3 KiB
Markdown
# Scratchpad — Contract Purchase Order Support
|
||
|
||
## Context / Decisions (2026-01-05)
|
||
|
||
- One PO per invoice (single-assignment invoice scope; does not require a single active contract per client).
|
||
- Invoice stores PO number as a snapshot at creation time.
|
||
- PO number appears in:
|
||
- invoice metadata in-app
|
||
- default invoice PDF header (when present)
|
||
- accounting export/sync reference surfaces (QBO + Xero)
|
||
- PO amount (`po_amount`) is advisory “authorized total spend”.
|
||
- Warn, do not block.
|
||
- Consume only for finalized invoices; unconsume when no longer finalized.
|
||
- Overages: warn and allow override.
|
||
- Batch invoicing: if any invoices in the batch could exceed a PO limit, prompt upfront with allow vs skip; don’t prompt if there’s no possibility of overage.
|
||
- Scope includes everything billed on the invoice (contract + non-contract charges); PO context is invoice-level.
|
||
|
||
## Relevant Code Pointers
|
||
|
||
- Contract assignment PO fields: `client_contracts.po_required`, `client_contracts.po_number`, `client_contracts.po_amount`
|
||
- Billing engine attaches `client_contract_id` to charges: `server/src/lib/billing/billingEngine.ts`
|
||
- Invoice generation entry point: `server/src/lib/actions/invoiceGeneration.ts`
|
||
- Invoice persistence: `server/src/lib/services/invoiceService.ts`
|
||
- Accounting exports:
|
||
- QBO API: `server/src/lib/adapters/accounting/quickBooksOnlineAdapter.ts`
|
||
- QBO CSV: `server/src/lib/adapters/accounting/quickBooksCSVAdapter.ts`
|
||
- Xero API: `server/src/lib/adapters/accounting/xeroAdapter.ts`
|
||
- Xero CSV: `server/src/lib/adapters/accounting/xeroCsvAdapter.ts`
|
||
|
||
## Known Gaps / Notes
|
||
|
||
- Current PO-required validation in `generateInvoice` references legacy `client_plan_bundles`; needs alignment to `client_contracts`.
|
||
- Invoice table does not currently have an invoice-level PO field; requires migration.
|
||
- Xero adapter currently uses `reference` to carry invoice number; we likely need a combined format (invoice + PO) without breaking matching.
|
||
- Finalization mapping in UI/actions:
|
||
- `finalizeInvoiceWithKnex` sets `invoices.status = 'sent'` and sets `finalized_at`.
|
||
- `unfinalizeInvoice` clears `finalized_at` and forces `status = 'draft'` when status is not already draft.
|
||
- FinalizedTab treats invoices as “finalized” when `finalized_at` is set OR `status !== 'draft'`.
|