portal_publico/portal_api/nao_conformidades/CHANGELOG.md

13 KiB
Raw Blame History

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ão Tipo 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 de clientes_maior_incidencia/colaboradores_maior_incidencia de {..., quantidade} pra {..., nc, reclamacao, outros, total} (Count condicional por tipo na mesma .values().annotate()); o filtro tipo_ocorrencia__in=TIPOS_NC que 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) em NCOcorrencia) e "Abertura da ação" (Avg(acao.data_emissao - ocorrencia.data_analise) em NCAcao), via ExpressionWrapper+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ção estã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-chip de indicador-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__toggle de custo-contratacao.css/.js) que revela um painel abaixo da linha (.ncf-detail-row), lazy — só busca GET /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. NCOcorrenciaDetailSerializer ganhou os campos que faltavam (origem, fornecedores_relacionados, riscos_relacionados, data_relato, representante_gerente, prazo_finalizar); NCOcorrenciaSerializer (base, usado pelas listagens) ganhou emissor_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 do GET /acoes/{id}/ que já devolve a lista completa).
  • 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: NCAcaoViewSet trocou ?vencidas=/?vence_em= por um único ?status_prazo=<bucket> (_filtra_status_prazo, replica os cortes de classificacao.status_prazo() como filtro de data sobre vencimento_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. NCAcaoSerializer ganhou ocorrencia_clientes/ocorrencia_area (campos só-leitura via source="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 (NCAcao nã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.