Correção de alguns valores no dash principal
This commit is contained in:
118
README.md
118
README.md
@@ -2113,3 +2113,121 @@ docker compose exec app bundle exec rspec \
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><strong>🔢 Dashboard × Operações: por que os números não batiam — notas x visitas + painel de avulsas (24/08/2026)</strong></summary>
|
||||
|
||||
> ⚠️ **STATUS: implementado, ainda NÃO executado.** Não há Ruby/Bundler nem Postgres na máquina de
|
||||
> desenvolvimento — foi conferida a sintaxe de todos os `.rb` e `.erb` alterados. **A suíte e a
|
||||
> validação com dado real continuam pendentes** — roteiro no fim desta seção.
|
||||
|
||||
### 🎯 O problema
|
||||
Mesmo período filtrado, dois números diferentes:
|
||||
|
||||
| | Dashboard financeiro | Dashboard de Operações |
|
||||
|---|---|---|
|
||||
| Total | 4977 | 4973 |
|
||||
| Entregues / Sucesso | 4852 | 4851 |
|
||||
| Falhadas / Recusas | 124 | 122 |
|
||||
| Pendentes | 1 | 0 |
|
||||
|
||||
Não era arredondamento: **as duas telas contam coisas diferentes**, e nada na interface dizia isso.
|
||||
|
||||
- O **financeiro** conta **visitas** (idas ao local). É o recorte certo lá, porque é por ida que o
|
||||
motorista recebe — `Entrega.contar_atendidas` conta linhas, e a consolidação paga em cima disso.
|
||||
- **Operações** conta **notas fiscais** (último status de cada NF). É o recorte certo aqui, porque
|
||||
é o que o cliente paga e o que confere nos documentos físicos — o mesmo critério da aba ENTREGAS
|
||||
da planilha entregue (`Analytics::PlanilhaEntregas`).
|
||||
|
||||
Uma NF que falhou dia 10 e foi entregue dia 12 vale **2 no financeiro e 1 em Operações**. Correto
|
||||
nos dois — mas invisível.
|
||||
|
||||
### 🐛 Três defeitos reais por trás disso
|
||||
|
||||
**1. O dedup rodava sobre a tabela inteira, não sobre o período.** O `ROW_NUMBER() ... rn = 1`
|
||||
ficava numa CTE **antes** do filtro de data. Se o último checkout de uma NF era **posterior** ao fim
|
||||
do período, a visita que aconteceu **dentro** do período sumia da contagem do mês. Subcontagem
|
||||
silenciosa em todo fechamento. Agora o dedup acontece em `#linhas`, **depois** do período e do
|
||||
cross-filter.
|
||||
|
||||
**2. O dedup escondia todo o insucesso reentregue.** NF que falhou duas vezes antes de entregar
|
||||
aparecia como 100% de sucesso, e o motivo sumia do "Índices de falha".
|
||||
|
||||
**3. O período nem era aplicado no modo Operação.** `montar([@operacao], ...)` era chamado **sem
|
||||
`inicio:`/`fim:`** e o seletor de data só aparecia no modo Global — comparar as duas telas "no mesmo
|
||||
período" era literalmente impossível.
|
||||
|
||||
### 🆕 O que mudou na tela
|
||||
|
||||
**Operações — camada "Visitas ao local"** (abaixo dos 4 cards): visitas realizadas, retentativas,
|
||||
insucessos por visita e "entregues na 2ª ida ou mais". Os 4 cards de cima seguem contando **notas**.
|
||||
|
||||
**Operações — painel "Notas fora da operação"**: NFs entregues no período que **não estão em nenhuma
|
||||
planilha `gade_entregas_*`** — os planos avulsos e de inclusão. Contavam no financeiro e o
|
||||
`INNER JOIN` com a tabela da operação as descartava aqui. Agora aparecem com NF, plano/título,
|
||||
motorista, unidade, data, resultado e motivo, com quebra por plano de origem.
|
||||
|
||||
**Operações — período no modo Operação**: o seletor passa a valer também aqui, mas **só quando o
|
||||
operador escolhe uma faixa** (`inicio`/`fim` na URL). Sem escolha, o recorte segue sendo a operação
|
||||
inteira — senão abrir uma operação de meses atrás cairia no mês corrente e mostraria zero. Botão
|
||||
**"Operação inteira"** volta ao recorte natural.
|
||||
|
||||
**Financeiro — linha "N notas fiscais"** no card Total Entregas, ao lado de "visitas atendidas".
|
||||
|
||||
### ⚙️ Pontos não-óbvios
|
||||
|
||||
**Duas camadas no mesmo objeto.** `OperacaoMetricas#visitas` = uma linha por ida; `#linhas` = uma
|
||||
linha por NF (a última visita). Todos os KPIs, o donut, os motivos, o mapa e a tabela espelho
|
||||
continuam saindo de `#linhas` — ou seja, **a tela segue batendo com a planilha do cliente**. Só a
|
||||
faixa nova lê `#visitas`.
|
||||
|
||||
**`uniq` por `tracking_id`.** Sem a CTE, se a mesma `nota_fiscal` estiver repetida dentro de uma
|
||||
tabela de operação (ou em duas tabelas do UNION global), o `INNER JOIN` devolvia a **mesma visita**
|
||||
mais de uma vez. Agora colapsa.
|
||||
|
||||
**Filtro de conta unificado.** `Entrega.condicao_conta_sql` nasceu para as queries cruas de
|
||||
`Analytics` usarem exatamente o mesmo recorte de `DB_EXISTING_ACCOUNT_ID` do scope
|
||||
`da_conta_gade` — antes o dashboard filtrava conta e Operações não.
|
||||
|
||||
**`NOT IN` com `NULL` devolve zero linhas.** Cada `SELECT` da união em `NotasForaOperacao` filtra
|
||||
`nota_fiscal IS NOT NULL`; sem isso um único NULL numa planilha deixaria o painel vazio para sempre.
|
||||
Tem spec para isso.
|
||||
|
||||
### 📂 Arquivos
|
||||
```
|
||||
app/models/entrega.rb (contas_gade + condicao_conta_sql)
|
||||
app/services/analytics/operacao_metricas.rb (visitas x linhas; dedup pós-filtro; conta)
|
||||
app/services/analytics/notas_fora_operacao.rb (NOVO — avulsas/inclusão)
|
||||
app/controllers/operacoes_dashboard_controller.rb (período no modo Operação; @fora_operacao)
|
||||
app/controllers/dashboard_controller.rb (@notas_atendidas)
|
||||
app/views/operacoes_dashboard/_painel.html.erb (faixa "Visitas ao local")
|
||||
app/views/operacoes_dashboard/_fora_operacao.html.erb (NOVO — painel de avulsas)
|
||||
app/views/operacoes_dashboard/index.html.erb (seletor de período + render do painel)
|
||||
app/views/dashboard/index.html.erb (linha "N notas fiscais")
|
||||
spec/services/analytics/operacao_metricas_spec.rb (+ NF com retentativa)
|
||||
spec/services/analytics/notas_fora_operacao_spec.rb (NOVO)
|
||||
```
|
||||
|
||||
> **Sem migration e sem gem nova** — model, services, controllers, views e specs.
|
||||
|
||||
### ⏳ Pendente — roteiro
|
||||
```bash
|
||||
# 1. Suíte (não pôde ser executada aqui — sem Ruby/Bundler local)
|
||||
docker compose exec app bundle exec rspec \
|
||||
spec/services/analytics/operacao_metricas_spec.rb \
|
||||
spec/services/analytics/notas_fora_operacao_spec.rb \
|
||||
spec/models/entrega_spec.rb
|
||||
|
||||
# 2. Conferir a coluna que carrega o nome do PLANO ("(Avulsa)", "INCLUSÃO").
|
||||
# NotasForaOperacao::COLUNAS_PLANO tenta title, notes, comments, route_id nessa ordem.
|
||||
# Se o painel mostrar "SEM PLANO IDENTIFICADO", a coluna certa é outra:
|
||||
docker compose exec app bin/rails runner 'puts Entrega.column_names.sort'
|
||||
|
||||
# 3. Com dado real, no mesmo período nas duas telas:
|
||||
# financeiro "N notas fiscais" == Operações Global "Total de Entregas"
|
||||
# financeiro "Total Entregas" == Operações "Visitas ao local" + notas fora da operação
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
Reference in New Issue
Block a user