docs(policy-ocr): vigencia/agente/prima are keyed in by hand on the PVL layout
Build and Push Images / Build jorgecuadros-web (push) Successful in 3m4s
Build and Push Images / Build jorgecuadros-api (push) Successful in 3m14s

Confirmed with Luz, who handles GMX policies at the office: the three
fields the especificación does not carry are entered manually. The review
screen already supports it — all three are editable and `postPremium`
enables off the typed premium, so no code change was needed.

The parser's note said "esos datos están en la carátula de la póliza",
which now sends the reviewer looking for the wrong document. It says
"captúrelos a mano" instead, and names the consequence of leaving the
vigencia blank: `Policy.policyTo` is nullable and the renewals window
filters `policyTo: { gte, lte }`, so a policy confirmed without one never
matches and never gets a renewal notice — silently, permanently, with
nothing downstream erroring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 00:20:09 -07:00
co-authored by Claude Opus 5
parent 45be0ad77d
commit 14c4d44acb
3 changed files with 31 additions and 5 deletions
@@ -310,7 +310,13 @@ describe("parsePolicy / GMX especificación (PVL Hogar)", () => {
expect(p.agentName).toBeNull(); expect(p.agentName).toBeNull();
expect(p.netPremium).toBeNull(); expect(p.netPremium).toBeNull();
expect(p.total).toBeNull(); expect(p.total).toBeNull();
expect(p.notes.join(" ")).toMatch(/no trae vigencia, agente ni prima/i); // The note must tell the reviewer to key them in — those three are
// captured by hand on this layout — and must say what silently breaks if
// the vigencia is left empty.
const notes = p.notes.join(" ");
expect(notes).toMatch(/no trae vigencia, agente ni prima/i);
expect(notes).toMatch(/captúrelos a mano/i);
expect(notes).toMatch(/avisos de renovación/i);
}); });
it("takes the currency from the printed limits, not from the M.N. sublimits", () => { it("takes the currency from the printed limits, not from the M.N. sublimits", () => {
@@ -603,9 +603,15 @@ function parseGmxEspecificacion(page: OcrPage): ParsedPolicy {
const { coverages, currency } = parseEspecificacionCoverages(lines, notes); const { coverages, currency } = parseEspecificacionCoverages(lines, notes);
// These are absent by design on this document, not failures to read. Say so // These are absent by design on this document, not failures to read. Say so
// explicitly, or the reviewer reads four empty fields as a broken parse. // explicitly, or the reviewer reads three empty fields as a broken parse.
//
// The renewal consequence is spelled out rather than left implied: a Policy
// confirmed with a null `policyTo` never matches the renewals window query
// (`renewals.service.ts`, `policyTo: { gte, lte }`), so it drops out of the
// renewal notices silently and stays out. Nothing downstream errors.
notes.push( notes.push(
"la especificación PVL no trae vigencia, agente ni prima; esos datos están en la carátula de la póliza", "la especificación PVL no trae vigencia, agente ni prima; captúrelos a mano " +
"(sin vigencia la póliza no entra en los avisos de renovación)",
); );
return { return {
+16 -2
View File
@@ -171,8 +171,22 @@ Three things about the especificación are worth knowing before touching it:
above it (`efectuados.`, `Y CADA PÉRDIDA.`), and without it coverages get above it (`efectuados.`, `Y CADA PÉRDIDA.`), and without it coverages get
named after the last word of the preceding prose. named after the last word of the preceding prose.
- **Vigencia, agente and prima are absent by design**, not unread. The parser - **Vigencia, agente and prima are absent by design**, not unread. The parser
says so in a note, so a reviewer seeing four empty fields does not read it says so in a note, so a reviewer seeing three empty fields does not read it
as a broken parse. The caratula is the document that carries them. as a broken parse.
**These three are keyed in by hand** — confirmed 2026-08-14 with Luz, who
handles GMX policies at the office. The review screen already has editable
inputs for all three, and `postPremium` enables off the *typed* premium, so
a hand-entered prima posts to the ledger exactly like a parsed one. No code
change was needed to support this; it is a process decision, recorded here
because the parser's own note now instructs the reviewer accordingly.
> **A blank vigencia is silently permanent.** `Policy.policyTo` is nullable
> and the renewals window query filters `policyTo: { gte, lte }`
> (`renewals.service.ts`), so a policy confirmed without one **never matches
> and never gets a renewal notice** — no error, no warning, and nothing
> later notices. This is why the parser's note names the consequence instead
> of just listing the missing fields.
An excluded catastrophic risk is recorded as excluded **in the risk label** An excluded catastrophic risk is recorded as excluded **in the risk label**
(`Terremoto o erupción volcánica — Sección Edificio: EXCLUIDO`) with a null (`Terremoto o erupción volcánica — Sección Edificio: EXCLUIDO`) with a null