Inclusão operadoras grupo Tecnomyl
This commit is contained in:
parent
cdc02fb311
commit
302bbea163
@ -138,7 +138,8 @@
|
||||
"Bash(C:/Users/Depaula/Documents/Portal/.venv/Scripts/python.exe -c ' *)",
|
||||
"Bash(cp \"C:/Users/Depaula/Documents/Logos/P.I.D. Logo Design Aniversário/Logo intro animation/export-56/Logo - DE PAULA CONTADORES - escrita branca \\(1\\).png\" \"C:/Users/Depaula/Documents/Portal/static/img/pid-aniversario-depaula-wordmark.png\" *)",
|
||||
"Bash(grep -o '`' \"C:/Users/Depaula/Documents/Portal/static/js/login-intro-aniversario.js\")",
|
||||
"Bash(\"./.venv/Scripts/python.exe\" -)"
|
||||
"Bash(\"./.venv/Scripts/python.exe\" -)",
|
||||
"Bash(grep -n \"codigo_empresa\\\\` \\(código da empresa\\\\|unimed_1778_tecnomyl\\\\*\\\\*\\\\|Trava de compatibilidade generalizada\" portal_api/planos_saude/CLAUDE.md)"
|
||||
],
|
||||
"additionalDirectories": [
|
||||
"C:\\Users\\Depaula\\AppData\\Local\\Temp\\claude\\c--Users-Depaula-Documents-Portal\\3ea0ee22-e5fd-4030-98b1-ee2e71c16ce0\\scratchpad\\halloween-design",
|
||||
|
||||
@ -370,3 +370,20 @@ Depois de validar a regra da Unimed já cadastrada pra Tecnomyl (empresa 1778, v
|
||||
3. **`_regra_amil_898_tecnomyl`** (`regras_empresa.py`, registrada como `amil_898_tecnomyl`): diferente da Tecnomyl-Unimed (teto por família), é uma regra **por pessoa**, sem interação entre membros da família — lê `linha.tipo_pessoa` (fallback "D" se em branco) e manda 100% pra `valor_empresa` (T/D) ou 100% pra `valor` (A). `chave_casamento="cpf"`, `tipos_lancamento=("mensalidade",)`. Validado com o pipeline completo (`processa_importacao` ponta a ponta, com uma planilha padrão sintética) além do teste isolado da função — nenhum erro na divisão titular/dependente vs. agregado.
|
||||
|
||||
**Nota operacional pra próxima competência**: alguns dependentes do arquivo real vêm sem CPF (crianças, principalmente) — normalizado pra `"00000000000"`. Como a estratégia "cpf" (`_casa_por_cpf`) não aceita `vinculos_por_nome` (só a estratégia "nome" reaplica automaticamente um vínculo salvo), uma dessas pessoas vinculada manualmente via "Vincular pessoa" precisa ser vinculada de novo toda competência — comportamento pré-existente de qualquer operadora "cpf" (Amil, SulAmérica, MetLife), não introduzido por esta regra. Ainda não cadastrado em `RegraCusteioPlanoSaude` (banco de produção) — cadastro fica a cargo do usuário direto pela tela "Cadastro de Regras", não é feito por script/ORM neste ambiente.
|
||||
|
||||
|
||||
### Rodada 133 — Regra de empresa passa a cobrir um grupo econômico (`codigos_empresa`)
|
||||
|
||||
Ao cadastrar a regra de custeio da empresa **1855 (H2O INNOVATION LTDA)** na Unimed, o usuário levou um erro de validação: *"Esta regra especial foi cadastrada para a empresa código 1778, não para 1855."* Não era bug — era a trava de código de empresa funcionando como projetada. O que não estava previsto no desenho é que **1778 (Tecnomyl) e 1855 fazem parte do mesmo grupo e têm a MESMA condição negociada** (ajuda de custo de até R$ 661,61 por família), confirmado pelo usuário.
|
||||
|
||||
O registro `REGRAS_EMPRESA` assumia **1 regra = 1 empresa** (`codigo_empresa`, string). Passou a assumir 1 regra = N empresas do mesmo grupo: o campo virou **`codigos_empresa`, uma tupla** (`("1778", "1855")` na `unimed_1778_tecnomyl`; as outras duas regras ficaram com um código só, na mesma forma de tupla). A alternativa — duplicar a regra, uma entrada por empresa com o mesmo cálculo — foi descartada: no dia que o teto mudar, seria preciso lembrar de mexer nas duas.
|
||||
|
||||
Os três pontos que conferiam o código foram ajustados pra testar **pertinência na tupla** em vez de igualdade: as duas validações de cadastro (`RegraCusteioPlanoSaudeSerializer.validate()` e `ImportacaoPlanoSaudeCreateSerializer.validate()`, `serializers.py`) e a trava de processamento (`regras_empresa.valida_regra_empresa()`, que exige que a planilha padrão anexada tenha linha de alguma das empresas cobertas). As mensagens de erro passaram a listar os códigos aceitos ("... código 1778 ou 1855, não para X").
|
||||
|
||||
Nada mudou no cálculo: `_aplica_teto_familia` nunca olhou o código da empresa — ele só aparecia na trava. Verificado rodando a validação com planilhas das empresas 1778, 1855 e 9999 (as duas primeiras passam, a terceira continua bloqueada) e reconferindo o teto por família nos três cenários (abaixo do teto, acima do teto, titular sozinho).
|
||||
|
||||
**Ainda na mesma rodada**, o usuário pediu pra cobrir mais duas empresas do grupo: a tupla fechou em `("1778", "1855", "1872", "1927")` — 1778 Tecnomyl Brasil, 1855 H2O Innovation, 1872 GS3 Digital e 1927 YVY Agricultura Digital. Os nomes das duas novas foram conferidos no Questor (consulta só leitura) antes de entrarem no registro, justamente pra pegar dígito trocado: um código errado aqui não dá erro nenhum, só aplica o teto de um cliente na folha de outro. O `label` passou a listar os quatro códigos ("1778/1855/1872/1927 - Unimed (Tecnomyl)") — quem cadastra a regra escolhe pelo código da empresa, então esconder os códigos no label tiraria justamente a conferência que importa no seletor.
|
||||
|
||||
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.
|
||||
|
||||
@ -261,18 +261,19 @@ Cobre regras de custeio negociadas com uma empresa específica que não cabem no
|
||||
- **Mutuamente exclusivo por TIPO, não em bloco**: no formulário de Cadastro de Regras, escolher uma regra especial trava (marca + desabilita + esconde os radios titular/dependente) só os checkboxes "Mensalidade"/"Coparticipação" que essa regra específica cobre — `aplicarTiposRegraEmpresa()` em `importacao-plano-saude.js`, chamada sempre que a regra selecionada muda (ao marcar/desmarcar o checkbox, ou ao escolher uma regra no picker). Uma regra que só cobre mensalidade (Tecnomyl) deixa "Coparticipação" livre pra configuração manual normalmente — mesmo comportamento de antes pra essa regra específica; a novidade é só que agora isso é decidido pelos `tipos_lancamento` de CADA regra, não fixo no código do formulário. Um checkbox travado (`.disabled`) não dispara `change` por clique do usuário, então a exclusividade mútua não precisa de nenhuma lógica extra nos handlers de "Mensalidade"/"Coparticipação" — só o handler de "Regra especial da empresa"/a seleção no picker chamam `aplicarTiposRegraEmpresa()`.
|
||||
- No backend, `RegraCusteioPlanoSaudeSerializer.validate()`/`ImportacaoPlanoSaudeCreateSerializer.validate()` calculam `regra_empresa_tipos` (interseção entre `REGRAS_EMPRESA[chave]["tipos_lancamento"]` e os tipos selecionados — erro claro se vier vazia), conferem `chave_casamento_para_tipo(tipo) == REGRAS_EMPRESA[chave]["chave_casamento"]` pra cada tipo coberto (não mais um "exige nome" hardcoded) e zeram `custeio_por_tipo[tipo]` só pros tipos em `regra_empresa_tipos` (os demais tipos selecionados continuam com custeio manual normal). `regras_empresa.valida_regra_empresa(regra_empresa_key, chave_casamento_por_tipo, linhas_sistema, tipos_selecionados)` devolve `(aplica, tipos_cobertos)` — `pipeline.processa_importacao` passa `regra_empresa_fn` pra `casa_individuos_com_planilha` só quando `tipo_lancamento in tipos_cobertos`.
|
||||
- **`matcher._casa_por_cpf` passou a suportar `regra_empresa_fn`** (antes só `_casa_por_nome` suportava) — sem agrupar por família (essa estratégia não tem esse conceito): acumula `(linha, valor_total)` de todo indivíduo casado por CPF e chama `regra_empresa_fn(linhas_e_valores, tipo_lancamento)` uma vez só, no fim, com todos os pares do tipo de lançamento inteiro. `aplica(linhas_e_valores, tipo_lancamento)` é a assinatura de toda regra agora (segundo argumento novo) — permite uma mesma função se comportar diferente por tipo (ver Ottimizza abaixo); a Tecnomyl recebe o parâmetro mas ignora (só é chamada pra "mensalidade" mesmo, via `tipos_lancamento`).
|
||||
- **Registro** (`REGRAS_EMPRESA`): cada entrada tem `label`, `codigo_empresa` (código da empresa na planilha padrão pra qual a regra foi negociada), `operadora`, `chave_casamento`, `tipos_lancamento`, `aplica` (função que faz o cálculo) e `observacoes`. Pra cadastrar uma regra nova: escrever a função e registrar aqui — nada mais precisa mudar (`GET /api/importacoes-plano-saude/regras-empresa/` já reflete o registro, incluindo `tipos_lancamento`, consumido tanto pelo seletor dentro do Cadastro de Regras quanto pelo resumo só-leitura de "Nova Importação").
|
||||
- **Registro** (`REGRAS_EMPRESA`): cada entrada tem `label`, `codigos_empresa` (**tupla** dos códigos de empresa na planilha padrão pra os quais a regra foi negociada), `operadora`, `chave_casamento`, `tipos_lancamento`, `aplica` (função que faz o cálculo) e `observacoes`.
|
||||
- **Por que uma tupla e não um código só** (mudou numa rodada posterior, a pedido do usuário): uma mesma condição negociada pode valer pra várias empresas do MESMO grupo econômico. Caso real que motivou: ao cadastrar a regra de custeio da empresa 1855 (H2O Innovation, grupo Tecnomyl) o usuário levou um "Esta regra especial foi cadastrada para a empresa código 1778, não para 1855" — a trava funcionando como projetada, mas o desenho de 1 regra = 1 empresa não previa grupo. A alternativa seria duplicar a regra (uma entrada por empresa, mesmo cálculo) — pior, porque o dia que o teto mudar é preciso lembrar de mexer nas duas. **Só incluir um código novo na tupla com confirmação de que as condições são idênticas**: o cálculo não olha o código da empresa pra nada, então um código a mais aqui aplica silenciosamente o teto de um cliente na folha de outro. Pra cadastrar uma regra nova: escrever a função e registrar aqui — nada mais precisa mudar (`GET /api/importacoes-plano-saude/regras-empresa/` já reflete o registro, incluindo `tipos_lancamento`, consumido tanto pelo seletor dentro do Cadastro de Regras quanto pelo resumo só-leitura de "Nova Importação").
|
||||
- **Resumo de custeio + observações, só-leitura na tela de Revisão** (`#ips-review-regra-empresa-obs`, entre o cabeçalho "Revisão" e as abas Mensalidade/Coparticipação/Auditoria/Alterações — pedido explícito do usuário, rodada 108, "assim o usuário tem certeza que está conferindo com base na regra do cliente"): mostra sempre "Tipos cobertos" + uma linha por tipo de lançamento (`Mensalidade`/`Coparticipação` — "Titular: .../Dependente: ..." ou "usa regra especial da empresa — <label>", mesmo texto de `renderResumoRegra()` de "Nova Importação", calculado à parte em `renderReviewResumoCusteio()`/`reviewResumoTextoTipo()` porque a Revisão não tem `regrasEmpresaCache` carregado quando aberta direto do histórico — cobertura por regra especial é detectada por `custeio_por_tipo[tipo]` vir vazio com `regra_empresa` preenchido, não por consulta a essa cache), seguido da observação quando existir uma. `ImportacaoPlanoSaudeDetailSerializer.regra_empresa_observacoes` resolve `REGRAS_EMPRESA[obj.regra_empresa]["observacoes"]` a cada carregamento; o mesmo bloco cai pra `regra_custeio_salva_observacoes` quando não há regra empresa.
|
||||
- **`unimed_1778_tecnomyl`** — Tecnomyl (código 1778 na Unimed) tem ajuda de custo de até R$ 661,61 por família (titular + dependentes juntos, não por pessoa): família com mensalidade total acima do teto tem o excedente descontado do empregado; igual ou abaixo do teto, a empresa cobre 100%. Repassada pelo cliente em 08/2026. `chave_casamento="nome"` (precisa agrupar família), `tipos_lancamento=("mensalidade",)` — coparticipação dela segue sempre o custeio normal configurado no mesmo cadastro (radios titular/dependente), sem nenhuma ligação com a regra.
|
||||
- **`unimed_1778_tecnomyl`** — grupo Tecnomyl na Unimed (`codigos_empresa=("1778", "1855", "1872", "1927")` — 1778 Tecnomyl Brasil, 1855 H2O Innovation, 1872 GS3 Digital e 1927 YVY Agricultura Digital, mesmas condições confirmadas pelo usuário em 2026-09-22; a chave da regra manteve o nome antigo, que carrega só o 1778, pra não invalidar o `regra_empresa_chave` já gravado nas regras de custeio e importações existentes) tem ajuda de custo de até R$ 661,61 por família (titular + dependentes juntos, não por pessoa): família com mensalidade total acima do teto tem o excedente descontado do empregado; igual ou abaixo do teto, a empresa cobre 100%. Repassada pelo cliente em 08/2026. `chave_casamento="nome"` (precisa agrupar família), `tipos_lancamento=("mensalidade",)` — coparticipação dela segue sempre o custeio normal configurado no mesmo cadastro (radios titular/dependente), sem nenhuma ligação com a regra.
|
||||
- **Cálculo é por família, com prioridade explícita: dependentes primeiro, titular absorve o residual** (`regras_empresa._aplica_teto_familia`) — decisão explícita do cliente, e diferente de uma primeira versão (revertida) que distribuía o teto **proporcionalmente** entre todas as linhas. O algoritmo percorre primeiro os dependentes (na ordem em que aparecem no arquivo da operadora), cada um recebendo `valor_empresa = min(seu valor, o que sobrou do teto)`; só depois de todos os dependentes processados o titular absorve o que sobrou do teto (`teto_restante`), com o excedente (se houver) virando desconto do empregado nessa mesma linha. Ex.: família com dependente de R$559,57 e titular de R$314,12 (teto R$661,61) — dependente sai com `valor_empresa=559,57`/`valor=0` (coberto integralmente), sobra `661,61-559,57=102,04` de teto pro titular, que sai com `valor_empresa=102,04`/`valor=212,08`. Se os dependentes sozinhos já consumirem o teto inteiro, o titular fica com `valor_empresa=0` (desconto integral) e, se ainda sobrar dependente sem cobrir depois disso, esse dependente também é parcialmente descontado. Validado rodando o pipeline direto com os dois exemplos passados pelo cliente (família de R$800 → R$661,61 empresa/R$138,39 empregado no total; família abaixo do teto → 100% empresa) e reproduzindo exatamente um caso real reportado pelo usuário (família Caroline Fernandes/Luciano Ramos, R$873,69 no total) depois do ajuste de prioridade.
|
||||
- **`_aplica_teto_familia` é duck-typed de propósito** (`linhas_e_valores: List[Tuple[Any, float]]`, `_eh_linha_titular()` própria em vez de `LinhaSistema.eh_linha_titular()`): roda tanto contra `LinhaSistema` (pipeline, na criação da importação) quanto contra `ImportacaoPlanoSaudeLinha` (model Django, no recálculo pós "Vincular pessoa" — ver `views._recalcula_familia_regra_empresa` e "Resolução manual de auditoria por nome" acima) — as duas classes têm os mesmos atributos de string (`nome_dependente`/`cpf_dependente`/`valor_empresa`/`valor`), só a segunda não tem o método `eh_linha_titular()`.
|
||||
- **`sulamerica_5775_ottimizza`** — Ottimizza (código 1889) na SulAmérica (5775, ver "SulAmérica Saúde" acima): critério fixo, sem teto/percentual — mensalidade do titular é 100% custeada pela empresa, mensalidade do dependente é 100% descontada do empregado, e toda coparticipação (titular ou dependente) é 100% descontada do empregado. `chave_casamento="cpf"` (o parser já resolve cada indivíduo por CPF, sem precisar agrupar família — `_regra_sulamerica_5775_ottimizza` decide por linha, olhando só `_eh_linha_titular(linha)` e o `tipo_lancamento` recebido), `tipos_lancamento=("mensalidade", "coparticipacao")` — as duas cobertas pela mesma função, que ramifica por `tipo_lancamento`. Reproduz exatamente o padrão observado na planilha real da Ottimizza (toda linha de titular só vem com "Benefício Mensalidade" preenchido, toda linha de dependente só com "Desconto Mensalidade", "Benefício Coparticipação" nunca preenchido) — confirmado rodando `pipeline.processa_importacao` de ponta a ponta com a regra ativa contra o arquivo real e batendo centavo a centavo com as 4 colunas somadas direto da planilha (R$ 23.722,14 empresa/R$ 1.404,26 empregado de mensalidade; R$ 1.540,84 empregado de coparticipação).
|
||||
- **`amil_898_tecnomyl`** — Amil Odonto (código 898) na Tecnomyl (empresa 1778), repassada pelo cliente em 09/2026: a empresa custeia 100% da mensalidade de titular e dependente direto (cônjuge, filho(a)); agregados (avós, tios, sobrinhos, sogros, pai/mãe, irmãos) têm a mensalidade 100% descontada do empregado, no MESMO valor por pessoa — a regra é só sobre QUEM paga, não sobre um teto/percentual, por isso cobre os dois planos do contrato (E200 a R$ 20,81/pessoa e "DENTAL 200" a R$ 16,01/pessoa) sem precisar de nenhum valor fixo no código. `chave_casamento="cpf"` (o parser já resolve cada indivíduo por CPF), `tipos_lancamento=("mensalidade",)` — Amil Odonto não traz coparticipação (ver docstring do parser).
|
||||
- **`amil_898_tecnomyl`** — Amil Odonto (código 898) no grupo Tecnomyl (`codigos_empresa=("1778", "1855", "1872", "1927")`, os mesmos quatro da regra da Unimed acima — estendida pro grupo em 2026-09-22, logo depois dela, a pedido do usuário), repassada pelo cliente em 09/2026: a empresa custeia 100% da mensalidade de titular e dependente direto (cônjuge, filho(a)); agregados (avós, tios, sobrinhos, sogros, pai/mãe, irmãos) têm a mensalidade 100% descontada do empregado, no MESMO valor por pessoa — a regra é só sobre QUEM paga, não sobre um teto/percentual, por isso cobre os dois planos do contrato (E200 a R$ 20,81/pessoa e "DENTAL 200" a R$ 16,01/pessoa) sem precisar de nenhum valor fixo no código. `chave_casamento="cpf"` (o parser já resolve cada indivíduo por CPF), `tipos_lancamento=("mensalidade",)` — Amil Odonto não traz coparticipação (ver docstring do parser).
|
||||
- **Diferente das outras duas regras: não usa família nem `_eh_linha_titular`** — é a primeira regra de empresa que precisa diferenciar dependente de agregado por PESSOA, uma distinção que a planilha padrão do sistema não guarda (só sabe titular/não-titular). Por isso `LinhaSistema`/`ImportacaoPlanoSaudeLinha`/`ImportacaoPlanoSaudeDePaulaLinha` ganharam `tipo_pessoa` ('T'/'D'/'A', ver "Modelos" acima) — gravado **sempre** (não só quando há uma regra de empresa ativa) pelo `matcher.py` no momento do casamento, a partir do `Individuo.tipo` original do arquivo da operadora (`_casa_por_cpf`: `linha_destino.tipo_pessoa = ind.tipo`; `_casa_por_nome`: `linha_titular.tipo_pessoa = "T"` / `linha_dep.tipo_pessoa = m.tipo`). `_regra_amil_898_tecnomyl` só lê `getattr(linha, "tipo_pessoa", "") or "D"` de cada linha (fallback pra "D" — custeada pela empresa — quando em branco, ex.: linha incluída manualmente via "Adicionar linha", que nunca passa pelo casamento automático).
|
||||
- **"Vincular pessoa" (resolução manual de auditoria) também grava `tipo_pessoa`**: como a linha resolvida nunca passou pelo casamento automático (senão não estaria em branco), `ImportacaoPlanoSaudeAuditoriaViewSet.resolver`/`...DePaulaAuditoriaViewSet.resolver` gravam `linha.tipo_pessoa = item.tipo` antes de chamar `_recalcula_familia_regra_empresa(_de_paula)` — sem isso, uma linha vinculada manualmente cairia sempre no fallback "D", tratando um agregado vinculado à mão como se fosse dependente.
|
||||
- **Nota operacional**: dependentes sem CPF no arquivo real da Tecnomyl (comum em crianças) caem em auditoria "CPF não encontrado" se a planilha padrão do Questor tiver o CPF real dessa pessoa — e, ao contrário da estratégia "nome", a estratégia "cpf" não reaplica um vínculo salvo automaticamente numa competência futura (`_casa_por_cpf` não aceita `vinculos_por_nome`), então a mesma pessoa precisa ser vinculada de novo todo mês. Comportamento pré-existente de qualquer operadora "cpf" (Amil, SulAmérica, MetLife), não uma limitação introduzida por esta regra — ver nota em "Formato Excel (898, Tecnomyl)" acima.
|
||||
- Validado rodando `extrai()` + a regra + o pipeline completo (`processa_importacao`) contra o arquivo real da competência 08/2026 — ver detalhe em "Formato Excel (898, Tecnomyl)" acima.
|
||||
- **Trava de compatibilidade generalizada**: `regras_empresa.valida_regra_empresa()` recusa explicitamente (`RegraEmpresaIncompativelError`, capturada à parte em `views.py` pra devolver a mensagem certa, não o erro genérico de "formato de arquivo") se (a) nenhum tipo selecionado na importação está entre os `tipos_lancamento` da regra, (b) a operadora escolhida não usa a `chave_casamento` que a regra exige pra algum tipo coberto, ou (c) a planilha padrão anexada não tem nenhuma linha com o `codigo_empresa` esperado pela regra — trava contra aplicar a regra de uma empresa a outra por engano (vale pras duas regras, não só pra Tecnomyl como antes).
|
||||
- **Trava de compatibilidade generalizada**: `regras_empresa.valida_regra_empresa()` recusa explicitamente (`RegraEmpresaIncompativelError`, capturada à parte em `views.py` pra devolver a mensagem certa, não o erro genérico de "formato de arquivo") se (a) nenhum tipo selecionado na importação está entre os `tipos_lancamento` da regra, (b) a operadora escolhida não usa a `chave_casamento` que a regra exige pra algum tipo coberto, ou (c) a planilha padrão anexada não tem nenhuma linha com **nenhum** dos `codigos_empresa` esperados pela regra — trava contra aplicar a regra de uma empresa a outra por engano (vale pras duas regras, não só pra Tecnomyl como antes). A mesma conferência de código existe no CADASTRO da regra (`RegraCusteioPlanoSaudeSerializer.validate()`/`ImportacaoPlanoSaudeCreateSerializer.validate()`, `serializers.py`): ao marcar "Regra especial da empresa", o código da empresa sendo cadastrada precisa estar entre os `codigos_empresa` da regra escolhida.
|
||||
- **"Vincular pessoa" (resolução manual de auditoria) também generalizada**: `_recalcula_familia_regra_empresa` (views.py) filtra por `linha.tipo_lancamento` (o tipo da própria linha resolvida), não mais fixo em `"mensalidade"`, e passa esse tipo como segundo argumento pra `regra["aplica"]`; `resolver()` decide se aplica esse caminho checando se `item.tipo_lancamento` está em `REGRAS_EMPRESA[chave]["tipos_lancamento"]`, não mais comparando com a string `"mensalidade"` direto.
|
||||
|
||||
## API (tabela completa)
|
||||
|
||||
@ -142,10 +142,16 @@ def _regra_sulamerica_5775_ottimizza(linhas_e_valores: List[Tuple[Any, float]],
|
||||
|
||||
# Pra cadastrar uma regra nova: escrever a função `_regra_...(linhas_e_valores,
|
||||
# tipo_lancamento)` acima (ou reaproveitar `_aplica_teto_familia` se for só
|
||||
# um teto por família) e registrar aqui. `codigo_empresa` é o código da
|
||||
# empresa na planilha padrão pra qual a regra foi negociada — usado só pra
|
||||
# travar contra aplicar a regra errada numa planilha de outra empresa (ver
|
||||
# `valida_regra_empresa` abaixo). `chave_casamento` é a estratégia que a
|
||||
# um teto por família) e registrar aqui. `codigos_empresa` é a tupla de
|
||||
# códigos de empresa na planilha padrão pra os quais a regra foi negociada —
|
||||
# usado só pra travar contra aplicar a regra errada numa planilha de outra
|
||||
# empresa (ver `valida_regra_empresa` abaixo). É uma TUPLA, e não um código
|
||||
# só, porque uma mesma condição negociada pode valer pra várias empresas do
|
||||
# mesmo grupo econômico (caso do grupo Tecnomyl: a ajuda de custo da Unimed
|
||||
# é a mesma na 1778 e na 1855) — nesse caso é a MESMA regra, não uma cópia
|
||||
# por empresa. Só incluir um código aqui com confirmação de que as condições
|
||||
# são idênticas: o valor errado aqui vira desconto errado na folha de alguém.
|
||||
# `chave_casamento` é a estratégia que a
|
||||
# OPERADORA precisa usar pra esta regra fazer sentido ("nome" pra regras que
|
||||
# agrupam família — a função só recebe as linhas de UMA família por vez;
|
||||
# "cpf" pra regras por pessoa, sem agrupamento — a função recebe TODAS as
|
||||
@ -158,14 +164,19 @@ def _regra_sulamerica_5775_ottimizza(linhas_e_valores: List[Tuple[Any, float]],
|
||||
# colaborador conferir a regra aplicada sem precisar abrir o código.
|
||||
REGRAS_EMPRESA: Dict[str, dict] = {
|
||||
"unimed_1778_tecnomyl": {
|
||||
"label": "1778 - Unimed (Tecnomyl)",
|
||||
"codigo_empresa": "1778",
|
||||
"label": "1778/1855/1872/1927 - Unimed (Tecnomyl)",
|
||||
# Grupo Tecnomyl, todas com a MESMA ajuda de custo negociada
|
||||
# (confirmado pelo usuário em 2026-09-22): 1778 Tecnomyl Brasil,
|
||||
# 1855 H2O Innovation, 1872 GS3 Digital, 1927 YVY Agricultura
|
||||
# Digital. Nomes conferidos no Questor antes de entrarem aqui.
|
||||
"codigos_empresa": ("1778", "1855", "1872", "1927"),
|
||||
"operadora": "unimed_saude",
|
||||
"chave_casamento": "nome",
|
||||
"tipos_lancamento": ("mensalidade",),
|
||||
"aplica": _regra_unimed_1778_tecnomyl,
|
||||
"observacoes": (
|
||||
"A Tecnomyl oferece uma ajuda de custo de até R$ 661,61 por família "
|
||||
"O grupo Tecnomyl (empresas 1778, 1855, 1872 e 1927) oferece uma "
|
||||
"ajuda de custo de até R$ 661,61 por família "
|
||||
"(titular + dependentes juntos, independente da quantidade de "
|
||||
"dependentes). Os dependentes são custeados primeiro; o titular "
|
||||
"absorve o valor residual do teto, e o excedente (se houver) é "
|
||||
@ -174,14 +185,17 @@ REGRAS_EMPRESA: Dict[str, dict] = {
|
||||
),
|
||||
},
|
||||
"amil_898_tecnomyl": {
|
||||
"label": "1778 - Amil (Tecnomyl)",
|
||||
"codigo_empresa": "1778",
|
||||
# Mesmo grupo (e mesmos códigos) da regra da Unimed acima —
|
||||
# confirmado pelo usuário em 2026-09-22, logo depois da Unimed.
|
||||
"label": "1778/1855/1872/1927 - Amil (Tecnomyl)",
|
||||
"codigos_empresa": ("1778", "1855", "1872", "1927"),
|
||||
"operadora": "amil_odonto_mensalidade_898",
|
||||
"chave_casamento": "cpf",
|
||||
"tipos_lancamento": ("mensalidade",),
|
||||
"aplica": _regra_amil_898_tecnomyl,
|
||||
"observacoes": (
|
||||
"A Tecnomyl arca com o valor integral da mensalidade de titular e "
|
||||
"O grupo Tecnomyl (empresas 1778, 1855, 1872 e 1927) arca com o "
|
||||
"valor integral da mensalidade de titular e "
|
||||
"dependente direto (cônjuge, filho(a)). Agregados — avós, tios, "
|
||||
"sobrinhos, sogros e similares — têm a mensalidade 100% "
|
||||
"descontada do empregado, no mesmo valor por pessoa. Cobre os "
|
||||
@ -191,7 +205,7 @@ REGRAS_EMPRESA: Dict[str, dict] = {
|
||||
},
|
||||
"sulamerica_5775_ottimizza": {
|
||||
"label": "1889 - SulAmérica (5775)",
|
||||
"codigo_empresa": "1889",
|
||||
"codigos_empresa": ("1889",),
|
||||
"operadora": "sulamerica_saude",
|
||||
"chave_casamento": "cpf",
|
||||
"tipos_lancamento": ("mensalidade", "coparticipacao"),
|
||||
@ -251,10 +265,14 @@ def valida_regra_empresa(
|
||||
f"— esta operadora não é compatível para o tipo {tipo!r}."
|
||||
)
|
||||
|
||||
codigo_esperado = regra.get("codigo_empresa")
|
||||
if codigo_esperado and not any(l.codigo_empresa == codigo_esperado for l in linhas_sistema):
|
||||
# Basta a planilha ter linha de UMA das empresas cobertas — uma regra de
|
||||
# grupo (ver `codigos_empresa` no registro acima) é usada numa importação
|
||||
# de cada vez, então a planilha traz só a empresa daquela importação.
|
||||
codigos_esperados = regra.get("codigos_empresa", ())
|
||||
if codigos_esperados and not any(l.codigo_empresa in codigos_esperados for l in linhas_sistema):
|
||||
esperados = " ou ".join(codigos_esperados)
|
||||
raise RegraEmpresaIncompativelError(
|
||||
f"A regra \"{regra['label']}\" foi cadastrada para a empresa código {codigo_esperado}, "
|
||||
f"A regra \"{regra['label']}\" foi cadastrada para a empresa código {esperados}, "
|
||||
"mas a planilha padrão anexada não tem nenhuma linha com esse código."
|
||||
)
|
||||
return regra["aplica"], tipos_cobertos
|
||||
|
||||
@ -1122,12 +1122,16 @@ class RegraCusteioPlanoSaudeSerializer(serializers.ModelSerializer):
|
||||
)
|
||||
}
|
||||
)
|
||||
if regra_re["codigo_empresa"] != codigo_empresa:
|
||||
# Uma regra pode cobrir mais de uma empresa quando são do mesmo
|
||||
# grupo e a condição negociada é idêntica (ver `codigos_empresa`
|
||||
# em planos_saude.regras_empresa).
|
||||
if codigo_empresa not in regra_re["codigos_empresa"]:
|
||||
aceitos = " ou ".join(regra_re["codigos_empresa"])
|
||||
raise serializers.ValidationError(
|
||||
{
|
||||
"regra_empresa_chave": (
|
||||
f"Esta regra especial foi cadastrada para a empresa código "
|
||||
f"{regra_re['codigo_empresa']}, não para {codigo_empresa}."
|
||||
f"{aceitos}, não para {codigo_empresa}."
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -1594,12 +1598,16 @@ class RegraCusteioPlanoSaudeDePaulaSerializer(serializers.ModelSerializer):
|
||||
)
|
||||
}
|
||||
)
|
||||
if regra_re["codigo_empresa"] != codigo_empresa:
|
||||
# Uma regra pode cobrir mais de uma empresa quando são do mesmo
|
||||
# grupo e a condição negociada é idêntica (ver `codigos_empresa`
|
||||
# em planos_saude.regras_empresa).
|
||||
if codigo_empresa not in regra_re["codigos_empresa"]:
|
||||
aceitos = " ou ".join(regra_re["codigos_empresa"])
|
||||
raise serializers.ValidationError(
|
||||
{
|
||||
"regra_empresa_chave": (
|
||||
f"Esta regra especial foi cadastrada para a empresa código "
|
||||
f"{regra_re['codigo_empresa']}, não para {codigo_empresa}."
|
||||
f"{aceitos}, não para {codigo_empresa}."
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user