portal_publico/.claude/skills/importacao-plano-saude/SKILL.md

20 KiB
Raw Blame History

name description
importacao-plano-saude Guia de manutenção/extensão da etapa de leitura e validação da ferramenta "Importação de Plano de Saúde" do Portal De Paula (portal_api/planos_saude/, tela importacao-plano-saude.html) — extração dos relatórios de operadora (11 parsers já implementados), casamento contra a planilha padrão e regras de negócio (regra de custeio, "Regra empresa", vínculos de nome), independente do sistema contábil de destino. Documenta quais operadoras já estão validadas com dado real, os gaps conhecidos por operadora, e como adicionar uma operadora nova. Direciona pra uma skill específica de sistema contábil (Questor hoje; Contabit no futuro) pra tudo que for leiaute/geração de arquivo final. Usar ao dar manutenção nos parsers, ao investigar uma divergência de valores, ou ao decidir se uma operadora nova pode ser cadastrada com segurança.

Importação de Plano de Saúde: leitura, extração e regras de negócio

0. O que este documento é (e o que não é)

Este SKILL.md documenta a etapa comum a qualquer sistema contábil de destino: ler o relatório de faturamento de uma operadora de plano de saúde/odontológico, extrair beneficiários e valores, e aplicar as regras de negócio (custeio, "Regra empresa", nome divergente) que decidem quanto cada lançamento deve valer — tudo isso antes de qualquer formatação específica de sistema. É o complemento "por quê"/"cuidado com X" da documentação técnica, já 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.

Se a tarefa é sobre Cadastro de Regras, planilha padrão, leiaute ou geração do CSV/ZIP final: pare aqui. Esta skill não cobre isso — é escopo da skill específica do sistema contábil de destino. A ferramenta hoje só gera saída pro Questor: se for esse o caso (ou se não tiver sido dito o contrário), carregar importacao-questor-plano-saude antes de prosseguir. Se o destino for o Contabit (formato alternativo de rubricas 338/200/201), avisar o usuário que essa etapa ainda não foi implementada, nem a skill correspondente existe ainda — não inventar um leiaute Contabit sem confirmação explícita (ver gap 3 na seção 3).

1. Operadoras já parametrizadas hoje (fotografia em 25/08/2026)

A ferramenta atende hoje várias empresas/operadoras reais, nenhuma com tratamento especial no código de extração — toda operadora passa pelo mesmo OperadoraParser/pipeline.processa_importacao genérico. É uma fotografia, não um fato permanente: cresce todo mês, reconsultar ImportacaoPlanoSaude (python manage.py shell) antes de confiar nela pra uma decisão importante.

1.1 Os 11 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
unimed_cascavel_saude (158) nome 0 até 27/08/2026 Nunca foi rodada dentro da ferramenta até então — parser novo, mesma empresa-modelo da Humana (1972). Outra Unimed regional, layout de PDF sem nenhuma sobreposição com unimed_saude (5060). Coparticipação pode vir de duas fontes possíveis pro mesmo mês (tabela embutida no relatório de mensalidade OU extrato separado — "o modelo de arquivo é gerado pela operadora"), nunca somadas: OperadoraParser.finaliza() (hook novo, chamado só depois de ver todos os arquivos da importação) resolve qual usar, preferindo o extrato separado — ver operadoras/unimed_cascavel/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.

Nota sobre o codigo_operadora: hoje esse código vem sempre do cadastro de operadora do Questor (única integração existente — usado tanto pro label de exibição quanto pra filtrar a consulta SQL da planilha padrão, ver skill importacao-questor-plano-saude). Se o Contabit vier a ter um cadastro de operadora próprio e divergente, isso pode precisar de um campo adicional por sistema — a confirmar quando essa frente for aberta, não assumir que o mesmo código serve pros dois.

2. Regras de negócio que decidem o valor de cada lançamento

A ferramenta é genérica multi-empresa/multi-operadora. O pipeline, os parsers por operadora e as regras gerais (nunca aproximar nome automaticamente, valor negativo nunca vai pro lançamento 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:

  • Regra de custeio configurável por empresa+operadora: quem paga o quê (empresa/empregado/regra específica) é decidido uma vez e reaproveitado todo mês, em vez de decidir de novo a cada competência. Hoje isso é feito via "Cadastro de Regras" (RegraCusteioPlanoSaude) — documentado na skill importacao-questor-plano-saude, porque nasce ligado ao cadastro de empresa/operadora do Questor (codigo_empresa/codigo_operadora resolvidos contra o banco de lá).
  • "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. Independente do sistema de destino — a regra decide o valor, não o formato de saída.
  • 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 5) — são gaps de operadora/leiaute de custeio, 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 na planilha padrão: 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 lançamento final até alguém resolver), mas pode não ser o resultado esperado pelo cliente.
  3. Sistema Contabit (formato alternativo de rubricas 338/200/201, usado por processos manuais antigos): fora do escopo da ferramenta hoje, que só sabe estruturar pro Questor (skill importacao-questor-plano-saude). Quando essa frente for aberta, provavelmente vai exigir um LinhaSistema/casamento próprios também — o formato de "planilha padrão" contra o qual tudo casa hoje já é moldado no leiaute do Questor (NOMEFUNC/CPFFUNC/CODIGOOUTEMP...), não é só a geração do arquivo final que muda entre sistemas.

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 sistema de destino (codigo_operadora) — sem isso não dá pra registrar em pipeline.OPERADORAS nem montar o label de exibição. Hoje o destino é sempre o Questor (ver nota na seção 1.1); 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 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:

  1. 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. Cuidado com espaçamento entre palavras: nem sempre extract_text()/extract_text(layout=True) preservam os espaços reais do PDF (já visto na Unimed Cascavel, onde só extract_words(x_tolerance=1) resolveu) — se as linhas saírem com palavras coladas, inspecionar page.chars direto pra confirmar o espaçamento real antes de desenhar qualquer regex.
  2. 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):

  1. 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 (ex.: Unimed Cascavel, onde a coparticipação pode vir embutida OU separada dependendo do mês).
  2. 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:

  1. Implementar OperadoraParser.extrai() em operadoras/<nome>/<arquivo>.py e registrar em pipeline.OPERADORAS com o codigo_operadora do passo 1.
  2. 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").
  3. 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 (planilha padrão hoje é sempre a do Questor — ver skill importacao-questor-plano-saude).

4.2 Regras de negócio que nunca devem ser reinterpretadas

Válidas pra qualquer operadora nova, sem exceção — e independentes do sistema contábil de destino:

  • 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 lançamento 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. 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" (skill importacao-questor-plano-saude), 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.