From 18f200df9f848e6f65aa8bb262aa5707ca780807 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Wed, 1 Jul 2026 14:05:39 +0330 Subject: [PATCH] update prepare time --- ...on20260701120000_addPrepareTimeToOrders.ts | 13 ++++++ src/common/enums/message.enum.ts | 2 + .../orders/controllers/orders.controller.ts | 15 +++++++ .../orders/dto/admin-update-order-fees.dto.ts | 22 ++++++++++ src/modules/orders/entities/order.entity.ts | 3 ++ .../orders/providers/orders.service.ts | 41 +++++++++++++++++++ 6 files changed, 96 insertions(+) create mode 100644 database/migrations/Migration20260701120000_addPrepareTimeToOrders.ts create mode 100644 src/modules/orders/dto/admin-update-order-fees.dto.ts diff --git a/database/migrations/Migration20260701120000_addPrepareTimeToOrders.ts b/database/migrations/Migration20260701120000_addPrepareTimeToOrders.ts new file mode 100644 index 0000000..9e9698e --- /dev/null +++ b/database/migrations/Migration20260701120000_addPrepareTimeToOrders.ts @@ -0,0 +1,13 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20260701120000_addPrepareTimeToOrders extends Migration { + + override async up(): Promise { + this.addSql(`alter table "orders" add column "prepare_time" int null;`); + } + + override async down(): Promise { + this.addSql(`alter table "orders" drop column "prepare_time";`); + } + +} diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 837f12c..ab6ff53 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -697,6 +697,8 @@ export const enum OrderMessage { FOOD_NOT_BELONGS_TO_RESTAURANT = 'غذا به این رستوران تعلق ندارد', CREDIT_CARD_PAYMENT_DETAILS_REQUIRED = 'حداقل یکی از توضیحات پرداخت یا پیوست رسید کارت به کارت الزامی است', DISCOUNT_AMOUNT_EXCEEDS_ORDER_TOTAL = 'مبلغ تخفیف بیشتر از مبلغ سفارش است', + CANNOT_UPDATE_CANCELED_OR_COMPLETED_ORDER = 'امکان ویرایش سفارش لغو شده یا تکمیل شده وجود ندارد', + NO_FIELDS_TO_UPDATE = 'حداقل یک فیلد برای بروزرسانی الزامی است', } export const enum CartMessage { diff --git a/src/modules/orders/controllers/orders.controller.ts b/src/modules/orders/controllers/orders.controller.ts index 1e0eddc..1fab77f 100644 --- a/src/modules/orders/controllers/orders.controller.ts +++ b/src/modules/orders/controllers/orders.controller.ts @@ -12,6 +12,7 @@ import { UpdateOrderStatusDto } from '../dto/update-order-status.dto'; import { Permissions } from 'src/common/decorators/permissions.decorator'; import { Permission } from 'src/common/enums/permission.enum'; import { AdminCreateOrderDto } from '../dto/admin-create-order.dto'; +import { AdminUpdateOrderFeesDto } from '../dto/admin-update-order-fees.dto'; import { FoodOrderReportDto } from '../dto/food-order-report.dto'; import { DailyOrderReportDto } from '../dto/daily-order-report.dto'; @@ -113,6 +114,20 @@ export class OrdersController { return this.ordersService.changeOrderStatus(orderId, restId, status, 'admin', dto?.desc); } + @UseGuards(AdminAuthGuard) + @Permissions(Permission.MANAGE_ORDERS) + @Patch('admin/orders/:orderId/fees') + @ApiOperation({ summary: 'Update order delivery fee, packing fee, and preparation time' }) + @ApiParam({ name: 'orderId', description: 'Order ID' }) + @ApiBody({ type: AdminUpdateOrderFeesDto }) + updateOrderFees( + @Param('orderId') orderId: string, + @Body() dto: AdminUpdateOrderFeesDto, + @RestId() restId: string, + ) { + return this.ordersService.updateOrderFees(orderId, restId, dto); + } + @UseGuards(AdminAuthGuard) @Permissions(Permission.VIEW_REPORTS) @ApiOperation({ summary: 'Get Stats for report page' }) diff --git a/src/modules/orders/dto/admin-update-order-fees.dto.ts b/src/modules/orders/dto/admin-update-order-fees.dto.ts new file mode 100644 index 0000000..9974731 --- /dev/null +++ b/src/modules/orders/dto/admin-update-order-fees.dto.ts @@ -0,0 +1,22 @@ +import { ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNumber, IsOptional, Min } from 'class-validator'; + +export class AdminUpdateOrderFeesDto { + @ApiPropertyOptional({ description: 'Delivery fee amount', minimum: 0 }) + @IsOptional() + @IsNumber() + @Min(0) + deliveryFee?: number; + + @ApiPropertyOptional({ description: 'Packing fee amount', minimum: 0 }) + @IsOptional() + @IsNumber() + @Min(0) + packingFee?: number; + + @ApiPropertyOptional({ description: 'Estimated preparation time in minutes', minimum: 0 }) + @IsOptional() + @IsNumber() + @Min(0) + prepareTime?: number; +} diff --git a/src/modules/orders/entities/order.entity.ts b/src/modules/orders/entities/order.entity.ts index 0458707..4c50f90 100644 --- a/src/modules/orders/entities/order.entity.ts +++ b/src/modules/orders/entities/order.entity.ts @@ -83,6 +83,9 @@ export class Order extends BaseEntity { @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 }) packingFee: number = 0; + @Property({ type: 'int', nullable: true }) + prepareTime?: number | null; + @Property({ type: 'decimal', precision: 10, scale: 0 }) total!: number; diff --git a/src/modules/orders/providers/orders.service.ts b/src/modules/orders/providers/orders.service.ts index a9611bc..903b4e1 100644 --- a/src/modules/orders/providers/orders.service.ts +++ b/src/modules/orders/providers/orders.service.ts @@ -26,6 +26,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter'; import { OrderCreatedEvent, OrderStatusChangedEvent } from '../events/order.events'; import { OrderMessage } from 'src/common/enums/message.enum'; import { AdminCreateOrderDto } from '../dto/admin-create-order.dto'; +import { AdminUpdateOrderFeesDto } from '../dto/admin-update-order-fees.dto'; import { FoodOrderReportDto, FoodOrderReportSortBy } from '../dto/food-order-report.dto'; import { DailyOrderReportDto, DailyOrderReportSortBy } from '../dto/daily-order-report.dto'; type OrderItemData = { food: Food; quantity: number; unitPrice: number; discount: number }; @@ -243,6 +244,46 @@ export class OrdersService { return order; } + async updateOrderFees(orderId: string, restId: string, dto: AdminUpdateOrderFeesDto): Promise { + if (dto.deliveryFee === undefined && dto.packingFee === undefined && dto.prepareTime === undefined) { + throw new BadRequestException(OrderMessage.NO_FIELDS_TO_UPDATE); + } + + const order = await this.getOrderOrFail(orderId, restId); + + if ([OrderStatus.CANCELED, OrderStatus.COMPLETED].includes(order.status)) { + throw new BadRequestException(OrderMessage.CANNOT_UPDATE_CANCELED_OR_COMPLETED_ORDER); + } + + const feesChanged = dto.deliveryFee !== undefined || dto.packingFee !== undefined; + + if (dto.deliveryFee !== undefined) { + order.deliveryFee = dto.deliveryFee; + } + if (dto.packingFee !== undefined) { + order.packingFee = dto.packingFee; + } + if (dto.prepareTime !== undefined) { + order.prepareTime = dto.prepareTime; + } + + if (feesChanged) { + order.total = order.subTotal - order.totalDiscount + order.deliveryFee + order.packingFee + order.tax; + + const pendingPayment = await this.em.findOne(Payment, { + order: { id: orderId }, + status: PaymentStatusEnum.Pending, + }); + + if (pendingPayment) { + pendingPayment.amount = order.total; + } + } + + await this.em.persistAndFlush(order); + return order; + } + /** * Validates cart and prepares all required data for order creation */