diff --git a/PLAN.md b/PLAN.md index 46f1551..d72ffde 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,5 +1,11 @@ # Unified Customer / Insurance / Utilities Platform — Migration & Rebuild Plan +> **Looking for what is still outstanding?** → [`docs/BACKLOG.md`](docs/BACKLOG.md). +> This document is the plan and its running status; the backlog collects every +> open item — blocked-on-Jorge decisions, live data defects, unbuilt features +> and deploy blockers — in one list, checked against the code rather than +> against these notes. + ## Context Jorge Cuadros & Assoc. runs two lines of business — property/utility management (`UTILITIES.accdb`) and insurance brokerage (`SEGUROS 16.mdb` + its linked backend `SEGUROS 16_be.mdb`) — out of separate, decades-old MS Access databases, plus a third file (`SCOTHIA.mdb`) that's the office's own Scotiabank checking-account register ("chequera"). The same people are customers of both business lines, but today there's no shared customer record: a person's utility account and their insurance policies live in unrelated systems with independent, inconsistent copies of their name/address/contact info. The bank register is a fourth, disconnected source of truth for the money actually moving through the office's own account. diff --git a/README.md b/README.md index fffb5d1..77826db 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ Internal platform for a Baja California insurance brokerage and property-service firm: a single expedient joining each client's **properties/services**, **insurance policies**, **account statement**, and the firm's **checkbook**. It replaces a legacy PHP/Access app (see `RESUME.md` and `PLAN.md` for the full -history and rebuild rationale). +history and rebuild rationale, and [`docs/BACKLOG.md`](docs/BACKLOG.md) for +everything still outstanding). The UI is Spanish-first; the codebase and this document are in English. diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md new file mode 100644 index 0000000..4bb99e5 --- /dev/null +++ b/docs/BACKLOG.md @@ -0,0 +1,205 @@ +# Backlog — what is pending, missing, and not yet built + +One place for work that is known-outstanding. Compiled 2026-08-02 from +`PLAN.md`, `RESUME.md`, the four specs and the two OCR docs, then **checked +against the code and the dev database** rather than trusted — several items in +those documents had already been closed, and two defects they describe are +still live. + +This file is an index, not a replacement. Each item points at the document that +carries the reasoning. Close an item *there* as well as here, or the two drift. + +**Verified against dev at compile time** (re-run before trusting the numbers): + +``` +policy_types: AUTO, LICENCIAS, MULT +policies NULL policyTypeId: 5 +policies pending liquidación: 226 +customers: 1536 +last tag: v1.0.6 (2026-08-02 02:06 UTC) — 14 commits, 5 migrations behind HEAD +``` + +--- + +## 0. Ship-blocked — read before the next deploy + +**Everything from the notificaciones arc is unreleased.** `v1.0.6` predates it. +Five migrations are waiting: + +``` +20260801120000_ocr_batch_discarded +20260801130000_renewal_email_notifications +20260801200000_mass_email_notifications +20260802120000_renewal_notices_unified_log +20260802140000_app_settings +``` + +Plus three backup-pipeline fixes that have never reached prod (`860d483`, +`567b033`, `898cf48` — the last prod run went green through the whole chain and +died on the final step wanting `deploy/.env.prod`). + +> ### ⚠️ `SES_*` is unset in Gitea, and the renewal sweep is on by default +> +> The variables are wired through both deploy workflows and the app stack, but +> the secrets were never set. The production image runs `NODE_ENV=production`, +> which disables the stdout dev fallback, so every send fails and logs +> `FAILED`. +> +> The pólizas sweep defaults to **enabled, 06:00 America/Tijuana**. Deploying +> current master with SES still blank means a sweep that fails every single +> night, silently except for the log. +> +> **Before cutting `v1.0.7`:** either set the Gitea secrets, or disable the +> pólizas schedule at `/notificaciones` → "Programación de envíos" first. +> See [`MASS_EMAIL_NOTIFICATIONS.md`](MASS_EMAIL_NOTIFICATIONS.md) and +> [`DEPLOY_AND_MIGRATIONS.md`](DEPLOY_AND_MIGRATIONS.md). + +Also outstanding on the deploy path: every pre-existing database still needs +its one-time `prisma migrate resolve --applied 0000_init`. + +--- + +## 1. Blocked on a decision from Jorge + +Nothing here is a build problem. Each one makes the work either impossible or a +guess. + +| # | Question | Blocks | Source | +|---|---|---|---| +| 1.1 | What "garantías" refers to | §2 liquidación's exclusion filter | INSURANCE §2 | +| 1.2 | What "Solicitud Atlas" is — application form or certificate | §3 entirely | INSURANCE §3 | +| 1.3 | Carrier API **direction**: outbound quote/issue (ANA supports today) or inbound portfolio sync (no evidence either carrier offers it) | whether §4 is buildable at all | INSURANCE §4 | +| 1.4 | CFE amount: the rounded barcode figure (`$268`, what is paid at the window) or the exact breakdown total (`$268.88`) | the parser currently takes the barcode | STATEMENT_OCR / RECEIPT §2 | +| 1.5 | The Seguros USD bank's name, currency and details | multi-bank is built; that account does not exist yet | RECEIPT §3 | +| 1.6 | Recycling triggers — exact "1 year inactive" / "cancelled" definitions, and whether recycling ever means true data purge | §4 recycling | RECEIPT §4 | +| 1.7 | Notice body in Spanish or English | `Customer` carries no language preference | INSURANCE §1 | +| 1.8 | How to model `TRASPASOS PAYPAL` — a clearing account, not a customer, carrying −7.03M MXN over 309 movements and therefore topping the adeudo worklist | deliberately not special-cased in code | RESUME §6 | +| 1.9 | The 78 policyholders with no email — skip silently or produce a print worklist | recommendation is the worklist | INSURANCE §1 | + +1.3 also needs the practical half: WSDL + credentials for +`server.anaseguros.com.mx/ananetws/service.asmx`, whether a cartera download +exists for an agent's own book, whether GMX daños has any machine interface at +all, and whether one Grupo Valore credential spans both carriers. All four go +in the same phone call — (55) 5480-4000. + +--- + +## 2. Live data defects — open, and confirmed open today + +### 2.1 `policy_types` is missing `INCENDIO` and `M_EMPR`, and 5 policies are orphaned + +`policyTypeId` is `String?` with a plain relation, so Prisma's default is +`SetNull`. The spec's recommended `onDelete: Restrict` was **never applied**. +Five `m_empr` policies lost their ramo; four of them are pending liquidación +and are invisible to every ramo-filtered query — including the pending report +§2 is supposed to produce. + +Fix alongside the liquidación work (3.1), since it distorts that feature's own +report. Source: INSURANCE "Two defects found while verifying this spec". + +### 2.2 ≤41 MULT second settlements were dropped in migration + +`MULT`/`INCENDIO` carry two settlement slots and `M EMPR` carries four; `Policy` +collapses to one. Spec recommends moving settlement onto +`PolicyPaymentInstallment` rather than adding a second slot. Open sub-question: +whether to backfill the lost rows. + +### 2.3 Three dead tables + +`EmailTemplate`, `EmailCampaign` and `EmailLog` exist in the schema with +**zero references anywhere in `apps/api/src` or `apps/web/src`**. They were +scaffolded for plan step 10's "email campaigns"; notificaciones shipped against +`email_notification_log` instead. Either wire them or drop them — a schema that +carries tables nothing writes teaches the next reader the wrong thing. + +--- + +## 3. Spec'd, not built + +| # | Item | State | Source | +|---|---|---|---| +| 3.1 | **Liquidación batch workflow** | ~70% of the fields already wired end to end. **226 policies pending.** Needs the ramo-parameterized pending report + `POST /policies/liquidate-batch` under a new `policy:liquidate` (MANAGER). Smallest remaining piece of step 12 | INSURANCE §2 | +| 3.2 | **Certificate rendering** | The report half is buildable now off the same `format: "letter"` machinery as `aviso-renovacion`. Portal delivery waits on steps 8/9. Whole section waits on 1.2 | INSURANCE §3 | +| 3.3 | **Carrier API integration** | Blocked on 1.3. ANA's SOAP service is real with a known operation list; GMX publishes nothing machine-readable and writes the larger half of this book | INSURANCE §4 | +| 3.4 | **Customer-number recycling** | Not started. `Customer.customerNumber` **does not exist in the schema**. Backfill needs care: ~140 utilities rows and all insurance-only customers carry synthetic `rownum_N`/`insrow_N` placeholders, not real `NUM id`s. Last unbuilt piece of step 11 | RECEIPT §4 | + +Note 3.3 partly overlaps what [`POLICY_OCR.md`](POLICY_OCR.md) already does — +an OCR path that turns a carrier PDF into a `Policy` row covers some of what +the API was wanted for, and unlike the API it is not waiting on a phone call. + +--- + +## 4. Roadmap steps with no spec + +| # | Item | State | +|---|---|---| +| 4.1 | **Step 8 — VPS provisioning** | Not started. Provider undecided (Hetzner vs DigitalOcean), size, Tailscale + MySQL replica. Pure ops; the design is settled. RESUME calls this *the only genuinely blocking item left on the roadmap* | +| 4.2 | **Step 9 — sync worker** | Not built. Unblocked now that `utility_dbo` and the portal code are on disk, but depends on 4.1. Portal write points to poll: `peticion_gas`, PayPal payments, `notifications_settings`, `verification_codes` | +| 4.3 | **Step 10 — reports / campaigns / admin** | Mostly done by other work. `/reportes` exists; "email campaigns" landed as `/notificaciones` against a different table (see 2.3) | +| 4.4 | **Phase B sync in production** | Verified 32/32 against dev, never run from the `/operaciones` UI (the `OpsService` path) nor against a prod-shaped database | + +--- + +## 5. Gaps in features that already shipped + +Each of these is a known, deliberate stopping point rather than a bug. + +**Notificaciones** — [`MASS_EMAIL_NOTIFICATIONS.md`](MASS_EMAIL_NOTIFICATIONS.md) +- No multi-replica lock on the servicios sweep (pólizas has one via + `scheduled_job_states`). Safe only while the deployment stays single-replica. +- No per-recipient preview of a sent body in the UI. +- No SNS bounce/complaint webhook. `providerMessageId` is captured so one can + be added. +- No `SKIPPED_NO_EMAIL` worklist (see 1.9). + +**Policy OCR** — [`POLICY_OCR.md`](POLICY_OCR.md) +- **GMX only.** The dispatcher is a `[provider, pattern]` table plus a parser + map, so a second carrier is one function and two entries — but no other + layout has been seen, and guessing produces a parser nobody can verify. +- **The `recibo` PDF is unread.** The GMX certificate carries no premium at + all; reading the separate receipt and pairing it to its certificate is what + would let `postPremium` stop being a manual tick. +- **No versioning.** A re-issued policy arrives as a new certificate with the + same number and confirm updates the existing row. Nothing records that this + is the 2027 issue of that policy. + +**Statement OCR** — [`STATEMENT_OCR.md`](STATEMENT_OCR.md) +- **CFE / CESPT / Telnor have no unit suite.** They predate the gas/predial + extension and were verified end to end against the 46-page corpus only. + Close this if those parsers are ever touched. +- No way to re-run a corrected parser over a stored batch, though the source + PDFs are kept precisely so it is possible. +- Handwritten folder numbers are deliberately not an input to matching + (Tesseract read `405` as `205`). + +**Bank** — the concept→ramo classifier is **won't-build**, not pending. +`concepto` is a payee name (0 of 22,354 match a category) and TABLA RAMODOS is +a property-management expense chart, not the business-line split it was assumed +to be. `/banco` intentionally has no category dimension. Recorded here only +because `bank_transactions.categoryId` being null on every row otherwise reads +as unfinished work. + +--- + +## 6. Security / hygiene + +- **The old repo's `dbConnection.php` has a plaintext MySQL password committed + to git history.** Not carried into this platform, but the credential is + already exposed and has not been rotated. Rotate regardless of this repo. +- The pre-migrate backup step sets `NODE_TLS_REJECT_UNAUTHORIZED=0` because + Portainer serves a self-signed certificate. Scoped to that one step; the real + fix is replacing the certificate. + +--- + +## Source documents + +| Document | What it carries | +|---|---| +| [`../PLAN.md`](../PLAN.md) | build sequencing, locked decisions, per-step status | +| [`../RESUME.md`](../RESUME.md) | session history and §6 open items | +| [`INSURANCE_FEATURES_SPEC.md`](INSURANCE_FEATURES_SPEC.md) | §1 renewal emails (built), §2 liquidación, §3 certificate, §4 carrier APIs | +| [`RECEIPT_CAPTURE_SPEC.md`](RECEIPT_CAPTURE_SPEC.md) | §1 Editor (built), §2 OCR (built), §3 multi-bank (built), §4 recycling | +| [`MASS_EMAIL_NOTIFICATIONS.md`](MASS_EMAIL_NOTIFICATIONS.md) | mass email + schedules, as built | +| [`STATEMENT_OCR.md`](STATEMENT_OCR.md) · [`POLICY_OCR.md`](POLICY_OCR.md) | the two OCR intakes, as built | +| [`DEPLOY_AND_MIGRATIONS.md`](DEPLOY_AND_MIGRATIONS.md) | release chain, galactus, known caveats |