# 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 RENEW X MES` | | 2nd notice | `RENEW2` | `CONTROL RENEW2 X MES` (or `X MES` sibling) | | 3rd notice | `RENEW3` | `CONTROL 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. > **Built 2026-08-01/02.** The three generations above are now > `RenewalNotice.generation` 1/2/3, mailed by `apps/api/src/renewals/` on an > operator-editable cadence (default 06:00 daily) and driven from the > **Pólizas** tab of `/notificaciones`. The `CONTROL … X MES` companion > reports have no equivalent and need none: every attempt — sent, failed, or > skipped for a missing address — lands in `email_notification_log`. See > [`MASS_EMAIL_NOTIFICATIONS.md`](MASS_EMAIL_NOTIFICATIONS.md) and > [`INSURANCE_FEATURES_SPEC.md`](INSURANCE_FEATURES_SPEC.md) §1. This document > stays a record of the **legacy** report chain, not of what shipped. ## 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.