Tela dedicada que abre pelo botao "Detalhar carteira" na listagem de
clientes, com leitura acionavel da carteira em vez de so uma tabela.
- `/reports/carteira` passa a devolver `faturamentoTotal`,
`participacaoPct` e `faturamentoRepTotal`, permitindo medir o peso de
cada cliente no faturamento do rep.
- `CarteirePage`: cards de resumo (ativos / em risco / inativos / sem
pedido), faturamento total e InsightCards que apontam concentracao em
poucos clientes, maior cliente em risco, maior inativo e clientes nunca
positivados.
- Clientes classificados por dias sem comprar (30d em risco, 60d inativo),
com navegacao direta para a ficha.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Representante abre chamado a partir de um pedido e conversa com a empresa
por thread de mensagens; o gerente responde e resolve.
- API: `SacModule` com 5 rotas do rep (listar, detalhe, criar, responder,
cancelar) e 4 da empresa (/ger/chamados: listar, detalhe, responder,
resolver). Escopo do rep e por cod_vendedor; o da empresa, por id_empresa.
- Modelos `Chamado` + `ChamadoMensagem` e migrations correspondentes.
- O chamado pode referenciar tanto um pedido nascido no SAR (id_pedido +
num_ped_sar) quanto um pedido historico do ERP (num_ped_erp) — dai
id_pedido e num_ped_sar serem nullable. `nome_cliente` fica
desnormalizado para evitar join em toda listagem.
- Web: `ChamadosPage` (rep), `GerChamadosPage` (gerente) e
`AbrirChamadoModal`, acessivel tambem pelo detalhe do pedido. Entradas
"SAC" no menu do rep e do gerente.
- `useOrderErpConsulta` ganha flag `enabled` para a busca de pedido ERP so
disparar quando o modal esta aberto.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Kanban de oportunidades por etapa (lead, proposta, negociacao, ganho,
perdido) com CRUD completo.
- API: `FunilModule` com GET/POST/PATCH/DELETE em /funil, escopado por
id_empresa + cod_vendedor.
- Modelo `Oportunidade` + migration `sar.oportunidades`. Uma oportunidade
referencia um cliente do ERP (id_cliente) ou e um prospect livre
(nome_prospect / empresa_prospect).
- Contrato Zod compartilhado em `funil.contract.ts`.
- Web: `FunilPage` com colunas por etapa, entrada no menu do rep e rota
/funil.
Nota: a UI ainda nao expoe seletor de cliente, entao toda oportunidade
criada pela tela nasce como prospect livre; `idCliente` e `idPedido` ja
sao suportados no backend, mas ficam inalcancaveis pela interface.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- NewOrderPage: etapas 1-5 — merge de cards, busca de produto melhorada
(autocomplete com estoque + modal catálogo), responsividade mobile (tabela →
cards, footer compacto), histórico de compras do cliente e campo de
endereço de entrega (cadastrado vs livre)
- endEntrega: campo TEXT adicionado em sar.pedidos; contrato CreatePedidoSchema
e PedidoDetailSchema atualizados; aparece no detalhe (OrderDetailPage,
OrderDetailModal) e no PDF (OrderPrintPage)
- Cancelamento de orçamento: PATCH /orders/:id/cancel — rep cancela seu
próprio orçamento (situa 0); histórico registrado; UI com Modal.confirm
substituindo alert() em OrderActionsMenu
- Fix: useClientDetail chamado para cliente selecionado manualmente, garantindo
que clientEndStr seja preenchido mesmo sem clientIdParam na URL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Agrega GR-metas por cod_grupo (todos os reps) e realizado por grupo
via JOIN em vw_peditens_erp/vw_produtos, retornando MetaItem[] no
endpoint /dashboard/manager. GerPainel exibe tabela com Meta /
Realizado / Falta / % Meta (barra + número) ordenada por valorMeta
desc; card oculto quando não há grupos cadastrados.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## API
- GET /dashboard/manager: KPIs agregados (faturamento, pedidos, ticket médio,
promoções ativas), meta total do período, ranking top 10 com clientes
atendidos e % meta, positivação por representante, venda necessária/dia
- Parâmetros opcionais ?mes&ano para filtrar período
- GET /equipe: lista de reps com pedidos, faturamento, ticket médio e % meta
(deduplicação de vw_representantes)
- GET/POST /politicas/descontos: alçada de desconto por rep (AlcadaDesconto)
- GET/POST/PATCH/DELETE /politicas/promocoes: CRUD de promoções com validade
- Metas lidas de sar.vw_metas (tipo=GR), não de vw_metas do ERP
## Schema
- Novo model Promocao (sar.promocoes) criado via prisma db execute
## Frontend
- Cockpit /ger: GerPainel, EquipePage, PoliticasPage
- GerPainel: filtro mes/ano, cards KPI, cards de meta vs realizado
(atingimento, falta, venda/dia), ranking com clientes atendidos,
positivação de clientes por representante (paginada)
- Sidebar e BottomNav role-aware: rep / supervisor / gerente (manager+admin)
- Clientes acessível ao gerente (carteira completa de todos os reps)
- Rotas: HomeRoute redireciona por role; /ger, /ger/equipe, /ger/politicas
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Contatos: grid de cards 3 colunas, botão "Novo Contato" no topo da página
- Sync ERP↔SAR corrigido: vw_contatos aceita formato COR#<id>, trigger normaliza id_empresa (9001→1)
- CTR + NF-e: layout 50/50 — lista de títulos abertos com badge vencido/a vencer e lista de notas com botão copiar chave NF-e
- Histórico de pedidos: UNION SAR+ERP, top 5 + modal "Ver todos"
- Produtos mais comprados: top 5 com último preço + modal "Ver todos"
- Novos endpoints: ctr-list, notas, orders-history, top-produtos
- AppShell: overflow-x travado, sem scroll horizontal na aplicação
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Catálogo: modal de detalhe centralizado (1100px) com identificação, estoque,
preços base e lista de preços por pauta do representante (DISTINCT para evitar duplicatas)
- Contrato: loteMulVenda promovido para ProdutoSummarySchema; PautaPrecoSchema adicionado
- Pedido novo: valida lote múltiplo de venda — qtd inicial = lote, step = lote,
InputNumber em erro quando inválido, alerta e bloqueio do submit
- Consulta ERP: tela OrdersErpPage com listagem de pedidos do ERP (P/E), contrato
PedidoErpConsulta, endpoint no orders.controller, rota e sidebar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Clicar no nome do cliente na lista navega direto para /clientes/:id (ficha completa
com contatos e pedidos). Botão 'Abrir ficha' adicionado no footer do drawer de preview.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adiciona useNavigate() no corpo de ClientsPage (faltava; sub-componentes já tinham).
Substitui Space direction→orientation (4 ocorrências) e Progress trailColor→railColor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
apiFetch já serializa o body internamente; passá-lo como JSON.stringify(data) causava
double-encoding e 400 Bad Request no cadastro via browser.
Substitui orientation por titlePlacement no Divider de Contatos (AntD 6.x).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DB:
- sar.vw_contatos: view sobre gestao.contato com join sig.corrent
(id_entidade CHAR20 = id_corrent; cod_vendedor para filtro por rep)
- sar.contatos_novos: staging com nome, cargo, departamento, telefone,
celular, whatsapp, email, dt_aniversario, anotacoes
- trg_contato_novo: AFTER INSERT → gestao.contato; grava id_contato_erp
API:
- GET /clients/:id/contacts — lista contatos ativos do cliente
- POST /clients/:id/contacts — cria via staging; retorna { idContatoErp, sincronizado }
Web:
- ClientContacts: tabela de contatos no detalhe do cliente
- Links diretos WhatsApp (wa.me) + mailto por contato
- Modal "Novo Contato" com todos os campos; feedback de código ERP no sucesso
- whatsapp é o campo principal para envio futuro via ChatWoot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Web (NewClientPage):
- Segmentado PJ/CPF com campos específicos por categoria
- PJ: razão social, nome fantasia, CNPJ, IE, indicador IE, consumidor final
- PF: nome completo, CPF
- Endereço com auto-preenchimento via ViaCEP (CEP → rua/bairro/município)
- Município: Select com busca server-side (/catalog/municipios)
- Contato: DDD, telefone, email
- Comercial: forma pagamento, tabela de preços, limite crédito
- Feedback: código ERP exibido no sucesso; alerta de erro de sync sem bloquear
API:
- GET /catalog/municipios?q= — busca municipios da vw_municipios
- POST /clients/novo — INSERT em sar.clientes_novos; trigger sincroniza para sig.corrent
- Retorna { id, idCorrentErp, sincronizado, erroSync } após o trigger disparar
- Fix trigger fn_sync_cliente_to_erp: NULLIF para cgcpf vazio (evita violação
do unique index key_corrent_cgcpf quando cliente não tem CPF/CNPJ)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Triggers SAR→ERP (scripts/sar-triggers.sql):
- trg_pedido_aprovado: ao aprovar (situa 1→2) em sar.pedidos, replica
automaticamente em sig.pedidos + sig.peditens e grava erp_id_pedido
- trg_cliente_novo: INSERT em sar.clientes_novos replica em sig.corrent
com todos os campos obrigatórios; grava id_corrent_erp e flag sincronizado
- Corrige trigger tsvectorupdate do ERP para PG 18 (bpchar→text cast)
- Adiciona coluna erp_id_pedido em sar.pedidos e tabela sar.clientes_novos
Migrations Prisma:
- Remove 5 migrations obsoletas com nomes PascalCase (Order, Client, etc.)
que não batiam com os @@map snake_case do schema atual
- Cria baseline 20260624000000_init_baseline apontando para estado correto
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Evita TypeError quando SW serve cache antigo sem esses campos no shape
da resposta do dashboard.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
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>
- JOIN de vw_pedidos_erp com vw_clientes pelo id_cliente + id_empresa
- Campos nomeCliente e razaoCliente adicionados ao PedidoSummary (contrato)
- Tabela, cards mobile e drawer exibem razão social (fallback para nome)
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
NewOrderPage:
- Layout de página única com cards (remove wizard em steps)
- AutoComplete de cliente com busca na API
- Badge de confirmação ao selecionar cliente
- Select de Pauta (API real) e Condição de Pagamento (mock)
- Campos Contato e Nº OC
- AutoComplete de produto por catálogo com pauta aplicada
- Soma qty automaticamente se produto já está no carrinho
- Tabela de itens com qty/desconto editáveis inline
- Rodapé fixo com total e botão Finalizar verde
OrdersPage:
- Cards de métricas (total, vendido, pendentes, aprovados, ticket médio)
- Filtros por status e período (hoje / 7d / 30d)
- Tabela com row-click colorido por status
- Drawer lateral com detalhes, itens e timeline de histórico
- Menu de ações por linha (ver, duplicar, PDF, cancelar)
- Cards mobile responsivos
Layout global:
- Botão Novo Pedido na Topbar (sempre visível)
- FAB verde fixo (bottom-right) no AppShell
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- GET /api/v1/auth/me retorna perfil real do ERP (vw_representantes)
- Contrato UserProfile adicionado ao shared api-interface
- Hook useCurrentUser() no frontend consome o endpoint
- Cockpit rafael → rep, sandra → supervisor (pastas e componentes)
- Topbar exibe iniciais do usuário e dropdown com nome, role e "Sair"
- Logout limpa token e recarrega para voltar ao DevLogin
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
AntD 6 deprecou direction em favor de orientation.
14 ocorrências em ClientsPage, NewOrderPage, RafaelPainel e SandraPainel.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- Catálogo só mostra produtos com preço preenchido (vl_preco1 > 0) por default
- Novo endpoint GET /catalog/pautas — retorna as 6 pautas do representante logado
- GET /catalog?idPauta=N — usa preço da pauta selecionada (vw_pauta_produtos)
- CatalogPage: dropdown "Selecionar pauta de preços" com as pautas do rep
- product.contract: adiciona PautaSchema e idPauta no ProdutoListQuerySchema
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- OrdersService.list: substitui sar.pedidos por vw_pedidos_erp — 44k pedidos
históricos do rep 29 visíveis; sar.pedidos continua sendo a tabela de escrita
para novos pedidos SAR que serão integrados ao ERP
- DashboardService: atingido/pedidosMes/recentes/inativos todos via vw_pedidos_erp;
supervisor usa vw_pedidos_erp para pedidosDia
- PedidoSummarySchema: id relaxado de uuid() para string(); adiciona numero,
statusDescr e fonte ('sar'|'erp')
- orders.ts: corrige bug — apiFetch retorna JSON diretamente, não Response;
remove res.ok/res.json() incorretos
- OrdersPage: coluna Nº mostra numero do ERP; statusDescr no badge
- DevLogin: atualiza para PAVEI COMERCIO cod 29
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
FR-6.1/6.2: Sandra recebe push quando pedido entra em pending_approval;
Rafael recebe quando pedido é aprovado ou recusado. Service worker registrado
em background (PWA-ready via public/sw.js).
FR-6.3: Badge na Topbar busca GET /notifications/pending-count (supervisores
veem count de pending_approval; reps veem 0). Intervalo de 30s.
FR-6.4: Botão Compartilhar no OrderDetailPage para pedidos approved/invoiced
(apenas reps). Usa navigator.share() com texto formatado para WhatsApp.
Infra: modelo PushSubscription (Prisma), NotificationsModule (subscribe/
unsubscribe/pending-count + PushService VAPID), VAPID keys em .env,
integração no OrdersService (create → supervisores, approve/reject → repId).
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
GET /dashboard/supervisor com fila de aprovações, KPIs do dia vs semana
anterior e top 3 reps com mais clientes inativos. SandraPainel com polling
30s. Rota / role-aware: rep → RafaelPainel, supervisor/manager → SandraPainel.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /dashboard/rep retorna meta mensal, comissão (fixa + FLEX), clientes
inativos >30 dias e pedidos dos últimos 7 dias. RepTarget model com migration.
RafaelPainel conectado à API real via useRepDashboard().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PATCH /orders/:id/approve e /reject com alçada role-gated; OrderDetailPage
com modais de aprovação e recusa; ApprovalQueuePage para Sandra; badge de
pendências na Sidebar; DevLogin com 4 perfis (rep, supervisor, gerente).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fecha loop B+C — Web consome @sar/api-interface em runtime, não só build.
- Vite proxy /api → localhost:3000 (zero CORS em dev, mesma URL em prod via Nginx)
- api-client.ts: fetch wrapper parseando RFC 9457 problem+json em ApiError
- useApiPing: TanStack Query + PingResponseSchema.parse — drift servidor falha alto
- FoundationStatus pill na Topbar (verde/vermelho/cinza + Tooltip com requestId)
Validado via curl proxy:4200 → 200 ok contratual; /nope → 404 problem+json.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>