refactor(erp): integração direta com banco ERP — schema sar
Revoga ADR 0006 (BD-por-workspace separado). O SAR agora conecta ao banco PostgreSQL do ERP (módulo SIG) e usa o schema `sar` para tudo. PRISMA - Remove: Client, Product, Order, OrderItem, OrderStatusHistory, RepTarget, RepDiscountLimit, PushSubscription (modelos isolados) - Adiciona: Pedido, PedidoItem, HistoricoPedido, AlcadaDesconto, MetaRepresentante, PushSubscription (mapeados para sar.*) - IDs: id_cliente/cod_vendedor/id_empresa são INTEGER (ERP) - situa: Int (1=Pendente 2=Aprovado 3=Cancelado 4=Faturado) - JWT: workspace_id:string → id_empresa:number - URL: inclui ?schema=sar para Prisma rotear ao schema ERP SERVICES - ClientsService: $queryRawUnsafe contra sar.vw_clientes + sar.pedidos - CatalogService: $queryRawUnsafe contra sar.vw_produtos + sar.vw_estoque - OrdersService: Prisma models Pedido/PedidoItem/HistoricoPedido/AlcadaDesconto - DashboardService: MetaRepresentante + queries raw para inativos - NotificationsService: PushSubscription com codVendedor + idEmpresa CONTRATOS (api-interface) - client.contract: campos ERP (idCliente, nome, cgcpf, cod_vendedor…) - order.contract: PedidoSummary/PedidoDetail/CreatePedido + SITUA_LABEL - product.contract: ProdutoSummary/ProdutoDetail (vw_produtos) - auth.contract: workspaceId:string → idEmpresa:number WEB - Todos os cockpits e queries atualizados para os novos tipos Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,64 +2,53 @@ import { z } from 'zod';
|
||||
|
||||
// Contratos canônicos de C2 — Consulta de Clientes.
|
||||
// Consumidos pela API (output tipado) e pela Web (TanStack Query + parse).
|
||||
// ADR 0006 revogado: id UUID → idCliente Int (sig.corrent.id_corrent)
|
||||
|
||||
// ─── Enums ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const FinancialStatusSchema = z.enum(['regular', 'attention', 'blocked']);
|
||||
export type FinancialStatus = z.infer<typeof FinancialStatusSchema>;
|
||||
|
||||
// Calculado em runtime a partir de lastOrderAt (não persiste no banco).
|
||||
export const ActivityStatusSchema = z.enum(['active', 'alert', 'inactive']);
|
||||
export type ActivityStatus = z.infer<typeof ActivityStatusSchema>;
|
||||
|
||||
// ─── Address ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export const AddressSchema = z.object({
|
||||
street: z.string().min(1),
|
||||
number: z.string().min(1),
|
||||
complement: z.string().optional(),
|
||||
district: z.string().min(1),
|
||||
city: z.string().min(1),
|
||||
state: z.string().length(2), // UF
|
||||
zip: z.string().regex(/^\d{8}$/), // sem máscara
|
||||
});
|
||||
export type Address = z.infer<typeof AddressSchema>;
|
||||
|
||||
// ─── Client Summary (lista) ───────────────────────────────────────────────────
|
||||
|
||||
export const ClientSummarySchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
tradeName: z.string().nullable(),
|
||||
taxId: z.string(),
|
||||
financialStatus: FinancialStatusSchema,
|
||||
idCliente: z.number().int(),
|
||||
idEmpresa: z.number().int(),
|
||||
nome: z.string(),
|
||||
razao: z.string().nullable(),
|
||||
cgcpf: z.string().nullable(),
|
||||
email: z.string().nullable(),
|
||||
telefone: z.string().nullable(),
|
||||
codVendedor: z.number().int(),
|
||||
limiteCreditoStr: z.string().nullable(),
|
||||
activityStatus: ActivityStatusSchema,
|
||||
lastOrderAt: z.iso.datetime().nullable(),
|
||||
lastOrderValue: z.string().nullable(), // Decimal serializado como string
|
||||
openOrdersCount: z.number().int().nonnegative(),
|
||||
dtUltimaCompra: z.iso.datetime().nullable(),
|
||||
});
|
||||
export type ClientSummary = z.infer<typeof ClientSummarySchema>;
|
||||
|
||||
// ─── Client Detail (ficha) ───────────────────────────────────────────────────
|
||||
|
||||
export const ClientDetailSchema = ClientSummarySchema.extend({
|
||||
email: z.string().email().nullable(),
|
||||
phone: z.string().nullable(),
|
||||
address: AddressSchema.nullable(),
|
||||
creditLimit: z.string().nullable(), // Decimal serializado como string; null = não definido
|
||||
erpCode: z.string().nullable(),
|
||||
syncedAt: z.iso.datetime().nullable(),
|
||||
createdAt: z.iso.datetime(),
|
||||
updatedAt: z.iso.datetime(),
|
||||
ativo: z.number().int(),
|
||||
pessoa: z.number().int().nullable(),
|
||||
inscricaoEstadual: z.string().nullable(),
|
||||
endereco: z.string().nullable(),
|
||||
numEndereco: z.string().nullable(),
|
||||
bairro: z.string().nullable(),
|
||||
cep: z.string().nullable(),
|
||||
ddd: z.string().nullable(),
|
||||
obs: z.string().nullable(),
|
||||
codPauta: z.number().int().nullable(),
|
||||
dtCadastro: z.string().nullable(),
|
||||
dtAtual: z.string().nullable(),
|
||||
});
|
||||
export type ClientDetail = z.infer<typeof ClientDetailSchema>;
|
||||
|
||||
// ─── List query + response ────────────────────────────────────────────────────
|
||||
|
||||
export const ClientListQuerySchema = z.object({
|
||||
q: z.string().optional(), // busca nome/taxId
|
||||
status: ActivityStatusSchema.optional(), // filtro de atividade
|
||||
financialStatus: FinancialStatusSchema.optional(),
|
||||
q: z.string().optional(),
|
||||
status: ActivityStatusSchema.optional(),
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().min(1).max(200).default(50),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user