feat(api,web): regras de desconto por pauta e cards de supervisor na equipe
Regras de Desconto (Politicas Comerciais): - Nova dimensao de escopo por pauta de precos: cria 1 regra por pauta selecionada (idPautas[]), aplicada a todos os produtos da pauta. - Coluna id_pauta em sar.regras_desconto (migration idempotente + schema canonico); motor de alcada (orders) casa regra<->pedido por idPauta. - Remove a aba/endpoints "Desconto por Representante" (gestao de alcada_desconto); o teto por rep continua no motor (default 5%) e no carrinho (/politicas/alcada), agora liberado via Regra de Desconto. Aba Equipe do gerente: - GET /equipe/supervisores: cards de disputa por supervisor (faturamento, meta, positivacao, variacao mensal, reps ativos). - SupervisorDetalheModal: detalhe do time (ranking + positivacao) reusando /dashboard/supervisor escopado por codSupervisor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,3 +16,30 @@ export const EquipeResponseSchema = z.object({
|
||||
syncedAt: z.iso.datetime(),
|
||||
});
|
||||
export type EquipeResponse = z.infer<typeof EquipeResponseSchema>;
|
||||
|
||||
// Card de disputa/análise por supervisor na aba Equipe (visão gerente/admin).
|
||||
// Time = reps cujo cod_supervisor aponta para este supervisor + o próprio
|
||||
// supervisor (mesma semântica de getTeamCodes). Métricas agregadas do time no mês.
|
||||
export const SupervisorCardSchema = z.object({
|
||||
codSupervisor: z.number().int(),
|
||||
nomeSupervisor: z.string().nullable(),
|
||||
faturamentoMes: z.number(),
|
||||
faturamentoMesAnterior: z.number(),
|
||||
variacaoPct: z.number().nullable(), // null quando não há base no mês anterior
|
||||
pedidosMes: z.number().int(),
|
||||
ticketMedio: z.number(),
|
||||
metaTotal: z.number(),
|
||||
pctMeta: z.number(),
|
||||
totalClientes: z.number().int(),
|
||||
clientesPositivados: z.number().int(),
|
||||
pctPositivacao: z.number(),
|
||||
repsTotal: z.number().int(),
|
||||
repsAtivos: z.number().int(), // reps do time que faturaram no mês
|
||||
});
|
||||
export type SupervisorCard = z.infer<typeof SupervisorCardSchema>;
|
||||
|
||||
export const SupervisoresResponseSchema = z.object({
|
||||
supervisores: z.array(SupervisorCardSchema),
|
||||
syncedAt: z.iso.datetime(),
|
||||
});
|
||||
export type SupervisoresResponse = z.infer<typeof SupervisoresResponseSchema>;
|
||||
|
||||
@@ -1,25 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const AlcadaDescontoItemSchema = z.object({
|
||||
codVendedor: z.number().int(),
|
||||
codGrupo: z.number().int(),
|
||||
limitePerc: z.number(),
|
||||
nomeVendedor: z.string().nullable(),
|
||||
});
|
||||
export type AlcadaDescontoItem = z.infer<typeof AlcadaDescontoItemSchema>;
|
||||
|
||||
export const AlcadaDescontosResponseSchema = z.object({
|
||||
descontos: z.array(AlcadaDescontoItemSchema),
|
||||
});
|
||||
export type AlcadaDescontosResponse = z.infer<typeof AlcadaDescontosResponseSchema>;
|
||||
|
||||
export const UpsertDescontoBodySchema = z.object({
|
||||
codVendedor: z.number().int().positive(),
|
||||
codGrupo: z.number().int().default(0),
|
||||
limitePerc: z.number().min(0).max(100),
|
||||
});
|
||||
export type UpsertDescontoBody = z.infer<typeof UpsertDescontoBodySchema>;
|
||||
|
||||
export const PromocaoSchema = z.object({
|
||||
id: z.number().int(),
|
||||
descricao: z.string(),
|
||||
@@ -71,7 +51,7 @@ export type UpdatePromocaoBody = z.infer<typeof UpdatePromocaoBodySchema>;
|
||||
|
||||
// ─── Regras de Desconto ───────────────────────────────────────────────────────
|
||||
// Regra criada pelo Gerente: % de desconto liberado por grupo e/ou subgrupo de
|
||||
// produto e/ou representante, com vigência. Campos nulos = "todos".
|
||||
// produto, pauta de preços e/ou representante, com vigência. Campos nulos = "todos".
|
||||
|
||||
export const RegraDescontoSchema = z.object({
|
||||
id: z.number().int(),
|
||||
@@ -80,6 +60,8 @@ export const RegraDescontoSchema = z.object({
|
||||
grupo: z.string().nullable(),
|
||||
codSubgrupo: z.number().int().nullable(),
|
||||
subgrupo: z.string().nullable(),
|
||||
idPauta: z.number().int().nullable(),
|
||||
pauta: z.string().nullable(),
|
||||
codVendedor: z.number().int().nullable(),
|
||||
nomeVendedor: z.string().nullable(),
|
||||
descPct: z.number(),
|
||||
@@ -96,12 +78,14 @@ export const RegrasDescontoResponseSchema = z.object({
|
||||
});
|
||||
export type RegrasDescontoResponse = z.infer<typeof RegrasDescontoResponseSchema>;
|
||||
|
||||
// Criação aceita VÁRIOS grupos e/ou subgrupos — uma regra por alvo (grupos
|
||||
// selecionados viram regras por grupo; subgrupos viram regras por subgrupo).
|
||||
// Criação aceita VÁRIOS grupos, subgrupos e/ou pautas — uma regra por alvo
|
||||
// (cada grupo vira regra por grupo; cada subgrupo, regra por subgrupo; cada
|
||||
// pauta, regra por pauta). O representante escolhido é copiado em todas.
|
||||
export const CreateRegraDescontoBodySchema = z.object({
|
||||
descricao: z.string().min(1).max(200),
|
||||
codGrupos: z.array(z.number().int()).max(100).optional(),
|
||||
codSubgrupos: z.array(z.number().int()).max(100).optional(),
|
||||
idPautas: z.array(z.number().int()).max(100).optional(),
|
||||
codVendedor: z.number().int().nullable().optional(),
|
||||
descPct: z.number().min(0).max(100),
|
||||
dataInicio: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
@@ -114,6 +98,7 @@ export const UpdateRegraDescontoBodySchema = z.object({
|
||||
descricao: z.string().min(1).max(200).optional(),
|
||||
codGrupo: z.number().int().nullable().optional(),
|
||||
codSubgrupo: z.number().int().nullable().optional(),
|
||||
idPauta: z.number().int().nullable().optional(),
|
||||
codVendedor: z.number().int().nullable().optional(),
|
||||
descPct: z.number().min(0).max(100).optional(),
|
||||
dataInicio: z
|
||||
@@ -166,6 +151,8 @@ export const RegraVigenteSchema = z.object({
|
||||
grupo: z.string().nullable(),
|
||||
codSubgrupo: z.number().int().nullable(),
|
||||
subgrupo: z.string().nullable(),
|
||||
idPauta: z.number().int().nullable(),
|
||||
pauta: z.string().nullable(),
|
||||
descPct: z.number(),
|
||||
dataFim: z.string(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user