# Changelog — Dashboard Contábil > Histórico específico desta aplicação, extraído de `plano.md` (mesma numeração de rodada usada lá, para referência cruzada). ### 92. Dashboard Contábil (Relatórios > Contabilidade) — v1: execução, auditoria e análise Pedido: otimizar a conferência de balancetes hoje feita manualmente pelo Fisco/Contábil (`ITD-FISCO-7513`). Nova aplicação em Relatórios > Contabilidade: o contador anexa o PDF de Balancete + DRE (modelo Questor, mesmo relatório hoje enviado ao cliente), a ferramenta extrai as contas e roda um motor de 10 regras de auditoria (balanceamento Ativo x Passivo, débito ≠ crédito, saldo negativo de caixa, contas transitórias/genéricas com saldo, contas que deveriam ficar zeradas, sinal de saldo invertido, variação atípica de saldo/DRE mês a mês, percentual custo/receita fora do padrão da própria empresa), apresentando os achados numa tela de revisão com observações por conta e conclusão da análise. Decisões confirmadas com o usuário: entrada só em PDF (levantada a alternativa de XLSX estruturado do Questor, mais confiável de extrair, mas mantido PDF como pedido originalmente); histórico de variação mês a mês fica no próprio banco do Portal, não depende de nenhum relatório complementar anexado; nasce restrita ao perfil "Inovação" (dado financeiro de cliente sensível, mesmo padrão de "Não Conformidades"); o botão "Gerar Dashboard HTML" (que substituiria o BI Contábil por um relatório final ao administrador da empresa, com exportação em XLSX) fica **desabilitado** nesta rodada, escopo de uma rodada futura. Desafio técnico principal: o relatório Questor de Balancete/DRE desenha cada caractere em posição própria e inclui, por baixo do texto real, uma grade densa de caracteres de espaço cobrindo toda a linha — isso quebra a extração padrão do pdfplumber (`extract_words`/`extract_text`), que trata esses espaços como separadores reais e fragmenta números em dígitos isolados. Resolvido reconstruindo cada linha direto de `page.chars`, ignorando espaços literais e reinserindo um só quando o vão horizontal indica uma quebra de campo real — validado rodando de fato contra o PDF real de referência do usuário antes de escrever o parser definitivo. 4 models novos (`ContabilApuracao`/`ContabilConta`/`ContabilLinhaDre`/`ContabilAchado`, migração `0055`), pacote `portal_api/dashboard_contabil/` sem ORM (`parser.py`/`regras.py`/`pipeline.py`/`modelos.py`), subgrupo "Contabilidade" novo em `catalogo.py` dentro de `relatorios`. Detalhe técnico completo no `CLAUDE.md` desta pasta. ### 93. Dashboard Contábil — DRE agrupada por árvore + botão "Gerar Dashboard" implementado A DRE (aba "DRE" da revisão) ganhou a mesma árvore recolhível que o Balancete já tinha (`renderDre()` reaproveita o algoritmo de `renderContas()`, agora sobre `ContabilLinhaDre.nivel`). O botão "Gerar Dashboard" (renomeado de "Gerar Dashboard HTML") saiu do estado desabilitado da rodada 92: gera um documento HTML autocontido, com a marca do escritório, com indicadores financeiros (ROA, ROE, Kanitz, EBIT, EBITDA, Liquidez Corrente/Seca/Geral, Composição/Grau de Endividamento, IPL), gráfico de evolução do Resultado Líquido (Chart.js via CDN), DRE/Balancete agrupados e as observações que o contador já registrou na aplicação. Escopo confirmado com o usuário: sempre uma apuração por vez (sem "Filial"/consolidação multi-empresa do BI antigo que este relatório substitui, fora de escopo). Novo módulo `indicadores.py` (funções puras): grupos do Balancete por código fixo calibrado contra o balancete de referência (mesmo risco já aceito em `regra_saldo_negativo_caixa`), com o Passivo Não Circulante calculado por eliminação (sempre exato, não precisa de mais um código). Liquidez Corrente/Seca, Composição/Grau de Endividamento e IPL validados byte a byte contra a captura de tela do usuário (bateram exatamente). EBITDA fica indisponível na primeira apuração de uma empresa (precisa da variação de Depreciação/Amortização entre dois meses). Kanitz usa a fórmula padrão, não validada contra o BI antigo (valor de referência do usuário fora da faixa clássica do índice) — marcado como estimativa no relatório. O relatório é dividido em 3 abas (Balancete → D.R.E. → Indicadores, nessa ordem pedida pelo usuário), com "Observações da Análise" fora das abas, sempre visível; na impressão a barra de abas some e as 3 aparecem seguidas, cada uma numa página. Nova action `ContabilApuracaoViewSet.dashboard()` renderiza `templates/dashboard-contabil-relatorio.html` via `render_to_string` e devolve `text/html` puro — primeiro documento HTML autocontido do Portal (até então só PDF/CSV/ZIP). Novo `portal_api/templatetags/contabil_extras.py` (primeiro uso de template tags customizadas no projeto) com filtros `moeda`/`percentual`/`indice`/`competencia`. Detalhe técnico completo (todas as fórmulas e limitações conhecidas) no `CLAUDE.md` desta pasta. **Dois ajustes, ainda na mesma rodada, antes do usuário testar de fato**: Balancete e D.R.E. do relatório ganharam a mesma árvore recolhível da tela de revisão (calculada no servidor — `_contabil_arvore_contexto()` em `views.py` — já que o relatório é HTML estático, não uma SPA); e a logo do escritório, que não estava carregando, foi corrigida trocando a action de POST pra **GET** — a causa raiz era o frontend abrir o resultado via `fetch`+blob (`URL.createObjectURL`), e um documento carregado de uma URL `blob:` tem origem sintética, quebrando a URL relativa `{% static %}` da logo; com GET, o frontend só faz `window.open()` direto na URL da API (navegação de verdade, sem blob). **Terceiro ajuste, mesmo dia**: usuário achou o visual simples demais e pediu mais bonito/dinâmico/animado. Fontes "Manrope"/"Inter", gradiente+glow no cabeçalho, ícones SVG, abas com indicador deslizante, contagem animada nos cards (sempre terminando no valor exato já formatado pelos filtros Django, nunca recalculado em JS) e cor por sinal/limiar só onde é seguro sem inventar nada (ROA/ROE/EBIT/EBITDA por sinal, Liquidez por ≥1 — convenções de mercado; Kanitz/Endividamento/IPL ficam sem cor, sem limiar validado). Toda animação de entrada segue a regra de nunca fixar `opacity:0` fora de `@keyframes`, pra `@media print` bastar sozinho pra devolver tudo ao normal na impressão. Corrigido também um bug de condição de corrida entre dois listeners de `beforeprint` que competiam entre si quando a aba "Indicadores" nunca tinha sido aberta. Detalhe completo no `CLAUDE.md` desta pasta ("Visual"). ### 94. Dashboard Contábil — observações do relatório redistribuídas por aba Pedido: a seção única "Observações da Análise" (fora de todas as abas) misturava observações de Balancete, D.R.E. e achados de auditoria juntas, sem separar por contexto. Passou a ficar assim: a aba **Balancete** termina com "Observações do Balancete" (só as observações de conta); a aba **D.R.E.** termina com "Observações da D.R.E." (só as observações de linha); a aba **Indicadores** termina com "Todas as Observações da Análise" — as três listas juntas (contas + linhas de DRE + achados), cada item prefixado com a origem ("Balancete — ...", "D.R.E. — ...", "Auditoria — ..."), já que ali não há mais uma aba pra dar esse contexto sozinha. Puramente reorganização de template (`dashboard-contabil-relatorio.html`) — as três listas já vinham prontas do backend (`views.py`), nenhuma mudança de backend foi necessária. **Correção ainda na mesma rodada**: o prefixo do terceiro grupo tinha nascido "Achado de Auditoria —", contrariando um pedido já feito antes pelo usuário de nunca expor a palavra "achado" em texto visível (mesmo motivo pelo qual a aba de revisão já se chama "Observações", não "Achados" — `dashboard-contabil.html`, `data-dc-tab="achados"` com o texto "Observações"). Trocado pra "Auditoria —", no mesmo padrão dos outros dois grupos (nomeado pela origem/demonstração, não pelo tipo de registro interno). Vale como regra geral pra qualquer texto novo desta aplicação: `achado`/`Achado` só em nome de variável/model/classe CSS, nunca em texto visível ao usuário. Detalhe completo no `CLAUDE.md` desta pasta. ### 95. Card de achado (tela de revisão) expande a conta usada no apontamento Pedido: nos cards de "Observações" (aba Achados da revisão), permitir expandir e ver a conta do Balancete que embasou aquele apontamento — até então só o título/mensagem da regra apareciam, sem o dado de origem. `renderAchados()` (`dashboard-contabil.js`) resolve `achado.conta` (id) pra objeto completo procurando em `apuracaoAtual.contas`, já carregado junto na mesma resposta de `/api/contabil-apuracoes/{id}/` — nenhuma chamada de API nova, nenhuma mudança de backend. Um botão "Ver conta usada no apontamento" aparece só nos achados vinculados a uma conta específica (achados das regras 3-7, que têm `conta` preenchida); as duas regras gerais (balanceamento Ativo x Passivo, débito ≠ crédito) não mostram o botão, já que não têm uma conta única por trás. Expandido, mostra código/descrição/saldo anterior/débito/crédito/saldo atual da conta, num `