Files
sar/libs/shared/api-interface/src/lib/product.contract.ts
julian a2bab75bad feat(web+api): catálogo com detalhe do produto, preços por pauta e consulta de pedidos ERP
- 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>
2026-06-24 20:27:19 +00:00

86 lines
3.3 KiB
TypeScript

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.
// ─── Produto Summary (lista) ──────────────────────────────────────────────────
export const ProdutoSummarySchema = z.object({
idErp: z.number().int(),
codigo: z.string(),
descricao: z.string(),
unidade: z.string().nullable(),
vlPreco1: z.string(), // preço base (vw_produtos) ou preço da pauta selecionada
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(),
loteMulVenda: z.number().int().nullable(),
});
export const PautaSchema = z.object({
idPauta: z.number().int(),
codigo: z.number().int(),
descricao: z.string(),
});
export type Pauta = z.infer<typeof PautaSchema>;
// ─── Forma de Pagamento (vw_formas_pagamento) ─────────────────────────────────
export const FormaPagamentoSchema = z.object({
codigo: z.number().int(),
descricao: z.string(),
numParcelas: z.number().int().nullable(),
txAcrescimo: z.string(),
});
export type FormaPagamento = z.infer<typeof FormaPagamentoSchema>;
export type ProdutoSummary = z.infer<typeof ProdutoSummarySchema>;
// ─── Produto Detail ───────────────────────────────────────────────────────────
export const PautaPrecoSchema = z.object({
idPauta: z.number().int(),
codigo: z.number().int(),
descricao: z.string(),
preco: z.string(),
});
export type PautaPreco = z.infer<typeof PautaPrecoSchema>;
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(),
precoComIpi: z.string().nullable(),
precoPromocional: z.string().nullable(),
pautaPrecos: z.array(PautaPrecoSchema),
});
export type ProdutoDetail = z.infer<typeof ProdutoDetailSchema>;
// ─── List query + response ────────────────────────────────────────────────────
export const ProdutoListQuerySchema = z.object({
q: z.string().optional(),
codGrupo: z.coerce.number().int().optional(),
idPauta: 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 ProdutoListQuery = z.infer<typeof ProdutoListQuerySchema>;
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 ProdutoListResponse = z.infer<typeof ProdutoListResponseSchema>;