The office files every customer in a numbered paper folder and reuses the
number once an account closes. That number was the legacy NUMid, which is
also an import key and the portal login, so reusing it meant re-pointing an
identity. It is now customers.folderNumber: a plain label nothing joins on.
- Unique only among non-archived customers, checked in CustomersService
(create, update, restore). Archived customers keep theirs as history.
- GET /customers/folder/next suggests the lowest free number; the new
customer form pre-fills it.
- Backfilled from the utilities NUMid (dev: 1,171 folders, 0 shared); the
importer copies it on load. Insurance-only customers start without one.
- Reports, the printed statement and the customer file show "Carpeta"
instead of NUMid. The list searches by it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the 2026-10-08 meeting.
- Clientes en rojo summed every row a customer ever had, so the opening
balance's history counted twice, cash receipts twice and the 2025
archive on top: 195 customers in the red against 22 real ones. It now
takes the balance scope balances() uses (extracted as balanceScopeSql),
sorts by most recent real movement, and filters by ramo, date, minimum
owed and inactive customers (status = 0, the internal accounts).
- Pagos no efectuados asked "no charge of any kind in N days"; legacy asks
"no row of THAT service's concept in the period". Rewritten to legacy's
question, per customer, defaulting to one billing cycle back. It also
passed ELECTRICITY/PHONE, which are not ServiceKind values, so Luz and
Telefono never returned a row. Sin fondos rows show as their own status.
- Faltantes demanded account, meter AND route of every kind; water and
phone have no route. Now the legacy fields per kind.
- Reporte de efectivo counted every credit BALANCE FORWARD as cash
(958 of 1093 rows on dev).
- Every report carries the legacy NUMid; policy reports add the
insurance file number. The statement header shows it too.
- Printed headers showed placeholder company details: prod sets no
COMPANY_* variables. Defaults are now the office's real locator, plus a
US mailing address line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`r.netPremium` / `r.total` are strings, so an empty-string value leaked
""` into the JSX instead of rendering nothing. Wrap in Boolean() so the
guard is a real conditional.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
- New reports backend (registry, service, controller, outputs, types)
with catalog endpoint + slug/CSV/XLSX/PDF/print outputs.
- /reportes catalog + /reportes/[slug] runner; ReportRunner + ContextReports
components wire pre-filtered links from domain pages.
- Fix: /reportes/[slug] now reads searchParams and forwards initialParams to
ReportRunner so /reportes/edo-cuenta-datos?customerId=... auto-runs
instead of dropping the id and forcing a manual customer search.
- /inicio landing page; root + login redirect to /inicio.
- Company header env vars + logo asset for PDF/print rendering.
- exceljs + pdfkit deps.