Files
sar/_bmad-output/implementation-artifacts/investigations/pedidos-valores-vinculacao-investigation.md
julian 1647871a39 feat(web+api): redesign ClientsPage/OrdersPage e corrige dados empresa 9001
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>
2026-05-30 14:08:56 +00:00

11 KiB
Raw Blame History

Investigation: Pedidos — Valores incorretos e vinculação clientes × vendedor

Hand-off Brief

  1. 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 situa entre ERP e SAR.
  2. 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.
  3. O que é necessário agora. Verificar a DDL de vw_pedidos_erp diretamente no banco (\d+ sarweb.vw_pedidos_erp) e confirmar se ela normaliza situa — 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:77FROM vw_pedidos_erp e
  • sarweb_views.sql — define sarweb.vw_pedidos, não vw_pedidos_erp
  • grep -rn "vw_pedidos_erp" → apenas orders.service.ts e dashboard.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 = NULLactivityStatus = '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:56DEV_EMPRESA_ID sobrescreve JWT
Bug #2 origem Banco de dados (DDL não versionada)
Bug #3 origem orders.service.ts:43,45,98-99situa 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:

  1. DEV aponta para empresa errada (DEV_EMPRESA_ID=1 vs real=9001) — dados diferentes entre dev e prod.
  2. situa ERP ≠ SAR — cores/filtros de status errados para pedidos históricos.
  3. dt_ultima_compra ignora ERP — carteira toda aparece inativa.
  4. status filter pós-paginação — totais e paginação quebrados.
  5. vw_pedidos_erp não versionada — comportamento opaco, possível fonte de duplicação.

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

  1. Alterar DEV_EMPRESA_ID=9001 no .env
  2. Reiniciar a API
  3. Abrir /clientes → verificar se clientes aparecem e se activityStatus faz sentido
  4. Abrir /pedidos → verificar se pedidos aparecem e se status está correto
  5. Rodar \d+ sarweb.vw_pedidos_erp no banco e trazer o resultado para continuar a investigação

Side Findings

  • ALERT_DAYS=30 e INACTIVE_DAYS=60 estão hardcoded em clients.service.ts:11-12. O comentário diz "Configuráveis por empresa futuramente" — tarefa pendente.
  • orders.service.ts:46 usa interpolação de string direta para numPedSar (ILIKE). O escSql() de clients.service.ts:24 não é reutilizado aqui — potencial SQL injection menor para campo de busca.
  • vw_pedidos em sarweb_views.sql não é usada em lugar nenhum do código fonte — vw_pedidos_erp é usada em seu lugar. Possível que vw_pedidos_erp seja um rename ou extensão da vw_pedidos.