docs: add BACKLOG.md, one list of everything outstanding
Open work was spread across six documents: PLAN's per-step status, RESUME §6, two specs' collected open questions, and the "Not built" sections of the two OCR docs. Nothing tracked the two live data defects except a paragraph inside INSURANCE_FEATURES_SPEC, and nothing at all recorded that master is 14 commits and 5 migrations past the last tag. Compiled by reading those six, then checking each claim against the code and the dev database rather than trusting the prose — which is how the dead-table finding surfaced and how both insurance defects were confirmed still open. Leads with the ship blocker: SES_* is unset in Gitea while the pólizas sweep defaults to enabled at 06:00, so deploying current master gives a nightly sweep that fails every run. Set the secrets or disable the schedule before cutting v1.0.7. Findings not previously written down anywhere: - policy_types still holds only AUTO/LICENCIAS/MULT and 5 policies still have a NULL policyTypeId; policyTypeId is still `String?` with Prisma's default SetNull, so the spec's recommended Restrict was never applied. - EmailTemplate / EmailCampaign / EmailLog have zero references in apps/api/src or apps/web/src. Scaffolded for step 10's "email campaigns"; notificaciones shipped against email_notification_log instead. Either wire them or drop them. - Customer.customerNumber does not exist, so recycling is not merely unbuilt but unstarted at the schema level. Linked from PLAN.md and README so it is findable from either entry point. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
# Unified Customer / Insurance / Utilities Platform — Migration & Rebuild Plan
|
# 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
|
## 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.
|
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.
|
||||||
|
|||||||
@@ -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**,
|
firm: a single expedient joining each client's **properties/services**,
|
||||||
**insurance policies**, **account statement**, and the firm's **checkbook**.
|
**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
|
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.
|
The UI is Spanish-first; the codebase and this document are in English.
|
||||||
|
|
||||||
|
|||||||
+205
@@ -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 |
|
||||||
Reference in New Issue
Block a user