This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { Migration } from '@mikro-orm/migrations';
|
||||
|
||||
export class Migration20260701120000_addPrepareTimeToOrders extends Migration {
|
||||
|
||||
override async up(): Promise<void> {
|
||||
this.addSql(`alter table "orders" add column "prepare_time" int null;`);
|
||||
}
|
||||
|
||||
override async down(): Promise<void> {
|
||||
this.addSql(`alter table "orders" drop column "prepare_time";`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<Order> {
|
||||
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
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user