Insurance module: policy browser (list/search/detail) + renewals view

Plan step 4. Adds the policies API and the Spanish-first /polizas pages on
top of the customer records the customer module already exposes.

API (apps/api/src/policies):
- GET /policies — search over policy number, customer name, agent, vehicle
  license plate, insured-driver name and legacy id; filters for vigencia
  bucket, ramo, aseguradora and liquidation state; five sort orders.
- GET /policies/stats — bucket counts plus premium in force split by
  currency (MXN and USD can't be summed).
- GET /policies/facets — ramos/aseguradoras with counts for the dropdowns.
- GET /policies/:id — full policy plus the owning customer.

Vigencia is derived from policyTo as active/expiring/expired/undated.
"undated" is a real bucket rather than an error case: 528 of the 2378
migrated policies carry no end date at all.

Web:
- /polizas — renewals-first browser; the stat cells double as vigencia
  filters, with a secondary row for ramo, aseguradora and sort order.
- /polizas/[id] — vigencia hero, condiciones y primas, pagos, vehículos,
  asegurados/beneficiarios, siniestros, the verbatim legacy coverage
  columns, and documents.
- Nav gains Clientes | Pólizas with a real active state, and the two
  modules cross-link in both directions.

Also fixes a display bug on the customer detail page: it headlined
policies.total, which is dead data — only 2 of 2378 rows are non-zero
(1585 are literally 0, 791 null), and one of those two is lower than its
own net premium. That rendered "$0.00 Total" on 1585 policies. Premium
headlines and the premium sort now use netPremium (2377/2378 populated);
total is shown only where it is non-zero, as raw source data.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 21:27:38 -07:00
co-authored by Claude Opus 4.8
parent fa9b696752
commit e2aba8bd17
13 changed files with 1957 additions and 16 deletions
+15 -2
View File
@@ -204,8 +204,21 @@ Homebrew. No Access ODBC driver, `node_modules` not installed, staging Parquet n
doc_1/doc_2), but only 3 cells populated across 1520 rows; the *_MENS tables are mail-merge
templates (correctly excluded). The 538MB/882MB source files are mostly Access bloat, not
documents. **Migration steps 1-4 COMPLETE.**
- **NEXT:** the Customer module API/web (Spanish-first) — all relational data + documents are
loaded, so build the unified customer list/search/detail view.
- **Customer module (plan step 3) — DONE**: `apps/api/src/customers/` (list/search/detail/stats)
+ `apps/web` `/clientes` and `/clientes/[id]`, Spanish-first, verified against real data.
- **Insurance module (plan step 4) — DONE**: `apps/api/src/policies/` (`GET /policies` with
search over policy number / customer / agent / plate / driver name, vigencia buckets
active|expiring|expired|undated, ramo + aseguradora + liquidada filters, 5 sorts;
`/policies/stats`, `/policies/facets`, `/policies/:id`) + web `/polizas` (renewals-first
browser, clickable stat cells) and `/polizas/[id]`. Cross-links both ways with the customer
view. **Data finding:** the `policies.total` column is dead — only 2 of 2378 rows are
non-zero (1585 are literally 0, 791 null) and one of those two is *lower* than its own net
premium, so every premium headline and the premium sort use `netPremium` (populated on
2377/2378). This also fixed a live bug on the customer detail page, which was showing
"$0.00 Total" for 1585 policies.
- **NEXT:** plan step 5 — Utilities module (properties / services / trust accounts as a
first-class browser, the way `/polizas` now is for insurance), then step 6, the shared
billing/statements view.
- Full pipeline reproducible in one command: `run_all.py --env <env>` runs customers ->
properties -> policies -> transactions -> bank in order (all idempotent); add `--stage`
to re-extract from the Access files first. Verified end-to-end against dev.