ajustes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { createOrderSchema, addItemOrderSchema, updateItemStatusSchema, updateOrderSchema, payOrderSchema } from '../schemas/order.schema';
|
||||
import { createOrderSchema, addItemOrderSchema, updateItemStatusSchema, updateItemObservacaoSchema, updateOrderSchema, payOrderSchema, partialPaymentSchema } from '../schemas/order.schema';
|
||||
import * as orderService from '../services/order.service';
|
||||
import { z } from 'zod';
|
||||
|
||||
@@ -23,6 +23,23 @@ export async function listOrdersController(request: FastifyRequest, reply: Fasti
|
||||
return reply.send(orders);
|
||||
}
|
||||
|
||||
export async function listPaymentsController(request: FastifyRequest, reply: FastifyReply) {
|
||||
const querySchema = z.object({
|
||||
dataInicio: z.string().optional(),
|
||||
dataFim: z.string().optional(),
|
||||
});
|
||||
|
||||
const query = querySchema.parse(request.query);
|
||||
const filter: any = {};
|
||||
if (query.dataInicio) filter.dataInicio = query.dataInicio;
|
||||
if (query.dataFim) filter.dataFim = query.dataFim;
|
||||
|
||||
const payments = await orderService.listPayments(
|
||||
(filter.dataInicio || filter.dataFim) ? filter : undefined
|
||||
);
|
||||
return reply.send(payments);
|
||||
}
|
||||
|
||||
export async function createOrderController(request: FastifyRequest, reply: FastifyReply) {
|
||||
const data = createOrderSchema.parse(request.body);
|
||||
const order = await orderService.createOrder(data);
|
||||
@@ -59,6 +76,17 @@ export async function updateItemStatusController(request: FastifyRequest, reply:
|
||||
return reply.send(item);
|
||||
}
|
||||
|
||||
export async function updateItemObservacaoController(request: FastifyRequest, reply: FastifyReply) {
|
||||
const { id } = z.object({ id: z.string().uuid() }).parse(request.params);
|
||||
const data = updateItemObservacaoSchema.parse(request.body);
|
||||
try {
|
||||
const item = await orderService.updateItemObservacao(id, data);
|
||||
return reply.send(item);
|
||||
} catch (error: any) {
|
||||
return reply.status(400).send({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function payOrderController(request: FastifyRequest, reply: FastifyReply) {
|
||||
const { id } = z.object({ id: z.string().uuid() }).parse(request.params);
|
||||
const body = request.body && Object.keys(request.body as object).length > 0
|
||||
@@ -72,6 +100,17 @@ export async function payOrderController(request: FastifyRequest, reply: Fastify
|
||||
}
|
||||
}
|
||||
|
||||
export async function registerPaymentController(request: FastifyRequest, reply: FastifyReply) {
|
||||
const { id } = z.object({ id: z.string().uuid() }).parse(request.params);
|
||||
const data = partialPaymentSchema.parse(request.body);
|
||||
try {
|
||||
const pagamento = await orderService.registerPayment(id, data);
|
||||
return reply.status(201).send(pagamento);
|
||||
} catch (error: any) {
|
||||
return reply.status(400).send({ error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateOrderController(request: FastifyRequest, reply: FastifyReply) {
|
||||
const { id } = z.object({ id: z.string().uuid() }).parse(request.params);
|
||||
const data = updateOrderSchema.parse(request.body);
|
||||
|
||||
@@ -6,6 +6,9 @@ export async function orderRoutes(app: FastifyInstance) {
|
||||
// Listagem geral (qualquer autenticado)
|
||||
app.get('/comandas', { preHandler: [authenticate] }, orderController.listOrdersController);
|
||||
|
||||
// Listagem de pagamentos (qualquer autenticado)
|
||||
app.get('/pagamentos', { preHandler: [authenticate] }, orderController.listPaymentsController);
|
||||
|
||||
// Comandas (Garçom, Caixa, Admin)
|
||||
app.post('/comandas', { preHandler: [requireRoles(['GARCOM', 'CAIXA', 'ADMIN'])] }, orderController.createOrderController);
|
||||
app.post('/comandas/:id/itens', { preHandler: [requireRoles(['GARCOM', 'CAIXA', 'ADMIN'])] }, orderController.addItemToOrderController);
|
||||
@@ -13,11 +16,15 @@ export async function orderRoutes(app: FastifyInstance) {
|
||||
|
||||
// Pagamento (Caixa, Admin)
|
||||
app.post('/comandas/:id/pagar', { preHandler: [requireRoles(['CAIXA', 'ADMIN'])] }, orderController.payOrderController);
|
||||
app.post('/comandas/:id/pagamentos', { preHandler: [requireRoles(['CAIXA', 'ADMIN'])] }, orderController.registerPaymentController);
|
||||
|
||||
// Painel Cozinha / Bar
|
||||
app.get('/painel/itens', { preHandler: [requireRoles(['COZINHA', 'BARMAN', 'ADMIN'])] }, orderController.listKitchenBarController);
|
||||
app.patch('/painel/itens/:id/status', { preHandler: [requireRoles(['COZINHA', 'BARMAN', 'ADMIN'])] }, orderController.updateItemStatusController);
|
||||
|
||||
// Editar observação de item (Garçom, Caixa, Admin)
|
||||
app.patch('/comandas/itens/:id/observacao', { preHandler: [requireRoles(['GARCOM', 'CAIXA', 'ADMIN'])] }, orderController.updateItemObservacaoController);
|
||||
|
||||
// Excluir item de comanda (apenas CAIXA e ADMIN)
|
||||
app.delete('/comandas/itens/:id', { preHandler: [requireRoles(['CAIXA', 'ADMIN'])] }, orderController.deleteItemFromOrderController);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,25 @@ export const payOrderSchema = z.object({
|
||||
formaPagamento: z.enum(['DINHEIRO', 'CARTAO_CREDITO', 'CARTAO_DEBITO', 'PIX']),
|
||||
});
|
||||
|
||||
export const partialPaymentSchema = z.object({
|
||||
valor: z.number().min(0).optional(),
|
||||
formaPagamento: z.enum(['DINHEIRO', 'CARTAO_CREDITO', 'CARTAO_DEBITO', 'PIX']),
|
||||
observacao: z.string().optional(),
|
||||
itemIds: z.array(z.string().uuid()).optional(),
|
||||
}).refine(
|
||||
(data) => (data.itemIds && data.itemIds.length > 0) || (data.valor !== undefined && data.valor > 0),
|
||||
{ message: 'Informe um valor positivo ou selecione itens para pagamento' }
|
||||
);
|
||||
|
||||
export type CreateOrderInput = z.infer<typeof createOrderSchema>;
|
||||
export type AddItemOrderInput = z.infer<typeof addItemOrderSchema>;
|
||||
export const updateItemObservacaoSchema = z.object({
|
||||
observacao: z.string().optional(),
|
||||
});
|
||||
|
||||
export type UpdateItemStatusInput = z.infer<typeof updateItemStatusSchema>;
|
||||
export type UpdateItemObservacaoInput = z.infer<typeof updateItemObservacaoSchema>;
|
||||
export type UpdateOrderInput = z.infer<typeof updateOrderSchema>;
|
||||
export type PayOrderInput = z.infer<typeof payOrderSchema>;
|
||||
export type PartialPaymentInput = z.infer<typeof partialPaymentSchema>;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { prisma } from '../lib/prisma';
|
||||
import { CreateOrderInput, AddItemOrderInput, UpdateItemStatusInput, UpdateOrderInput, PayOrderInput } from '../schemas/order.schema';
|
||||
import { CreateOrderInput, AddItemOrderInput, UpdateItemStatusInput, UpdateItemObservacaoInput, UpdateOrderInput, PayOrderInput, PartialPaymentInput } from '../schemas/order.schema';
|
||||
|
||||
export interface ListOrdersFilter {
|
||||
statuses?: string[];
|
||||
@@ -7,6 +7,42 @@ export interface ListOrdersFilter {
|
||||
dataFim?: string;
|
||||
}
|
||||
|
||||
export interface ListPaymentsFilter {
|
||||
dataInicio?: string;
|
||||
dataFim?: string;
|
||||
}
|
||||
|
||||
export async function listPayments(filter?: ListPaymentsFilter) {
|
||||
const where: any = {};
|
||||
|
||||
if (filter?.dataInicio || filter?.dataFim) {
|
||||
where.criadoEm = {};
|
||||
if (filter.dataInicio) {
|
||||
const [y, m, d] = filter.dataInicio.split('-').map(Number);
|
||||
where.criadoEm.gte = new Date(y, m - 1, d, 0, 0, 0, 0);
|
||||
}
|
||||
if (filter.dataFim) {
|
||||
const [y, m, d] = filter.dataFim.split('-').map(Number);
|
||||
where.criadoEm.lte = new Date(y, m - 1, d, 23, 59, 59, 999);
|
||||
}
|
||||
}
|
||||
|
||||
return prisma.pagamento.findMany({
|
||||
where,
|
||||
orderBy: { criadoEm: 'desc' },
|
||||
include: {
|
||||
comanda: {
|
||||
select: {
|
||||
id: true,
|
||||
identificador: true,
|
||||
nomeCliente: true,
|
||||
status: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function listOrders(filter?: ListOrdersFilter) {
|
||||
const where: any = {};
|
||||
|
||||
@@ -30,7 +66,8 @@ export async function listOrders(filter?: ListOrdersFilter) {
|
||||
include: {
|
||||
itens: {
|
||||
include: { produto: true }
|
||||
}
|
||||
},
|
||||
pagamentos: true,
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -116,6 +153,28 @@ export async function updateItemStatus(itemId: string, data: UpdateItemStatusInp
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateItemObservacao(itemId: string, data: UpdateItemObservacaoInput) {
|
||||
const item = await prisma.itemPedido.findUnique({
|
||||
where: { id: itemId },
|
||||
include: { comanda: true },
|
||||
});
|
||||
|
||||
if (!item) {
|
||||
throw new Error('Item do pedido não encontrado');
|
||||
}
|
||||
|
||||
if (item.comanda.status !== 'ABERTA') {
|
||||
throw new Error('Não é possível editar itens de uma comanda fechada/paga/cancelada');
|
||||
}
|
||||
|
||||
return prisma.itemPedido.update({
|
||||
where: { id: itemId },
|
||||
data: {
|
||||
observacao: data.observacao ?? null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function payOrder(comandaId: string, data?: PayOrderInput) {
|
||||
const comanda = await prisma.comanda.findUnique({
|
||||
where: { id: comandaId },
|
||||
@@ -128,15 +187,110 @@ export async function payOrder(comandaId: string, data?: PayOrderInput) {
|
||||
if (comanda.status !== 'ABERTA') {
|
||||
throw new Error('Comanda não está aberta para pagamento');
|
||||
}
|
||||
|
||||
return prisma.comanda.update({
|
||||
|
||||
const totalFinal = calcularTotalFinal(comanda);
|
||||
const restante = Math.max(totalFinal - comanda.valorPago, 0);
|
||||
|
||||
if (restante <= 0) {
|
||||
throw new Error('Comanda já foi paga integralmente');
|
||||
}
|
||||
|
||||
const [, comandaAtualizada] = await prisma.$transaction([
|
||||
prisma.pagamento.create({
|
||||
data: {
|
||||
comandaId,
|
||||
valor: restante,
|
||||
formaPagamento: data?.formaPagamento ?? 'DINHEIRO',
|
||||
observacao: 'Pagamento integral',
|
||||
},
|
||||
}),
|
||||
prisma.comanda.update({
|
||||
where: { id: comandaId },
|
||||
data: {
|
||||
status: 'PAGA',
|
||||
fechadoEm: new Date(),
|
||||
formaPagamento: data?.formaPagamento,
|
||||
valorPago: totalFinal,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return comandaAtualizada;
|
||||
}
|
||||
|
||||
function calcularTotalFinal(comanda: { total: number; desconto: number; acrescimo: number; taxaServico: boolean }): number {
|
||||
let total = comanda.total || 0;
|
||||
total -= (comanda.desconto || 0);
|
||||
total += (comanda.acrescimo || 0);
|
||||
if (comanda.taxaServico) total += total * 0.10;
|
||||
return Math.max(total, 0);
|
||||
}
|
||||
|
||||
export async function registerPayment(comandaId: string, data: PartialPaymentInput) {
|
||||
const comanda = await prisma.comanda.findUnique({
|
||||
where: { id: comandaId },
|
||||
data: {
|
||||
status: 'PAGA',
|
||||
fechadoEm: new Date(),
|
||||
formaPagamento: data?.formaPagamento,
|
||||
},
|
||||
include: { itens: true },
|
||||
});
|
||||
|
||||
if (!comanda) {
|
||||
throw new Error('Comanda não encontrada');
|
||||
}
|
||||
|
||||
if (comanda.status === 'PAGA') {
|
||||
throw new Error('Comanda já foi paga integralmente');
|
||||
}
|
||||
|
||||
if (comanda.status === 'CANCELADA') {
|
||||
throw new Error('Não é possível pagar uma comanda cancelada');
|
||||
}
|
||||
|
||||
const totalFinal = calcularTotalFinal(comanda);
|
||||
const restante = Math.max(totalFinal - comanda.valorPago, 0);
|
||||
|
||||
if (restante <= 0) {
|
||||
throw new Error('Comanda já foi paga integralmente');
|
||||
}
|
||||
|
||||
let valorPagamento = data.valor ?? 0;
|
||||
|
||||
if (data.itemIds && data.itemIds.length > 0) {
|
||||
const itensSelecionados = comanda.itens.filter(i => data.itemIds!.includes(i.id));
|
||||
if (itensSelecionados.length === 0) {
|
||||
throw new Error('Nenhum item válido selecionado');
|
||||
}
|
||||
valorPagamento = itensSelecionados.reduce((acc, i) => acc + i.precoUnitario * i.quantidade, 0);
|
||||
}
|
||||
|
||||
if (!valorPagamento || valorPagamento <= 0) {
|
||||
throw new Error('Valor do pagamento deve ser maior que zero');
|
||||
}
|
||||
|
||||
if (valorPagamento > restante) {
|
||||
throw new Error(`Valor excede o restante. Disponível: R$ ${restante.toFixed(2)}`);
|
||||
}
|
||||
|
||||
const novoValorPago = comanda.valorPago + valorPagamento;
|
||||
const totalmentePago = novoValorPago >= totalFinal;
|
||||
|
||||
const pagamento = await prisma.$transaction([
|
||||
prisma.pagamento.create({
|
||||
data: {
|
||||
comandaId,
|
||||
valor: valorPagamento,
|
||||
formaPagamento: data.formaPagamento,
|
||||
observacao: data.observacao ?? null,
|
||||
},
|
||||
}),
|
||||
prisma.comanda.update({
|
||||
where: { id: comandaId },
|
||||
data: {
|
||||
valorPago: novoValorPago,
|
||||
...(totalmentePago ? { status: 'PAGA', fechadoEm: new Date() } : {}),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return pagamento[0];
|
||||
}
|
||||
|
||||
export async function updateOrder(comandaId: string, data: UpdateOrderInput) {
|
||||
@@ -168,7 +322,8 @@ export async function updateOrder(comandaId: string, data: UpdateOrderInput) {
|
||||
include: {
|
||||
itens: {
|
||||
include: { produto: true }
|
||||
}
|
||||
},
|
||||
pagamentos: true,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user