Files
jorgecuadros-platform/docs/RENEWAL_NOTICES.md
T
rmancinasandClaude Sonnet 5 f7ae0d5342
Build and Push Images / Build jorgecuadros-web (push) Failing after 59s
Build and Push Images / Build jorgecuadros-api (push) Successful in 1m59s
feat(reports): parameterized renewal-notice report + legacy report reference
Replaces ~40 legacy Access renewal-notice report clones (one per carrier
per coverage tier, e.g. AMPL/RC/LIC RENEW X MES/VENCE ATLAS 13/2013) with
one parameterized aviso-renovacion report driven by real Policy/Vehicle/
coveragesJson data instead of hand-typed label text per clone.

- schema.prisma: add RenewalNotice, replacing the legacy CONTROL <ramo>
  RENEW[2/3] X MES paper log of which notice generation was sent
- reports: new "letter" ReportFormat + aviso-renovacion registry entry +
  LetterLayout renderer in ReportRunner.tsx
- docs/RENEWAL_NOTICES.md + migration/legacy_report_defs/: extracted (via
  Application.SaveAsText, since the VBA project wouldn't load) and
  documented the legacy report/query chain this replaces

Coveragesjson key names and a mark-as-sent mutation are still unverified/
unbuilt — see caveats in docs/RENEWAL_NOTICES.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 23:05:21 -07:00

140 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Insurance Renewal Notices ("Atlas" reports)
Staff refer to this report in the UI as "the Atlas report", but **Atlas
isn't a report — it's a carrier**: `ATLAS, S.A.` is one of the insurance
companies (`COMP` column) SEGUROS brokers policies for, alongside
`QUALITAS, S.A.` and others. The legacy frontend (`SEGUROS 16.mdb`) never
parameterized carrier or coverage tier in its renewal-notice report — it
cloned the entire report + query chain once per carrier per coverage
variant instead. This doc explains that clone pattern and the underlying
workflow so the new platform can replace ~40 cloned Access objects with
one parameterized feature.
## Why this needed extra tooling
`objects.json`/`LEGACY_DATABASES_OBJECTS.md` (see `migration/catalog_objects.py`)
only capture report *names* — DAO's catalog interface doesn't expose a
report's `RecordSource` or control layout, only the full Access object
model does, and that model refused to load here
(`"The Visual Basic for Applications project in the database is corrupt"`,
a common failure mode for old .mdb files opened in a newer Access build).
The workaround: `Application.SaveAsText(acReport, name, path)` exports a
report's complete design as plain text without touching the VBA project.
The raw (binary-blob-stripped) exports for the ATLAS renewal reports are
committed in [`migration/legacy_report_defs/`](../migration/legacy_report_defs/):
- `AMPL_R_RENEW_X_MES_NEW_ATLAS_13.txt` — Auto/Amplia (full coverage)
- `AMPL_RENEW_X_MES_NEW_ATLAS_2013.txt` — Auto/Amplia, alternate batch
- `RC_RENEW_X_MES_NEW_ATLAS_13.txt` — Auto/RC (liability only)
- `RCR_RENEW_X_MES_NEWATLAS_2013.txt` — Auto/RC, renewal-of-renewal variant
- `LIC_RENEW_X_VENCE_ATLAS_2013.txt` — Driver's-license insurance
(`PrtDevMode`/`PrtMip`/`OleData`/`GUID` binary properties — printer
settings and object GUIDs, no business meaning — were stripped so the
files are readable text instead of multi-hundred-KB hex dumps.)
## The report chain
Each report is bound to a query that layers 23 other queries, filtered to
one carrier, with two typed parameters staff fill in every run:
```
Report: AMPL R RENEW X MES NEW ATLAS 13
RecordSource -> Query: AMPL R RENEW CALC ATLAS 13
FROM [AMPL R CALC VIG], [AMPL R MENS] (in-force calc view + installment schedule)
WHERE COMP = "ATLAS, S.A."
AND DatePart("m",[HASTA]) = [TECLEE MES DE VENCIMIENTO (1 A 12)] -- typed param
AND DatePart("yyyy",[HASTA]) = [TECLEE AÑO DE VENCIMIENTO (1999)] -- typed param
```
```
Report: LIC RENEW X VENCE ATLAS 2013
RecordSource -> Query of the SAME NAME (query and report share a name)
FROM [LIC MENS], LIC INNER JOIN DATGRAL ... INNER JOIN [VIGENT CASA] ...
WHERE DatePart("m",[hasta]) = [TECLEE MES DE VENCIMIENTO 1 A 12]
AND DatePart("yyyy",[hasta]) = [TECLE AÑO VENCIMIENTO (1999)]
AND LIC.COMP = "ATLAS, S.A."
```
Staff pick a line of business, type the expiry month + year, and the
report prints one notice per matching policy for that carrier that month.
On screen the report is captioned **"AVISO DE RENOVACION"** (auto lines)
or **"R E N E W A L N O T I C E"** (license-insurance line). Every page
prints the notice **twice** (identical top-half/bottom-half sections) —
one copy to mail, one for the office file.
## The multi-notice (reminder) workflow
Renewal reminders escalate through **three generations**, each its own
report clone, with a matching `CONTROL ...` companion report (a
send/checklist log):
| Generation | Report suffix | Control/log report |
|---|---|---|
| 1st notice | `RENEW` / (bare) | `CONTROL <LOB> RENEW X MES` |
| 2nd notice | `RENEW2` | `CONTROL <LOB> RENEW2 X MES` (or `X MES` sibling) |
| 3rd notice | `RENEW3` | `CONTROL <LOB> RENEW3 X MES` |
This pattern repeats per line of business: `AMPL`/`AMPL R` (auto full
coverage), `RC`/`RC R` (auto liability), `LIC` (driver's license), `RCR`,
`MF`/`MF2`/`MF3` (home/multi-risk), `MCA2`, `ME`, `INCEN` (fire) — none of
it is visible from the table schema alone, only from the report/query
names (see `docs/LEGACY_DATABASES_OBJECTS.md`, "What the Reports actually
reveal").
## What's hardcoded vs. what's real policy data
The extracted designs show the letter body mixes two very different kinds
of content:
1. **Per-policy data**, pulled live from the query: customer id, policy
number, vehicle (make/model/body/engine), expiry date.
2. **Static label text baked into the report design**, re-typed by hand
every time a batch was cloned for a new rate or carrier — e.g. (from
`AMPL_R_RENEW_X_MES_NEW_ATLAS_13.txt`):
- `"COLLISION DEDUCTIBLE $ 500.00 Dls. THEFT DEDUCTIBLE $ 1000.00 Dls. ..."`
- `"New Renewal annual Premium $ 365.25 Dls."`
- `"Total Annual Premium $ 405.25 Dls"`
- the whole CSL/medical-coverage recommendation and rental-car upsell
paragraphs
None of those dollar figures are formulas — they're literal text, which is
*why* there are so many near-duplicate reports: a new coverage tier or
rate meant cloning the whole report and hand-editing the labels, rather
than changing a parameter.
The underlying **data these figures should come from already exists** on
the source tables and is preserved (unmapped-but-captured) in
`Policy.coveragesJson` after migration — confirmed against
`docs/LEGACY_DATABASES.md`'s table appendix:
| Legacy column | Sanitized `coveragesJson` key | Meaning |
|---|---|---|
| `COBERTURA` | `cobertura` | Coverage days/territory tier (30/40/50/365) |
| `CSL LIMITE` | `csl_limite` | Combined single limit (liability) |
| `GASTOS MEDICO` | `gastos_medico` | Medical coverage amount |
| `SERVICIO ADICIONAL` | `servicio_adicional` (LICENCIAS: `servicio_adiconal`, a source typo) | Add-on service flag |
| `PROPIEDADES` | `propiedades` | Property-damage coverage amount |
| `PERSONAS` | `personas` | Per-person liability amount |
(`Policy.netPremium`/`total`/`currency` are already first-class columns —
see `packages/database/prisma/schema.prisma`.)
**Migration implication:** a rebuilt renewal notice should render these
from data (one parameterized template), not from report design text. See
`RenewalNotice` in `schema.prisma` and the `aviso-renovacion` entry in
`apps/api/src/reports/reports.registry.ts` for the first cut at this.
## Caveats
- Only the ATLAS variants were extracted verbatim; the QUALITAS and
"generic" (no-carrier-suffix) clones weren't pulled but are presumed
structurally identical modulo the `COMP` filter and hardcoded figures.
- `coveragesJson` key names above are derived from
`migration/extract.py`'s `sanitize_column_name` (lowercase,
non-alphanumeric → `_`) applied to the *source* column names in
`docs/LEGACY_DATABASES.md`, not verified against a live migrated
database (no staged output was present in this environment). Confirm
against real data before wiring a template to these keys.