""" Regras de custeio especiais por empresa ("Regra empresa" na tela de nova importação, terceiro item de "Tipo de importação" ao lado de Mensalidade/ Coparticipação) — ao contrário de RegraCusteioPlanoSaude (banco de regras salvas, editável pelo usuário, sempre no formato "por tipo de lançamento × titular/dependente"), as regras aqui são cadastradas diretamente no código pelo desenvolvedor quando o cliente repassa uma regra negociada com uma empresa específica que não se encaixa nesse formato — tipicamente porque são calculadas por FAMÍLIA (titular + todos os dependentes juntos), não por pessoa. Nunca expostas para o usuário cadastrar pela tela; só "Selecionar regra" na tela de nova importação, que lista as chaves já registradas aqui (GET /api/importacoes-plano-saude/regras-empresa/). Só se aplica ao tipo de lançamento "mensalidade" — coparticipação sempre segue o custeio normal configurado na tela (por isso REGRAS_EMPRESA não tem nada de coparticipação), e é por isso que "Regra empresa" e "Mensalidade" são mutuamente exclusivos no formulário: as duas são formas alternativas de configurar o MESMO tipo de lançamento "mensalidade". """ from typing import Any, Callable, Dict, List, Tuple from portal_api.planos_saude.leiaute_sistema import formata_valor_br from portal_api.planos_saude.modelos import LinhaSistema class RegraEmpresaIncompativelError(Exception): """Regra empresa selecionada não é compatível com esta importação (operadora sem casamento por nome, ou planilha padrão de uma empresa diferente daquela pra qual a regra foi cadastrada) — views.py devolve esta mensagem direto pro usuário, em vez do erro genérico de "formato de arquivo não conforme".""" def _eh_linha_titular(linha: Any) -> bool: """Mesmo critério de `LinhaSistema.eh_linha_titular()`, mas duck-typed — `_aplica_teto_familia` 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`), que não tem esse método.""" return not (linha.nome_dependente or "").strip() and not (linha.cpf_dependente or "").strip() def _aplica_teto_familia(linhas_e_valores: List[Tuple[Any, float]], teto: float) -> None: """Divide o teto de custeio da empresa (`teto`) entre as linhas de UMA família (titular + dependentes) — decisão explícita do cliente: DEPENDENTES TÊM PRIORIDADE no uso do teto (são cobertos primeiro, na ordem em que aparecem — cada um recebe `min(seu valor, o que sobrou do teto)`), e o TITULAR absorve por último o que sobrar do teto (o "residual"). Se os dependentes sozinhos já consumirem o teto inteiro, o titular fica com desconto integral (`valor_empresa=0`) e, se ainda sobrar dependente sem cobrir depois disso, esse dependente também é parcialmente descontado — ao contrário de uma divisão proporcional (tentada numa primeira versão e revertida): o pedido do cliente é "abater primeiro o valor dos dependentes", não repartir o teto igualmente entre todos.""" dependentes = [(linha, valor) for linha, valor in linhas_e_valores if not _eh_linha_titular(linha)] titulares = [(linha, valor) for linha, valor in linhas_e_valores if _eh_linha_titular(linha)] teto_restante = teto for linha, valor in dependentes + titulares: valor = max(0.0, valor) valor_empresa = round(min(valor, max(0.0, teto_restante)), 2) valor_empregado = round(valor - valor_empresa, 2) teto_restante = round(teto_restante - valor_empresa, 2) linha.valor_empresa = formata_valor_br(valor_empresa) linha.valor = formata_valor_br(valor_empregado) def _regra_unimed_1778_tecnomyl(linhas_e_valores: List[Tuple[Any, float]]) -> None: """Tecnomyl (código 1778 na Unimed) — ajuda de custo de até R$ 661,61 por família (titular + dependentes juntos, não por pessoa), repassada pelo cliente em 08/2026: família com mensalidade total acima do teto tem o excedente descontado do empregado; igual ou abaixo do teto, a empresa cobre 100% e o empregado não paga nada.""" _aplica_teto_familia(linhas_e_valores, teto=661.61) # Pra cadastrar uma regra nova: escrever a função `_regra_...(linhas_e_valores)` # 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). # `observacoes` é opcional (texto livre explicando a regra em português) — # exibida só-leitura no topo da tela de revisão (ver "regra_empresa_observacoes" # em ImportacaoPlanoSaudeDetailSerializer) pra o 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", "operadora": "unimed_saude", "aplica": _regra_unimed_1778_tecnomyl, "observacoes": ( "A Tecnomyl 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) é " "descontado do empregado. Coparticipação segue o custeio normal " "configurado nesta importação, sem relação com esta regra." ), }, } def lista_regras_empresa() -> List[Dict[str, str]]: return [ {"key": chave, "label": dados["label"], "observacoes": dados.get("observacoes", "")} for chave, dados in REGRAS_EMPRESA.items() ] def valida_regra_empresa( regra_empresa_key: str, chave_casamento: str, linhas_sistema: List[LinhaSistema] ) -> Callable[[List[Tuple[Any, float]]], None]: """Valida que a regra empresa escolhida pode ser aplicada nesta importação e devolve a função de aplicação já resolvida — chamado por pipeline.processa_importacao antes de rodar o casamento de mensalidade.""" regra = REGRAS_EMPRESA.get(regra_empresa_key) if regra is None: raise RegraEmpresaIncompativelError(f"Regra empresa desconhecida: {regra_empresa_key!r}.") if chave_casamento != "nome": raise RegraEmpresaIncompativelError( "Esta operadora não é compatível com 'Regra empresa' (requer casamento por nome)." ) codigo_esperado = regra.get("codigo_empresa") if codigo_esperado and not any(l.codigo_empresa == codigo_esperado for l in linhas_sistema): raise RegraEmpresaIncompativelError( f"A regra \"{regra['label']}\" foi cadastrada para a empresa código {codigo_esperado}, " "mas a planilha padrão anexada não tem nenhuma linha com esse código." ) return regra["aplica"]