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

207 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:77``FROM 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`
```sql
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`
```typescript
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:
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.
## 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
```sql
-- 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:
```sql
-- 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):
```sql
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`.