Web — ClientsPage: - Redesign completo: métricas reais via usePortfolioStats (4 queries count), donut Chart.js com totais reais, tabela sem ellipsis, coluna Cliente com nome fantasia/razão/CNPJ completos, drawer de detalhes e análise comercial, cards mobile, filtros de status/busca em tempo real. - Dados reais: substitui mock por useClientList/useClientDetail/useClientOrders; remove tipos fictícios (prospect/lead, cidade, totalComprado). Web — OrdersPage: - Métricas reais via useOrderStats (contagens por situa, não da página atual). - Coluna Cliente sem truncamento (minWidth: 240). - Cabeçalho, filtros e layout alinhados ao padrão da ClientsPage. API — orders.service.ts: - Normalização situa SIG→SAR: SIG usa 5=Cancelado; SAR usa 3=Cancelado. sigToSar(5→3) no mapper; sarToSig(3→5) no filtro SQL. API — clients.service.ts: - dt_ultima_compra corrigida: JOIN duplo (vw_pedidos_erp + sar.pedidos) com GREATEST() — clientes com histórico ERP mas sem pedido SAR deixam de aparecer todos como Inativo. - Filtro de activityStatus movido para SQL — total e paginação corretos. - findOne() atualizado com o mesmo JOIN duplo. Infra — .env: - DEV_EMPRESA_ID: 1 → 9001 — API aponta para dados reais da empresa SIG. Ex: pedido nº 141022 passa de R$1.765,48 para R$2.454,90. Docs — sarweb_views.sql: - Documenta as views reais em schema sar; remove schema sarweb inexistente. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
11 KiB
Investigation: Pedidos — Valores incorretos e vinculação clientes × vendedor
Hand-off Brief
- O que aconteceu. Pedidos exibem valores/status errados e a vinculação cliente↔vendedor está inconsistente; suspeita confirmada de 5 bugs distintos, sendo o mais grave o mismatch de códigos
situaentre ERP e SAR. - Onde o caso está. Cinco problemas confirmados ou fortemente deduzidos;
vw_pedidos_erp(view central usada pelo service) não está definida em nenhum arquivo de código — sua definição existe apenas no banco. - O que é necessário agora. Verificar a DDL de
vw_pedidos_erpdiretamente no banco (\d+ sarweb.vw_pedidos_erp) e confirmar se ela normalizasitua— isso resolve ou agrava o Bug #1.
Case Info
| Campo | Valor |
|---|---|
| Ticket | N/A |
| Data | 2026-05-30 |
| Status | Active |
| Sistema | Node 24 / NestJS 11 / Prisma 7 / PostgreSQL (SIG+GERENTE schemas) |
| Fontes | orders.service.ts, clients.service.ts, sarweb_views.sql, schema.prisma, .env, jwt-auth.guard.ts |
Problem Statement
Usuário relata: "pedidos com valores errados e vinculação clientes × vendedor"; complementou que a empresa usa id_empresa = 9001 (schema SIG) e a empresa gerencial é a 1 (schema GERENTE), e que às vezes dados estão duplicando nas telas.
Evidence Inventory
| Fonte | Status | Notas |
|---|---|---|
orders.service.ts |
Available | Lido completo — queries brutas sem esquema qualificado |
clients.service.ts |
Available | Lido completo |
sarweb_views.sql |
Available | Define vw_pedidos, NÃO define vw_pedidos_erp |
schema.prisma |
Available | Schema sar no PG; tabela pedidos é SAR-only |
jwt-auth.guard.ts |
Available | Em dev usa DEV_EMPRESA_ID e DEV_REP_CODE do .env |
.env |
Available | DEV_EMPRESA_ID=1, DEV_REP_CODE=29 |
DDL de vw_pedidos_erp no banco |
Missing | Não está no repo — requer \d+ direto no PG |
| Logs do PG / EXPLAIN com id_empresa=9001 | Missing | Requer execução manual |
Confirmed Findings
Finding 1: DEV_EMPRESA_ID=1 mas a empresa real é SIG (id=9001)
Evidência: .env:DEV_EMPRESA_ID=1 e jwt-auth.guard.ts:56 — em dev, idEmpresa é forçado para 1 (GERENTE), ignorando o JWT.
Detalhe: Em desenvolvimento, todas as queries usam WHERE id_empresa = 1 e buscam dados do schema GERENTE. A empresa real do usuário está no schema SIG com id_empresa = 9001. Portanto, os testes locais estão apontando para dados diferentes dos de produção.
Fix imediato: Alterar .env: DEV_EMPRESA_ID=9001.
Finding 2: vw_pedidos_erp não existe em nenhum arquivo do repositório
Evidência:
orders.service.ts:77—FROM vw_pedidos_erp esarweb_views.sql— definesarweb.vw_pedidos, nãovw_pedidos_erpgrep -rn "vw_pedidos_erp"→ apenasorders.service.tsedashboard.service.ts; zero arquivos SQL
Detalhe: A view existe somente no banco (criada manualmente). Seu comportamento exato — especialmente se normaliza situa do ERP para valores SAR — é desconhecido sem inspecionar o banco.
Finding 3: Mismatch de códigos situa entre ERP e SAR
Evidência: sarweb_views.sql:367-378 (GERENTE) vs sarweb_views.sql:411-418 (SIG) vs order.contract.ts:13-18 (SAR).
| Situa | GERENTE ERP | SIG ERP | SAR (app) |
|---|---|---|---|
| 1 | Pendente | Pendente | Ag. Aprovação |
| 2 | Liberado | Liberado | Aprovado |
| 3 | Faturado | Liberado | Cancelado |
| 4 | Cancelado | Faturado | Faturado |
| 5 | — | Cancelado | — |
Efeito confirmado (GERENTE): Um pedido FATURADO no ERP (situa=3) exibe cor vermelha (Cancelado) no SAR — porque OrderStatusBadge usa o número cru para definir tagColor. O texto pode estar correto (via statusDescr) mas a cor é errada.
Efeito no filtro de status: situaFilter = situa != null ? 'AND e.situa = ${situa}' : '' — se o usuário filtra situa=3 (SAR=Cancelado), recebe pedidos FATURADOS do GERENTE.
Finding 4: dt_ultima_compra ignorando histórico ERP — activityStatus sempre errado
Evidência: clients.service.ts:100 —
LEFT JOIN pedidos p ON p.id_cliente = c.id_cliente
AND p.id_empresa = c.id_empresa
AND p.situa != 3
pedidos aqui é a tabela SAR (sar.pedidos, Prisma), não os pedidos históricos do ERP.
Detalhe: Clientes que têm anos de histórico no ERP mas nunca fizeram pedido pelo SAR ficam com dt_ultima_compra = NULL → activityStatus = 'inactive'. Portanto, a carteira inteira aparece como "Inativo" na tela de Clientes.
Fix necessário: O JOIN deve usar vw_pedidos_erp (ou equivalente) em vez da tabela SAR.
Finding 5: Filtro por status de clientes aplicado DEPOIS da paginação SQL
Evidência: clients.service.ts:138 —
if (status) mapped = mapped.filter((c) => c.activityStatus === status);
O total vem de SELECT COUNT(*) sem o filtro de status (clients.service.ts:114-122).
Efeito: Ao filtrar por status=active, a API retorna menos itens que limit (ex.: 8 de 50), mas total ainda diz 2606. A paginação fica quebrada e a portfólio card mostra totais incorretos quando usa useClientList({ limit:1, status:X }).
Hypothesized Paths
Hypothesis 1: vw_pedidos_erp causa duplicação via UNION sem filtro por empresa
Status: Open
Teoria: vw_pedidos_erp faz UNION ALL de GERENTE + SIG sem filtrar por id_empresa, e para uma empresa que existe nos dois schemas (id=1 e id=9001), as mesmas ordens aparecem duas vezes.
Confirmaria: \d+ sarweb.vw_pedidos_erp mostrando UNION ALL sem cláusula WHERE por empresa, + query retornando duplicatas com id_empresa distintos.
Refutaria: View com UNION ALL onde cada SELECT tem AND id_empresa = X fixo.
Hypothesis 2: SAR-created orders nunca aparecem na listagem
Status: Open
Teoria: orders.service.ts list() só consulta vw_pedidos_erp (ERP), nunca sar.pedidos (Prisma). Pedidos criados pelo SAR somem da lista após criação.
Confirmaria: Criar pedido via SAR → abrir /pedidos → pedido não aparece na lista.
Refutaria: vw_pedidos_erp inclui sar.pedidos via UNION ALL.
Missing Evidence
| Gap | Impacto | Como obter |
|---|---|---|
DDL de vw_pedidos_erp |
Confirma/refuta H1, H2 e Bug #3 (situa) | \d+ sarweb.vw_pedidos_erp no psql |
| Query real com id_empresa=9001 | Confirma duplicação e valores | Rodar GET /orders com JWT prod ou DEV_ID=9001 |
Confirmar se sar.pedidos aparece na lista |
Confirma H2 | Criar pedido SAR → verificar lista /orders |
Source Code Trace
| Elemento | Detalhe |
|---|---|
| Bug #1 origem | jwt-auth.guard.ts:56 — DEV_EMPRESA_ID sobrescreve JWT |
| Bug #2 origem | Banco de dados (DDL não versionada) |
| Bug #3 origem | orders.service.ts:43,45,98-99 — situa cru do ERP |
| Bug #4 origem | clients.service.ts:100 — JOIN com sar.pedidos (não ERP) |
| Bug #5 origem | clients.service.ts:138 — filter JS pós-paginação SQL |
Conclusion
Confidence: Medium (root causes identificadas; DDL de vw_pedidos_erp é peça faltante)
Cinco bugs confirmados ou fortemente deduzidos explicam os sintomas:
- DEV aponta para empresa errada (
DEV_EMPRESA_ID=1vs real=9001) — dados diferentes entre dev e prod. situaERP ≠ SAR — cores/filtros de status errados para pedidos históricos.dt_ultima_compraignora ERP — carteira toda aparece inativa.statusfilter pós-paginação — totais e paginação quebrados.vw_pedidos_erpnão versionada — comportamento opaco, possível fonte de duplicação.
Recommended Next Steps
Fix imediato (sem risco)
Alterar .env: DEV_EMPRESA_ID=9001 para que dev espelhe produção.
Antes de qualquer outro fix: verificar vw_pedidos_erp no banco
-- Rodar diretamente no psql:
\d+ sarweb.vw_pedidos_erp
-- ou
SELECT pg_get_viewdef('sarweb.vw_pedidos_erp', true);
O resultado define o caminho dos próximos fixes.
Fix #3 — situa — normalizar na vw_pedidos_erp (ou no service)
Adicionar CASE na view (ou no service) mapeando ERP situa → SAR situa:
-- GERENTE: 2→2, 3→4(Faturado), 4→3(Cancelado)
-- SIG: 2→2, 4→4(Faturado), 5→3(Cancelado)
Fix #4 — dt_ultima_compra — usar ERP orders
Em clients.service.ts:100, substituir pedidos por vw_pedidos_erp (ou a view equivalente):
LEFT JOIN vw_pedidos_erp p
ON p.id_cliente = c.id_cliente
AND p.id_empresa = c.id_empresa
AND p.situa NOT IN (3, 4, 5) -- situa=cancelado nos dois sistemas
Fix #5 — status filter — mover para SQL
Em clients.service.ts, incluir o filtro de activityStatus na query SQL via subquery ou CTE com dt_ultima_compra, eliminando o filter JS pós-paginação.
Reproduction Plan
- Alterar
DEV_EMPRESA_ID=9001no.env - Reiniciar a API
- Abrir
/clientes→ verificar se clientes aparecem e seactivityStatusfaz sentido - Abrir
/pedidos→ verificar se pedidos aparecem e se status está correto - Rodar
\d+ sarweb.vw_pedidos_erpno banco e trazer o resultado para continuar a investigação
Side Findings
ALERT_DAYS=30eINACTIVE_DAYS=60estão hardcoded emclients.service.ts:11-12. O comentário diz "Configuráveis por empresa futuramente" — tarefa pendente.orders.service.ts:46usa interpolação de string direta paranumPedSar(ILIKE). OescSql()declients.service.ts:24não é reutilizado aqui — potencial SQL injection menor para campo de busca.vw_pedidosemsarweb_views.sqlnão é usada em lugar nenhum do código fonte —vw_pedidos_erpé usada em seu lugar. Possível quevw_pedidos_erpseja um rename ou extensão davw_pedidos.