Inclusão das regas de custei na tela de revisão

This commit is contained in:
Gabriel 2026-09-16 15:15:13 -03:00
parent 444d76a159
commit ca9f53837c
6 changed files with 107 additions and 14 deletions

View File

@ -70,7 +70,8 @@
"Bash(curl -s http://127.0.0.1:8000/static/css/layout.css)", "Bash(curl -s http://127.0.0.1:8000/static/css/layout.css)",
"Bash(curl -s http://127.0.0.1:8000/static/css/halloween-cobweb.css)", "Bash(curl -s http://127.0.0.1:8000/static/css/halloween-cobweb.css)",
"Bash(\"/c/Users/Depaula/Documents/Portal/.venv/Scripts/python.exe\" _scratch_insert_seasonal_toggle.py)", "Bash(\"/c/Users/Depaula/Documents/Portal/.venv/Scripts/python.exe\" _scratch_insert_seasonal_toggle.py)",
"Bash(rm _scratch_insert_seasonal_toggle.py)" "Bash(rm _scratch_insert_seasonal_toggle.py)",
"Bash(sort -t' ' -k2 -n -u)"
] ]
} }
} }

View File

@ -335,3 +335,12 @@ Pedido do usuário (2026-09-15): o nome do arquivo baixado em "Gerar Arquivo" er
- **`ImportacaoPlanoSaudeViewSet.gerar()`**: o `.zip` (2 tipos) passou a se chamar `"<nome_base>.zip"` em vez de `"importacao_plano_saude_<id>.zip"`; o `.csv` único (1 tipo) passou a se chamar `"<nome_base> - <tipo>.csv"` (ex.: `"1970 - 5060 - mensalidade.csv"`) em vez de só `"<tipo>.csv"` — mesma ambiguidade existia aí também (duas execuções da mesma empresa com operadoras diferentes e só 1 tipo cada geravam dois `mensalidade.csv` indistinguíveis). Os nomes dos arquivos **dentro** do `.zip` (`mensalidade.csv`/`coparticipacao.csv`) não mudaram, já estão desambiguados pelo nome do próprio zip. - **`ImportacaoPlanoSaudeViewSet.gerar()`**: o `.zip` (2 tipos) passou a se chamar `"<nome_base>.zip"` em vez de `"importacao_plano_saude_<id>.zip"`; o `.csv` único (1 tipo) passou a se chamar `"<nome_base> - <tipo>.csv"` (ex.: `"1970 - 5060 - mensalidade.csv"`) em vez de só `"<tipo>.csv"` — mesma ambiguidade existia aí também (duas execuções da mesma empresa com operadoras diferentes e só 1 tipo cada geravam dois `mensalidade.csv` indistinguíveis). Os nomes dos arquivos **dentro** do `.zip` (`mensalidade.csv`/`coparticipacao.csv`) não mudaram, já estão desambiguados pelo nome do próprio zip.
- **`ImportacaoPlanoSaudeDePaulaViewSet.gerar()`** recebeu o mesmo ajuste (`prefixo_fallback="importacao_plano_saude_de_paula"`, preservando o fallback distinto de antes) — os dois `gerar()` compartilham exatamente o mesmo código de geração de arquivo, então ficaria inconsistente resolver só um dos dois. - **`ImportacaoPlanoSaudeDePaulaViewSet.gerar()`** recebeu o mesmo ajuste (`prefixo_fallback="importacao_plano_saude_de_paula"`, preservando o fallback distinto de antes) — os dois `gerar()` compartilham exatamente o mesmo código de geração de arquivo, então ficaria inconsistente resolver só um dos dois.
- Sem migração, sem mudança de contrato de API — só o nome do arquivo no header `Content-Disposition` da resposta binária. - Sem migração, sem mudança de contrato de API — só o nome do arquivo no header `Content-Disposition` da resposta binária.
### Rodada 108 — Resumo da regra de custeio na tela de Revisão (tipos cobertos + mensalidade/coparticipação)
Pedido do usuário (2026-09-16), com print da tela de Revisão mostrando um espaço vazio abaixo do código/nome da operadora: até então, esse espaço só mostrava a **observação** da regra empresa/regra de custeio salva (`regra_empresa_observacoes`/`regra_custeio_salva_observacoes`, já existentes no `ImportacaoPlanoSaudeDetailSerializer` — nenhum campo novo no backend foi necessário) — sem o resumo de tipos cobertos e do próprio custeio por titular/dependente que "Nova Importação" já mostra (`#ips-imp-resumo`, ao resolver uma regra salva). O usuário queria os dois juntos na Revisão, "assim o usuário tem certeza que está conferindo com base na regra do cliente".
- **`#ips-review-regra-empresa-obs`** (`importacao-plano-saude.html`) ganhou três `<p>` novos antes do label/texto de observação (`#ips-review-resumo-tipos`/`-mensalidade`/`-coparticipacao`, reaproveitando as classes `.ips-imp-resumo__tipos`/`.ips-imp-resumo__linha` já existentes) — mesmo bloco visual de sempre, só que agora mostra o resumo sempre (quando a importação tem algum `tipos_lancamento`, o que é sempre o caso) e a observação só quando existir uma. `.ips-regra-empresa-observacoes` (CSS) virou `display:flex; flex-direction:column; gap:var(--space-2)` pra espaçar as linhas novas do mesmo jeito que `.ips-imp-resumo` já fazia.
- **`renderReviewResumoCusteio(importacao)`** (`importacao-plano-saude.js`, nova) monta o resumo a partir dos campos já presentes na importação (`tipos_lancamento`/`custeio_por_tipo`/`regra_empresa`/`regra_empresa_label`) — **não** reaproveita `renderResumoRegra()`/`regrasEmpresaCache` de "Nova Importação" porque essa cache só é buscada ao abrir os modais de "Nova Importação"/"Cadastro de Regras", ficando vazia quando a Revisão é aberta direto do histórico. Em vez disso, `reviewResumoTextoTipo()` detecta cobertura por "regra especial da empresa" olhando se `custeio_por_tipo[tipo]` veio vazio (sem `titular`/`dependente`) com `regra_empresa` preenchido — o mesmo sinal que `ImportacaoPlanoSaudeCreateSerializer.validate()` já grava pra tipos cobertos por uma regra empresa (ver "zeram custeio_por_tipo[tipo] só pros tipos em regra_empresa_tipos" no `CLAUDE.md` deste pacote) — evitando uma segunda fonte de verdade sobre quais tipos a regra cobre.
- `abrirRevisao()` chama essa função e decide se o bloco inteiro fica visível (`temResumo || temObs`) — antes só aparecia com observação cadastrada, agora aparece por padrão em toda importação com tipo de lançamento selecionado.
- Sem migração, sem endpoint novo — só leitura de campos que a Revisão já carregava.

View File

@ -225,7 +225,7 @@ Cobre regras de custeio negociadas com uma empresa específica que não cabem no
- 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`. - 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`). - **`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`, `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").
- **Observações da regra, só-leitura na tela de Revisão** (`#ips-review-regra-empresa-obs`) — inalterado: `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. - **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`** — 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.
- **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. - **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()`. - **`_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()`.

View File

@ -586,9 +586,10 @@
color: var(--text-primary); color: var(--text-primary);
} }
/* Observações da regra empresa na tela de Revisão — só leitura, mesma /* Resumo da regra aplicada + observações na tela de Revisão — só leitura,
linguagem visual de .ips-regra-observacoes (formulário de nova mesma linguagem visual de .ips-imp-resumo (formulário de nova
importação), fica entre o cabeçalho "Revisão" e as abas importação, cujas classes __tipos/__linha são reaproveitadas aqui dentro),
fica entre o cabeçalho "Revisão" e as abas
Mensalidade/Coparticipação/Auditoria/Alterações. */ Mensalidade/Coparticipação/Auditoria/Alterações. */
.ips-regra-empresa-observacoes { .ips-regra-empresa-observacoes {
margin-bottom: var(--space-4); margin-bottom: var(--space-4);
@ -596,10 +597,13 @@
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
background: var(--bg-canvas); background: var(--bg-canvas);
border: 1px solid var(--border-subtle); border: 1px solid var(--border-subtle);
display: flex;
flex-direction: column;
gap: var(--space-2);
} }
.ips-regra-empresa-observacoes__label { .ips-regra-empresa-observacoes__label {
margin: 0 0 var(--space-1); margin: 0;
font-size: 0.72rem; font-size: 0.72rem;
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;

View File

@ -360,6 +360,9 @@ document.addEventListener("DOMContentLoaded", async () => {
const reviewEmpresa = document.getElementById("ips-review-empresa"); const reviewEmpresa = document.getElementById("ips-review-empresa");
const reviewSubtitle = document.getElementById("ips-review-subtitle"); const reviewSubtitle = document.getElementById("ips-review-subtitle");
const reviewRegraEmpresaObs = document.getElementById("ips-review-regra-empresa-obs"); const reviewRegraEmpresaObs = document.getElementById("ips-review-regra-empresa-obs");
const reviewResumoTipos = document.getElementById("ips-review-resumo-tipos");
const reviewResumoMensalidade = document.getElementById("ips-review-resumo-mensalidade");
const reviewResumoCoparticipacao = document.getElementById("ips-review-resumo-coparticipacao");
const reviewRegraEmpresaObsLabel = document.getElementById("ips-review-regra-empresa-obs-label"); const reviewRegraEmpresaObsLabel = document.getElementById("ips-review-regra-empresa-obs-label");
const reviewRegraEmpresaObsTexto = document.getElementById("ips-review-regra-empresa-obs-texto"); const reviewRegraEmpresaObsTexto = document.getElementById("ips-review-regra-empresa-obs-texto");
const reviewError = document.getElementById("ips-review-error"); const reviewError = document.getElementById("ips-review-error");
@ -1340,6 +1343,68 @@ document.addEventListener("DOMContentLoaded", async () => {
} }
} }
// Mesmo texto de linha usado em renderResumoRegra() acima, mas pra tela
// de Revisão — que não parte de uma "regra" em memória, e sim dos campos
// já persistidos na própria importação. Cobertura por regra especial da
// empresa é detectada pelo custeio_por_tipo[tipo] vir vazio (sem
// titular/dependente) quando `regraEmpresaChave` está presente — mesmo
// sinal que ImportacaoPlanoSaudeCreateSerializer.validate() grava (ver
// "zeram custeio_por_tipo[tipo] só pros tipos em regra_empresa_tipos" no
// CLAUDE.md deste pacote) — evita depender de regrasEmpresaCache, que não
// é carregado quando a Revisão é aberta direto do histórico.
function reviewResumoTextoTipo(tipoLabel, tipo, custeioPorTipo, regraEmpresaChave, regraEmpresaLabel) {
const entrada = custeioPorTipo[tipo] || {};
if (regraEmpresaChave && !entrada.titular && !entrada.dependente) {
return `${tipoLabel}: usa regra especial da empresa — ${regraEmpresaLabel || regraEmpresaChave}`;
}
const titular = resumoModoPessoa(entrada.titular);
const dependente = resumoModoPessoa(entrada.dependente);
return `${tipoLabel} — Titular: ${titular}. Dependente: ${dependente}.`;
}
// Preenche o resumo de custeio (tipos cobertos + mensalidade/
// coparticipação) dentro de #ips-review-regra-empresa-obs — devolve se
// havia algo pra mostrar, pra abrirRevisao() decidir se o bloco inteiro
// (resumo + observação) precisa ficar visível.
function renderReviewResumoCusteio(importacao) {
const tipos = importacao.tipos_lancamento || [];
const temResumo = tipos.length > 0;
if (reviewResumoTipos) {
reviewResumoTipos.textContent = `Tipos cobertos: ${tiposLabel(tipos)}`;
reviewResumoTipos.hidden = !temResumo;
}
const custeioPorTipo = importacao.custeio_por_tipo || {};
if (reviewResumoMensalidade) {
if (tipos.includes("mensalidade")) {
reviewResumoMensalidade.textContent = reviewResumoTextoTipo(
"Mensalidade",
"mensalidade",
custeioPorTipo,
importacao.regra_empresa,
importacao.regra_empresa_label
);
reviewResumoMensalidade.hidden = false;
} else {
reviewResumoMensalidade.hidden = true;
}
}
if (reviewResumoCoparticipacao) {
if (tipos.includes("coparticipacao")) {
reviewResumoCoparticipacao.textContent = reviewResumoTextoTipo(
"Coparticipação",
"coparticipacao",
custeioPorTipo,
importacao.regra_empresa,
importacao.regra_empresa_label
);
reviewResumoCoparticipacao.hidden = false;
} else {
reviewResumoCoparticipacao.hidden = true;
}
}
return temResumo;
}
// Monta o FormData de "Nova Importação" a partir da regra resolvida — // Monta o FormData de "Nova Importação" a partir da regra resolvida —
// mesmo formato de campos que ImportacaoPlanoSaudeCreateSerializer já // mesmo formato de campos que ImportacaoPlanoSaudeCreateSerializer já
// espera (custeio_<tipo>_<pessoa>, limite_valor_.../percentual_...), só // espera (custeio_<tipo>_<pessoa>, limite_valor_.../percentual_...), só
@ -1720,22 +1785,33 @@ document.addEventListener("DOMContentLoaded", async () => {
reviewSubtitle.textContent = reviewSubtitle.textContent =
`${importacao.nome_operadora} — criada em ${new Date(importacao.criado_em).toLocaleString("pt-BR")}${regraEmpresaSufixo}${competenciaSufixo}`; `${importacao.nome_operadora} — criada em ${new Date(importacao.criado_em).toLocaleString("pt-BR")}${regraEmpresaSufixo}${competenciaSufixo}`;
if (reviewRegraEmpresaObs) { if (reviewRegraEmpresaObs) {
// As duas regras (empresa e de custeio salva) são mutuamente exclusivas // Resumo (tipos cobertos + mensalidade/coparticipação) sempre que a
// na criação — nunca as duas preenchidas ao mesmo tempo — mas o bloco é // importação tiver algum tipo de lançamento (praticamente sempre) —
// o mesmo pra qualquer uma que tenha observação cadastrada. // pedido explícito do usuário, pra conferir a regra do cliente sem
// precisar abrir o Cadastro de Regras. As duas observações (regra
// empresa e de custeio salva) são mutuamente exclusivas na criação —
// nunca as duas preenchidas ao mesmo tempo.
const temResumo = renderReviewResumoCusteio(importacao);
let temObs = false;
if (importacao.regra_empresa_observacoes) { if (importacao.regra_empresa_observacoes) {
reviewRegraEmpresaObsLabel.textContent = "Observações da regra empresa"; reviewRegraEmpresaObsLabel.textContent = "Observações da regra empresa";
reviewRegraEmpresaObsTexto.textContent = importacao.regra_empresa_observacoes; reviewRegraEmpresaObsTexto.textContent = importacao.regra_empresa_observacoes;
reviewRegraEmpresaObs.hidden = false; reviewRegraEmpresaObsLabel.hidden = false;
reviewRegraEmpresaObsTexto.hidden = false;
temObs = true;
} else if (importacao.regra_custeio_salva_observacoes) { } else if (importacao.regra_custeio_salva_observacoes) {
const nome = importacao.regra_custeio_salva_nome ? ` — ${importacao.regra_custeio_salva_nome}` : ""; const nome = importacao.regra_custeio_salva_nome ? ` — ${importacao.regra_custeio_salva_nome}` : "";
reviewRegraEmpresaObsLabel.textContent = `Observações da regra de custeio salva${nome}`; reviewRegraEmpresaObsLabel.textContent = `Observações da regra de custeio salva${nome}`;
reviewRegraEmpresaObsTexto.textContent = importacao.regra_custeio_salva_observacoes; reviewRegraEmpresaObsTexto.textContent = importacao.regra_custeio_salva_observacoes;
reviewRegraEmpresaObs.hidden = false; reviewRegraEmpresaObsLabel.hidden = false;
reviewRegraEmpresaObsTexto.hidden = false;
temObs = true;
} else { } else {
reviewRegraEmpresaObs.hidden = true; reviewRegraEmpresaObsLabel.hidden = true;
reviewRegraEmpresaObsTexto.hidden = true;
reviewRegraEmpresaObsTexto.textContent = ""; reviewRegraEmpresaObsTexto.textContent = "";
} }
reviewRegraEmpresaObs.hidden = !(temResumo || temObs);
} }
reviewError.textContent = ""; reviewError.textContent = "";
renderTabs(); renderTabs();

View File

@ -500,8 +500,11 @@
</div> </div>
<div class="ips-regra-empresa-observacoes" id="ips-review-regra-empresa-obs" hidden> <div class="ips-regra-empresa-observacoes" id="ips-review-regra-empresa-obs" hidden>
<p class="ips-regra-empresa-observacoes__label" id="ips-review-regra-empresa-obs-label"></p> <p class="ips-imp-resumo__tipos" id="ips-review-resumo-tipos"></p>
<p class="ips-regra-empresa-observacoes__texto" id="ips-review-regra-empresa-obs-texto"></p> <p class="ips-imp-resumo__linha" id="ips-review-resumo-mensalidade" hidden></p>
<p class="ips-imp-resumo__linha" id="ips-review-resumo-coparticipacao" hidden></p>
<p class="ips-regra-empresa-observacoes__label" id="ips-review-regra-empresa-obs-label" hidden></p>
<p class="ips-regra-empresa-observacoes__texto" id="ips-review-regra-empresa-obs-texto" hidden></p>
</div> </div>
<p class="modal-error" id="ips-review-error"></p> <p class="modal-error" id="ips-review-error"></p>