From 530e0e85e310f67ec9ba5cced3b4850709df8fc2 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 18 Sep 2026 08:27:56 -0300 Subject: [PATCH] =?UTF-8?q?Revis=C3=A3o=20da=20regra=20do=20checklist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- portal_api/dashboard_contabil/CHANGELOG.md | 6 +++ portal_api/dashboard_contabil/CLAUDE.md | 4 +- static/js/dashboard-contabil.js | 49 ++++++++++++++++------ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/portal_api/dashboard_contabil/CHANGELOG.md b/portal_api/dashboard_contabil/CHANGELOG.md index e2420d9..3b372e5 100644 --- a/portal_api/dashboard_contabil/CHANGELOG.md +++ b/portal_api/dashboard_contabil/CHANGELOG.md @@ -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. 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. diff --git a/portal_api/dashboard_contabil/CLAUDE.md b/portal_api/dashboard_contabil/CLAUDE.md index e86d27d..d0831b7 100644 --- a/portal_api/dashboard_contabil/CLAUDE.md +++ b/portal_api/dashboard_contabil/CLAUDE.md @@ -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`. -**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. - `"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): 1. `"nenhum"` → PATCH só na própria sintética (`validado=true`) → vira `"parcial"` (a menos que, coincidentemente, todo descendente já estivesse validado). diff --git a/static/js/dashboard-contabil.js b/static/js/dashboard-contabil.js index 7b92ef4..fc4d77d 100644 --- a/static/js/dashboard-contabil.js +++ b/static/js/dashboard-contabil.js @@ -1295,7 +1295,12 @@ document.addEventListener("DOMContentLoaded", async () => { // bisnetos...) — usado pelo estado tri-state do botão "validado" de uma // conta/linha sintética (com filhos), ver dcEstadoValidacaoGrupo() logo // 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 idx = itens.findIndex((item) => item.id === id); const descendentes = []; @@ -1303,7 +1308,8 @@ document.addEventListener("DOMContentLoaded", async () => { const nivelPai = niveis[idx]; for (let j = idx + 1; j < itens.length; j++) { 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; } @@ -1321,13 +1327,23 @@ document.addEventListener("DOMContentLoaded", async () => { // 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 // nenhum descendente está validado; "completo" (verde, mesma cor de uma - // folha validada) quando TODO descendente está validado — independente do - // campo `validado` da própria sintética, já que "está tudo marcado" é o - // que importa; "parcial" (amarelo) em qualquer combinação intermediária — - // inclui o caso de só a própria sintética ter sido marcada (primeiro - // clique), sinalizando "em andamento" até os descendentes acompanharem. - function dcEstadoValidacaoGrupo(item, descendentes) { - if (descendentes.length && descendentes.every((d) => d.validado)) return "completo"; + // folha validada) quando TODO descendente está validado; "parcial" + // (amarelo) em qualquer combinação intermediária — inclui o caso de só a + // própria sintética ter sido marcada (primeiro clique), sinalizando "em + // andamento" até os descendentes acompanharem. + // + // "completo" usa só os descendentes FOLHA (`descendentesFolhas`, sem + // 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"; return "nenhum"; } @@ -1344,7 +1360,11 @@ document.addEventListener("DOMContentLoaded", async () => { 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") { return { classe: " dc-conta-validado-btn--validado", @@ -1394,7 +1414,8 @@ document.addEventListener("DOMContentLoaded", async () => { } 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") { const alvos = [conta, ...descendentes].filter((item) => item.validado); @@ -1442,7 +1463,8 @@ document.addEventListener("DOMContentLoaded", async () => { } 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") { const alvos = [linha, ...descendentes].filter((item) => item.validado); @@ -1491,7 +1513,8 @@ document.addEventListener("DOMContentLoaded", async () => { } 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") { const alvos = [linha, ...descendentes].filter((item) => item.validado);