feat(mvp-rep): formas de pagamento do ERP + suporte offline completo
Formas de pagamento: - Endpoint GET /catalog/payment-methods lendo vw_formas_pagamento filtrado por ativa=1 e integrar_sar=1 - FormaPagamento schema/type no shared api-interface - Hook useFormasPagamento (staleTime 1h) substituindo lista hardcoded Offline (FR-4.2 / NFR-2.1–2.4): - IndexedDB queue: lib/offline/idb.ts + order-queue.ts sem deps externos - NewOrderPage detecta !navigator.onLine → enqueueOrder() → toast + reset - useOfflineSync: auto-sync ao reconectar (POST orders + PATCH transmit) - usePendingOrders: fila reativa via CustomEvents - AppShell: banner offline + useOfflineSync() global - OrdersPage: seção de pedidos pendentes com retry/descartar - sw.js: network-first para API GETs cacheáveis + stale-while-revalidate para assets + app shell navigate fallback Docs: - architecture.md: documento de decisões de arquitetura do SAR MVP Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,3 +5,4 @@ export * from './lib/order.contract';
|
||||
export * from './lib/product.contract';
|
||||
export * from './lib/dashboard.contract';
|
||||
export * from './lib/notifications.contract';
|
||||
export * from './lib/company.contract';
|
||||
|
||||
@@ -20,6 +20,7 @@ export const ClientSummarySchema = z.object({
|
||||
email: z.string().nullable(),
|
||||
telefone: z.string().nullable(),
|
||||
codVendedor: z.number().int(),
|
||||
nomeVendedor: z.string().nullable().optional(),
|
||||
limiteCreditoStr: z.string().nullable(),
|
||||
activityStatus: ActivityStatusSchema,
|
||||
dtUltimaCompra: z.iso.datetime().nullable(),
|
||||
|
||||
21
libs/shared/api-interface/src/lib/company.contract.ts
Normal file
21
libs/shared/api-interface/src/lib/company.contract.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
// Dados legais da empresa matriz (a que fatura o pedido) — cabeçalho do PDF.
|
||||
// Fonte: gestao.empresa (matriz) + vw_municipios.
|
||||
export const EmpresaInfoSchema = z.object({
|
||||
idEmpresa: z.number().int(),
|
||||
razaoSocial: z.string(),
|
||||
nomeFantasia: z.string().nullable(),
|
||||
cnpj: z.string().nullable(),
|
||||
inscricaoEstadual: z.string().nullable(),
|
||||
endereco: z.string().nullable(),
|
||||
numero: z.string().nullable(),
|
||||
complemento: z.string().nullable(),
|
||||
bairro: z.string().nullable(),
|
||||
cidade: z.string().nullable(),
|
||||
uf: z.string().nullable(),
|
||||
cep: z.string().nullable(),
|
||||
telefone: z.string().nullable(),
|
||||
email: z.string().nullable(),
|
||||
});
|
||||
export type EmpresaInfo = z.infer<typeof EmpresaInfoSchema>;
|
||||
@@ -11,6 +11,31 @@ export const ClienteInativoSchema = z.object({
|
||||
});
|
||||
export type ClienteInativo = z.infer<typeof ClienteInativoSchema>;
|
||||
|
||||
// Dimensão de meta. O ERP (vw_metas.tipo) define como o cliente acompanha metas:
|
||||
// GL = global, GR = por grupo. Motor único; outras dimensões (marca/subgrupo/
|
||||
// produto) entram aqui depois sem mudar a forma.
|
||||
export const MetaDimensaoSchema = z.enum(['global', 'grupo']);
|
||||
export type MetaDimensao = z.infer<typeof MetaDimensaoSchema>;
|
||||
|
||||
// Linha de meta vs realizado por grupo, multi-medida (codigo=null = rollup global).
|
||||
// fator = R$/kg (valor/peso); o ERP guarda vl_fator na meta.
|
||||
export const MetaItemSchema = z.object({
|
||||
codigo: z.number().int().nullable(),
|
||||
rotulo: z.string(),
|
||||
pedidos: z.number().int(), // qtd de pedidos faturados no grupo (realizado)
|
||||
valorMeta: z.number(),
|
||||
valorReal: z.number(),
|
||||
qtdMeta: z.number(),
|
||||
qtdReal: z.number(),
|
||||
pesoMeta: z.number(),
|
||||
pesoReal: z.number(),
|
||||
fatorMeta: z.number(),
|
||||
fatorReal: z.number(),
|
||||
pct: z.number(), // % de valor (real/meta) — base da barra de progresso
|
||||
falta: z.number(), // valor faltante p/ a meta
|
||||
});
|
||||
export type MetaItem = z.infer<typeof MetaItemSchema>;
|
||||
|
||||
export const RepDashboardSchema = z.object({
|
||||
meta: z.object({
|
||||
atingido: z.number(),
|
||||
@@ -18,6 +43,9 @@ export const RepDashboardSchema = z.object({
|
||||
pct: z.number(),
|
||||
falta: z.number(),
|
||||
}),
|
||||
// Dimensão detectada do ERP e detalhamento por grupo (vazio quando global).
|
||||
metaDimensao: MetaDimensaoSchema.default('global'),
|
||||
metasPorGrupo: z.array(MetaItemSchema).default([]),
|
||||
comissao: z.object({
|
||||
fixa: z.number(),
|
||||
flex: z.number(),
|
||||
@@ -32,6 +60,7 @@ export type RepDashboard = z.infer<typeof RepDashboardSchema>;
|
||||
|
||||
export const RepInativosSummarySchema = z.object({
|
||||
codVendedor: z.number().int(),
|
||||
nomeVendedor: z.string().nullable().optional(),
|
||||
inativosCount: z.number().int(),
|
||||
});
|
||||
export type RepInativosSummary = z.infer<typeof RepInativosSummarySchema>;
|
||||
|
||||
@@ -6,13 +6,24 @@ import { z } from 'zod';
|
||||
|
||||
// ─── Situa ────────────────────────────────────────────────────────────────────
|
||||
|
||||
// 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)]);
|
||||
// Ciclo de vida do pedido SAR:
|
||||
// 0=Orçamento → 1=Ag. Aprovação (se desconto > alçada) → 2=Transmitido
|
||||
// Estados que o SAR controla: Orçamento e Transmitido (1 é o gate de desconto).
|
||||
// Após Transmitido, o status passa a refletir o ERP (Emitido/Cancelado/Aguardando…)
|
||||
// — espelhado quando a integração existir.
|
||||
export const SituaPedidoSchema = z.union([
|
||||
z.literal(0),
|
||||
z.literal(1),
|
||||
z.literal(2),
|
||||
z.literal(3),
|
||||
z.literal(4),
|
||||
]);
|
||||
export type SituaPedido = z.infer<typeof SituaPedidoSchema>;
|
||||
|
||||
export const SITUA_LABEL: Record<number, string> = {
|
||||
0: 'Orçamento',
|
||||
1: 'Ag. Aprovação',
|
||||
2: 'Aprovado',
|
||||
2: 'Transmitido',
|
||||
3: 'Cancelado',
|
||||
4: 'Faturado',
|
||||
};
|
||||
@@ -54,6 +65,7 @@ export const PedidoSummarySchema = z.object({
|
||||
nomeCliente: z.string().nullable().optional(),
|
||||
razaoCliente: z.string().nullable().optional(),
|
||||
codVendedor: z.number().int(),
|
||||
nomeVendedor: z.string().nullable().optional(),
|
||||
situa: z.number().int(),
|
||||
statusDescr: z.string().optional(), // descrição legível do status
|
||||
dtPedido: z.string(),
|
||||
|
||||
@@ -28,6 +28,16 @@ export const PautaSchema = z.object({
|
||||
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 ───────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user