Revisão da regra do checklist

This commit is contained in:
Gabriel 2026-09-18 08:27:56 -03:00
parent aab55ec16e
commit 530e0e85e3
3 changed files with 44 additions and 15 deletions

View File

@ -312,3 +312,9 @@ Usuário pediu que a tabela de execuções (histórico de análises, `#dc-list-t
Usuário reportou, com print da aba Observações da revisão, que valores monetários dentro do texto dos achados apareciam sem padronização (ex.: "R$ 643545.85" em vez de "R$ 643.545,85"), inconsistente com o padrão já usado em Balancete/DRE/relatório "Gerar Dashboard". Causa raiz: `regras.py` interpola `Decimal` direto num f-string (`f"R$ {conta.saldo_atual}"`) pra montar `AchadoDetectado.mensagem` — usa a formatação padrão do Python (sem separador de milhar, ponto decimal), nunca o padrão BR. Corrigido com um helper novo, `_moeda()` (`regras.py`), duplicado localmente em vez de importado — mesmo padrão já usado (por arquivo) em `indicadores/recibo.py`/`custo_contratacao/pdf.py`/`templatetags/contabil_extras.py`, já que este pacote é Python puro. As 9 mensagens de achado que interpolavam `R$ {valor}` direto foram todas ajustadas. Usuário reportou, com print da aba Observações da revisão, que valores monetários dentro do texto dos achados apareciam sem padronização (ex.: "R$ 643545.85" em vez de "R$ 643.545,85"), inconsistente com o padrão já usado em Balancete/DRE/relatório "Gerar Dashboard". Causa raiz: `regras.py` interpola `Decimal` direto num f-string (`f"R$ {conta.saldo_atual}"`) pra montar `AchadoDetectado.mensagem` — usa a formatação padrão do Python (sem separador de milhar, ponto decimal), nunca o padrão BR. Corrigido com um helper novo, `_moeda()` (`regras.py`), duplicado localmente em vez de importado — mesmo padrão já usado (por arquivo) em `indicadores/recibo.py`/`custo_contratacao/pdf.py`/`templatetags/contabil_extras.py`, já que este pacote é Python puro. As 9 mensagens de achado que interpolavam `R$ {valor}` direto foram todas ajustadas.
Só vale pra achados gerados dali em diante — um achado já persistido mantém o texto antigo até a apuração ser reprocessada ou recriada; sem migração de dados pra reformatar texto já gravado (parsear número dentro de frase livre por regex arriscaria confundir valor monetário com código de conta, ex. "1.01.01.001"). Fora do escopo desta rodada: `regra_variacao_atipica_dre` ainda formata percentual com ponto decimal (`"12.34%"`, não `"12,34%"`) — mesma família de bug, não pedida desta vez. Detalhe técnico completo no `CLAUDE.md` desta pasta. Só vale pra achados gerados dali em diante — um achado já persistido mantém o texto antigo até a apuração ser reprocessada ou recriada; sem migração de dados pra reformatar texto já gravado (parsear número dentro de frase livre por regex arriscaria confundir valor monetário com código de conta, ex. "1.01.01.001"). Fora do escopo desta rodada: `regra_variacao_atipica_dre` ainda formata percentual com ponto decimal (`"12.34%"`, não `"12,34%"`) — mesma família de bug, não pedida desta vez. Detalhe técnico completo no `CLAUDE.md` desta pasta.
### 139. Bug real: cor "validado" não subia pra conta "mãe" quando só os "netos" eram marcados
Usuário reportou, com print de uma árvore de 3 níveis (Balancete), que uma conta "mãe" (ex. "CAIXA E EQUIVALENTES DE CAIXA") não ficava verde mesmo depois de toda conta-folha dentro dela ("netos", ex. os 4 bancos dentro de "DEPÓSITOS BANCÁRIOS A VISTA") já estarem validadas uma a uma — mesmo a conta "filha" intermediária ("DEPÓSITOS BANCÁRIOS A VISTA") já aparecendo corretamente verde. Causa raiz: `dcEstadoValidacaoGrupo()` (`dashboard-contabil.js`, tri-state do botão "validado" de uma sintética, ver rodada de "Tri-state numa conta/linha sintética") decidia o estado "completo" checando `descendentes.every(d => d.validado)` sobre **todos** os descendentes (filhos, netos, bisnetos), não só os de folha — quando os netos são marcados diretamente (sem clicar na própria "filha" intermediária pra cascatear), o campo `validado` da "filha" no banco continua `False` mesmo que ela já apareça "completa" visualmente (calculada a cada render); a "mãe", ao olhar todos os descendentes, incluía essa "filha" com `validado=False` e nunca fechava o grupo.
Corrigido restringindo o critério de "completo" só aos descendentes **folha** (sem filhos) — `dcDescendentes()` ganhou um quarto parâmetro opcional (`somenteFolhas`), reaproveitando o mesmo cálculo de "tem filho" já usado por `temFilhos`; `dcEstadoValidacaoGrupo(item, descendentes, descendentesFolhas)` passou a checar `descendentesFolhas.every(d => d.validado)` pro estado "completo", mantendo `descendentes` (todos) pro "parcial" (uma sintética marcada sozinha, sem cascatear, ainda deve sinalizar "em andamento" num ancestral). Os 4 pontos que calculam o estado (`dcValidadoInfo()` + os 3 handlers de clique — Balancete/D.R.E./Análise Vertical) foram atualizados pra passar os dois conjuntos. Puramente lógica de exibição/cálculo no frontend — nenhuma mudança de model/endpoint.

View File

@ -86,10 +86,10 @@ Diferente de `IndicadorApuracaoViewSet`/`ImportacaoPlanoSaudeViewSet` (onde a ch
Segundo botão ao lado do de observação (mesma célula, `.dc-obs-cell-actions`): um ícone de check (`.dc-conta-validado-btn`, cor `--teal` quando marcado — deliberadamente não `--accent`, que já é usado pra "observação preenchida" e também é a cor de tema escolhida pelo usuário, ver "Temas de cor" no `tokens.css`) pro campo novo `validado` — puramente informativo ("já conferi esta conta/linha durante a revisão"), sem gate em nada (não bloqueia conclusão, não afeta achado/relatório). PATCH via `pidAtualizarValidadoContaContabil`/`...LinhaDreContabil`. Segundo botão ao lado do de observação (mesma célula, `.dc-obs-cell-actions`): um ícone de check (`.dc-conta-validado-btn`, cor `--teal` quando marcado — deliberadamente não `--accent`, que já é usado pra "observação preenchida" e também é a cor de tema escolhida pelo usuário, ver "Temas de cor" no `tokens.css`) pro campo novo `validado` — puramente informativo ("já conferi esta conta/linha durante a revisão"), sem gate em nada (não bloqueia conclusão, não afeta achado/relatório). PATCH via `pidAtualizarValidadoContaContabil`/`...LinhaDreContabil`.
**Tri-state numa conta/linha sintética (com filhos)** (pedido explícito do usuário, rodada seguinte): uma folha continua um toggle simples (verde/cinza), mas uma sintética passa a ter 3 estados, calculados a cada render a partir dos **descendentes** (todos, não só os filhos diretos — `dcDescendentes(itens, nivelFn, id)`, generaliza `dcFilhosDiretos` pra não parar no primeiro nível) via `dcEstadoValidacaoGrupo(item, descendentes)`: **Tri-state numa conta/linha sintética (com filhos)** (pedido explícito do usuário, rodada seguinte): uma folha continua um toggle simples (verde/cinza), mas uma sintética passa a ter 3 estados, calculados a cada render a partir dos **descendentes** (todos, não só os filhos diretos — `dcDescendentes(itens, nivelFn, id)`, generaliza `dcFilhosDiretos` pra não parar no primeiro nível) via `dcEstadoValidacaoGrupo(item, descendentes, descendentesFolhas)`:
- `"nenhum"` (cor padrão) — nem a sintética nem nenhum descendente está validado. - `"nenhum"` (cor padrão) — nem a sintética nem nenhum descendente está validado.
- `"parcial"` (`--gold`, mesmo amarelo dos badges de severidade média) — qualquer combinação intermediária, **inclusive** só a própria sintética marcada (1º clique) sem nenhum descendente ainda. - `"parcial"` (`--gold`, mesmo amarelo dos badges de severidade média) — qualquer combinação intermediária, **inclusive** só a própria sintética marcada (1º clique) sem nenhum descendente ainda.
- `"completo"` (`--teal`, mesma cor de uma folha validada) — **todo** descendente está validado, checado primeiro e independente do campo `validado` da própria sintética (o que importa é "está tudo marcado embaixo", não se o cabeçalho do grupo foi clicado). - `"completo"` (`--teal`, mesma cor de uma folha validada) — **todo** descendente FOLHA está validado, checado primeiro. Usa só `descendentesFolhas` (`dcDescendentes(..., somenteFolhas=true)`), não `descendentes` completo — **bug real, rodada 139**: numa árvore de 3+ níveis (mãe → filha → netos), validar os netos direto sem clicar na própria "filha" intermediária nunca marca o campo `validado` dela no banco (só o estado visual dela é "completo", calculado por render); checar todo `descendentes` (incluindo a "filha") na "mãe" fazia o grupo nunca fechar, mesmo com todo neto validado. `descendentes` (todos, não só folha) continua valendo pro "parcial" — uma sintética marcada sozinha, sem cascatear, ainda deve sinalizar "em andamento" num ancestral.
`dcClicarValidadoConta(id)`/`dcClicarValidadoLinha(id)` (novas, chamadas pelo listener de clique de `[data-dc-conta-validado]`/`[data-dc-dre-validado]`) implementam o ciclo de 3 cliques pedido pelo usuário, recalculando o estado a cada clique (nunca guardado à parte): `dcClicarValidadoConta(id)`/`dcClicarValidadoLinha(id)` (novas, chamadas pelo listener de clique de `[data-dc-conta-validado]`/`[data-dc-dre-validado]`) implementam o ciclo de 3 cliques pedido pelo usuário, recalculando o estado a cada clique (nunca guardado à parte):
1. `"nenhum"` → PATCH só na própria sintética (`validado=true`) → vira `"parcial"` (a menos que, coincidentemente, todo descendente já estivesse validado). 1. `"nenhum"` → PATCH só na própria sintética (`validado=true`) → vira `"parcial"` (a menos que, coincidentemente, todo descendente já estivesse validado).

View File

@ -1295,7 +1295,12 @@ document.addEventListener("DOMContentLoaded", async () => {
// bisnetos...) — usado pelo estado tri-state do botão "validado" de uma // bisnetos...) — usado pelo estado tri-state do botão "validado" de uma
// conta/linha sintética (com filhos), ver dcEstadoValidacaoGrupo() logo // conta/linha sintética (com filhos), ver dcEstadoValidacaoGrupo() logo
// abaixo e o clique em [data-dc-conta-validado]/[data-dc-dre-validado]. // abaixo e o clique em [data-dc-conta-validado]/[data-dc-dre-validado].
function dcDescendentes(itens, nivelFn, id) { // `somenteFolhas` restringe aos descendentes SEM filhos (contas/linhas
// reais, nunca sintéticas) — usado pelo cálculo de "completo" de
// dcEstadoValidacaoGrupo (ver comentário lá) pra ignorar o campo
// `validado` de um descendente sintético intermediário, que nunca é
// marcado sozinho quando só os netos (folhas) são validados um a um.
function dcDescendentes(itens, nivelFn, id, somenteFolhas = false) {
const niveis = itens.map(nivelFn); const niveis = itens.map(nivelFn);
const idx = itens.findIndex((item) => item.id === id); const idx = itens.findIndex((item) => item.id === id);
const descendentes = []; const descendentes = [];
@ -1303,7 +1308,8 @@ document.addEventListener("DOMContentLoaded", async () => {
const nivelPai = niveis[idx]; const nivelPai = niveis[idx];
for (let j = idx + 1; j < itens.length; j++) { for (let j = idx + 1; j < itens.length; j++) {
if (niveis[j] <= nivelPai) break; if (niveis[j] <= nivelPai) break;
descendentes.push(itens[j]); const temFilho = j + 1 < itens.length && niveis[j + 1] > niveis[j];
if (!somenteFolhas || !temFilho) descendentes.push(itens[j]);
} }
return descendentes; return descendentes;
} }
@ -1321,13 +1327,23 @@ document.addEventListener("DOMContentLoaded", async () => {
// Estado do botão "validado" de uma conta/linha COM filhos (sintética) — // Estado do botão "validado" de uma conta/linha COM filhos (sintética) —
// pedido explícito do usuário: "nenhum" (cor padrão) quando nem ela nem // pedido explícito do usuário: "nenhum" (cor padrão) quando nem ela nem
// nenhum descendente está validado; "completo" (verde, mesma cor de uma // nenhum descendente está validado; "completo" (verde, mesma cor de uma
// folha validada) quando TODO descendente está validado — independente do // folha validada) quando TODO descendente está validado; "parcial"
// campo `validado` da própria sintética, já que "está tudo marcado" é o // (amarelo) em qualquer combinação intermediária — inclui o caso de só a
// que importa; "parcial" (amarelo) em qualquer combinação intermediária — // própria sintética ter sido marcada (primeiro clique), sinalizando "em
// inclui o caso de só a própria sintética ter sido marcada (primeiro // andamento" até os descendentes acompanharem.
// clique), sinalizando "em andamento" até os descendentes acompanharem. //
function dcEstadoValidacaoGrupo(item, descendentes) { // "completo" usa só os descendentes FOLHA (`descendentesFolhas`, sem
if (descendentes.length && descendentes.every((d) => d.validado)) return "completo"; // filhos), não todo `descendentes` (que inclui sintéticas intermediárias
// — ex.: uma "mãe" com "filhas" que por sua vez têm "filhos"/netos): o
// campo `validado` de uma sintética intermediária nunca é marcado sozinho
// quando o usuário valida os netos um a um direto (sem clicar na própria
// filha) — usar todo `descendentes` nesse caso fazia a mãe nunca virar
// "completo" mesmo com todos os netos validados, já que a filha
// intermediária ficava com `validado=false` na checagem. `descendentes`
// (todo a árvore) continua valendo pro "parcial" — uma sintética marcada
// sozinha (sem cascatear) ainda deve sinalizar "em andamento" no ancestral.
function dcEstadoValidacaoGrupo(item, descendentes, descendentesFolhas) {
if (descendentesFolhas.length && descendentesFolhas.every((d) => d.validado)) return "completo";
if (item.validado || descendentes.some((d) => d.validado)) return "parcial"; if (item.validado || descendentes.some((d) => d.validado)) return "parcial";
return "nenhum"; return "nenhum";
} }
@ -1344,7 +1360,11 @@ document.addEventListener("DOMContentLoaded", async () => {
pressed: item.validado, pressed: item.validado,
}; };
} }
const estado = dcEstadoValidacaoGrupo(item, dcDescendentes(itens, nivelFn, item.id)); const estado = dcEstadoValidacaoGrupo(
item,
dcDescendentes(itens, nivelFn, item.id),
dcDescendentes(itens, nivelFn, item.id, true)
);
if (estado === "completo") { if (estado === "completo") {
return { return {
classe: " dc-conta-validado-btn--validado", classe: " dc-conta-validado-btn--validado",
@ -1394,7 +1414,8 @@ document.addEventListener("DOMContentLoaded", async () => {
} }
const descendentes = dcDescendentes(contas, dcContaNivel, id); const descendentes = dcDescendentes(contas, dcContaNivel, id);
const estado = dcEstadoValidacaoGrupo(conta, descendentes); const descendentesFolhas = dcDescendentes(contas, dcContaNivel, id, true);
const estado = dcEstadoValidacaoGrupo(conta, descendentes, descendentesFolhas);
if (estado === "completo") { if (estado === "completo") {
const alvos = [conta, ...descendentes].filter((item) => item.validado); const alvos = [conta, ...descendentes].filter((item) => item.validado);
@ -1442,7 +1463,8 @@ document.addEventListener("DOMContentLoaded", async () => {
} }
const descendentes = dcDescendentes(linhas, nivelFn, id); const descendentes = dcDescendentes(linhas, nivelFn, id);
const estado = dcEstadoValidacaoGrupo(linha, descendentes); const descendentesFolhas = dcDescendentes(linhas, nivelFn, id, true);
const estado = dcEstadoValidacaoGrupo(linha, descendentes, descendentesFolhas);
if (estado === "completo") { if (estado === "completo") {
const alvos = [linha, ...descendentes].filter((item) => item.validado); const alvos = [linha, ...descendentes].filter((item) => item.validado);
@ -1491,7 +1513,8 @@ document.addEventListener("DOMContentLoaded", async () => {
} }
const descendentes = dcDescendentes(linhas, nivelFn, id); const descendentes = dcDescendentes(linhas, nivelFn, id);
const estado = dcEstadoValidacaoGrupo(linha, descendentes); const descendentesFolhas = dcDescendentes(linhas, nivelFn, id, true);
const estado = dcEstadoValidacaoGrupo(linha, descendentes, descendentesFolhas);
if (estado === "completo") { if (estado === "completo") {
const alvos = [linha, ...descendentes].filter((item) => item.validado); const alvos = [linha, ...descendentes].filter((item) => item.validado);