13 KiB
Changelog — Não Conformidades
Histórico específico desta aplicação, extraído de
plano.md(mesma numeração de rodada usada lá, para referência cruzada).
Rodada 88 — Não Conformidades (Relatórios > Qualidade)
Pedido: a área de Qualidade tinha uma skill do Claude (analise-ncs, ver projects/Controladoria - Analise NCs/) que gerava, sob demanda, um Excel de 7 abas a partir de dois arquivos exportados do Sigsistem (ocorrências .xlsx + ações .xls, esse último na verdade HTML). O usuário pediu uma aplicação dentro do Portal que evoluísse essa skill pra uma gestão contínua: identificar com facilidade novas ocorrências sem análise (cobrança), análises novas sem ação aberta, acompanhamentos de ação recentes ainda sem tratativa da Qualidade, e priorizar ações vencidas/vencendo em 7 dias — com a possibilidade de marcar manualmente o que já foi tratado, e um dashboard quantitativo (ações abertas, motivos de abertura, clientes/colaboradores com maior incidência).
Decisões de negócio confirmadas com o usuário antes de implementar:
- O status de "tratado" pela Qualidade é um controle interno do Portal, sem escrita de volta no Sigsistem (sistema externo, sem integração) — mas precisa reabrir sozinho quando uma importação nova trouxer algo diferente do que existia no momento em que foi tratado (ex.: novo acompanhamento numa ação já marcada tratada).
- Guardar o histórico completo de acompanhamentos de cada ação, não só o último (diferente da skill original).
- "Colaborador com maior incidência" no dashboard = coluna
Indicado para Descrever Análise da Ocorrência; "motivo de abertura" =Tipo(s) de Causa(s) da Ocorrência(nãoTipo de Ocorrência). - Permissão de toggle único (mesmo padrão de Indicador de Desempenho/Importação de Plano de Saúde), restrita por padrão — só "Integração e Inovação" nasce com acesso, diferente de "Relatório Setorial" (que herda automático via
SECTORAL_KEYS).
O que foi construído: pacote Python puro portal_api/nao_conformidades/ (parsing sem pandas, mesmo padrão de planos_saude/indicadores — validado que o volume real, ~236 ocorrências/290 ações/929 acompanhamentos, não justifica introduzir pandas como dependência direta), portando a lógica de referência do script gerar_relatorio.py da skill (inclusive o parsing por regex do .xls-que-é-HTML, com uma diferença: aqui se guarda TODAS as entradas de acompanhamento, não só a última). 4 models novos (NaoConformidadeImportacao, NCOcorrencia, NCAcao, NCAcompanhamento, migrações 0052/0053), 3 ModelViewSet + 1 function view de dashboard, subgrupo qualidade novo em catalogo.MODULE_APPS["relatorios"] (mesmo formato de subgrupo-com-uma-tool já usado em Auditorias), override em seed_portal.py pra restringir o acesso por padrão, e uma tela nova com 3 abas (Gestão/Dashboard/Importar).
Núcleo técnico — reabertura automática por diff (nao_conformidades/diff.py): cada NCOcorrencia/NCAcao guarda um snapshot_tratativa (congelado no momento em que a Qualidade marca como tratada); a cada importação nova, se o item já estava tratado, o snapshot congelado é comparado contra o estado recém-extraído — ação reabre por comparação de data do último acompanhamento (robusto contra reformatação cosmética do export); ocorrência reabre por hash de texto da análise (normalizado) ou por uma ação nova ter sido aberta. Validado contra a amostra real (projects/Controladoria - Analise NCs/ocorrencias_pa_27082026.xlsx/acoes_27082026.xls) via chamada direta da função de diff simulando um acompanhamento mais recente — reproduziu o motivo textual esperado.
Validação end-to-end com dados reais: subiu o servidor local, logou como gabriel, fez upload dos dois arquivos de amostra pela API — os totais bateram com o relatório de referência da skill (Relatorio_Qualidade_20260827.xlsx) em todos os indicadores (236 ocorrências, 13 sem análise, 8 análise sem ação, 38 NCs sem ação corretiva, 2 ações vencidas, 130 vencendo em ≤7 dias), exceto "Total de Ações Abertas" (290 aqui vs. 311 na skill) — a skill original contava também linhas de ocorrência sem nenhuma ação de fato aberta (artefato do script de referência, todas = v.copy() sem filtrar por Código da Ação presente); esta ferramenta conta só ações reais, decisão deliberada de manter (documentada em CLAUDE.md). Reimportar o mesmo arquivo é idempotente (0 novos, tudo "atualizado", 0 acompanhamentos duplicados via hash). Endpoints de marcar-tratado/reabrir (ocorrência e ação) testados via API.
Dois bugs de robustez corrigidos durante a validação com dados reais: (1) assunto estourava CharField(max_length=255) — um export real trouxe um valor com 395 caracteres (texto livre do Sigsistem, sem limite aparente); virou TextField, migração 0053. (2) o serializer de ocorrência tentava ler obj.sem_analise (propriedade que só existe no dataclass OcorrenciaExtraida, não no model Django NCOcorrencia) — corrigido pra computar not obj.descricao_analise.strip() direto no SerializerMethodField.
Não testado nesta rodada: a interação real em navegador (não havia ferramenta de automação de browser disponível ainda neste ambiente) — a tela foi construída seguindo rigorosamente os padrões visuais/estruturais já validados de importacao-plano-saude.html/indicador-desempenho.html, o parsing/pipeline/API foram validados de ponta a ponta com dados reais via chamadas HTTP diretas, e o HTML renderizado pelo Django foi conferido — mas o fluxo de clique/preenchimento na tela em si ainda precisava de uma primeira conferência visual. Resolvido na rodada seguinte (ver abaixo).
Rodada 89 — Redesign de Dashboard e Gestão, a partir de teste real na tela
Depois de testar a tela construída na rodada 88, o usuário deu uma lista concreta de feedback de UX/design. Mudanças:
Dashboard:
- Vira a aba padrão ao abrir a tela (era Gestão) — dá o resumo geral primeiro.
- Cards ("Ações vencidas", "Vence em ≤7 dias", "Ocorrências sem análise", "Análises sem ação") viram clicáveis: levam pra Gestão já com o filtro/seção correspondente aplicado (drill-down). "Ocorrências abertas"/"Ações abertas" (totais) e "NCs sem ação corretiva" continuam só informativos — decisão do usuário, sem lista equivalente pra este último.
- Linhas dos 3 rankings (Motivos/Clientes/Colaboradores) também ficam clicáveis: preenchem a busca da Gestão (ver abaixo) com aquele valor.
- Rankings de Clientes/Colaboradores passam a mostrar 3 contagens (NC vermelho, Reclamação de Cliente dourado, Outros neutro) + um total, em vez de um número só — decisão explícita do usuário depois de perguntado se devia restringir o filtro a NC/Reclamação (como "Clientes" já fazia) ou mostrar tudo com clareza visual: "pode trazer tudo nos dois casos... tratar com cores o tipo de ocorrência e depois um totalizador". Um botão
?/tooltip + legenda (3 bolinhas coloridas) explicam o critério. Endpoint/dashboard/mudou o formato declientes_maior_incidencia/colaboradores_maior_incidenciade{..., quantidade}pra{..., nc, reclamacao, outros, total}(Countcondicional por tipo na mesma.values().annotate()); o filtrotipo_ocorrencia__in=TIPOS_NCque só "Clientes" tinha antes foi removido (os dois rankings agora agrupam todos os tipos). - Dois KPIs de tempo médio novos, não interativos, sobre todo o histórico (sem filtro de período): "Preenchimento da análise" (
Avg(data_analise - data_emissao)emNCOcorrencia) e "Abertura da ação" (Avg(acao.data_emissao - ocorrencia.data_analise)emNCAcao), viaExpressionWrapper+DurationField+Avg. Validado contra a amostra real: ~2,7 e ~18,7 dias. Sem um terceiro KPI de "tempo médio de resolução" — checado antes de implementar:Data de Finalização/Dias para Finalizaçãoestão 100% em branco no export real (confirma o que já estava documentado — todas as ações exportadas estão em aberto), e o usuário confirmou explicitamente pra não criar um card pra um dado que não existe ("pode não fazer o card, pois não temos esta informação"). - Seletor de período virou uma fileira de chips (
.ncf-chip/.ncf-chip-row, mesmo padrão visual de.ind-departamento-chipdeindicador-desempenho.css, duplicado aqui porque essa folha não é carregada nesta página) — antes era um<select>nativo que destoava do resto do Portal.
Gestão:
- Cada linha das 3 listas (Sem Análise/Análise sem Ação/Ações) ganhou um botão de expandir (
.ncf-expand-btn, chevron que gira 90°, mesmo padrão de.cc-painel-fiscal__toggledecusto-contratacao.css/.js) que revela um painel abaixo da linha (.ncf-detail-row), lazy — só buscaGET /ocorrencias/{id}/ou/acoes/{id}/na primeira vez que é aberta.- Ocorrência: mostra tudo que faltava na linha (relato completo, descrição da análise completa, cliente(s), pessoas/fornecedores relacionados, riscos relacionados, origem, prazo pra finalizar, ações vinculadas) — resolve os dois pedidos separados do usuário ("ver qual é aquela ocorrência" e "ver o texto todo da análise") com um único mecanismo, já que a maioria dos campos (causa, análise, cliente) já vem na própria listagem e só o resto (relato, origem etc.) precisa do fetch.
NCOcorrenciaDetailSerializerganhou os campos que faltavam (origem,fornecedores_relacionados,riscos_relacionados,data_relato,representante_gerente,prazo_finalizar);NCOcorrenciaSerializer(base, usado pelas listagens) ganhouemissor_relato(fixando uma coluna "Emissor" que sempre mostrava "—" desde a rodada 88, por não estar no serializer usado pela lista). - Ação: removido o botão "Histórico" separado (ficava colado no "Marcar como tratado") e o modal
#ncf-acompanhamentos-modal— agora é o mesmo painel de expandir, mostrando o texto completo da ação + o histórico de acompanhamentos ordenado do mais recente pro mais antigo, 3 primeiros visíveis com "Mostrar mais (N)" revelando o resto (tudo client-side, a partir doGET /acoes/{id}/que já devolve a lista completa).
- Ocorrência: mostra tudo que faltava na linha (relato completo, descrição da análise completa, cliente(s), pessoas/fornecedores relacionados, riscos relacionados, origem, prazo pra finalizar, ações vinculadas) — resolve os dois pedidos separados do usuário ("ver qual é aquela ocorrência" e "ver o texto todo da análise") com um único mecanismo, já que a maioria dos campos (causa, análise, cliente) já vem na própria listagem e só o resto (relato, origem etc.) precisa do fetch.
- A coluna única "Status" das Ações (que misturava vencimento e tratativa, confuso — usuário perguntou "qual o critério de separação") virou duas colunas: Prazo (Vencida/Vence em ≤7/≤30 dias/No prazo/Sem vencimento) e Tratativa (Pendente/Tratado + selo Reaberto). O filtro virou dois grupos de chips combináveis (Prazo × Tratativa) em vez de uma lista só. Backend:
NCAcaoViewSettrocou?vencidas=/?vence_em=por um único?status_prazo=<bucket>(_filtra_status_prazo, replica os cortes declassificacao.status_prazo()como filtro de data sobrevencimento_efetivo). - Campo de busca único no topo da Gestão (
#ncf-gestao-search) filtra as 3 listas ao mesmo tempo, client-side (sobre os dados já carregados, sem round-trip — volume atual não justifica), por colaborador/responsável/emissor/cliente/assunto.NCAcaoSerializerganhouocorrencia_clientes/ocorrencia_area(campos só-leitura viasource="ocorrencia.…") pra permitir buscar/exibir Ações por empresa — o segundo também corrigiu a coluna "Área" da tabela de Ações, que sempre mostrava "—" desde a rodada 88 (NCAcaonão tem campo próprio de área, é da ocorrência).
Bug de corrida corrigido durante o teste: ativarTab() tinha um efeito colateral (carregava a Gestão sozinha ao trocar de aba); os drill-downs do Dashboard chamavam ativarTab("gestao") E também recarregavam explicitamente com o filtro escolhido — as duas chamadas concorrentes escreviam na mesma lista em memória, e a que terminasse por último vencia (às vezes a errada, mostrando "Todas" em vez do filtro clicado). Corrigido tornando ativarTab() puramente de UI (nunca dispara carregamento) — só quem clica explicitamente num botão de aba ou numa função de drill-down decide carregar, sempre com await, nunca em paralelo com outra chamada à mesma lista. Também corrigido um bug de CSS: o painel de detalhe (única <td> da linha, portanto sempre "a última") herdava text-align:right de .pa-table td:last-child, deixando o texto do painel alinhado à direita — resolvido com uma classe própria de maior especificidade (.pa-table td.ncf-detail-cell).
Validado com Playwright (instalado ad-hoc neste ambiente pra esta rodada — npx playwright install chromium + um script Node local, não é dependência do projeto): login, drill-down dos 4 cards, clique numa linha de ranking, expandir ocorrência e ação, alternar tratativa (marcar/reabrir) com o filtro "Tratadas" refletindo a mudança, filtro combinado Prazo+Tratativa+busca — sem erros de console/rede além do 401 esperado da checagem de sessão antes do login.