From 74c49e45c4a8aaf747741a43c21c970e82d326c7 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Mon, 28 Sep 2026 08:24:01 -0300 Subject: [PATCH] =?UTF-8?q?Revalida=C3=A7=C3=A3o=20da=20leitura=20da=20ope?= =?UTF-8?q?radora=205060=20-=20Unimed.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- portal_api/planos_saude/CHANGELOG.md | 10 ++++++++ portal_api/planos_saude/CLAUDE.md | 1 + .../planos_saude/operadoras/unimed/saude.py | 23 +++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/portal_api/planos_saude/CHANGELOG.md b/portal_api/planos_saude/CHANGELOG.md index a66b168..07504ea 100644 --- a/portal_api/planos_saude/CHANGELOG.md +++ b/portal_api/planos_saude/CHANGELOG.md @@ -387,3 +387,13 @@ Nada mudou no cálculo: `_aplica_teto_familia` nunca olhou o código da empresa Na sequência o usuário confirmou que a **Amil Odonto (898) também vale pro grupo inteiro**, e a `amil_898_tecnomyl` recebeu a mesma tupla de quatro códigos. Vale notar que incluir um código aqui só DESTRAVA a possibilidade de cadastrar aquela regra pra aquela empresa — não aplica nada sozinho: uma empresa do grupo que não tenha o plano odontológico simplesmente nunca vai ter uma regra de custeio cadastrada pra Amil. **Cuidado ao mexer**: incluir um código a mais nessa tupla aplica silenciosamente o teto de um cliente na folha de outro — só fazer com confirmação de que as condições são idênticas. + + +### Rodada 134 — Unimed Saúde (5060): coparticipação em PDF perdia beneficiários em silêncio + +Na importação da empresa **1879 (MARV Empreendimentos)**, competência 09/2026, a coparticipação extraída somava R$ 1.697,28 contra R$ 2.102,94 impressos no relatório ("Total do Titulo"). Duas causas, as duas no parser (`operadoras/unimed/saude.py`): + +1. A coluna "Mat/Orig", entre o grau de dependência e o CPF, vem preenchida em algumas linhas (ex.: "AMANDA DESTROTITULAR 241 067.678.609-05"). `_PESSOA_COPARTICIPACAO_RE` não aceitava nada entre grau e CPF, então a linha da pessoa não casava. Quando era a primeira pessoa da família, todos os itens dela eram descartados **sem ir pra auditoria** (AMANDA R$ 55,00 e JESSICA R$ 142,48). Corrigido tornando a coluna opcional. +2. Código de serviço "ANE" (anestesia) não reconhecido por `_ITEM_COPARTICIPACAO_RE`: R$ 208,18 faltando na MARIA LUCIENE. Acrescentado. + +Validado contra o arquivo real: 6 beneficiários, R$ 2.102,94, batendo com o "Total do Titulo" e com cada "Total da Familia". A mensalidade da mesma competência não foi afetada (12 beneficiários, R$ 5.438,89). A importação id 97, em revisão, foi criada antes da correção e precisa ser refeita. diff --git a/portal_api/planos_saude/CLAUDE.md b/portal_api/planos_saude/CLAUDE.md index d8d0de5..e8dcca9 100644 --- a/portal_api/planos_saude/CLAUDE.md +++ b/portal_api/planos_saude/CLAUDE.md @@ -166,6 +166,7 @@ Até uma rodada anterior, "Arquivo da operadora" (passo 2 de "Nova Importação" - **Validado contra os dois arquivos reais** (não só texto colado numa conversa — o texto que sai de um PDF colado no chat **não é** o que `pdfplumber.extract_text()` de fato produz, então não serve pra desenhar regex com confiança; só o arquivo real confirma). Bate exatamente com "Total da Familia"/"Total da Sequencia" impresso no próprio relatório (1.372,08 de coparticipação, 6.061,74 de mensalidade) e com o casamento por CPF contra a planilha padrão real da empresa — toda família presente na planilha bateu centavo a centavo; a família ausente da planilha de teste foi corretamente pra auditoria, não ignorada silenciosamente. - **Bug real corrigido (competência 09/2026, empresa Questor 604)**: `_GRAUS_DEPENDENCIA` não previa "COMPANHEIRO"/"COMPANHEIRA" — a coluna "Grau Dep." tem largura fixa de 10 caracteres, então esse grau (11 caracteres) sai truncado no relatório real como "COMPANHEIR". Sem essa entrada, a linha desse dependente não casava com `_PESSOA_COPARTICIPACAO_RE`, e o item de serviço dele (que ainda batia em `_ITEM_COPARTICIPACAO_RE`) era somado por engano no `pessoa_atual` anterior — na prática, no titular da mesma família (primeiro caso real de família com coparticipação em titular **e** dependente ao mesmo tempo; até então só se via titular sozinho). Corrigido acrescentando "COMPANHEIRO"/"COMPANHEIRA"/"COMPANHEIR" (a forma truncada, a que de fato aparece) a `_GRAUS_DEPENDENCIA`; validado rodando `extrai()` de ponta a ponta contra o arquivo real (família R$144,35 → titular R$134,33 + dependente R$10,02, batendo com "Total da Familia" impresso, e total geral do arquivo R$606,61 batendo com a soma dos "Total da Familia" das 3 famílias do documento). A importação já existente no banco (id 88, competência 09/2026) tinha sido corrigida manualmente na tela de Revisão antes deste fix (`ImportacaoPlanoSaudeAlteracao` ids 29/30) — não precisou de correção retroativa, só as importações futuras dependiam deste ajuste no parser. - **Bug real corrigido (empresa Questor 1970, "Rede Brasil de Mídia OOH", competência 08/2026)**: `_ITEM_COPARTICIPACAO_RE` exigia fronteira de palavra (`\b`) dos dois lados do código de "Tipo Serviço" (CON/EXA/HOS/CLI/ODO/MED). Neste arquivo real, esse código vem **colado sem espaço** ao final do nome do Prestador (ex.: "...MARCELO FABRICCON 10101012...", "...LUCIANO GUSTAVEXA 40316572..." — mesmo estilo de coluna colada já visto no "Beneficiario" da nota acima, só que aqui na coluna de tipo de serviço), então nenhuma linha de item deste arquivo casava — resultado final era **zero beneficiários** ("Nenhum beneficiário foi encontrado neste arquivo"), mesmo com a detecção do layout e o casamento de linha de pessoa funcionando normalmente. O mesmo arquivo trouxe de quebra um grau de dependência ("OUTROS DEP") e um código de tipo de serviço ("CIR", cirurgia) não previstos. Corrigido: `_ITEM_COPARTICIPACAO_RE` perdeu a fronteira de palavra à esquerda (mantida só à direita) e ganhou "CIR"; "OUTROS DEP" foi acrescentado a `_GRAUS_DEPENDENCIA` (sem isso, mesmo corrigindo o regex do item, a coparticipação dessa dependente cairia por engano na pessoa anterior do bloco, mesmo bug da nota acima). Validado rodando `extrai()` de ponta a ponta contra o arquivo real: 2 beneficiários (KARLA VANESSA R$247,74 + RAPHAELA SOUZ R$183,28), somando R$431,02, batendo exatamente com "Total da Familia" impresso; o arquivo de mensalidade da mesma competência (regex própria, não afetada) continuou extraindo os mesmos 3 beneficiários de sempre. +- **Bug real corrigido (empresa Questor 1879, MARV Empreendimentos, competência 09/2026)**: a coluna "Mat/Orig" (entre o grau e o CPF) vem preenchida em algumas linhas (ex.: "AMANDA DESTROTITULAR 241 067.678.609-05"), e `_PESSOA_COPARTICIPACAO_RE` não previa isso. A linha da pessoa não casava e, quando era a primeira da família, todos os itens dela eram **descartados em silêncio, sem ir pra auditoria** (AMANDA R$ 55,00 e JESSICA R$ 142,48 sumiram). O mesmo arquivo trouxe o código de serviço "ANE" (anestesia), não reconhecido (R$ 208,18 faltando na MARIA LUCIENE). Corrigido aceitando a coluna opcional e acrescentando "ANE" a `_ITEM_COPARTICIPACAO_RE`; validado contra o arquivo real: 6 beneficiários, R$ 2.102,94, batendo com "Total do Titulo" e com cada "Total da Familia" (antes: 4 beneficiários, R$ 1.697,28). Mensalidade do mesmo mês não foi afetada (R$ 5.438,89, 12 beneficiários, batendo com "Total por Contratante"). A importação id 97 (em revisão) foi criada antes da correção e precisa ser refeita. ## Planilha padrão via Questor (SQL) diff --git a/portal_api/planos_saude/operadoras/unimed/saude.py b/portal_api/planos_saude/operadoras/unimed/saude.py index 8e615a3..58cb6a0 100644 --- a/portal_api/planos_saude/operadoras/unimed/saude.py +++ b/portal_api/planos_saude/operadoras/unimed/saude.py @@ -141,6 +141,19 @@ Particularidades identificadas no PDF de coparticipação analítico (caso 2): arquivo de mensalidade da mesma competência não foi afetado por este bug (usa `_LINHA_MENSALIDADE_RE`, uma regex própria) e continuou extraindo os mesmos 3 beneficiários de antes. + +9. **Bug real corrigido (empresa Questor 1879, MARV Empreendimentos, + competência 09/2026)**: a coluna "Mat/Orig", entre o grau e o CPF, vem + preenchida em algumas linhas (ex.: "AMANDA DESTROTITULAR 241 + 067.678.609-05") — `_PESSOA_COPARTICIPACAO_RE` não previa isso, então a + linha da pessoa não casava e, como era a primeira da família + (`pessoa_atual` = None), todos os itens dela eram descartados em + silêncio, sem nem ir pra auditoria. O mesmo arquivo trouxe o código de + serviço "ANE" (anestesia), que também não era reconhecido. Corrigido + aceitando a coluna opcional e acrescentando "ANE"; validado contra o + arquivo real: 6 beneficiários, R$ 2.102,94, batendo com "Total do + Titulo" e com cada "Total da Familia" impresso (antes: 4 beneficiários, + R$ 1.697,28). """ import csv import re @@ -176,7 +189,11 @@ _LINHA_MENSALIDADE_RE = re.compile( _PESSOA_COPARTICIPACAO_RE = re.compile( r"^\s*(?P\d+\.\d+)(?P.+?)" r"(?P" + "|".join(_GRAUS_DEPENDENCIA) + r")" - r"\s+(?P\d{3}\.\d{3}\.\d{3}-\d{2})" + r"\s+(?:\d+\s+)?(?P\d{3}\.\d{3}\.\d{3}-\d{2})" + # `(?:\d+\s+)?` = coluna "Mat/Orig", opcional: vazia na maioria das + # linhas, mas preenchida em algumas (ex.: "AMANDA DESTROTITULAR 241 + # 067.678.609-05", empresa 1879, competência 09/2026). Sem isso a linha + # da pessoa não casava e todos os itens dela eram descartados em silêncio. # Confirmado contra o arquivo real: `benef`+`nome`+`grau` vêm colados, # SEM espaço nenhum entre eles (ex.: "0975.0167003824292ANDREIA # STORMTITULAR") — só depois do grau é que vem um espaço, antes do CPF. @@ -184,7 +201,9 @@ _PESSOA_COPARTICIPACAO_RE = re.compile( # o casamento deste relatório usa CPF, não nome (ver # `chave_casamento_para_tipo`). ) -_ITEM_COPARTICIPACAO_RE = re.compile(r"(?:EXA|CON|HOS|CLI|ODO|MED|CIR)\b") +_ITEM_COPARTICIPACAO_RE = re.compile(r"(?:EXA|CON|HOS|CLI|ODO|MED|CIR|ANE)\b") +# "ANE" (anestesia) visto pela primeira vez na empresa 1879, competência +# 09/2026 (ex.: "...LARISSA FALCAOANE 31602231..."). # Sem fronteira de palavra à esquerda de propósito: confirmado contra um # segundo arquivo real (cliente Rede Brasil de Mídia OOH, competência # 08/2026) que esse código de "Tipo Serviço" pode vir colado sem nenhum