feat(web+api): ficha do cliente — contatos, CTR, NF-e, pedidos e produtos
- Contatos: grid de cards 3 colunas, botão "Novo Contato" no topo da página - Sync ERP↔SAR corrigido: vw_contatos aceita formato COR#<id>, trigger normaliza id_empresa (9001→1) - CTR + NF-e: layout 50/50 — lista de títulos abertos com badge vencido/a vencer e lista de notas com botão copiar chave NF-e - Histórico de pedidos: UNION SAR+ERP, top 5 + modal "Ver todos" - Produtos mais comprados: top 5 com último preço + modal "Ver todos" - Novos endpoints: ctr-list, notas, orders-history, top-produtos - AppShell: overflow-x travado, sem scroll horizontal na aplicação Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -111,6 +111,54 @@ export const ContatoResultSchema = z.object({
|
||||
});
|
||||
export type ContatoResult = z.infer<typeof ContatoResultSchema>;
|
||||
|
||||
// ─── Produtos mais comprados por cliente ─────────────────────────────────────
|
||||
|
||||
export const TopProdutoSchema = z.object({
|
||||
idProduto: z.number().int(),
|
||||
descricao: z.string(),
|
||||
unidade: z.string().nullable(),
|
||||
qtdTotal: z.number(),
|
||||
numPedidos: z.number().int(),
|
||||
ultimaCompra: z.string(),
|
||||
ultimoPreco: z.number(),
|
||||
});
|
||||
export type TopProduto = z.infer<typeof TopProdutoSchema>;
|
||||
|
||||
// ─── Contas a Receber (resumo por cliente) ───────────────────────────────────
|
||||
|
||||
export const CtrSummarySchema = z.object({
|
||||
qtdAberto: z.number().int(),
|
||||
totalAberto: z.number(),
|
||||
qtdVencido: z.number().int(),
|
||||
totalVencido: z.number(),
|
||||
maiorAtrasoDias: z.number().int(),
|
||||
});
|
||||
export type CtrSummary = z.infer<typeof CtrSummarySchema>;
|
||||
|
||||
// ─── Contas a Receber (lista de títulos em aberto) ────────────────────────────
|
||||
|
||||
export const CtrTituloSchema = z.object({
|
||||
idCtr: z.number().int(),
|
||||
numero: z.string(),
|
||||
prefixo: z.string(),
|
||||
dtEmissao: z.string(),
|
||||
dtVencimento: z.string(),
|
||||
saldo: z.number(),
|
||||
});
|
||||
export type CtrTitulo = z.infer<typeof CtrTituloSchema>;
|
||||
|
||||
// ─── Notas Fiscais por cliente ───────────────────────────────────────────────
|
||||
|
||||
export const NotaFiscalSchema = z.object({
|
||||
idNf: z.number().int(),
|
||||
numero: z.number().int(),
|
||||
serie: z.string(),
|
||||
dtEmissao: z.string(),
|
||||
vlNf: z.number(),
|
||||
chaveAcessoNfe: z.string().nullable(),
|
||||
});
|
||||
export type NotaFiscal = z.infer<typeof NotaFiscalSchema>;
|
||||
|
||||
// ─── Municipio ───────────────────────────────────────────────────────────────
|
||||
|
||||
export const MunicipioSchema = z.object({
|
||||
|
||||
Reference in New Issue
Block a user