feat(api,web): funil de vendas do representante
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>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
-- CreateTable: sar.oportunidades (Funil de Vendas)
|
||||
-- Definicao espelha scripts/sar-erp-schema.sql: provision-client.ts roda o schema SQL
|
||||
-- antes de `prisma migrate deploy`, entao esta migration precisa ser idempotente.
|
||||
-- ATENCAO: bancos ERP usam LATIN1 - nao usar em-dash nem box-drawing aqui.
|
||||
CREATE TABLE IF NOT EXISTS sar.oportunidades (
|
||||
id SERIAL PRIMARY KEY,
|
||||
id_empresa INTEGER NOT NULL,
|
||||
cod_vendedor INTEGER NOT NULL,
|
||||
id_cliente INTEGER,
|
||||
nome_prospect VARCHAR(200),
|
||||
empresa_prospect VARCHAR(200),
|
||||
titulo VARCHAR(200) NOT NULL,
|
||||
etapa VARCHAR(20) NOT NULL DEFAULT 'lead',
|
||||
valor_estimado NUMERIC(15,2),
|
||||
observacoes TEXT,
|
||||
id_pedido UUID REFERENCES sar.pedidos(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sar_oport_vend ON sar.oportunidades(id_empresa, cod_vendedor);
|
||||
CREATE INDEX IF NOT EXISTS idx_sar_oport_etapa ON sar.oportunidades(etapa);
|
||||
@@ -171,6 +171,32 @@ model Promocao {
|
||||
@@map("promocoes")
|
||||
}
|
||||
|
||||
// ─── Oportunidade (Funil de Vendas) ──────────────────────────────────────────
|
||||
//
|
||||
// Oportunidade de venda do representante. Pode referenciar cliente ERP (id_cliente)
|
||||
// ou ser um prospect livre (nome_prospect / empresa_prospect).
|
||||
// etapa: lead | proposta | negociacao | ganho | perdido
|
||||
|
||||
model Oportunidade {
|
||||
id Int @id @default(autoincrement())
|
||||
idEmpresa Int @map("id_empresa")
|
||||
codVendedor Int @map("cod_vendedor")
|
||||
idCliente Int? @map("id_cliente")
|
||||
nomeProspect String? @map("nome_prospect") @db.VarChar(200)
|
||||
empresaProspect String? @map("empresa_prospect") @db.VarChar(200)
|
||||
titulo String @db.VarChar(200)
|
||||
etapa String @default("lead") @db.VarChar(20)
|
||||
valorEstimado Decimal? @db.Decimal(15, 2) @map("valor_estimado")
|
||||
observacoes String?
|
||||
idPedido String? @db.Uuid @map("id_pedido")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@index([idEmpresa, codVendedor])
|
||||
@@index([etapa])
|
||||
@@map("oportunidades")
|
||||
}
|
||||
|
||||
// ─── PushSubscription (C6) ───────────────────────────────────────────────────
|
||||
//
|
||||
// Subscription VAPID Web Push por usuário. endpoint é único por dispositivo/browser.
|
||||
|
||||
Reference in New Issue
Block a user