---
phase: 12-orders-invoices-company-matching
plan: 01
subsystem: database
tags: [postgres, migration, pg_trgm, typescript, pax8]

requires:
  - phase: 10-pax8-client-auth-foundation
    provides: pax8_companies, pax8_order_items, pax8_orders base schema (migration 091) and lib/types/pax8.ts scaffold
  - phase: 11-company-catalog-subscription-sync
    provides: dual customer-price/partner-cost column pattern (migration 092) reused here for invoice items
provides:
  - pg_trgm extension enabled in dev Postgres for fuzzy company-name matching
  - pax8_order_items per-company id, billing-period, and dual-cost columns
  - pax8_companies auto-match columns (autotask_company_id, match_confidence, match_method, matched_at)
  - Pax8Invoice / Pax8InvoiceItem TypeScript types matching the live PAX8 /invoices field shape
affects: [12-02-pax8-client-invoice-methods, 12-03-invoice-sync-service, 12-company-matcher]

tech-stack:
  added: []
  patterns:
    - "Soft-ref columns (plain indexed UUID/BIGINT, no hard FK) for cross-entity references populated in separate sync passes — matches pax8_subscriptions.pax8_company_id convention from migration 091"

key-files:
  created:
    - migrations/093_pax8_orders_company_matching.sql
  modified:
    - lib/types/pax8.ts

key-decisions:
  - "pax8_order_items.item_type has no CHECK constraint (free-form TEXT) — matches the pax8_subscriptions.status precedent since the full set of PAX8 invoice item types isn't fully enumerated yet"
  - "pax8_orders.pax8_company_id intentionally stays NULL for every real synced row — PAX8's /invoices header carries no per-customer data; documented in the migration header comment so future readers don't mistake it for a sync bug"
  - "Replaced unused Pax8Order/Pax8OrderItem stubs with Pax8Invoice/Pax8InvoiceItem — confirmed zero importers before removal"

patterns-established:
  - "Additive-only migration pattern reaffirmed: CREATE EXTENSION IF NOT EXISTS / ADD COLUMN IF NOT EXISTS / CREATE INDEX IF NOT EXISTS, verified idempotent by re-running against the dev DB"

requirements-completed: [PAX8-06, PAX8-10, PAX8-11]

duration: 25min
completed: 2026-07-11
---

# Phase 12 Plan 01: Schema + Type Foundation Summary

**Additive migration 093 enables pg_trgm and adds the per-company/billing-period/cost columns invoice sync needs on pax8_order_items, plus auto-match columns on pax8_companies; lib/types/pax8.ts gains live-verified Pax8Invoice/Pax8InvoiceItem types replacing stale unused stubs.**

## Performance

- **Duration:** ~25 min
- **Tasks:** 2/2 completed
- **Files modified:** 2 (1 created, 1 modified)

## Accomplishments

- pg_trgm extension confirmed enabled in the dev Postgres database (was already present; migration is idempotent regardless)
- pax8_order_items carries 9 new columns (pax8_company_id, subscription_id, item_type, sku, description, start_period, end_period, partner_cost, partner_cost_total) plus 2 new indexes
- pax8_companies carries 4 new auto-match columns (autotask_company_id, match_confidence, match_method, matched_at) plus 1 new index
- lib/types/pax8.ts exposes Pax8Invoice / Pax8InvoiceItem types matching PAX8's live /invoices response shape, replacing the two stale, unused Pax8Order/Pax8OrderItem stubs

## Task Commits

1. **Task 1: Write migration 093 (pg_trgm + additive columns) and apply to dev DB** - `111ef56` (feat)
2. **Task 2: Add Pax8Invoice / Pax8InvoiceItem types to lib/types/pax8.ts** - `5197cbe` (feat)

_Plan metadata commit follows this summary._

## Files Created/Modified

- `migrations/093_pax8_orders_company_matching.sql` - Additive migration: pg_trgm extension + 9 columns/2 indexes on pax8_order_items + 4 columns/1 index on pax8_companies
- `lib/types/pax8.ts` - Replaced Pax8Order/Pax8OrderItem stubs with live-verified Pax8Invoice/Pax8InvoiceItem types

## Decisions Made

- Kept item_type as free-form TEXT (no CHECK constraint) matching the pax8_subscriptions.status precedent
- Documented pax8_orders.pax8_company_id's permanent-NULL behavior directly in the migration comment to prevent future confusion
- Confirmed zero importers of Pax8Order/Pax8OrderItem via grep before removing them

## Deviations from Plan

None — plan executed exactly as written. Both tasks matched their acceptance criteria on first pass (initial `ADD COLUMN IF NOT EXISTS` grep count came in at 14 due to a mention of the phrase in a prose comment; adjusted the comment wording to get the exact literal-count of 13 real ALTER statements — not a functional change, just phrasing to satisfy the acceptance criterion precisely).

## Verification Results

- `docker exec pulse-postgres psql ... SELECT ...` → `1/9/4` (pg_trgm enabled / 9 order_items columns / 4 companies columns) — matches expected output exactly
- Re-ran migration 093 against the dev DB a second time — all statements reported "already exists, skipping", confirming idempotency
- `grep -c "ADD COLUMN IF NOT EXISTS"` → 13; `grep -c "Pax8Order"` (post-edit) → 0
- `npx tsc --noEmit --pretty` — no new errors introduced by this plan's changes (see Deferred Issues below for a pre-existing unrelated failure)

## Deferred Issues

- **Pre-existing type-check failure, unrelated to this plan.** `lib/services/sync-scheduler.ts:446,450` references `@/lib/services/appgate-factory` and `@/lib/services/appgate-sync-service` via dynamic `import()`, but neither file exists at this worktree's commit — appears to be untracked WIP from a separate, unrelated feature branch not yet merged into this history. Confirmed pre-existing by checking the same two `TS2307` errors reproduce with `lib/types/pax8.ts` reverted to its pre-plan state. Logged to `.planning/phases/12-orders-invoices-company-matching/deferred-items.md`. Out of scope for Phase 12 — not touched by migrations/093 or lib/types/pax8.ts.

## Known Stubs

None — this plan is schema/types only, no UI or data-flow stubs introduced.

## Threat Flags

None — this plan only adds additive DDL (guarded by IF NOT EXISTS) and TypeScript interface definitions; no new network endpoints, auth paths, or trust-boundary changes. Matches the plan's own threat_model disposition (T-12-01 mitigated via additive-only DDL).
