44 KiB
Changelog — Indicador de Desempenho
Histórico específico desta aplicação, extraído de
plano.md.Formato: uma entrada por rodada,
### Rodada N — Título; quando a rodada não tem número registrado,### Títulosó. A numeração de rodada não é global — cada aplicação conta as próprias, e o mesmo número designa trabalhos diferentes em arquivos diferentes. Ao citar uma rodada, sempre nomear o arquivo. Ver o topo deplano.md.
Rodada 38 — Indicador de Desempenho (Geradoc)
Pedido (2026-08-12): substituir a apuração manual do indicador de desempenho do Fiscontábil — feita numa planilha (projects/Indicadores/FISCO CONTABIL *.ods) com fórmulas quebradas por edições manuais acumuladas — por uma ferramenta completa dentro de Geradoc, com histórico de apurações mensais e recibo em PDF por colaborador. A maior feature construída até aqui em número de models/endpoints (6 models, 6 ModelViewSet, pacote de negócio próprio portal_api/indicadores/ com 6 arquivos).
Escopo confirmado com o usuário: v1 cobre só o Fiscontábil (papéis Balancete/Liberação Fiscal/Conciliação); o "tipo" do colaborador é derivado por empresa via a planilha "Serviços Tareffa", não é cadastro; só 3 critérios (entrega de balancetes/liberações fiscais/conciliações no prazo) são calculados automaticamente, todo o resto é marcação manual do RH; critérios e percentuais por tipo são cadastros genéricos editáveis pela tela, não hardcoded (percentuais nunca editados in-place, só um histórico com vigente_desde); ajuste manual em dois níveis (por critério, e pelo percentual agregado Individual/Grupo/Departamento, este último aplicado de uma vez a todo o grupo/departamento — "cada gerente representa um grupo"); recibo é documento interno do RH, sem visão do próprio colaborador nesta v1. Validado com dados reais de 43 colaboradores, o que revelou e corrigiu dois bugs de robustez: openpyxl em modo read_only precisa de .close() explícito no Windows (senão bloqueia excluir o upload depois) e campos percentuais precisaram de max_digits=7 (não 6) pra não estourar em cálculos que batem exatamente 100%. Primeiro histórico populado via seed_indicador_desempenho (idempotente), com os valores exatos da planilha antiga. Migrações 0024 a 0028. Detalhe completo em CLAUDE.md desta pasta.
Ajuste pequeno feito logo depois de testar a tela de revisão no navegador: a coluna "Meta (%)" da tabela "Metas de Grupo e Departamento" era um campo de texto livre (permitindo qualquer percentual) — o usuário apontou que, pra Grupo/Departamento, só existem duas possibilidades reais ("será pago ou não"), então o campo virou um <select> Sim/Não (100%/0%), reaproveitando o mesmo raciocínio já aplicado aos critérios individuais de Grupo/Departamento (que também só têm Sim/Não, sem "não se aplica"/"não faz"). O percentual Individual de cada colaborador (composição ponderada dos 3 níveis) continua livre, por poder ser legitimamente fracionário.
Segundo ajuste, também depois de testar no navegador: o resumo do card de cada colaborador só mostrava "Individual: X%", sem explicar como esse número foi composto. Pedido: mostrar a composição completa (ex.: "Individual: 57,14% (peso 60%) Grupo: 100% (peso 10%) Departamento: 100% (peso 30%) Total Indicador: 74,29%"), com cada um dos 3 níveis em verde/vermelho conforme foi atingido (100%) ou não; o Total em si fica neutro (sem cor), pra não repetir a mesma informação 4 vezes. O percentual bruto de Individual (antes da composição) e o peso de cada nível eram calculados em calculo.recalcula_colaborador mas descartados depois de usados — extraída a lógica de filtro de respostas por nível pra uma função reaproveitável (respostas_aplicaveis) e adicionada composicao_individual(), exposta como campo computado (composicao_individual) em IndicadorApuracaoColaboradorSerializer, sem nenhuma migração (não persiste nada novo, só reconstrói pra exibição). Aproveitado pra adicionar prefetch_related na action retrieve de IndicadorApuracaoViewSet (colaboradores__empresas, colaboradores__respostas__criterio), já que o campo novo faria mais uma consulta por colaborador na tela de revisão sem isso. Layout do cabeçalho do card também foi reorganizado a pedido do usuário: a composição saiu de baixo do nome/gerente pra ficar ao lado, centralizada, numa coluna própria do grid; e o lápis de ajuste manual do Total passou a ficar sempre ao lado do valor (isolado numa linha própria que nunca quebra), não mais embaixo quando o rótulo "Total Indicador" (mais longo que o antigo "Individual") não coubesse na coluna.
Terceiro ajuste: a tabela de Metas de Grupo/Departamento tinha um <select> Sim/Não duplicado por critério — um ao lado do texto que descreve o critério (bulk, via aplicar-em-lote) e outro na coluna "Meta (%)" à direita (que já ajusta pct_grupo/pct_departamento direto). O usuário pediu pra remover o primeiro, mantendo só o da direita — renderMetaCriteriosHtml voltou a ser só texto informativo (nome + peso do critério), e o listener de change associado a .ind-meta-criterio-select foi removido. Responder um critério específico continua possível por colaborador, dentro do card de revisão (renderRespostasGrupoHtml) — só o atalho de responder em lote pela tabela de metas deixou de existir.
Quarto ajuste: o modal "Ajuste Indicador em Lote" (ajusta pct_individual de vários colaboradores selecionados de uma vez) tinha um campo de texto livre "Percentual Individual (0 a 100)". Mesmo raciocínio das rodadas anteriores — o RH só usa esse ajuste em lote pra dois casos reais ("considerar atingido, mesmo quem não bateu a meta" ou "desfazer o ajuste manual") — trocado por dois botões, "Sim" (aplica pct_individual=100 a todos os selecionados) e "Reverter" (chama a action recalcular de cada colaborador selecionado, voltando ao cálculo automático). Nenhuma mudança de backend — os dois endpoints por-colaborador já existiam (PATCH e recalcular), só o disparo em paralelo (Promise.all) mudou de "um valor pra todos" pra "uma ação pra todos". Ajustado de novo logo em seguida: "Reverter" e "Sim" (renomeado pra "Ajustar") viraram os dois btn-solid, mesma cor — só "Cancelar" ficou btn-outline — já que as duas ações são igualmente "reais", não uma primária/secundária.
Quinto ajuste: o checkbox "Só com honorário não encontrado" (filtrava a lista de colaboradores pra só quem tinha alguma empresa sem honorário) virou um botão dedicado, "Visualizar Empresas sem Honorário" (com contador), que abre um modal próprio. Pedido explícito do usuário: a mesma empresa pode aparecer sob mais de um colaborador (um responsável pelo balancete, outro pela liberação fiscal, outro pela conciliação financeira) — preencher o honorário uma vez deve valer pra todos eles de uma vez, já que o honorário é da empresa, não da pessoa. Isso não era possível antes: o campo de preencher honorário existente (PATCH /api/indicadores-apuracoes-empresas/{id}/) só ajustava uma linha por id.
O que foi construído: o modal agrupa as IndicadorApuracaoEmpresa com honorario_nao_encontrado=True da apuração por codigo_empresa (mostrando os colaboradores/tipos responsáveis por cada uma), com um campo de honorário por grupo. Novo endpoint POST /api/indicadores-apuracoes/{id}/ajustar-honorario-empresa/ (IndicadorApuracaoViewSet.ajustar_honorario_empresa, serializer IndicadorApuracaoAjusteHonorarioEmpresaSerializer) atualiza todas as linhas com aquele código na apuração de uma vez, recalculando cada colaborador afetado — mesmo padrão de ajustar_grupo/ajustar_departamento (aplicar uma mudança a um escopo de uma vez, não registro a registro). O endpoint por-linha antigo continua existindo, usado pela tabela "Empresas" de dentro do card do colaborador (caso raro de querer corrigir só uma linha). Nenhuma migração — só um endpoint novo, sem campo novo no model.
Bug corrigido logo depois de testar: numa apuração com muitas empresas sem honorário, o modal crescia além da altura da tela (mesma causa raiz já documentada em CLAUDE.md da raiz pra .calendar-day — um filho de flex-column só rola em vez de esticar o pai quando o próprio pai também tem uma altura limitada e o filho tem min-height:0). Corrigido dando max-height:85vh a .ind-esh-modal-card e overflow-y:auto/min-height:0 à lista (.ind-esh-list) — título, aviso e os botões de ação ficam sempre visíveis, só a lista de empresas rola internamente quando não cabe.
Sexto ajuste, três pedidos numa rodada só: (1) ao preencher o honorário (linha única ou em lote pelo modal novo), deixar uma nota "honorário ajustado manualmente" na tabela "Empresas" de dentro do card do colaborador — campo novo IndicadorApuracaoEmpresa.honorario_ajustado_manualmente (migração 0029, junto com a mudança do item 2), marcado pelos dois caminhos de ajuste (linha única e em lote) e exposto no serializer; sem UI de reverter, já que não existe "automático" pra essa linha voltar (o código nunca casou com a planilha). (2) Listar as empresas por código, não por nome, na mesma tabela — trocado IndicadorApuracaoEmpresa.Meta.ordering de ["nome_empresa", "id"] pra ["codigo_empresa", "id"] (mesma migração 0029). Bug reportado logo depois de testar: "80" e "503" apareciam no fim da lista, depois de "2134" — codigo_empresa é CharField, então ordenar só por ele é alfabético ('8'/'5' são "maiores" que '1'/'2' como caractere, mesmo o número sendo menor), não numérico. Corrigido (migração 0030) ordenando primeiro por Length("codigo_empresa") e só depois pelo valor — pra códigos sem zero à esquerda, string mais curta é sempre número menor, então isso reproduz a ordem numérica certa sem precisar converter pra inteiro (que quebraria com erro de banco se algum código não fosse só dígitos). (3) Botão "Visualizar Empresas sem Honorário" ganhou cor de atenção (--danger, mesma linguagem visual do input/selo de honorário não encontrado) — não reaproveitado .btn-danger-outline (que tem margin-right:auto, pensado pra separar um botão "Excluir" dentro de .modal-actions, efeito colateral indesejado no toolbar) — classe própria .ind-empresas-sem-honorario-btn só com as cores.
Sétimo ajuste, testando o modal "Empresas sem Honorário" com dados reais: três pedidos. (1) Ordenar também por código nessa lista (estava só por nome) — reaproveitado o mesmo critério "tamanho da string primeiro" da correção anterior, agora também em JS (empresasAgrupadasPorCodigo), já que essa lista é montada em memória a partir do que já foi carregado, não vem de uma query com Meta.ordering. (2) Código antes do nome no cabeçalho de cada item — trocada a ordem dos dois <span> (.ind-esh-codigo primeiro), mesma ordem da tabela "Empresas" do colaborador (coluna "Código" antes de "Empresa"). (3) Um segundo botão/modal, "Verificar Empresas Ajustadas Manualmente", pra rever e corrigir um honorário já ajustado — antes só dava pra preencher uma vez (o modal "sem honorário" some da lista assim que honorario_nao_encontrado vira falso, sem nenhum caminho de volta pra editar de novo). Backend: o filtro de ajustar_honorario_empresa mudou de honorario_nao_encontrado=True pra Q(honorario_nao_encontrado=True) | Q(honorario_ajustado_manualmente=True) — o mesmo endpoint agora cobre preenchimento inicial e correção, sem endpoint novo. Frontend: as funções de agrupar/renderizar/salvar dos dois modais foram generalizadas (parametrizadas por um filtro e pelos ids de cada um) em vez de duplicadas; o modal de correção pré-preenche o campo com o valor atual (o de preenchimento inicial continua em branco).
Ajustado de novo na sequência, testando o segundo botão/modal recém-criado: o usuário pediu pra não ter um botão separado — "facilitando a usabilidade da ferramenta". Revertido pra um popup só: o botão/modal "Verificar Empresas Ajustadas Manualmente" foi removido, e sua lista virou uma segunda seção dentro do próprio popup "Empresas sem Honorário" (.ind-esh-section-title como divisor), embaixo da lista original. As duas listas passaram a viver num wrapper único que rola (.ind-esh-scroll), com o título e o botão "Fechar" sempre visíveis fora dele — antes cada modal tinha sua própria rolagem. renderEmpresasHonorario() (nova função) renderiza as duas listas de uma vez, tanto ao abrir o popup quanto depois de qualquer "Salvar" — necessário porque corrigir uma empresa "sem honorário" faz ela migrar pra seção "ajustada manualmente" na hora, então as duas sempre precisam refletir o estado atual juntas. Nenhuma mudança de backend nesta correção.
Ajustado uma terceira vez, testando a versão com as duas seções sempre visíveis: pedido pra a seção "ajustadas manualmente" ficar escondida por padrão, atrás de um botão no final do modal — "lá seja possível a correção" quando o usuário quiser ver. Adicionado #ind-empresas-ajustadas-toggle-btn (largura cheia, com contador, alterna "Visualizar"/"Ocultar Empresas Ajustadas Manualmente (N)") logo depois da lista principal, escondendo #ind-empresas-ajustadas-section por padrão (hidden, resetada a cada abertura do popup) e só renderizando/mostrando a lista quando o botão é clicado. renderEmpresasHonorario() ajustada pra só re-renderizar a seção "ajustadas" se ela já estiver aberta — evita trabalho à toa quando ela está escondida, mas mantém sincronizada se o usuário já estiver com ela visível ao salvar algo na lista principal. Nenhuma mudança de backend.
Rodada 39 — Indicador de Desempenho: checklist de validação por colaborador
Pedido: um checkbox no início de cada card de colaborador (tela de revisão), pra o RH marcar quem já validou — ao marcar, a borda do card fica verde, pra dar visibilidade de quem ainda está pendente numa apuração com muitos colaboradores.
O que foi construído: campo novo IndicadorApuracaoColaborador.validado (migração 0031) — booleano simples, sem relação com nenhum cálculo (nem participa de calculo.recalcula_colaborador). Novo endpoint POST /api/indicadores-apuracoes-colaboradores/{id}/marcar-validado/ só grava esse campo. Diferente de todos os outros ajustes desta tela (que recarregam a apuração inteira e re-renderizam tudo depois de qualquer mudança), marcar/desmarcar o checklist atualiza só o card clicado no DOM, sem recarregar nem re-renderizar a lista inteira — decisão deliberada, já que essa ação tende a ser repetida muitas vezes seguidas numa conferência longa, e um refresh completo fecharia outros cards já expandidos e resetaria a posição de rolagem a cada clique. Erro de rede reverte o checkbox e o estado local, mesmo padrão de outros toggles imediatos do app.
Detalhe de acessibilidade descoberto ao implementar: o gate de clique que expande/recolhe o card no cabeçalho precisou excluir o <label> inteiro do checkbox, não só o <input> — clicar na área do label fora do glifo do checkbox dispara dois eventos de clique encadeados (um no label, outro sintético no input), e só excluir o input pelo seletor deixava o primeiro clique (target=label) passar batido, expandindo/recolhendo o card ao mesmo tempo que marcava/desmarcava o validado.
Rodada 40 — Indicador de Desempenho: corrigir o responsável por uma empresa/papel
Pedido, com exemplo concreto: a empresa 168 (MULTIVERSA CONSULTORIA LTDA) tinha "Valéria Bonete — Fiscal" como responsável, mas devia ser "Alan Lima Cassulli — Fiscal". Ou seja, reatribuir qual colaborador responde por um papel (tipo) de uma empresa — diferente de tudo que já existia na tela (honorário, percentuais), que nunca mexia em quem é o responsável, só em valores.
O que foi construído: novo botão "Corrigir Responsável" (popup próprio, busca por nome/código entre todas as empresas da apuração — não só as com honorário pendente) — selecionar uma empresa mostra a mesma lista de responsáveis já usada no popup "Empresas sem Honorário" ("Nome — Tipo"), mas agora com um <select> por linha pra escolher outro colaborador da apuração. Novo endpoint POST /api/indicadores-apuracoes-empresas/{id}/trocar-responsavel/ (IndicadorApuracaoEmpresaViewSet.trocar_responsavel) só troca a FK colaborador da linha (codigo_empresa/tipo/honorário continuam intactos) e recalcula os dois colaboradores envolvidos — o que perdeu a empresa e o que ganhou, já que o conjunto de empresas de cada um mudou. Validações no backend: o novo colaborador precisa existir na mesma apuração, não pode ser o mesmo de já é, e não pode já ser responsável por essa mesma empresa/tipo (evitaria duas linhas duplicadas). O <select> de cada linha já exclui o colaborador atual das opções e nasce num placeholder desabilitado, pra nunca reatribuir sem escolha explícita do usuário. Nenhuma migração — só um endpoint novo, reaproveitando o model existente.
Ajuste pequeno na sequência, no botão "Visualizar Empresas sem Honorário" (popup "Empresas sem Honorário", rodada anterior): ele continuava vermelho e com o mesmo texto mesmo quando não havia mais nenhuma empresa pendente. Pedido: nesse caso, o botão devia virar "Visualizar Empresas com Honorário Ajustado" e perder a cor de atenção, já que não há mais nada de errado pra resolver. atualizarBotaoEmpresasSemHonorario() (JS) passou a alternar a classe .ind-empresas-sem-honorario-btn (antes fixa no HTML, agora só aplicada via JS quando semHonorario > 0) e o texto do botão conforme o total de empresas pendentes.
Rodada 41 — Indicador de Desempenho: filtro por setor organizacional, com meta de Departamento própria por setor
Pedido, olhando a tabela "Metas de Grupo e Departamento" real (competência 2026-07, 43 colaboradores): trocar o filtro por gerente por botões de departamento/setor no topo da revisão — selecionar um setor (ex.: "Fisco/Contábil") devia restringir a tabela de Metas a só a linha de Departamento daquele setor + os gerentes dele, e a lista de colaboradores abaixo a só quem tem esses gerentes. O pedido também trazia uma regra de negócio concreta: "Contabilidade" e "Fiscal / Tributário" formam um setor só ("Fisco/Contábil"); os demais setores são independentes; os próprios líderes do Fisco/Contábil (João Candido Rodrigues, Lhais Vergilio Delavy, Paloma Ramão, Elizangela dos Santos) deviam aparecer num setor à parte, "Gerentes"; e Luciane Gonzaga no setor "Rocket".
Antes de implementar, esclarecido com o usuário (a mudança tinha implicações que iam além de "só um filtro visual", ver perguntas feitas): (1) cada setor passaria a ter sua própria meta de Departamento (pct_departamento), não mais um valor único pra toda a apuração — escolhido em vez de manter uma meta global só filtrada na tela; (2) a associação pessoa→setor devia ser um cadastro editável, não fixo no código — mas o usuário revelou, na sequência, que o setor de cada colaborador já existe como coluna ("departamento") na planilha Serviços Tareffa, então não precisava de um cadastro manual pra maioria dos casos.
Investigado o arquivo real (projects/Indicadores/Serviços Tareffa.xlsx) pra confirmar: a coluna "departamento" tem valores "Contabilidade"/"Fiscal / Tributário"/"Rocket"/"Condomínio"/"Pessoa Física IRPF"/"Auditoria Fisco/Contábil" por linha de serviço. Rodando a extração real dos 43 colaboradores da apuração existente, confirmou-se que os 4 líderes citados pelo usuário têm o setor bruto da planilha igual ao setor operacional de quem lideram (ex.: João Candido Rodrigues aparece como "Contabilidade", não "Gerentes") — ou seja, a fusão automática Contabilidade/Fiscal→Fisco/Contábil não bastava pra colocá-los em "Gerentes"; só Luciane Gonzaga já vinha certa ("Rocket") sem precisar de nada extra.
O que foi construído (ver CLAUDE.md desta pasta → "Departamento organizacional" pro detalhe técnico completo — mecanismo depois substituído por completo na rodada 45):
- Campo novo
IndicadorApuracaoColaborador.setor(migração0032) — valor bruto da coluna "departamento" da planilha, lido emleiaute.le_servicos_tareffa(campo novoLinhaTareffa.setor) e propagado porpipeline.processa_apuracaoaté o colaborador (primeira ocorrência por responsável, mesmo padrão já usado pragerente). - Model novo
IndicadorSetorApelido(nome do colaborador → setor) — cadastro persistente e editável (aba "Apelidos de Setor" em Configurações,/api/indicadores-setores-apelidos/, CRUD completo) pros casos em que o setor bruto da planilha não reflete o setor "de verdade" da pessoa (os 4 líderes). Cadastrados no ambiente local: os 4 nomes → "Gerentes". portal_api/indicadores/setores.py(novo módulo, sem ORM direto além de lerIndicadorSetorApelido):resolve_setor(nome, setor_bruto, apelidos)aplica o apelido se existir, senão funde "Contabilidade"/"Fiscal / Tributário" em "Fisco/Contábil" (FUSAO_SETORES) e usa o valor bruto como está pros demais (Rocket, Condomínio, Pessoa Física IRPF, ...) — nenhum código novo é necessário quando um setor novo aparecer na planilha.pct_departamentodeixou de ser ajustado/recalculado pra toda a apuração de uma vez —IndicadorApuracaoViewSet.ajustar_departamento/recalcular_departamentoagora recebemsetorno corpo e aplicam só aos colaboradores daquele setor (setores.colaboradores_do_setor), mesma mecânica queajustar_grupo/recalcular_grupojá usavam por gerente.- Frontend (
indicador-desempenho.js): removido o<select id="ind-filtro-gerente">; adicionados botões de setor (#ind-filtro-setor,.ind-setor-chip) no topo da seção de Metas — clicar num setor filtra tanto a tabela de Metas (uma linha "Departamento (todo o <setor>)" + as linhas de Grupo só dos gerentes daquele setor) quanto a lista de colaboradores abaixo. Camposetor_bucket(SerializerMethodField, já resolvido no servidor) exposto por colaborador. - Apurações criadas antes desta mudança ficam com
setorem branco — feito um backfill pontual, viamanage.py shell, lendo de novo a planilha Tareffa já anexada à única apuração existente no ambiente (competência 2026-07); não existe management command dedicado pra isso ainda, caso surja uma apuração antiga sem o arquivo disponível.
Validado contra os dados reais da apuração existente antes de considerar pronto: sem apelido, o agrupamento automático já dava Fisco/Contábil=35 (incluindo os 4 líderes) + Condomínio=4 + Pessoa Física IRPF=1 + Rocket=2 + Auditoria Fisco/Contábil=1; com os 4 apelidos cadastrados, o resultado bateu exatamente com o pedido: Fisco/Contábil=32 (gerentes "João Candido Rodrigues"/"Lhais Vergilio Delavy"), Gerentes=4, Rocket=2, Condomínio=4, Pessoa Física IRPF=1.
Rodada 42 — Indicador de Desempenho: três ajustes rápidos de usabilidade na tela de revisão
Testando a rodada 41 no navegador, três pedidos pequenos em sequência:
-
Info no "Honorário Ajustado": pedido, com print, de um botão de informação ao lado do cabeçalho da coluna "Honorário Ajustado" (tabela "Empresas" de dentro do card do colaborador) — ao clicar, abre um popup pequeno explicando que o valor é o honorário proporcionalizado ao percentual do Indicador atingido, já mostrando o percentual real daquele colaborador (não um texto genérico). Não existia nenhum componente de popover/tooltip no projeto — construído do zero (
.ind-info-wrap/.ind-info-btn/.ind-info-popoveremindicador-desempenho.css), inspirado no mesmo mecanismo de.notif-dropdown(notifications.js): botão "i" (data-ind-info-toggle) alterna um<span>posicionado emabsolutelogo abaixo, e um listener emdocumentfecha ao clicar fora. Como o botão fica dentro de um<th>(que temtext-transform: uppercase/fonte pequena via.pa-table th), o popover precisou resetar essas propriedades pra virar texto normal de novo. Todo o texto (incluindo o percentual) é montado emrenderHonorarioAjustadoInfoHtml(colaborador), chamada por colaborador ao montar a tabela — sem mudança de backend,pct_individualjá vinha no payload. -
Filtro por setor no "Ajuste Indicador em Lote": o modal já tinha busca por nome; pedido pra também poder filtrar por departamento/setor. Adicionado um
<select id="ind-lote-global-setor">(opções = setores distintos da apuração, viapidIndAgruparPorSetor) que combina (E lógico) com a busca por nome — os dois filtros juntos decidem quem aparece no checklist e quem "Marcar todos os resultados da busca" marca. Nenhuma mudança de backend (filtragem 100% em memória sobrecolaborador.setor_bucket, já exposto desde a rodada 41). -
Modal de seleção pra "Gerar Recibos": antes, o botão gerava na hora pra todos os colaboradores da apuração, sem escolha. Pedido: abrir um modal parecido com o de "Ajuste Indicador em Lote" — mesma busca por nome + filtro por setor + checklist com "marcar todos os resultados da busca" — permitindo gerar recibo de um colaborador só, de alguns específicos, de um setor inteiro, ou de todo mundo. Implementado reaproveitando exatamente o mesmo padrão de UI do item 2 (funções/variáveis próprias, prefixo
gerarRecibos*, sem duplicarloteGlobal*), com uma diferença deliberada: o checklist já nasce com todo mundo marcado ao abrir (reproduz o comportamento antigo — gerar pra todos — sem exigir que o RH marque um por um; ele só desmarca quem não quer incluir desta vez).Mudança de backend:
IndicadorApuracaoViewSet.gerarpassou a aceitarcolaborador_ids(lista, opcional) no corpo — sem isso, gera pra todos (comportamento antigo); com isso, gera só pra quem foi pedido, validando que todos os ids pertencem à apuração (400 caso contrário). Decisão tomada sem perguntar, por ser a leitura mais correta do dado: a apuração só é marcadaconcluidaquando a seleção pedida cobre todos os colaboradores (sem filtro, ou uma seleção que bate com o total) — gerar um recibo avulso pra conferência não deveria fechar a apuração inteira como se o mês tivesse sido todo revisado. Validado viadjango.test.Clientlogado comogabriel: geração parcial (1 colaborador) mantevestatus="revisao"; geração de todos marcouconcluida; um id de outra apuração devolveu 400. O estado real da apuração no ambiente local (já estavaconcluidade um uso anterior) foi restaurado ao original depois do teste.
Rodada 43 — Indicador de Desempenho: três ajustes no PDF do recibo
Pedido com print real do PDF gerado (colaborador com o Individual ajustado manualmente pra 100%, e colaborador Natan da Costa com a tabela de Empresas ilegível):
-
Banner "PERCENTUAL DO INDICADOR INDIVIDUAL" mostrava o valor pago, não o medido: quando o RH/Diretoria ajusta o percentual Individual manualmente (
pct_individual_ajustado_manualmente=True),colaborador.pct_individualpassa a guardar só o valor sobrescrito — o percentual real medido (a composição dos 3 níveis) não ficava salvo em lugar nenhum depois do ajuste. Pedido: o banner deve sempre mostrar o valor efetivo/medido (ex.: 74,29%), independente do ajuste; embaixo, a linha que dizia "Percentual individual ajustado manualmente pelo RH" devia virar "Percentual Individual Ajustado Pela Direção" com o percentual ajustado ao lado (ex.: 100%) — os dois números lado a lado, pra ficar claro o que foi atingido e o que foi considerado.Implementado sem duplicar a fórmula:
calculo.composicao_individual()(já calculava os 3 níveis brutos pra exibição no card de revisão) ganhou uma chave nova,total_calculado— a mesma composição ponderada (_combina_niveis) querecalcula_colaboradorteria gravado empct_individualse não houvesse ajuste manual, calculada ali mesmo a partir dos níveis que a função já monta.recibo.py._banner_percentualpassou a mostrarcomposicao["total_calculado"]no banner principal (sempre o medido) e, só quandopct_individual_ajustado_manualmente, uma linha extra com o rótulo novo +colaborador.pct_individual(o valor pago). -
"Total Resultado" → "Total do Indicador": troca simples de rótulo na última linha da tabela "Empresas" (
_tabela_empresas). -
Overflow generalizado na tabela "Empresas" (causa raiz do "texto da coluna Tipo tapando o Hon. Ajustado" e dos valores da linha de total "ultrapassando as linhas"): toda célula da tabela, exceto "Empresa", era uma string solta, não um
Paragraph— string solta não quebra linha dentro da largura da coluna; combinado comALIGNà direita (aplicado a todas as colunas de valor, inclusive "Tipo" sem querer) ou ao negrito da linha de total (mais largo que a mesma string em peso normal), um valor mais largo que a coluna vazava visualmente por cima da célula vizinha em vez de quebrar linha — daí "Contador (com conciliador)" (right-aligned, mais largo que a coluna de Tipo) vazar pra esquerda cobrindo Hon. Ajustado, e os totais em negrito vazarem na última linha mesmo cabendo em peso normal. Corrigido convertendo toda célula emParagraphcom um estilo de alinhamento próprio (celula_centro/celula_direita/celula_negrito/celula_direita_negrito, novos em_estilos()) — agora qualquer valor mais largo que a coluna quebra em duas linhas em vez de vazar. Coluna "Tipo" também ganhou um dicionário de labels curtos só pro PDF (TIPO_LABEL_CURTO: "Contador SC"/"Contador CC" em vez de "Contador (sem/com conciliador)", abreviação que já era usada informalmente na documentação do projeto) — o label completo não cabia nem quebrando linha numa coluna estreita. Larguras de coluna também redistribuídas (Tipo de 1,5 pra 2,2cm, usando os ~1,3cm de folga que a tabela tinha sobre a largura útil da página A4).Validado gerando de verdade os PDFs de "Alan Lima Cassulli" (sem ajuste manual — banner mostra só o medido, sem linha extra) e "Natan da Costa" (com ajuste manual pra 100% — banner mostra 81,74% medido + linha "Percentual Individual Ajustado Pela Direção: 100,00%") a partir da apuração real do ambiente, lendo o PDF gerado de volta pra conferir visualmente — tabela "Empresas" sem nenhum vazamento em nenhum dos dois casos, "Contador CC"/"Contador SC" legíveis na coluna Tipo, "Total do Indicador" com os 4 valores certos sem sobrepor.
Rodada 44 — Indicador de Desempenho: recibo em PDF — "R$" separando do valor + capitalização do rótulo
Testando a rodada 43 com valores maiores (ex.: R$ 27.710,19), dois ajustes finos:
- "R$" quebrando pra uma linha acima do valor: ao converter as células da tabela "Empresas" pra
Paragraph(rodada 43), o espaço comum entre "R$" e o número virou um ponto de quebra de linha válido — quando o valor não cabia numa linha só, o reportlab quebrava bem ali, deixando "R$" sozinho acima do número em vez de vazar (o bug da rodada anterior), mas ainda longe do ideal. Pedido: "R$" deve estar sempre do lado esquerdo do valor, nunca acima. Trocado o espaço comum por (não separável) em_moeda()— sozinho isso só moveu o ponto de quebra pra dentro do próprio número (ex.: "R$ 27.710,1" / "9"), então a correção completa também exigiu abrir mais espaço de verdade pras colunas:LEFTPADDING/RIGHTPADDINGda tabela reduzidos de 6pt (padrão do reportlab) pra 3pt, e as larguras das colunas Honorário/Hon. Ajustado/Indiv./Grupo/Depto./Total redistribuídas (tirando um pouco de Código e Empresa, que tinham folga de sobra) pra caber o maior valor real visto na apuração (R$ 28.023,16) numa linha só, com padding. - Capitalização do rótulo: "Percentual Individual Ajustado Pela Direção" (Title Case) virou "Percentual individual ajustado pela direção" (só a primeira letra maiúscula).
Validado regerando os mesmos dois recibos da rodada 43 (Alan Lima Cassulli, Natan da Costa) — inclusive o valor mais alto da apuração inteira (R$ 28.023,16, CELLSHOP DUTY FREE no recibo de Natan da Costa) coube numa linha só, "R$" sempre grudado à esquerda do número, em toda a tabela e na linha de total.
Rodada 45 — Indicador de Desempenho: Departamento como entidade própria — critérios e percentuais por departamento
Pedido: "vamos passar a apurar as metas e regras por departamento" — critérios e percentuais deixam de ser globais (uma regra só pra toda a apuração) e passam a ser por departamento (ex.: a regra do Fisco/Contábil pode ser diferente da do Condomínio). Duas exigências explícitas: (1) precisa de um cadastro de verdade de departamentos, com relação a gerentes — um departamento pode ter mais de um gerente (ex.: Fisco/Contábil tem João Candido Rodrigues e Lhais Vergilio Delavy); por enquanto essa relação é mantida manualmente pela própria aplicação (alimentar da planilha fica pra decidir depois); (2) por enquanto replicar a mesma regra em todo departamento, mas a estrutura já precisa suportar customização.
Decisão tomada com o usuário antes de implementar (rodada consultada via pergunta direta, dado o tamanho da mudança): o novo cadastro de Departamento substitui por completo o mecanismo de "setor" da rodada 41 (coluna bruta da planilha + fusão automática + IndicadorSetorApelido) — fica só um conceito de departamento no sistema, usado tanto pras Metas quanto agora pra critérios/percentuais. O caso que o apelido resolvia (os 4 líderes de Fisco/Contábil) passa a ser coberto mapeando a gerente deles, "Elizangela de Paula Kuhn", pro departamento "Gerentes".
O que foi construído (ver CLAUDE.md desta pasta → "Departamento organizacional" pro detalhe técnico completo):
- Dois models novos:
IndicadorDepartamento(nome/ativo) eIndicadorDepartamentoGerente(gerente→departamento,nome_gerenteúnico — um gerente só pertence a um departamento, mas um departamento aceita vários gerentes). IndicadorCriterio/IndicadorPercentualTipoganharam FK obrigatóriadepartamento— cada departamento passa a ter seu próprio histórico de critérios/percentuais, de verdade (não é mais um bucket calculado, é uma tabela filtrada por FK).IndicadorApuracaoColaborador.setor(texto) viroudepartamento(FK nullable) — resolvido uma vez na criação da apuração, a partir dogerentedo colaborador viaIndicadorDepartamentoGerente(não mais da coluna bruta da planilha). Sem mapeamento pro gerente, o colaborador fica comdepartamento=Nonee vira um aviso no processamento.pipeline.processa_apuracao()também passou a agrupar os critérios automáticos por departamento (criterios_automaticos_por_departamento) — cada colaborador só calcula os critérios do seu departamento, não mais todos os critérios ativos da apuração.- Migração em 3 passos (schema com FK nullable →
RunPythoncriando "Fisco/Contábil" e apontando todo critério/percentual já existente pra ele, já que era literalmente o que a regra única representava → schema tornando a FK obrigatória) — mesmo padrão de qualquer FK NOT NULL adicionada numa tabela já populada. seed_indicador_desempenho.pyajustado pra criar/reaproveitar o departamento "Fisco/Contábil" antes de popular critérios/percentuais (senão quebraria ao rodar de novo, já que os models agora exigem departamento) — testado rodando de novo: atualizou os 7 critérios e reconheceu os 5 percentuais já existentes, sem duplicar nada.- Tela de Configurações: aba "Apelidos de Setor" virou aba "Departamentos" (cadastro de departamentos + botão "Gerenciar Gerentes" por linha, popup com lista de gerentes daquele departamento + campo pra adicionar um novo). Abas "Critérios" e "Percentuais por Tipo" ganharam um filtro por departamento no topo + coluna "Departamento" na tabela + campo obrigatório de departamento no modal de adicionar. Filtro de Metas/"Ajuste Indicador em Lote"/"Gerar Recibos" (todos já existentes desde a rodada 41) tiveram a chave de agrupamento trocada de string (
setor_bucket) pra id (departamento), mesma mecânica visual.
Validado via django.test.Client (login gabriel): criados 4 departamentos de teste + 6 relações gerente→departamento reproduzindo a estrutura real conhecida (Fisco/Contábil: João Candido Rodrigues + Lhais Vergilio Delavy; Rocket: Luciane Gonzaga; Condomínio: Cristiano Silverio; Pessoa Física IRPF: Daniel Gustavo Manenti; Gerentes: Elizangela de Paula Kuhn) e gerada uma apuração de teste de verdade com os 2 arquivos reais — distribuição resultante: Fisco/Contábil 32, Gerentes 5, Rocket 1, Condomínio 4, Pessoa Física IRPF 1 (só os colaboradores de Fisco/Contábil ganharam respostas de critério, já que só esse departamento tem critérios cadastrados — esperado). Tudo (apuração de teste, departamentos de teste, relações de teste) removido depois do teste, mantendo só o "Fisco/Contábil" real criado pela migração.
Limitação real encontrada nesse teste, documentada em CLAUDE.md desta pasta: resolver o departamento pelo gerente (não mais pelo colaborador individual) quebra o caso de uma gerente que supervisiona pessoas de departamentos diferentes — "Elizangela de Paula Kuhn" supervisiona os líderes de Fisco/Contábil e Luciane Gonzaga (que deveria cair em "Rocket", não em "Gerentes" junto com os outros). Como não existe mais uma exceção por colaborador individual (o antigo IndicadorSetorApelido cobria isso), Luciane Gonzaga passou a cair em "Gerentes" nesse teste — diferente do que a rodada 41 tinha estabelecido pra ela (Rocket). Sinalizado ao usuário como limitação conhecida do novo desenho; não corrigido nesta rodada por não ter sido pedido, e porque reintroduzir uma exceção por colaborador contrariaria a decisão de "só um mecanismo" tomada no início desta rodada — só mexer nisso se o usuário confirmar que quer. Continua em aberto (ver plano.md, "Roadmap").
Rodada 46 — Indicador de Desempenho: sugestões de gerente no popup "Gerenciar Gerentes"
Pedido, testando a rodada 45: o campo "Adicionar gerente" era só texto livre — o usuário pediu pra já vir preenchido com os nomes de gerente encontrados na última apuração, pra só precisar relacionar (clicar) em vez de redigitar cada nome (risco real de typo, já que o nome precisa bater exatamente com a coluna "gerente" da planilha pra apuração futura casar com o departamento certo).
Implementado 100% no frontend, sem endpoint novo: carregarGerentesSugeridos() busca a apuração mais recente (GET /api/indicadores-apuracoes/, a lista já vem ordenada por -competencia/-criado_em via IndicadorApuracao.Meta.ordering — não precisou de parâmetro novo), pega o detalhe dela (GET /api/indicadores-apuracoes/{id}/) e extrai os nomes distintos de colaborador.gerente, excluindo os que já estão em departamentoGerentesConfig (já mapeados pra algum departamento). O popup "Gerenciar Gerentes" ganhou uma seção "Sugestões" entre a lista atual e o campo de texto — cada sugestão é um botão (.checklist-item reaproveitado como <button>, mesmo padrão já usado em "Corrigir Responsável"/.ind-corrigir-resultado) que, ao ser clicado, já chama POST /api/indicadores-departamentos-gerentes/ pra aquele departamento. Lista recarregada (carregarGerentesSugeridos() de novo) depois de qualquer adição/remoção de gerente, em qualquer departamento, pra manter as sugestões sempre refletindo quem ainda falta mapear. O campo de texto livre continua disponível, pra gerentes que não apareceram na última apuração (colaborador novo, ainda sem apuração processada).
Validado com a apuração real do ambiente: as sugestões bateram exatamente com os 6 gerentes conhecidos (João Candido Rodrigues, Lhais Vergilio Delavy, Cristiano Silverio, Daniel Gustavo Manenti, Luciane Gonzaga, Elizangela de Paula Kuhn), já que nenhum deles está mapeado ainda no ambiente.
Rodada 47 — Melhorias da revisão de interface de 30/09/2026 (2026-10-01)
Pedido do usuário: aplicar os itens da revisão de interface de 30/09/2026 nesta tela (versão sem acessibilidade de teclado/leitor de tela, decisão do usuário). Só frontend (indicador-desempenho.js/.html/.css), sem migração nem mudança de API. Feito em duas etapas no mesmo pedido:
Primeira etapa (30/09):
- Percentuais e pesos com vírgula decimal (
Intl.NumberFormat("pt-BR")empidIndFormatPercentual/pidIndFormatPeso; antestoFixedmostrava "74.29%"). pidIndFormatDatatrata data pura "AAAA-MM-DD" sem fuso (antes mostrava o dia anterior no horário de Brasília);pidIndHojeIso()preenche o "Vigente desde" com a data local (antestoISOString()dava o dia seguinte depois das 21h).- O último
alertnativo viroupidAlert; campos de honorário/percentual pré-preenchidos com vírgula.
Segunda etapa (01/10):
- Card aberto não fecha mais: todo ajuste da revisão (resposta de critério, meta, honorário, responsável, ajuste e lote) redesenhava a tela e fechava o card que o RH conferia. Agora os cards abertos ficam em
cardsExpandidos(por id do colaborador) erecarregarRevisao()restaura também a rolagem da página. As respostas marcadas para o lote continuam marcadas depois do redesenho. - Sem envio duplo: Processar, Salvar (honorário, responsável, percentual, critério, percentual por tipo, departamento), Aplicar, Ajustar/Reverter em lote, Gerar ("Gerando…"), Adicionar gerente e as sugestões ficam travados com o texto de espera até a resposta (
pidIndComBotaoOcupado); selects de meta/resposta e botões de recalcular também. - Falha nunca silenciosa: abrir/excluir apuração, voltar, abrir Configurações, ativar/excluir critério, excluir percentual e excluir departamento tratam erro com
pidAlert. Erro na carga do histórico (inclusive a carga inicial) mostra "Não foi possível carregar. Recarregue a página; se persistir, contate a Inovação." na tabela, nunca "Nenhuma apuração realizada ainda". Falha na busca de sugestões de gerente diz isso em vez de "Nenhum gerente novo encontrado na última apuração". Se a ação deu certo e só a atualização da tela falhou, a mensagem diz isso (ex.: recibos já baixados). - Validação com campo e formato: cada mensagem diz qual campo falta ou está errado e leva o cursor a ele (Nova Apuração, Critério, Percentual por Tipo, ajuste de percentual, departamento, gerente). Números aceitam milhar (
pidIndParseDecimal: "1.234,56", "1.500"); o antigoreplace(",", ".")só trocava a primeira vírgula e rejeitava esse formato. - Trabalho perdido: Voltar/Cancelar/clique fora na Nova Apuração e nos modais de ajuste de percentual, Critério, Percentual por Tipo e Departamento pedem "Sair sem salvar as alterações?" só quando algum campo mudou (retrato dos campos ao abrir). O popup de gerentes pede o mesmo se houver nome digitado e não adicionado. Remover gerente de um departamento pede confirmação (perigosa).
- Estados vazios nas tabelas de Critérios, Percentuais e Departamentos ("Nenhum ... cadastrado." ou "Nenhum resultado para o filtro."). O filtro de departamento de Critérios/Percentuais não volta mais para "Todos" depois de salvar ou excluir.
- Acabamento: reticências "…", placeholders com exemplo ("Ex.: 08/2026…", "Ex.: 1.500,00…", nome do gerente),
inputmode="decimal"nos campos numéricos,namenos campos,color-schemetambém no "Vigente desde" (seguindo o tema claro), comentário ecursor: pointererrados removidos do campo de competência, popover do "Honorário Ajustado" centralizado por margem (a animação comtranslateYapagava otranslateX(-50%)e o fazia pular meia largura), hover etouch-action: manipulationno cabeçalho do card,overscroll-behavior: containno popup de Empresas sem Honorário etabular-numsno total do card, nos percentuais e na tabela Empresas.
Não feito (outra rodada, por decisão do pedido): estado da tela na URL e virtualização das listas.