--- name: importacao-questor-plano-saude description: Guia de manutenção/extensão da ferramenta "Importação de Plano de Saúde" do Portal De Paula (portal_api/planos_saude/, tela importacao-plano-saude.html) — atende várias empresas/operadoras reais, com 10 parsers de operadora já implementados. Documenta quais operadoras já estão validadas com dado real (e quais gaps conhecidos existem por operadora), como consultar ao vivo quais empresas têm regra de custeio cadastrada, e como adicionar uma operadora nova. Usar ao dar manutenção nos parsers, ao investigar uma divergência de valores numa importação, ou ao decidir se uma empresa/operadora nova pode ser cadastrada com segurança. --- # Importação de Plano de Saúde: manutenção e estado atual ## 0. O que este documento é (e o que não é) Este SKILL.md documenta **o processo de negócio** por trás da ferramenta. É o complemento "por quê"/"cuidado com X" da documentação técnica, que já está exaustivamente descrita em `CLAUDE.md` (seção "Importação de Plano de Saúde (Utilitários)"). Antes de tocar em qualquer parser ou regra de custeio, ler os dois: `CLAUDE.md` pra arquitetura (models, endpoints, formato de `custeio_por_tipo`, "Regra empresa", "Vínculos de nome salvos"), este arquivo pra contexto de negócio e pra lista de gaps ainda não confirmados como equivalentes ao processo manual que a ferramenta substitui. ## 1. Operadoras e empresas já parametrizadas hoje (fotografia do banco em 25/08/2026) A ferramenta atende hoje várias empresas/operadoras reais, nenhuma com tratamento especial no código — toda empresa passa pelo mesmo pipeline genérico, configurada via "Cadastro de Regras" (seção 1.2). Os números abaixo vieram de consultar o banco de produção direto (`RegraCusteioPlanoSaude`/`ImportacaoPlanoSaude`) na rodada em que este documento foi escrito. **É uma fotografia, não um fato permanente**: cresce todo mês, reconsultar antes de confiar nela pra uma decisão importante (`python manage.py shell`, os dois models citados). ### 1.1 Os 10 parsers de operadora existentes: uso real até agora | Operadora (`pipeline.OPERADORAS`) | Casamento | Importações no banco | Alguma concluída? | |---|---|---|---| | `unimed_saude` (5060, CSV ou 2 PDFs) | nome (mensalidade), CPF (coparticipação em PDF) | 8 | Sim (empresas 1123, 221, mais testes antigos) | | `itamed_saude` (3755) | nome | 11 | Sim (221, 197, 1684, 626) | | `dental_uni_odonto_mensalidade` (Dental Uni) | nome | 2 | **Não**, as 2 existentes estão em "revisão". **Tem 2 layouts de relatório**: um com `[Nº Cartão]` entre colchetes e indentação distinguindo titular/dependente (validado empresa 1084), outro sem colchete (Nº Cartão solto) e mesma indentação para titular/dependente, distinguido pela presença de "Total Fam" (validado empresa 503, "TAROBA CONSTRUCOES LTDA", 27/08/2026) — ver `operadoras/dental_uni/odonto_mensalidade.py` | | `unimed_oeste_pr_saude` (4709) | nome | 3 | Sim, mas de uma execução **anterior** à empresa 1601 hoje cadastrada (a de 1601 está em revisão) | | `bradesco_saude` (1386) | nome | 1 | Sim (empresa 221) | | `bradesco_dental_odonto_mensalidade` (3759) | nome | 1 | Sim (empresa 1684). **Ver ressalva abaixo** | | `unimed_vitoria_saude` (4750) | nome | 1 | Sim (empresa 792) | | `sulamerica_odonto_mensalidade` (4726) | CPF | 1 | Sim (empresa 792) | | `amil_odonto_mensalidade` (898) | CPF | 0 até 26/08/2026 | Nunca foi rodada dentro da ferramenta até então — ver nota abaixo, primeiro teste real achou e corrigiu um bug de parsing | | `humana_saude` (5064) | nome | 0 até 27/08/2026 | Nunca foi rodada dentro da ferramenta até então — parser novo, construído via o checklist da seção 4.1 direto no primeiro teste com o arquivo-modelo (empresa 1972). **Coparticipação nunca casa automaticamente** (decisão do usuário): sem CPF nem matrícula confiável na tabela "DESPESAS COBRADAS" (nome truncado por largura de coluna), cada evento vira direto um item de auditoria — ver `operadoras/humana/saude.py` | **Ressalva sobre a Bradesco Dental (3759):** o `CLAUDE.md` registra que este parser foi escrito só a partir de texto colado numa conversa, nunca confirmado contra o arquivo real. O banco, porém, já tem uma importação **concluída** pra esse operador (empresa 1684), ou seja, alguém rodou um arquivo real depois daquela ressalva ser escrita. "Concluída" só significa que o pipeline processou sem erro e o CSV foi gerado, **não** que os valores foram de fato conferidos linha a linha contra a fatura. Antes de remover a ressalva do `CLAUDE.md`, confirmar com o usuário se essa conferência manual aconteceu. **AMIL: primeiro teste real (26/08/2026) achou um bug de parsing, já corrigido.** O parser nunca tinha sido rodado contra um arquivo de verdade — no primeiro teste em produção (empresa 1751, contrato 2831804000), todo arquivo AMIL dava "Nenhum beneficiário foi encontrado" porque o regex exigia espaço entre a coluna do plano e a coluna "Tp.", mas nesse relatório real as duas vêm coladas sem espaço nenhum. Corrigido (ver `portal_api/planos_saude/CLAUDE.md`, seção "Amil Odonto (898)") e validado rodando `extrai()` de ponta a ponta: 161 beneficiários, R$ 1.630,93, batendo com os totais do próprio relatório. Continua valendo o cuidado geral: essa foi a primeira empresa/arquivo real confirmado, então tratar qualquer resultado da AMIL como "conferir contra a fatura" até mais empresas passarem pela ferramenta. ### 1.2 Empresas com "Cadastro de Regras" salvo (consultar ao vivo, não uma lista fixa aqui) Essa lista é dado puro do banco (`codigo_empresa`/`razao social`/`operadora` de `RegraCusteioPlanoSaude`), sem nenhuma análise em cima — mantê-la fixa aqui só garante que fique desatualizada a cada empresa nova cadastrada pela tela "Cadastro de Regras". Consultar direto quando precisar: ```python RegraCusteioPlanoSaude.objects.order_by("codigo_empresa").values_list("codigo_empresa", "nome") ``` **Único ponto que não é só dado de banco**: a empresa **221** (ROSSONI, PIOTTO & CIA LTDA) tem as 3 operadoras (Bradesco Saúde, Unimed e Itamed) com importação concluída — é a melhor empresa de referência hoje pra testar qualquer mudança no pipeline, exatamente por cobrir três parsers diferentes já validados. ## 2. O que a ferramenta cobre A ferramenta é genérica multi-empresa/multi-operadora — qualquer empresa cadastrada em "Cadastro de Regras" (seção 1.2) passa pelo mesmo pipeline. O pipeline, os parsers por operadora, o formato de custeio configurável e as regras gerais (nunca aproximar nome automaticamente, valor negativo nunca vai pro CSV final, um mesmo beneficiário pode aparecer em várias linhas/rubricas e precisa ser somado) estão descritos em detalhe no `CLAUDE.md`, não repetir aqui. Mecanismos que substituem uma decisão manual repetida por uma configuração reaproveitável: - **"Cadastro de Regras" por empresa+operadora** (`RegraCusteioPlanoSaude`): decidir "quem paga o quê" uma vez, reaproveitar todo mês, em vez de decidir de novo a cada competência. - **"Regra empresa"** (`portal_api/planos_saude/regras_empresa.py`): cobre custeios negociados que não cabem no desenho padrão "por tipo de lançamento × titular/dependente" — calculados por família inteira ou por critério fixo. Dois exemplos já cadastrados: um teto de R$ 661,61/família na Unimed (`unimed_1778_tecnomyl`, prioridade dependente primeiro e titular absorve o residual) e um critério fixo na Ottimizza/SulAmérica (`sulamerica_5775_ottimizza`). Ver `CLAUDE.md` pra lista completa e como cadastrar uma regra nova. - **Vínculos de nome salvos (DE/PARA)** (`VinculoNomeOperadora`): confirmar manualmente uma vez ("Vincular pessoa") que dois nomes divergentes são a mesma pessoa, e o sistema reaplica sozinho nas competências seguintes — nunca aproximação automática, sempre confirmação humana explícita uma vez por divergência. ## 3. Gaps conhecidos do pipeline genérico (por operadora) As regras abaixo foram identificadas comparando o pipeline genérico com o processo manual mais detalhado já visto até hoje (ver seção 6) — são gaps de **operadora/leiaute**, não peculiaridade de uma empresa só: qualquer empresa que negocie condição parecida com AMIL ou Bradesco pode ser afetada. 1. **AMIL, tipo "A" (agregado) vs. "D" (dependente direto):** o parser genérico (`operadoras/amil/odonto_mensalidade.py`) extrai o tipo (`T`/`D`/`A`), mas o resto do pipeline trata `D` e `A` como o mesmo "dependente" pra efeito de custeio (`matcher._regra_para_pessoa`, ver `CLAUDE.md`). Em pelo menos um contrato real já visto, a regra negociada exige que **todo tipo A seja 100% descontado do empregado, independente da regra configurada pra dependente**. Cadastrar uma empresa com beneficiários tipo A na AMIL sem confirmar essa condição arrisca custear um agregado pela empresa por engano. 2. **Bradesco, dependente sem linha no modelo do Questor:** já apareceu um caso em que a regra negociada era acumular o valor desse dependente na linha do **titular** (não é regra geral do leiaute, é específica daquele contrato). O `matcher.py` genérico, na ausência dessa regra, trata esse caso como "sem cadastro", indo pra auditoria — uma saída **segura** (não lança valor errado, só some do CSV até alguém resolver), mas pode não ser o resultado esperado pelo cliente. 3. **Auditoria pós-importação contra o relatório do Questor** (`Plano de Saúde - Lançamentos - Competência MM/AAAA`): não existe essa feature na ferramenta. Ver seção 5 pra reproduzir esse método manualmente, se for pedido. 4. **Sistema Contabit** (formato alternativo de rubricas 338/200/201, usado por processos manuais antigos): fora do escopo da ferramenta, que só gera o leiaute do Questor. Nenhum desses é motivo pra não usar a ferramenta — são pontos que quem for cadastrar uma empresa com essas operadoras/condições precisa confirmar antes (ou verificar que não se aplicam, ex.: se a empresa não tiver nenhum beneficiário tipo A). Até confirmar, mais seguro conferir manualmente o resultado da tela pra essas condições antes de considerar definitivo. ## 4. Como adicionar/ajustar um parser de operadora Ver `CLAUDE.md` (tabela de operadoras registradas, `OperadoraParser.extrai()`, `pipeline.OPERADORAS`) para o mecanismo técnico. ### 4.1 Checklist — se a pessoa só anexar o arquivo-modelo Objetivo: perguntar tudo que falta **antes** de escrever código, pra não precisar ajustar o parser várias vezes por falta de informação de negócio (que não tem como vir do arquivo sozinho). Nem toda pergunta se beneficia do mesmo momento — perguntar o que o arquivo nunca vai responder **antes** de abrir qualquer coisa; deixar a inspeção responder sozinha o que é auto-determinável; e só formular a pergunta de negócio mais específica **depois** de ver a estrutura real (evita uma pergunta genérica demais, tipo "existe regra especial?" em vez de "como tratar a categoria X que apareceu na tabela?"). **1. Perguntar antes de abrir o arquivo** (nunca vem do conteúdo, então inspecionar primeiro só atrasa): 1. Nome comercial da operadora + o código de cadastro dela no Questor (`codigo_operadora`) — sem isso não dá pra registrar em `pipeline.OPERADORAS` nem montar o label de exibição. Confirmado em duas operadoras diferentes (Dental Uni, Humana) que esse código nunca aparece no arquivo em si, então não há razão pra esperar a inspeção pra perguntar. 2. Qual empresa/código de cliente (Questor) esse arquivo representa — usado na trava de conferência que já existe (`ImportacaoPlanoSaudeViewSet.create()`). Mesmo quando o nome da pasta/arquivo já sugere um código (ex.: "503 - Dental Uni", "1972 - Humana"), **confirmar em vez de assumir** — é um palpite vindo do nome do arquivo, não do conteúdo. **2. Extrair e inspecionar** (sempre com o arquivo real — nunca a partir de texto colado numa conversa, ver [[feedback_pdf_parser_precisa_arquivo_real]]) — isto é autodeterminado, não precisa perguntar: 3. Abrir com a lib certa pro formato — `pdfplumber` pra PDF com texto selecionável; se `page.chars`/`extract_text()` vier vazio, é OCR (`docling`), não `pdfplumber` (ver nota em `operadoras/bradesco/saude.py`); `openpyxl` pra `.xlsx`; `csv` com fallback de encoding pra `.csv` acentuado. Rodar de fato contra o arquivo, não confiar em inspeção visual. 4. Olhar o texto/linhas extraídas com `repr()`, não a versão "bonita" — indentação, colchetes, colunas coladas sem espaço e quebra de nome em duas linhas só aparecem assim. A partir do texto real, identificar: **se o arquivo traz CPF de cada beneficiário ou só nome** (decide `chave_casamento` — CPF é sempre preferível quando existe; isso se responde lendo o arquivo, nunca perguntando à pessoa); como titular e dependente se distinguem (rótulo? indentação? um campo "total família" só preenchido num dos dois? uma coluna de tipo já em texto explícito?); formato do valor monetário (vírgula BR ou ponto americano); se algum nome quebra em mais de uma linha física ou é truncado por largura de coluna. **3. Confirmar depois de ver a estrutura real** (a inspeção já deu contexto suficiente pra fazer a pergunta certa, não uma genérica): 5. Quais tipos de lançamento vêm nesse arquivo — a inspeção já mostra quais tabelas existem (só mensalidade? só coparticipação? os dois juntos no mesmo arquivo, como a Humana? ou em arquivos separados por tipo, ver "Múltiplos arquivos de operadora" no `CLAUDE.md`) — a pergunta que falta responder é se essa composição **é sempre assim** todo mês, ou se pode variar. 6. Existe alguma regra de custeio negociada com o cliente além do padrão "empresa/empregado por tipo × titular/dependente" (teto por família, percentual fixo, um tipo sempre 100% descontado)? Usar o que a inspeção revelou pra perguntar de forma específica — ex.: se apareceu uma categoria "Agregado" na totalização (como no arquivo da Humana), perguntar diretamente como ela deve ser tratada, em vez de só "existe regra especial?" genérico. Se houver regra negociada, pedir **um exemplo numérico já calculado à mão** (ex.: "família com mensalidade R$X, empresa cobre R$Y, empregado paga R$Z") — é contra esse exemplo que a implementação é validada no passo 8 abaixo, não só "parece certo". **Escrever e validar:** 7. Implementar `OperadoraParser.extrai()` em `operadoras//.py` e registrar em `pipeline.OPERADORAS` com o `codigo_operadora` do passo 1. 8. Rodar `extrai()` de ponta a ponta contra o arquivo real e comparar total em R$ + contagem de beneficiários com os totais que o próprio relatório já imprime (nunca só "não deu erro" ou "contagem de linhas parece certa"). 9. Testar pela tela (`validar-arquivo`) e, se possível, uma importação completa contra a planilha padrão real da empresa antes de considerar pronto. ### 4.2 Regras de negócio que nunca devem ser reinterpretadas Válidas pra qualquer operadora nova, sem exceção: - Nome divergente nunca é resolvido por aproximação automática (fuzzy match). Sempre confirmação humana explícita, mesmo que pareça óbvio. - Nenhum valor negativo entra no CSV final, vira auditoria, nunca é zerado/truncado silenciosamente. - Um mesmo beneficiário pode gerar várias linhas/rubricas no arquivo da operadora (mensalidade mais retroativo, por exemplo). Sempre somar por indivíduo antes de decidir o valor final, nunca tratar cada linha isoladamente. ## 5. Auditoria pós-importação contra o relatório do Questor (ainda manual) Se for pedido pra conferir se o que foi gerado bateu com o que ficou lançado no Questor, e a pessoa tiver em mãos o PDF `Plano de Saúde - Lançamentos - Competência MM/AAAA` exportado do próprio sistema: 1. Extrair o texto do PDF (`pdfplumber`) e parsear por bloco de funcionário: linhas `Total Titular`/`Total Dependente - ` (já somam mensalidade e coparticipação daquela pessoa) e `Total Operadora - `. A linha `Total Operadora` só aparece **depois** dos totais daquele bloco, não antes (bufferizar e atribuir a operadora só quando essa linha aparecer). 2. Recompor o valor esperado por pessoa somando os CSVs de mensalidade e coparticipação já gerados. 3. Cruzar por **nome exato** (os nomes do PDF vêm do próprio Questor, sem precisar de tabela de equivalência aqui, diferente do cruzamento com o arquivo da operadora). 4. Pessoa com `VALOREMPRESA=0` e `VALOR=0` pode não gerar lançamento nenhum no sistema, não é divergência. 5. Confirmar que ninguém da lista de pendências/auditoria aparece lançado no sistema. 6. A linha `Total Empresa` no fim do PDF é o total geral de todas as operadoras, serve de conferência rápida contra a soma dos CSVs antes de entrar no detalhe pessoa a pessoa. ## 6. Histórico (contexto, não necessário no dia a dia) A ferramenta nasceu documentando o processo manual mensal de uma empresa específica — TECNOMYL (código Questor `1778`, operadoras AMIL/Unimed/Bradesco) —, feito à mão com scripts Python ad-hoc (protótipo em `projects/project/`) seguindo um runbook vivo por competência. Hoje TECNOMYL é só mais uma empresa entre as cadastradas em "Cadastro de Regras" (seção 1.2), sem tratamento especial no código — esse histórico só importa pra quem for: - **Entender a origem das regras genéricas do pipeline** (nome nunca resolvido por aproximação, valor negativo sempre vira auditoria, somar por indivíduo): vieram de decisões validadas com esse cliente no processo manual, documentadas em detalhe no runbook dele. - **Cadastrar TECNOMYL de vez na ferramenta**: ela ainda não tem `RegraCusteioPlanoSaude` cadastrada hoje. Existe um único registro histórico de importação (Unimed, `regra_empresa=unimed_1778_tecnomyl` já setado) parado em "revisão", de antes do "Cadastro de Regras" existir como tela separada. Antes de cadastrar, ver os gaps da seção 3 (a maioria foi identificada a partir do processo manual dela) e o aviso abaixo sobre vínculos de nome. - **Vínculos de nome**: as cerca de 30 equivalências de nome divergente já conhecidas do runbook da TECNOMYL (nome na operadora ≠ nome no Questor) não foram pré-carregadas como `VinculoNomeOperadora` — a primeira competência dela rodada pela tela vai gerar auditoria pra cada uma de novo, até serem confirmadas uma vez cada (ver seção 2). - **Localizar o runbook**: fora deste repositório, só na máquina de quem processa hoje — `Plano de Saúde - TM\MM-AAAA\` (por competência: `Memoria_Importacao_Questor.md`, CSVs gerados, `Faltantes_Questor_MM_AAAA.xlsx`, `Auditoria_Importacao_Questor_MM_AAAA.xlsx`). **Limitação real**: nada neste SKILL.md substitui esse runbook, só resume o que ele documenta de mais estável. Se for necessário que outra pessoa continue sem acesso a essa máquina, os arquivos precisam ser trazidos pra algum lugar compartilhado (git ou outro) — decisão que envolve dados reais de funcionários, não fazer sem o usuário confirmar onde/como.