feat(api,web): SAC de chamados sobre pedidos
Representante abre chamado a partir de um pedido e conversa com a empresa por thread de mensagens; o gerente responde e resolve. - API: `SacModule` com 5 rotas do rep (listar, detalhe, criar, responder, cancelar) e 4 da empresa (/ger/chamados: listar, detalhe, responder, resolver). Escopo do rep e por cod_vendedor; o da empresa, por id_empresa. - Modelos `Chamado` + `ChamadoMensagem` e migrations correspondentes. - O chamado pode referenciar tanto um pedido nascido no SAR (id_pedido + num_ped_sar) quanto um pedido historico do ERP (num_ped_erp) — dai id_pedido e num_ped_sar serem nullable. `nome_cliente` fica desnormalizado para evitar join em toda listagem. - Web: `ChamadosPage` (rep), `GerChamadosPage` (gerente) e `AbrirChamadoModal`, acessivel tambem pelo detalhe do pedido. Entradas "SAC" no menu do rep e do gerente. - `useOrderErpConsulta` ganha flag `enabled` para a busca de pedido ERP so disparar quando o modal esta aberto. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,3 +10,4 @@ export * from './lib/report.contract';
|
||||
export * from './lib/politicas.contract';
|
||||
export * from './lib/equipe.contract';
|
||||
export * from './lib/funil.contract';
|
||||
export * from './lib/sac.contract';
|
||||
|
||||
127
libs/shared/api-interface/src/lib/sac.contract.ts
Normal file
127
libs/shared/api-interface/src/lib/sac.contract.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const TIPOS_CHAMADO = [
|
||||
'avaria',
|
||||
'quantidade_divergente',
|
||||
'produto_errado',
|
||||
'prazo_entrega',
|
||||
'outro',
|
||||
] as const;
|
||||
|
||||
export const STATUS_CHAMADO = [
|
||||
'aberto',
|
||||
'em_analise',
|
||||
'aguardando_rep',
|
||||
'resolvido',
|
||||
'cancelado',
|
||||
] as const;
|
||||
|
||||
export const RESOLUCAO_CHAMADO = [
|
||||
'reposicao',
|
||||
'desconto',
|
||||
'cancelamento_item',
|
||||
'sem_acao',
|
||||
'outro',
|
||||
] as const;
|
||||
|
||||
export const TIPO_CHAMADO_LABEL: Record<string, string> = {
|
||||
avaria: 'Avaria / Dano',
|
||||
quantidade_divergente: 'Quantidade Divergente',
|
||||
produto_errado: 'Produto Errado',
|
||||
prazo_entrega: 'Prazo de Entrega',
|
||||
outro: 'Outro',
|
||||
};
|
||||
|
||||
export const STATUS_CHAMADO_LABEL: Record<string, string> = {
|
||||
aberto: 'Aberto',
|
||||
em_analise: 'Em Análise',
|
||||
aguardando_rep: 'Aguardando Rep.',
|
||||
resolvido: 'Resolvido',
|
||||
cancelado: 'Cancelado',
|
||||
};
|
||||
|
||||
export const RESOLUCAO_CHAMADO_LABEL: Record<string, string> = {
|
||||
reposicao: 'Reposição do Item',
|
||||
desconto: 'Desconto / Abatimento',
|
||||
cancelamento_item: 'Cancelamento do Item',
|
||||
sem_acao: 'Sem Ação Necessária',
|
||||
outro: 'Outro',
|
||||
};
|
||||
|
||||
export const ItemAfetadoSchema = z.object({
|
||||
codProduto: z.string(),
|
||||
nomeProduto: z.string(),
|
||||
qtd: z.number(),
|
||||
});
|
||||
|
||||
export const ChamadoMensagemSchema = z.object({
|
||||
id: z.number(),
|
||||
idChamado: z.number(),
|
||||
autor: z.enum(['rep', 'empresa']),
|
||||
texto: z.string(),
|
||||
createdAt: z.string(),
|
||||
});
|
||||
|
||||
export const ChamadoSchema = z.object({
|
||||
id: z.number(),
|
||||
idEmpresa: z.number(),
|
||||
codVendedor: z.number(),
|
||||
idPedido: z.string().nullable().optional(),
|
||||
numPedSar: z.number().nullable().optional(),
|
||||
numPedErp: z.number().nullable().optional(),
|
||||
idCliente: z.number(),
|
||||
nomeCliente: z.string().nullable().optional(),
|
||||
tipo: z.string(),
|
||||
assunto: z.string(),
|
||||
descricao: z.string(),
|
||||
status: z.string(),
|
||||
resolucao: z.string().nullable().optional(),
|
||||
notaResolucao: z.string().nullable().optional(),
|
||||
itensAfetados: z.array(ItemAfetadoSchema).nullable().optional(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
mensagens: z.array(ChamadoMensagemSchema).optional(),
|
||||
});
|
||||
|
||||
export const CreateChamadoSchema = z.object({
|
||||
idPedido: z.string().uuid().optional(),
|
||||
numPedSar: z.number().int().positive().optional(),
|
||||
numPedErp: z.number().int().positive().optional(),
|
||||
nomeCliente: z.string().optional(),
|
||||
idCliente: z.number().int().positive(),
|
||||
tipo: z.enum(TIPOS_CHAMADO),
|
||||
assunto: z.string().min(5).max(200),
|
||||
descricao: z.string().min(10),
|
||||
itensAfetados: z.array(ItemAfetadoSchema).optional(),
|
||||
});
|
||||
|
||||
export const AddMensagemSchema = z.object({
|
||||
texto: z.string().min(1).max(2000),
|
||||
});
|
||||
|
||||
export const ResolverChamadoSchema = z.object({
|
||||
resolucao: z.enum(RESOLUCAO_CHAMADO),
|
||||
notaResolucao: z.string().optional(),
|
||||
mensagemFinal: z.string().optional(),
|
||||
});
|
||||
|
||||
export const CancelChamadoSchema = z.object({
|
||||
motivo: z.string().optional(),
|
||||
});
|
||||
|
||||
export const ChamadoListResponseSchema = z.object({
|
||||
abertos: z.array(ChamadoSchema),
|
||||
fechados: z.array(ChamadoSchema),
|
||||
});
|
||||
|
||||
export type TipoChamado = (typeof TIPOS_CHAMADO)[number];
|
||||
export type StatusChamado = (typeof STATUS_CHAMADO)[number];
|
||||
export type ResolucaoChamado = (typeof RESOLUCAO_CHAMADO)[number];
|
||||
export type ItemAfetado = z.infer<typeof ItemAfetadoSchema>;
|
||||
export type ChamadoMensagem = z.infer<typeof ChamadoMensagemSchema>;
|
||||
export type Chamado = z.infer<typeof ChamadoSchema>;
|
||||
export type CreateChamadoDto = z.infer<typeof CreateChamadoSchema>;
|
||||
export type AddMensagemDto = z.infer<typeof AddMensagemSchema>;
|
||||
export type ResolverChamadoDto = z.infer<typeof ResolverChamadoSchema>;
|
||||
export type CancelChamadoDto = z.infer<typeof CancelChamadoSchema>;
|
||||
export type ChamadoListResponse = z.infer<typeof ChamadoListResponseSchema>;
|
||||
Reference in New Issue
Block a user