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:
@@ -3,12 +3,13 @@ import { z } from 'zod';
|
||||
// Contrato do auth dev stub — POST /api/v1/auth/dev/token.
|
||||
// Endpoint existe APENAS em development/test (NODE_ENV !== 'production').
|
||||
// CODING-RULES PGD-SEC-002: never use dev secret in production.
|
||||
// ADR 0006 revogado: workspaceId: string → idEmpresa: number (empresa no ERP)
|
||||
|
||||
const JwtRoleSchema = z.enum(['rep', 'supervisor', 'manager', 'admin']);
|
||||
|
||||
export const DevTokenRequestSchema = z.object({
|
||||
userId: z.string().min(1),
|
||||
workspaceId: z.string().min(1),
|
||||
idEmpresa: z.coerce.number().int().positive(),
|
||||
role: JwtRoleSchema,
|
||||
});
|
||||
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { z } from 'zod';
|
||||
import { OrderSummarySchema } from './order.contract';
|
||||
import { PedidoSummarySchema } from './order.contract';
|
||||
|
||||
// ADR 0006 revogado: OrderSummary → PedidoSummary, ids numéricos.
|
||||
|
||||
export const ClienteInativoSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
idCliente: z.number().int(),
|
||||
nome: z.string(),
|
||||
diasSemCompra: z.number().int(),
|
||||
ultimaCompraValor: z.string().nullable(),
|
||||
});
|
||||
@@ -22,20 +24,20 @@ export const RepDashboardSchema = z.object({
|
||||
total: z.number(),
|
||||
}),
|
||||
pedidosMes: z.number().int(),
|
||||
pedidosRecentes: z.array(OrderSummarySchema),
|
||||
pedidosRecentes: z.array(PedidoSummarySchema),
|
||||
clientesInativos: z.array(ClienteInativoSchema),
|
||||
syncedAt: z.iso.datetime(),
|
||||
});
|
||||
export type RepDashboard = z.infer<typeof RepDashboardSchema>;
|
||||
|
||||
export const RepInativosSummarySchema = z.object({
|
||||
repId: z.string(),
|
||||
codVendedor: z.number().int(),
|
||||
inativosCount: z.number().int(),
|
||||
});
|
||||
export type RepInativosSummary = z.infer<typeof RepInativosSummarySchema>;
|
||||
|
||||
export const SupervisorDashboardSchema = z.object({
|
||||
approvalQueue: z.array(OrderSummarySchema),
|
||||
approvalQueue: z.array(PedidoSummarySchema),
|
||||
pedidosDia: z.object({
|
||||
count: z.number().int(),
|
||||
total: z.number(),
|
||||
|
||||
@@ -1,128 +1,137 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// Contratos canônicos de C3 — Consulta de Pedidos.
|
||||
// Contratos canônicos de C3 — Pedidos SAR.
|
||||
// Consumidos pela API (output tipado) e pela Web (TanStack Query + parse).
|
||||
// ADR 0006 revogado: OrderStatus enum → situa Int (1=Pendente, 2=Aprovado, 3=Cancelado, 4=Faturado)
|
||||
|
||||
// ─── Enums ────────────────────────────────────────────────────────────────────
|
||||
// ─── Situa ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const OrderStatusSchema = z.enum([
|
||||
'budget',
|
||||
'pending_approval',
|
||||
'approved',
|
||||
'invoiced',
|
||||
'cancelled',
|
||||
]);
|
||||
export type OrderStatus = z.infer<typeof OrderStatusSchema>;
|
||||
// situa: 1=Pendente 2=Aprovado 3=Cancelado 4=Faturado
|
||||
export const SituaPedidoSchema = z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]);
|
||||
export type SituaPedido = z.infer<typeof SituaPedidoSchema>;
|
||||
|
||||
// ─── OrderItem ────────────────────────────────────────────────────────────────
|
||||
export const SITUA_LABEL: Record<number, string> = {
|
||||
1: 'Ag. Aprovação',
|
||||
2: 'Aprovado',
|
||||
3: 'Cancelado',
|
||||
4: 'Faturado',
|
||||
};
|
||||
|
||||
export const OrderItemSchema = z.object({
|
||||
// ─── PedidoItem ───────────────────────────────────────────────────────────────
|
||||
|
||||
export const PedidoItemSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
productCode: z.string(),
|
||||
productName: z.string(),
|
||||
quantity: z.string(), // Decimal serializado
|
||||
unitPrice: z.string(), // Decimal serializado
|
||||
discountPct: z.string(), // Decimal serializado
|
||||
subtotal: z.string(), // Decimal serializado
|
||||
idProduto: z.number().int(),
|
||||
codProduto: z.string().nullable(),
|
||||
descProduto: z.string().nullable(),
|
||||
ordem: z.number().int(),
|
||||
qtd: z.string(),
|
||||
precoUnitario: z.string(),
|
||||
descontoPerc: z.string(),
|
||||
total: z.string(),
|
||||
});
|
||||
export type OrderItem = z.infer<typeof OrderItemSchema>;
|
||||
export type PedidoItem = z.infer<typeof PedidoItemSchema>;
|
||||
|
||||
// ─── OrderStatusHistory ───────────────────────────────────────────────────────
|
||||
// ─── HistoricoPedido ──────────────────────────────────────────────────────────
|
||||
|
||||
export const OrderStatusHistorySchema = z.object({
|
||||
export const HistoricoPedidoSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
fromStatus: OrderStatusSchema.nullable(),
|
||||
toStatus: OrderStatusSchema,
|
||||
changedById: z.string(),
|
||||
note: z.string().nullable(),
|
||||
situaAnterior: z.number().int().nullable(),
|
||||
situaNova: z.number().int(),
|
||||
changedBy: z.number().int(),
|
||||
nota: z.string().nullable(),
|
||||
changedAt: z.iso.datetime(),
|
||||
});
|
||||
export type OrderStatusHistory = z.infer<typeof OrderStatusHistorySchema>;
|
||||
export type HistoricoPedido = z.infer<typeof HistoricoPedidoSchema>;
|
||||
|
||||
// ─── Order Summary (lista) ────────────────────────────────────────────────────
|
||||
// ─── Pedido Summary (lista) ───────────────────────────────────────────────────
|
||||
|
||||
export const OrderSummarySchema = z.object({
|
||||
export const PedidoSummarySchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
number: z.string(),
|
||||
clientId: z.string().uuid(),
|
||||
clientName: z.string(),
|
||||
repId: z.string(),
|
||||
status: OrderStatusSchema,
|
||||
discountPct: z.string(),
|
||||
subtotal: z.string(),
|
||||
numPedSar: z.string(),
|
||||
idCliente: z.number().int(),
|
||||
codVendedor: z.number().int(),
|
||||
situa: z.number().int(),
|
||||
dtPedido: z.string(),
|
||||
total: z.string(),
|
||||
issuedAt: z.iso.datetime(),
|
||||
approvedAt: z.iso.datetime().nullable(),
|
||||
invoicedAt: z.iso.datetime().nullable(),
|
||||
cancelledAt: z.iso.datetime().nullable(),
|
||||
});
|
||||
export type OrderSummary = z.infer<typeof OrderSummarySchema>;
|
||||
|
||||
// ─── Order Detail ─────────────────────────────────────────────────────────────
|
||||
|
||||
export const OrderDetailSchema = OrderSummarySchema.extend({
|
||||
notes: z.string().nullable(),
|
||||
approvedById: z.string().nullable(),
|
||||
idempotencyKey: z.string().nullable(),
|
||||
descontoPerc: z.string(),
|
||||
obs: z.string().nullable(),
|
||||
createdAt: z.iso.datetime(),
|
||||
updatedAt: z.iso.datetime(),
|
||||
items: z.array(OrderItemSchema),
|
||||
history: z.array(OrderStatusHistorySchema),
|
||||
});
|
||||
export type OrderDetail = z.infer<typeof OrderDetailSchema>;
|
||||
export type PedidoSummary = z.infer<typeof PedidoSummarySchema>;
|
||||
|
||||
// ─── Pedido Detail ────────────────────────────────────────────────────────────
|
||||
|
||||
export const PedidoDetailSchema = PedidoSummarySchema.extend({
|
||||
totalProdutos: z.string(),
|
||||
totalIpi: z.string(),
|
||||
totalIcmsst: z.string(),
|
||||
descontoValor: z.string(),
|
||||
acrescimo: z.string(),
|
||||
comissao: z.string(),
|
||||
pedFlex: z.string(),
|
||||
aprovadoPor: z.number().int().nullable(),
|
||||
aprovadoEm: z.iso.datetime().nullable(),
|
||||
motivoRecusa: z.string().nullable(),
|
||||
idempotencyKey: z.string().nullable(),
|
||||
updatedAt: z.iso.datetime(),
|
||||
itens: z.array(PedidoItemSchema),
|
||||
historico: z.array(HistoricoPedidoSchema),
|
||||
});
|
||||
export type PedidoDetail = z.infer<typeof PedidoDetailSchema>;
|
||||
|
||||
// ─── List query + response ────────────────────────────────────────────────────
|
||||
|
||||
export const OrderListQuerySchema = z.object({
|
||||
clientId: z.string().uuid().optional(),
|
||||
status: OrderStatusSchema.optional(),
|
||||
number: z.string().optional(), // busca parcial por número
|
||||
from: z.iso.datetime().optional(), // issuedAt >= from
|
||||
to: z.iso.datetime().optional(), // issuedAt <= to
|
||||
export const PedidoListQuerySchema = z.object({
|
||||
idCliente: z.coerce.number().int().optional(),
|
||||
situa: z.coerce.number().int().optional(),
|
||||
numPedSar: z.string().optional(),
|
||||
from: z.string().optional(),
|
||||
to: z.string().optional(),
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().min(1).max(200).default(50),
|
||||
});
|
||||
export type OrderListQuery = z.infer<typeof OrderListQuerySchema>;
|
||||
export type PedidoListQuery = z.infer<typeof PedidoListQuerySchema>;
|
||||
|
||||
export const OrderListResponseSchema = z.object({
|
||||
data: z.array(OrderSummarySchema),
|
||||
export const PedidoListResponseSchema = z.object({
|
||||
data: z.array(PedidoSummarySchema),
|
||||
total: z.number().int().nonnegative(),
|
||||
page: z.number().int().positive(),
|
||||
limit: z.number().int().positive(),
|
||||
});
|
||||
export type OrderListResponse = z.infer<typeof OrderListResponseSchema>;
|
||||
export type PedidoListResponse = z.infer<typeof PedidoListResponseSchema>;
|
||||
|
||||
// ─── Create Order (POST /orders) ──────────────────────────────────────────────
|
||||
// ─── Mutações ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export const CreateOrderItemSchema = z.object({
|
||||
productCode: z.string().min(1),
|
||||
productName: z.string().min(1),
|
||||
productCategory: z.string().default('geral'),
|
||||
quantity: z.number().positive(),
|
||||
unitPrice: z.number().positive(),
|
||||
discountPct: z.number().min(0).max(100).default(0),
|
||||
export const CreatePedidoItemSchema = z.object({
|
||||
idProduto: z.number().int().positive(),
|
||||
codProduto: z.string().optional(),
|
||||
descProduto: z.string().min(1),
|
||||
ordem: z.number().int().min(1),
|
||||
qtd: z.number().positive(),
|
||||
precoUnitario: z.number().nonnegative(),
|
||||
descontoPerc: z.number().min(0).max(100).default(0),
|
||||
});
|
||||
export type CreateOrderItem = z.infer<typeof CreateOrderItemSchema>;
|
||||
export type CreatePedidoItem = z.infer<typeof CreatePedidoItemSchema>;
|
||||
|
||||
export const CreateOrderSchema = z.object({
|
||||
clientId: z.string().uuid(),
|
||||
discountPct: z.number().min(0).max(100).default(0), // desconto global do pedido
|
||||
notes: z.string().optional(),
|
||||
export const CreatePedidoSchema = z.object({
|
||||
idCliente: z.number().int().positive(),
|
||||
descontoPerc: z.number().min(0).max(100).default(0),
|
||||
idPauta: z.number().int().optional(),
|
||||
codFormapag: z.number().int().optional(),
|
||||
obs: z.string().optional(),
|
||||
idempotencyKey: z.string().optional(),
|
||||
items: z.array(CreateOrderItemSchema).min(1),
|
||||
itens: z.array(CreatePedidoItemSchema).min(1),
|
||||
});
|
||||
export type CreateOrder = z.infer<typeof CreateOrderSchema>;
|
||||
export type CreatePedido = z.infer<typeof CreatePedidoSchema>;
|
||||
|
||||
// ─── Approve / Reject (PATCH /orders/:id/approve|reject) ─────────────────────
|
||||
|
||||
export const ApproveOrderSchema = z.object({
|
||||
// Opcional — supervisor pode ajustar o desconto global. Se omitido, mantém o original.
|
||||
discountPct: z.number().min(0).max(100).optional(),
|
||||
note: z.string().optional(),
|
||||
export const AprovarPedidoSchema = z.object({
|
||||
descontoPerc: z.number().min(0).max(100).optional(),
|
||||
nota: z.string().optional(),
|
||||
});
|
||||
export type ApproveOrder = z.infer<typeof ApproveOrderSchema>;
|
||||
export type AprovarPedido = z.infer<typeof AprovarPedidoSchema>;
|
||||
|
||||
export const RejectOrderSchema = z.object({
|
||||
reason: z.string().min(1, 'Motivo é obrigatório'), // FR-5.4
|
||||
export const RecusarPedidoSchema = z.object({
|
||||
motivo: z.string().min(1),
|
||||
});
|
||||
export type RejectOrder = z.infer<typeof RejectOrderSchema>;
|
||||
export type RecusarPedido = z.infer<typeof RecusarPedidoSchema>;
|
||||
|
||||
@@ -15,7 +15,7 @@ export const PingResponseSchema = z.object({
|
||||
status: z.literal('ok'),
|
||||
service: z.string().min(1),
|
||||
version: z.string().min(1),
|
||||
workspaceId: z.string().min(1),
|
||||
idEmpresa: z.number().int(),
|
||||
requestId: z.uuid(),
|
||||
uptimeSeconds: z.number().int().nonnegative(),
|
||||
now: z.iso.datetime(),
|
||||
|
||||
@@ -2,70 +2,57 @@ import { z } from 'zod';
|
||||
|
||||
// Contratos canônicos de C4 — Catálogo de Produtos.
|
||||
// Consumidos pela API (output tipado) e pela Web (TanStack Query + parse).
|
||||
// ADR 0006 revogado: produto lido diretamente de vw_produtos (ERP), sem sync.
|
||||
|
||||
// ─── Product Summary (lista) ──────────────────────────────────────────────────
|
||||
// ─── Produto Summary (lista) ──────────────────────────────────────────────────
|
||||
|
||||
export const ProductSummarySchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
code: z.string(),
|
||||
name: z.string(),
|
||||
category: z.string(),
|
||||
unitPrice: z.string(), // Decimal serializado
|
||||
stock: z.string().nullable(),
|
||||
active: z.boolean(),
|
||||
export const ProdutoSummarySchema = z.object({
|
||||
idErp: z.number().int(),
|
||||
codigo: z.string(),
|
||||
descricao: z.string(),
|
||||
unidade: z.string().nullable(),
|
||||
vlPreco1: z.string(),
|
||||
codGrupo: z.number().int().nullable(),
|
||||
grupo: z.string().nullable(),
|
||||
codSubgrupo: z.number().int().nullable(),
|
||||
subgrupo: z.string().nullable(),
|
||||
marca: z.string().nullable(),
|
||||
ativo: z.number().int(),
|
||||
qtdEstoque: z.string().nullable(),
|
||||
listaParauta: z.number().int().nullable(),
|
||||
});
|
||||
export type ProductSummary = z.infer<typeof ProductSummarySchema>;
|
||||
export type ProdutoSummary = z.infer<typeof ProdutoSummarySchema>;
|
||||
|
||||
// ─── Product Detail ───────────────────────────────────────────────────────────
|
||||
// ─── Produto Detail ───────────────────────────────────────────────────────────
|
||||
|
||||
export const ProductDetailSchema = ProductSummarySchema.extend({
|
||||
description: z.string().nullable(),
|
||||
erpCode: z.string().nullable(),
|
||||
syncedAt: z.iso.datetime().nullable(),
|
||||
createdAt: z.iso.datetime(),
|
||||
updatedAt: z.iso.datetime(),
|
||||
export const ProdutoDetailSchema = ProdutoSummarySchema.extend({
|
||||
referencia: z.string().nullable(),
|
||||
descricaoDetalhada: z.string().nullable(),
|
||||
vlPreco2: z.string().nullable(),
|
||||
vlPreco3: z.string().nullable(),
|
||||
aliqIpi: z.string().nullable(),
|
||||
pesoLiquido: z.string().nullable(),
|
||||
qtdVolume: z.string().nullable(),
|
||||
loteMulVenda: z.number().int().nullable(),
|
||||
precoComIpi: z.string().nullable(),
|
||||
precoPromocional: z.string().nullable(),
|
||||
});
|
||||
export type ProductDetail = z.infer<typeof ProductDetailSchema>;
|
||||
export type ProdutoDetail = z.infer<typeof ProdutoDetailSchema>;
|
||||
|
||||
// ─── List query + response ────────────────────────────────────────────────────
|
||||
|
||||
export const ProductListQuerySchema = z.object({
|
||||
q: z.string().optional(), // busca nome/código
|
||||
category: z.string().optional(), // filtra por categoria
|
||||
export const ProdutoListQuerySchema = z.object({
|
||||
q: z.string().optional(),
|
||||
codGrupo: z.coerce.number().int().optional(),
|
||||
page: z.coerce.number().int().positive().default(1),
|
||||
limit: z.coerce.number().int().min(1).max(200).default(50),
|
||||
});
|
||||
export type ProductListQuery = z.infer<typeof ProductListQuerySchema>;
|
||||
export type ProdutoListQuery = z.infer<typeof ProdutoListQuerySchema>;
|
||||
|
||||
export const ProductListResponseSchema = z.object({
|
||||
data: z.array(ProductSummarySchema),
|
||||
export const ProdutoListResponseSchema = z.object({
|
||||
data: z.array(ProdutoSummarySchema),
|
||||
total: z.number().int().nonnegative(),
|
||||
page: z.number().int().positive(),
|
||||
limit: z.number().int().positive(),
|
||||
});
|
||||
export type ProductListResponse = z.infer<typeof ProductListResponseSchema>;
|
||||
|
||||
// ─── Sync (importação da view do ERP) ────────────────────────────────────────
|
||||
|
||||
export const ProductSyncItemSchema = z.object({
|
||||
code: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
category: z.string().default('geral'),
|
||||
unitPrice: z.number().positive(),
|
||||
stock: z.number().nonnegative().optional(),
|
||||
active: z.boolean().default(true),
|
||||
erpCode: z.string().optional(),
|
||||
});
|
||||
export type ProductSyncItem = z.infer<typeof ProductSyncItemSchema>;
|
||||
|
||||
export const ProductSyncRequestSchema = z.object({
|
||||
items: z.array(ProductSyncItemSchema).min(1).max(5000),
|
||||
});
|
||||
export type ProductSyncRequest = z.infer<typeof ProductSyncRequestSchema>;
|
||||
|
||||
export const ProductSyncResponseSchema = z.object({
|
||||
upserted: z.number().int().nonnegative(),
|
||||
syncedAt: z.iso.datetime(),
|
||||
});
|
||||
export type ProductSyncResponse = z.infer<typeof ProductSyncResponseSchema>;
|
||||
export type ProdutoListResponse = z.infer<typeof ProdutoListResponseSchema>;
|
||||
|
||||
1
libs/shared/api-interface/tsconfig.tsbuildinfo
Normal file
1
libs/shared/api-interface/tsconfig.tsbuildinfo
Normal file
@@ -0,0 +1 @@
|
||||
{"fileNames":[],"fileInfos":[],"root":[],"options":{"allowSyntheticDefaultImports":true,"composite":false,"declaration":true,"declarationMap":true,"emitDecoratorMetadata":true,"esModuleInterop":true,"experimentalDecorators":true,"importHelpers":true,"module":199,"noFallthroughCasesInSwitch":true,"noImplicitOverride":true,"noImplicitReturns":true,"noPropertyAccessFromIndexSignature":true,"noUncheckedIndexedAccess":true,"skipLibCheck":true,"sourceMap":true,"strict":true,"target":10,"useDefineForClassFields":false,"verbatimModuleSyntax":false},"version":"5.9.3"}
|
||||
Reference in New Issue
Block a user