Alcada agora e bloqueio duro no pedido (sem fluxo de aprovar/recusar):
remove ApprovalQueuePage, CarteirePage e rota /aprovacoes, absorvendo
carteira e acompanhamento de pedidos dentro do SupervisorPainel.
Colunas de desempenho por representante extraidas para componente
compartilhado (rep-performance-columns) entre gerente e supervisor.
Ajusta dashboard e orders (controller/service/contract) para o novo fluxo.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Card expansivel no painel do rep com promocoes e regras de desconto
vigentes hoje para o representante logado. Endpoints vigentes agora
resolvem nomes de produto/grupo/subgrupo; hooks com refetch de 60s
para a condicao sumir logo apos encerrar ou inativar.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- KPIs financeiros agrupados (faturamento, meta, atingimento, falta) em metade da primeira dobra
- MapaBrasilCard: choropleth por UF (@svg-maps/brazil), tooltip, ranking lateral e modal Detalhar com representantes por estado
- API: faturamentoPorUf no /dashboard/manager (pedido -> cliente -> municipio -> UF)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Coluna "Ativa" com switch nas tabelas das duas abas (liga/desliga na
hora, sem abrir o modal) e campo Ativa no modal de edicao. Desativada,
a promocao/regra sai imediatamente do catalogo e da alcada.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Promocao: produtos com busca remota por codigo/descricao (multi) e
grupos com busca por nome/codigo (multi) - backend cria uma promocao
por alvo na mesma transacao ({ids})
- Regra de desconto: grupos e subgrupos multi (subgrupos filtram pelos
grupos escolhidos) - uma regra por alvo
- Edicao continua por linha, com selects de busca no lugar dos inputs
de texto livre
- Modais maiores (760px de largura, corpo com altura minima e rolagem)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Menu Catalogo no cockpit do gerente (rota /catalogo existente)
- /catalog/pautas: gestor recebe TODAS as pautas ativas com contagem de
reps que usam cada uma (rep segue vendo so as suas cod_pauta1..6)
- Detalhe do produto: gestor ve o preco em todas as pautas (rep segue
restrito as dele)
- Select de pauta mostra "N reps" quando disponivel
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Antes a meta vivia no localStorage do navegador (perdia em outro
dispositivo). Agora: coluna meta_positivacao_dia em config_empresa,
PUT /dashboard/manager/meta-positivacao-dia (gerente/admin), meta vem
no dashboard e o card tem botao Salvar (desabilitado sem mudanca).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- novosClientes por rep no dashboard do gerente: clientes da carteira
cuja PRIMEIRA compra (historico todo do ERP) caiu no periodo
- Coluna "Novos no mes" (tag verde +N, ordenavel) na tabela de
positivacao + total de novos clientes no cabecalho do card
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Painel do historico do cliente no lancamento agora abre sempre fechado,
com banner ambar chamativo ("Este cliente ja comprou N produtos com
voce") e botao Ver produtos - o rep le e escolhe abrir, em vez de a
lista ocupar a tela direto.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- vw_produtos passa a expor vol_m3 (coluna nova no FIM da view - CREATE
OR REPLACE exige; aplicada no dev)
- PedidoDetail calcula pesoTotal/m3Total (qtd x peso_liquido/vol_m3),
tanto para pedidos SAR quanto ERP
- Impressao mostra "Peso total: X kg" e "Volume: Y m3" ao lado dos
totais (some quando zerado)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Tabela sar.config_empresa (logo em data URL por empresa matriz) com
migration idempotente, espelho no schema SQL e GRANT no provision
- PUT /catalog/company/logo (gerente/admin) e logoBase64 no GET
/catalog/company - vale para a impressao de todos os representantes
- Nova tela /ger/config "Configuracoes" com upload/preview/remocao do
logo (PNG/JPG/WebP ate 500KB)
- Cabecalho da impressao redesenhado: logo a esquerda, dados da empresa
condensados em 3 linhas, numero/status/data compactos a direita -
ocupa ~metade da altura anterior no A4
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
O clique no botao de acoes propagava para o onClick da linha, abrindo o
detalhe por cima do menu. stopPropagation no bloco de acoes: o menu abre
sozinho e so a opcao escolhida executa algo.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PATCH /orders/:id edita orcamento (situa 0): substitui itens,
recalcula totais, valida alcada do dono do pedido; depois de
transmitido nao e mais editavel (400)
- Menu "Editar orcamento" abre o lancamento pre-carregado
(/pedidos/novo?editar=id) com cliente, itens, pauta, forma de
pagamento, obs e endereco; salvar faz PATCH em vez de POST
- Detalhe do pedido passa a incluir idPauta/codFormapag e
codGrupo/codSubgrupo por item (teto de desconto na edicao)
- Cancelar pedido abre card com motivo (select) + descricao livre;
gravado em motivo_recusa e no historico (ASCII: banco ERP e LATIN1,
em-dash quebrava o UPDATE)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- create() valida desconto global e por item (mesma regra do transmit):
Concluir com desconto acima do teto retorna 400 com a mensagem do
item que passou; transmit segue validando (defesa em profundidade)
- Botao Concluir desabilita em tempo real com o motivo quando algum
item esta acima do teto; caminho offline tambem barrado no submit
- Mensagem de alcada generalizada ("para continuar")
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- GET /politicas/alcada: limites de desconto do proprio usuario logado
- Carrinho (desktop e mobile) mostra "teto: X%" sob o campo de desconto
de cada item - max(alcada do grupo, promocao, regra vigente), espelho
do assertAlcadaItens; acima do teto o campo fica vermelho com aviso de
que a transmissao sera bloqueada
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Com alcada base 5% e regra de 15%, o limite do item era 5+15=20% e um
desconto de 18% passava na transmissao. Agora vale o MAIOR entre alcada
do grupo, promocao e regra vigente: regra de 15% bloqueia acima de 15%.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Acesso pelo IP da rede (http://192.168...) nao e contexto seguro e o
navegador nao expoe crypto.randomUUID, derrubando a conclusao do pedido.
Helper randomUUID() cai para UUID v4 via crypto.getRandomValues.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Item entra com 0% mesmo com promocao/regra vigente: o selo de condicao
comercial avisa o rep, que concede o desconto apenas se o cliente
negociar. A alcada no backend segue aceitando ate o % da regra.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- DevLogin com 3 usuarios reais: Pavei (rep 29), Sidnei (supervisor 191)
e Lucas (gerente 156); guard deixa de forcar DEV_REP_CODE em dev e usa
sub/id_empresa do JWT (fallback para os valores do .env); idEmpresa do
token dev opcional, default DEV_EMPRESA_ID
- getTeamCodes (vw_representantes.cod_supervisor): supervisor ve so a
equipe em pedidos SAR/ERP, detalhe, aprovar/recusar, clientes (lista e
carteira), dashboard supervisor, chamados SAC e relatorios
carteira/curva ABC; gerente/admin seguem vendo a empresa toda
- Painel gerencial: card "Positivacao Diaria de Clientes" (clientes
distintos por dia via vw_pedidos_erp) com campo de meta/dia
persistido em localStorage, linha de meta tracejada e contador de
dias na meta
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Nova tabela sar.regras_desconto (grupo, subgrupo, representante, % e
vigencia; campos nulos = todos) com migration idempotente, espelho no
sar-erp-schema.sql e GRANT no provision-client
- CRUD manager-only em /politicas/regras + aba "Regras de Desconto" na
tela de Politicas Comerciais (selects com nomes de rep/grupo/subgrupo
via /politicas/grupos-produto)
- GET /politicas/regras/vigentes (rep ve so as que o alcancam) e
GET /politicas/promocoes/vigentes para o catalogo
- Regra vigente pre-aplica o desconto no carrinho do novo pedido
- Selos "Promocao"/"Cond. especial" na busca de produto, catalogo do
pedido e pagina de catalogo; secao "Condicoes Comerciais" no modal de
detalhe do produto (descricao, % e validade)
- Empty imageStyle deprecado -> styles.image no carrinho
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Auditoria de segurança + robustez da área do representante antes de produção.
API (autorização e injeção):
- IDOR em clients: endpoints de detalhe (findOne, notas, ctr, top-produtos,
histórico, contatos) validam carteira do rep via assertClienteDaCarteira;
cliente alheio retorna 404. orders.create idem antes de aceitar idCliente.
- Role guard em dashboards supervisor/manager e ger/chamados (rep -> 403).
- Datas em $queryRawUnsafe agora exigem YYYY-MM-DD (DateOnlySchema + revalidação);
dtAniversario de contato validado e escapado.
- id_empresa de CTR/NF usa matrizEmpresa() em vez de 1/9001 hardcoded.
- Alçada de desconto: transmit valida desconto efetivo por item (preço vs tabela
pauta>promo>base + promoção ativa), não só o desconto global.
- Idempotency-Key escopado a empresa+vendedor; unsubscribe com DTO e dono.
Web (erros nunca silenciosos):
- Remove dupla serialização (apiFetch já faz JSON.stringify) em funil, sac e
politicas — corrige criar oportunidade, abrir chamado e editar políticas.
- Handler global de erro no QueryClient (query e mutation viram toast).
- Error boundary por rota (payload divergente não derruba o app em branco).
- Alert usa message= em vez de title= (AntD 6); sw.js usa globalThis.
Qualidade:
- Corrige ping.contract.spec (idEmpresa) e zera erros de lint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- `<Space direction>` -> `orientation` e `children` -> `content` no
Popconfirm, acompanhando o rename da API no Ant Design 6.
- `allowedHosts: true` no Vite para permitir acesso ao dev server por
hostname da rede local.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Na ficha do cliente, o card de titulos em aberto se esticava para o lado e
empurrava a secao de NF-e para fora da tela.
Causa: um track `1fr` de CSS Grid carrega `min-width: auto` implicito e se
recusa a encolher abaixo da largura intrinseca do conteudo. Como as tabelas
usam `scroll={{ x: 'max-content' }}`, a largura intrinseca delas e a soma de
todas as colunas — com titulos suficientes, a coluna estourava os 50% e
comia a vizinha. O `maxWidth: '100%'` ja presente nas tabelas nao ajudava,
porque quem crescia era o track, nao a tabela.
`minWidth: 0` nos filhos do grid desarma o `min-width: auto` e devolve o
scroll horizontal para dentro da tabela, que era o comportamento desejado.
Aplicado tambem em ClientsPage: o `<Col>` que hospeda a tabela de clientes
e estruturalmente identico (Col e flex item, mesmo `min-width: auto`) e so
nao quebrava porque a tabela tem `overflow: 'hidden'` no style — uma linha
que se le como puramente cosmetica (arredondar canto) e que, se removida
num refactor, reintroduziria o bug. A protecao vai para o lugar canonico.
Inclui ainda o span responsivo do campo Endereco na ficha.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- `listTopProdutos` selecionava `TRIM(p.codigo)` sem incluir `p.codigo` no
GROUP BY, entao GET /clients/:id/top-produtos respondia 500 com
"column p.codigo must appear in the GROUP BY clause". Um produto tem um
unico codigo, entao agrupar por ele nao altera o resultado.
- `/catalog/company` passa a ler `num_endereco` em vez de `numero`, que nao
correspondia a coluna real do ERP.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`nestjs-zod@4.3.1` arrasta `@nest-zod/z@2.0.0`, que registra um error map
GLOBAL no Zod chamando `zod.defaultErrorMap` — API removida no Zod 4 (o
projeto usa 4.4.3). O efeito nao era local: qualquer falha de validacao,
em qualquer endpoint, estourava durante a formatacao do erro e virava
500 em vez de resposta de validacao.
Reproduzido em GET /reports/meta sem query params; apos o upgrade retorna
422 com os campos invalidos discriminados.
A v5 nao depende mais de `@nest-zod/z` e e nativa Zod 4. So
`ZodValidationPipe` e `createZodDto` sao usados, ambos estaveis na v5.
Renomeia tambem `pnpm workspace:provision` -> `pnpm client:provision`,
completando o commit de infraestrutura (package.json e pnpm-lock.yaml
precisam viajar juntos, senao o pnpm tenta recriar o node_modules).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Revoga o ADR 0006. O isolamento multi-tenant deixa de ser um banco por
workspace e passa a ser o schema `sar` dentro do proprio banco ERP do
cliente, com `id_empresa` discriminando o tenant em todas as tabelas.
Provisionamento:
- `provision-client.ts` substitui `provision-workspace.ts`. Nao cria mais
banco: aplica schema + views + triggers num banco ERP ja existente.
Trata o encoding LATIN1 dos bancos ERP via `toLatinSafe()`.
- `sar-erp-schema.sql` vira a fonte unica: absorve as views que estavam em
`sarweb_views.sql` e passa a conter tambem `sar.chamados` /
`sar.chamado_mensagens`, que so existiam via migration.
Correcoes necessarias para o provisionamento rodar ponta a ponta:
- `migrate deploy` abortava com P3005, porque o schema SQL roda antes do
Prisma e o schema `sar` nunca esta vazio. O script agora detecta banco
pre-existente e faz o baseline sozinho.
- `_prisma_migrations` ia parar no schema `public` do ERP do cliente; a URL
de migracao agora leva `?schema=sar`.
- `sar.pedidos` nao tinha `end_entrega`, coluna que o schema.prisma ja
declarava desde a feature de endereco de entrega, e que derrubava
GET /orders com 500. Migration adicionada.
- `vw_sitpedido` se perdeu junto com `sarweb_views.sql` e e usada pela
consulta de pedidos ERP. Restaurada.
Remove o seed de dados demo e os fallbacks de DATABASE_URL hardcoded: sem
a variavel configurada a API agora falha explicitamente, em vez de apontar
silenciosamente para um banco de desenvolvimento inexistente.
Verificado ponta a ponta contra um banco ERP real: schema aplicado, 13
tabelas + 20 views, migrations registradas, e segunda execucao idempotente.
BREAKING CHANGE: DATABASE_URL passa a ser obrigatoria. O comando de
provisionamento muda para `pnpm client:provision --host <host> --db <banco>`
(rename efetivado no commit de dependencias).
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>
- FOR UPDATE é inválido com MAX() no PG 18; substituído por pg_advisory_xact_lock
- fconta=0 violava CHECK (1,2,3,4,5,6,9); corrigido para 1 (padrão SAR)
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>