From d1f85d78f44fa3357583b163f714e68e273d8cdb Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 19 Jul 2026 10:18:33 +0330 Subject: [PATCH] add payment --- src/common/enums/message.enum.ts | 2 + .../orders/controllers/orders.controller.ts | 15 ++++++ .../orders/dto/admin-add-payment.dto.ts | 16 +++++++ .../orders/providers/orders.service.ts | 47 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 src/modules/orders/dto/admin-add-payment.dto.ts diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index b74c6ff..665294c 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -710,6 +710,8 @@ export const enum OrderMessage { REFUND_AMOUNT_EXCEEDS_PAID = 'مبلغ بازگشت وجه بیشتر از مبلغ پرداخت‌شده است', REFUND_AMOUNT_EXCEEDS_OVERPAYMENT = 'مبلغ بازگشت وجه بیشتر از مانده منفی سفارش است', NO_PAID_PAYMENTS_TO_REFUND = 'پرداختی برای بازگشت وجه یافت نشد', + ADD_PAYMENT_NOT_ALLOWED = 'امکان افزودن پرداخت برای این سفارش وجود ندارد', + ADD_PAYMENT_NO_BALANCE = 'مانده‌ای برای پرداخت وجود ندارد', } export const enum CartMessage { diff --git a/src/modules/orders/controllers/orders.controller.ts b/src/modules/orders/controllers/orders.controller.ts index b8d5a82..4a13daa 100644 --- a/src/modules/orders/controllers/orders.controller.ts +++ b/src/modules/orders/controllers/orders.controller.ts @@ -18,6 +18,7 @@ import { AdminUpdateOrderItemDto } from '../dto/admin-update-order-item.dto'; import { FoodOrderReportDto } from '../dto/food-order-report.dto'; import { DailyOrderReportDto } from '../dto/daily-order-report.dto'; import { AdminRefundOrderDto } from '../dto/admin-refund-order.dto'; +import { AdminAddPaymentDto } from '../dto/admin-add-payment.dto'; @ApiTags('orders') @ApiBearerAuth() @@ -155,6 +156,20 @@ export class OrdersController { return this.ordersService.removeOrderItem(orderId, restId, itemId); } + @UseGuards(AdminAuthGuard) + @Permissions(Permission.MANAGE_ORDERS) + @Post('admin/orders/:orderId/payments') + @ApiOperation({ summary: 'Add a cash payment to an existing order' }) + @ApiParam({ name: 'orderId', description: 'Order ID' }) + @ApiBody({ type: AdminAddPaymentDto }) + addPayment( + @Param('orderId') orderId: string, + @Body() dto: AdminAddPaymentDto, + @RestId() restId: string, + ) { + return this.ordersService.addPayment(orderId, restId, dto); + } + @UseGuards(AdminAuthGuard) @Permissions(Permission.MANAGE_ORDERS) @Post('admin/orders/:orderId/refund') diff --git a/src/modules/orders/dto/admin-add-payment.dto.ts b/src/modules/orders/dto/admin-add-payment.dto.ts new file mode 100644 index 0000000..dd9cb8b --- /dev/null +++ b/src/modules/orders/dto/admin-add-payment.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { IsNumber, IsOptional, IsString, Min } from 'class-validator'; + +export class AdminAddPaymentDto { + @ApiProperty({ description: 'Payment amount', minimum: 1 }) + @Type(() => Number) + @IsNumber() + @Min(1) + amount!: number; + + @ApiPropertyOptional({ description: 'Payment description' }) + @IsOptional() + @IsString() + description?: string; +} diff --git a/src/modules/orders/providers/orders.service.ts b/src/modules/orders/providers/orders.service.ts index 45e8c33..f3df8fa 100644 --- a/src/modules/orders/providers/orders.service.ts +++ b/src/modules/orders/providers/orders.service.ts @@ -29,6 +29,7 @@ import { AdminUpdateOrderFeesDto } from '../dto/admin-update-order-fees.dto'; import { AdminAddOrderItemDto } from '../dto/admin-add-order-item.dto'; import { AdminUpdateOrderItemDto } from '../dto/admin-update-order-item.dto'; import { AdminRefundOrderDto } from '../dto/admin-refund-order.dto'; +import { AdminAddPaymentDto } from '../dto/admin-add-payment.dto'; import { FoodOrderReportDto, FoodOrderReportSortBy } from '../dto/food-order-report.dto'; import { DailyOrderReportDto, DailyOrderReportSortBy } from '../dto/daily-order-report.dto'; import { CashShiftsService } from './cash-shifts.service'; @@ -545,6 +546,52 @@ export class OrdersService { return order; } + async addPayment(orderId: string, restId: string, dto: AdminAddPaymentDto): Promise { + return this.em.transactional(async em => { + const order = await em.findOne( + Order, + { id: orderId, restaurant: { id: restId } }, + { populate: ['payments', 'user', 'paymentMethod', 'deliveryMethod', 'items', 'items.food'] }, + ); + if (!order) { + throw new NotFoundException(OrderMessage.NOT_FOUND); + } + + if (order.status === OrderStatus.CANCELED) { + throw new BadRequestException(OrderMessage.ADD_PAYMENT_NOT_ALLOWED); + } + + const balance = Number(order.balance ?? 0); + if (balance <= 0) { + throw new BadRequestException(OrderMessage.ADD_PAYMENT_NO_BALANCE); + } + + const amount = Number(dto.amount); + if (!amount || amount <= 0) { + throw new BadRequestException(PaymentMessage.AMOUNT_MUST_BE_GREATER_THAN_ZERO); + } + + const payment = em.create(Payment, { + order, + amount, + method: PaymentMethodEnum.Cash, + gateway: null, + status: PaymentStatusEnum.Paid, + paidAt: new Date(), + description: dto.description?.trim() || null, + }); + em.persist(payment); + + await em.flush(); + + return em.findOneOrFail( + Order, + { id: orderId }, + { populate: ['payments', 'user', 'paymentMethod', 'deliveryMethod', 'items', 'items.food'] }, + ); + }); + } + async refundOrder(orderId: string, restId: string, dto: AdminRefundOrderDto): Promise { return this.em.transactional(async em => { const order = await em.findOne(