# 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.

> **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.
