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>
## 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>
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>
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/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>