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>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
# 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 2–3 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.
|
||||
Reference in New Issue
Block a user