feat(api,web): politicas comerciais com multi-selecao de produtos, grupos e subgrupos

- Promocao: produtos com busca remota por codigo/descricao (multi) e
  grupos com busca por nome/codigo (multi) - backend cria uma promocao
  por alvo na mesma transacao ({ids})
- Regra de desconto: grupos e subgrupos multi (subgrupos filtram pelos
  grupos escolhidos) - uma regra por alvo
- Edicao continua por linha, com selects de busca no lugar dos inputs
  de texto livre
- Modais maiores (760px de largura, corpo com altura minima e rolagem)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 20:52:58 +00:00
parent 756e56d8d3
commit 76cd90ae95
4 changed files with 324 additions and 92 deletions

View File

@@ -39,17 +39,32 @@ export const PromocoesResponseSchema = z.object({
});
export type PromocoesResponse = z.infer<typeof PromocoesResponseSchema>;
// Criação aceita VÁRIOS produtos e/ou grupos — o backend cria uma promoção
// por alvo (mesma descrição/%/vigência), mantendo o motor de alçada intacto.
export const CreatePromocaoBodySchema = z.object({
descricao: z.string().min(1).max(255),
codProduto: z.string().optional(),
grpProd: z.string().optional(),
codProdutos: z.array(z.string().min(1)).max(100).optional(),
grpProds: z.array(z.string().min(1)).max(100).optional(),
descPct: z.number().min(0).max(100),
dataInicio: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
dataFim: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
});
export type CreatePromocaoBody = z.infer<typeof CreatePromocaoBodySchema>;
export const UpdatePromocaoBodySchema = CreatePromocaoBodySchema.partial().extend({
// Edição continua por linha (um alvo por promoção)
export const UpdatePromocaoBodySchema = z.object({
descricao: z.string().min(1).max(255).optional(),
codProduto: z.string().nullable().optional(),
grpProd: z.string().nullable().optional(),
descPct: z.number().min(0).max(100).optional(),
dataInicio: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.optional(),
dataFim: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.optional(),
ativa: z.boolean().optional(),
});
export type UpdatePromocaoBody = z.infer<typeof UpdatePromocaoBodySchema>;
@@ -81,10 +96,12 @@ 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).
export const CreateRegraDescontoBodySchema = z.object({
descricao: z.string().min(1).max(200),
codGrupo: z.number().int().nullable().optional(),
codSubgrupo: z.number().int().nullable().optional(),
codGrupos: z.array(z.number().int()).max(100).optional(),
codSubgrupos: 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}$/),
@@ -92,7 +109,21 @@ export const CreateRegraDescontoBodySchema = z.object({
});
export type CreateRegraDescontoBody = z.infer<typeof CreateRegraDescontoBodySchema>;
export const UpdateRegraDescontoBodySchema = CreateRegraDescontoBodySchema.partial().extend({
// Edição continua por linha (um alvo por regra)
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(),
codVendedor: z.number().int().nullable().optional(),
descPct: z.number().min(0).max(100).optional(),
dataInicio: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.optional(),
dataFim: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/)
.optional(),
ativa: z.boolean().optional(),
});
export type UpdateRegraDescontoBody = z.infer<typeof UpdateRegraDescontoBodySchema>;