refactor step 2
This commit is contained in:
@@ -19,10 +19,9 @@ export class Food extends BaseEntity {
|
|||||||
@OneToMany(() => Review, review => review.food, { cascade: [Cascade.ALL], orphanRemoval: true })
|
@OneToMany(() => Review, review => review.food, { cascade: [Cascade.ALL], orphanRemoval: true })
|
||||||
reviews = new Collection<Review>(this);
|
reviews = new Collection<Review>(this);
|
||||||
|
|
||||||
@OneToOne(() => Inventory, inventory => inventory.food, {
|
@OneToOne(() => Inventory, {
|
||||||
|
mappedBy: 'food',
|
||||||
nullable: true,
|
nullable: true,
|
||||||
cascade: [Cascade.ALL],
|
|
||||||
orphanRemoval: true,
|
|
||||||
})
|
})
|
||||||
inventory?: Inventory;
|
inventory?: Inventory;
|
||||||
|
|
||||||
@@ -62,12 +61,6 @@ export class Food extends BaseEntity {
|
|||||||
@Property({ type: 'boolean', default: false })
|
@Property({ type: 'boolean', default: false })
|
||||||
dinner: boolean = false;
|
dinner: boolean = false;
|
||||||
|
|
||||||
// @Property({ type: 'int', default: 0 })
|
|
||||||
// stock: number = 0;
|
|
||||||
|
|
||||||
// @Property({ type: 'int', default: 0 })
|
|
||||||
// stockDefault: number = 0;
|
|
||||||
|
|
||||||
@Property({ type: 'boolean', default: true })
|
@Property({ type: 'boolean', default: true })
|
||||||
isActive: boolean = true;
|
isActive: boolean = true;
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { Entity, Property, OneToOne } from '@mikro-orm/core';
|
import { Cascade, Entity, Property, OneToOne } from '@mikro-orm/core';
|
||||||
import { BaseEntity } from '../../../common/entities/base.entity';
|
import { BaseEntity } from '../../../common/entities/base.entity';
|
||||||
import { Food } from '../../foods/entities/food.entity';
|
import { Food } from '../../foods/entities/food.entity';
|
||||||
|
|
||||||
@Entity({ tableName: 'inventory' })
|
@Entity({ tableName: 'inventory' })
|
||||||
export class Inventory extends BaseEntity {
|
export class Inventory extends BaseEntity {
|
||||||
@OneToOne(() => Food, food => food.inventory)
|
@OneToOne(() => Food, {
|
||||||
|
cascade: [Cascade.ALL],
|
||||||
|
orphanRemoval: true,
|
||||||
|
})
|
||||||
food!: Food;
|
food!: Food;
|
||||||
|
|
||||||
@Property({ type: 'int' })
|
@Property({ type: 'int' })
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import { InventoryService } from './inventory.service';
|
|||||||
import { InventoryController } from './inventory.controller';
|
import { InventoryController } from './inventory.controller';
|
||||||
import { Inventory } from './entities/inventory.entity';
|
import { Inventory } from './entities/inventory.entity';
|
||||||
import { AuthModule } from '../auth/auth.module';
|
import { AuthModule } from '../auth/auth.module';
|
||||||
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [MikroOrmModule.forFeature([Inventory]), AuthModule],
|
imports: [MikroOrmModule.forFeature([Inventory]), AuthModule, JwtModule],
|
||||||
controllers: [InventoryController],
|
controllers: [InventoryController],
|
||||||
providers: [InventoryService],
|
providers: [InventoryService],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { UserId } from '../../../common/decorators/user-id.decorator';
|
|||||||
import { RestId } from 'src/common/decorators/rest-id.decorator';
|
import { RestId } from 'src/common/decorators/rest-id.decorator';
|
||||||
import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
|
import { AdminAuthGuard } from '../../auth/guards/adminAuth.guard';
|
||||||
import { FindOrdersDto } from '../dto/find-orders.dto';
|
import { FindOrdersDto } from '../dto/find-orders.dto';
|
||||||
|
import { OrderStatus } from '../interface/order-status';
|
||||||
|
|
||||||
@ApiTags('orders')
|
@ApiTags('orders')
|
||||||
@Controller()
|
@Controller()
|
||||||
@@ -148,16 +149,9 @@ export class OrdersController {
|
|||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: 'status',
|
name: 'status',
|
||||||
description: 'Order status',
|
description: 'Order status',
|
||||||
enum: [
|
enum: OrderStatus,
|
||||||
'readyForCustomerPickup',
|
|
||||||
'readyForDineIn',
|
|
||||||
'readyForDeliveryCar',
|
|
||||||
'readyForDeliveryCourier',
|
|
||||||
'shipping',
|
|
||||||
'delivered',
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
updateStatus(@Param('orderId') orderId: string, @Param('status') status: string, @RestId() restId: string) {
|
updateStatus(@Param('orderId') orderId: string, @Param('status') status: OrderStatus, @RestId() restId: string) {
|
||||||
return this.ordersService.updateStatus(orderId, status, restId);
|
return this.ordersService.updateStatus(orderId, status, restId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import { UserAddress } from '../../users/entities/user-address.entity';
|
|||||||
import { PaymentMethod } from '../../payments/entities/payment-method.entity';
|
import { PaymentMethod } from '../../payments/entities/payment-method.entity';
|
||||||
import { OrderItem } from './order-item.entity';
|
import { OrderItem } from './order-item.entity';
|
||||||
import { Delivery } from '../../delivery/entities/delivery.entity';
|
import { Delivery } from '../../delivery/entities/delivery.entity';
|
||||||
import { Payment } from 'src/modules/payments/entities/payment.entity';
|
|
||||||
|
|
||||||
@Entity({ tableName: 'orders' })
|
@Entity({ tableName: 'orders' })
|
||||||
@Unique({ properties: ['restaurant', 'orderNumber'] })
|
@Unique({ properties: ['restaurant', 'orderNumber'] })
|
||||||
@@ -48,7 +47,7 @@ export class Order extends BaseEntity {
|
|||||||
@ManyToOne(() => UserAddress, { nullable: true })
|
@ManyToOne(() => UserAddress, { nullable: true })
|
||||||
address?: UserAddress;
|
address?: UserAddress;
|
||||||
|
|
||||||
@OneToOne(() => Payment, payment => payment.order)
|
@ManyToOne(() => PaymentMethod)
|
||||||
paymentMethod: PaymentMethod;
|
paymentMethod: PaymentMethod;
|
||||||
|
|
||||||
@Property({ type: 'int', nullable: true })
|
@Property({ type: 'int', nullable: true })
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import type { CouponType } from 'src/modules/coupons/interface/coupon';
|
|||||||
// Delivered = 'delivered',
|
// Delivered = 'delivered',
|
||||||
// }
|
// }
|
||||||
export enum OrderStatus {
|
export enum OrderStatus {
|
||||||
CREATED = 'new',
|
NEW = 'new',
|
||||||
PENDING_PAYMENT = 'pendingPayment',
|
PENDING_PAYMENT = 'pendingPayment',
|
||||||
PAID = 'paid',
|
PAID = 'paid',
|
||||||
CONFIRMED = 'confirmed',
|
CONFIRMED = 'confirmed',
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ export class OrdersService {
|
|||||||
const { food, quantity, unitPrice, discount } = itemData;
|
const { food, quantity, unitPrice, discount } = itemData;
|
||||||
|
|
||||||
// Stock check
|
// Stock check
|
||||||
|
if (!food.inventory) {
|
||||||
|
throw new BadRequestException(`Food ${food.title} does not have inventory`);
|
||||||
|
}
|
||||||
if (food.inventory.availableStock < quantity) {
|
if (food.inventory.availableStock < quantity) {
|
||||||
throw new BadRequestException(`${food.title} is out of stock`);
|
throw new BadRequestException(`${food.title} is out of stock`);
|
||||||
}
|
}
|
||||||
@@ -109,8 +112,8 @@ export class OrdersService {
|
|||||||
|
|
||||||
em.persist(orderItem);
|
em.persist(orderItem);
|
||||||
|
|
||||||
// Update food stock
|
//reserve food stock
|
||||||
food.stock -= quantity;
|
|
||||||
em.persist(food);
|
em.persist(food);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,16 +312,10 @@ export class OrdersService {
|
|||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException('Order not found');
|
||||||
}
|
}
|
||||||
if (order.status !== OrderStatus.CREATED) {
|
|
||||||
throw new BadRequestException('Order must be created before confirming');
|
|
||||||
}
|
|
||||||
// Wallet payments should be paid before accepting (like Online)
|
|
||||||
if (order.paymentMethod?.method === PaymentMethodEnum.Online && order.paymentStatus !== PaymentStatusEnum.Paid) {
|
|
||||||
throw new BadRequestException('Order must be paid online before accepting');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cash, CardOnDelivery, and Wallet can be accepted even if payment status is pending
|
if (!this.canTransition(order.status, OrderStatus.CONFIRMED, order.paymentMethod.method)) {
|
||||||
// (they're paid on delivery or already deducted)
|
throw new BadRequestException('Invalid status transition');
|
||||||
|
}
|
||||||
|
|
||||||
order.status = OrderStatus.CONFIRMED;
|
order.status = OrderStatus.CONFIRMED;
|
||||||
await this.em.persistAndFlush(order);
|
await this.em.persistAndFlush(order);
|
||||||
@@ -330,17 +327,14 @@ export class OrdersService {
|
|||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException('Order not found');
|
||||||
}
|
}
|
||||||
if (!this.canTransition(order.status, OrderStatus.PREPARING, order.paymentMethod?.method)) {
|
if (!this.canTransition(order.status, OrderStatus.PREPARING, order.paymentMethod.method)) {
|
||||||
throw new BadRequestException('Invalid status transition');
|
throw new BadRequestException('Invalid status transition');
|
||||||
}
|
}
|
||||||
if (order.status !== OrderStatus.CONFIRMED) {
|
|
||||||
throw new BadRequestException('Order must be confirmed before preparing');
|
|
||||||
}
|
|
||||||
order.status = OrderStatus.PREPARING;
|
order.status = OrderStatus.PREPARING;
|
||||||
await this.em.persistAndFlush(order);
|
await this.em.persistAndFlush(order);
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
// just admin can reject the order any time
|
||||||
async rejectOrder(orderId: string, restId: string) {
|
async rejectOrder(orderId: string, restId: string) {
|
||||||
const order = await this.em.findOne(Order, { id: orderId, restaurant: { id: restId } });
|
const order = await this.em.findOne(Order, { id: orderId, restaurant: { id: restId } });
|
||||||
if (!order) {
|
if (!order) {
|
||||||
@@ -356,26 +350,12 @@ export class OrdersService {
|
|||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException('Order not found');
|
||||||
}
|
}
|
||||||
let orderStatus: OrderStatus | undefined;
|
|
||||||
|
|
||||||
switch (order.deliveryMethod.method) {
|
if (!this.canTransition(order.status, OrderStatus.READY, order.paymentMethod.method)) {
|
||||||
case DeliveryMethodEnum.DeliveryCourier:
|
throw new BadRequestException('Invalid status transition');
|
||||||
orderStatus = OrderStatus.ReadyForDeliveryCourier;
|
|
||||||
break;
|
|
||||||
case DeliveryMethodEnum.DeliveryCar:
|
|
||||||
orderStatus = OrderStatus.ReadyForDeliveryCar;
|
|
||||||
break;
|
|
||||||
case DeliveryMethodEnum.DineIn:
|
|
||||||
orderStatus = OrderStatus.ReadyForDineIn;
|
|
||||||
break;
|
|
||||||
case DeliveryMethodEnum.CustomerPickup:
|
|
||||||
orderStatus = OrderStatus.ReadyForCustomerPickup;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new BadRequestException('Invalid delivery method');
|
|
||||||
}
|
}
|
||||||
|
order.status = OrderStatus.READY;
|
||||||
|
|
||||||
order.status = orderStatus;
|
|
||||||
await this.em.persistAndFlush(order);
|
await this.em.persistAndFlush(order);
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
@@ -385,8 +365,8 @@ export class OrdersService {
|
|||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException('Order not found');
|
||||||
}
|
}
|
||||||
if (order.status !== OrderStatus.) {
|
if (!this.canTransition(order.status, OrderStatus.CANCELED, order.paymentMethod.method)) {
|
||||||
throw new BadRequestException('Order is not pending');
|
throw new BadRequestException('Invalid status transition');
|
||||||
}
|
}
|
||||||
order.status = OrderStatus.CANCELED;
|
order.status = OrderStatus.CANCELED;
|
||||||
await this.em.persistAndFlush(order);
|
await this.em.persistAndFlush(order);
|
||||||
@@ -399,143 +379,119 @@ export class OrdersService {
|
|||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException('Order not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const readyStatuses = [
|
if (!this.canTransition(order.status, OrderStatus.COMPLETED, order.paymentMethod.method)) {
|
||||||
OrderStatus.ReadyForCustomerPickup,
|
throw new BadRequestException('Invalid status transition');
|
||||||
OrderStatus.ReadyForDineIn,
|
|
||||||
OrderStatus.ReadyForDeliveryCar,
|
|
||||||
OrderStatus.ReadyForDeliveryCourier,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!readyStatuses.includes(order.status)) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
`Order must be in a ready status to mark as delivered. Current status: ${order.status}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
order.status = OrderStatus.COMPLETED;
|
||||||
order.status = OrderStatus.Delivered;
|
|
||||||
await this.em.persistAndFlush(order);
|
await this.em.persistAndFlush(order);
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateStatus(orderId: string, status: string, restId: string) {
|
async updateStatus(orderId: string, status: OrderStatus, restId: string) {
|
||||||
const order = await this.em.findOne(Order, { id: orderId, restaurant: { id: restId } });
|
const order = await this.em.findOne(Order, { id: orderId, restaurant: { id: restId } });
|
||||||
if (!order) {
|
if (!order) {
|
||||||
throw new NotFoundException('Order not found');
|
throw new NotFoundException('Order not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map string status to OrderStatus enum
|
if (!this.canTransition(order.status, status, order.paymentMethod.method)) {
|
||||||
const statusMap: Record<string, OrderStatus> = {
|
throw new BadRequestException('Invalid status transition');
|
||||||
ReadyForCustomerPickup: OrderStatus.ReadyForCustomerPickup,
|
|
||||||
readyForCustomerPickup: OrderStatus.ReadyForCustomerPickup,
|
|
||||||
ReadyForDineIn: OrderStatus.ReadyForDineIn,
|
|
||||||
readyForDineIn: OrderStatus.ReadyForDineIn,
|
|
||||||
ReadyForDeliveryCar: OrderStatus.ReadyForDeliveryCar,
|
|
||||||
readyForDeliveryCar: OrderStatus.ReadyForDeliveryCar,
|
|
||||||
ReadyForDeliveryCourier: OrderStatus.ReadyForDeliveryCourier,
|
|
||||||
readyForDeliveryCourier: OrderStatus.ReadyForDeliveryCourier,
|
|
||||||
shipping: OrderStatus.Shipping,
|
|
||||||
Delivered: OrderStatus.Delivered,
|
|
||||||
delivered: OrderStatus.Delivered,
|
|
||||||
};
|
|
||||||
|
|
||||||
const newStatus = statusMap[status];
|
|
||||||
if (!newStatus) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
`Invalid status: ${status}. Allowed statuses: shipping, Delivered, ReadyForCustomerPickup, ReadyForDineIn, ReadyForDeliveryCar, ReadyForDeliveryCourier`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
order.status = newStatus;
|
order.status = status;
|
||||||
await this.em.persistAndFlush(order);
|
await this.em.persistAndFlush(order);
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
transitions = {
|
transitions: Record<OrderStatus, OrderStatus[]> = {
|
||||||
CREATED: [OrderStatus.PENDING_PAYMENT, OrderStatus.CONFIRMED, OrderStatus.CANCELED],
|
[OrderStatus.NEW]: [OrderStatus.PENDING_PAYMENT, OrderStatus.CONFIRMED, OrderStatus.CANCELED],
|
||||||
PENDING_PAYMENT: [OrderStatus.PAID, OrderStatus.FAILED, OrderStatus.CANCELED],
|
[OrderStatus.PENDING_PAYMENT]: [OrderStatus.PAID, OrderStatus.FAILED, OrderStatus.CANCELED],
|
||||||
PAID: [OrderStatus.CONFIRMED, OrderStatus.REFUNDED],
|
[OrderStatus.PAID]: [OrderStatus.CONFIRMED, OrderStatus.REFUNDED],
|
||||||
CONFIRMED: [OrderStatus.PREPARING, OrderStatus.CANCELED],
|
[OrderStatus.CONFIRMED]: [OrderStatus.PREPARING, OrderStatus.CANCELED],
|
||||||
PREPARING: [OrderStatus.READY, OrderStatus.SHIPPED, OrderStatus.CANCELED],
|
[OrderStatus.PREPARING]: [OrderStatus.READY, OrderStatus.SHIPPED, OrderStatus.CANCELED],
|
||||||
READY: [OrderStatus.COMPLETED, OrderStatus.FAILED],
|
[OrderStatus.READY]: [OrderStatus.COMPLETED, OrderStatus.FAILED],
|
||||||
SHIPPED: [OrderStatus.COMPLETED, OrderStatus.FAILED],
|
[OrderStatus.SHIPPED]: [OrderStatus.COMPLETED, OrderStatus.FAILED],
|
||||||
}
|
[OrderStatus.COMPLETED]: [],
|
||||||
|
[OrderStatus.CANCELED]: [],
|
||||||
canTransition(from: OrderStatus, to: OrderStatus, paymentMethod: PaymentMethodEnum) {
|
[OrderStatus.FAILED]: [],
|
||||||
|
[OrderStatus.REFUNDED]: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
canTransition(from: OrderStatus, to: OrderStatus, paymentMethod: PaymentMethodEnum) {
|
||||||
if (!this.transitions[from]?.includes(to)) return false;
|
if (!this.transitions[from]?.includes(to)) return false;
|
||||||
|
|
||||||
if (paymentMethod === PaymentMethodEnum.Cash) {
|
if (paymentMethod === PaymentMethodEnum.Cash) {
|
||||||
if ([OrderStatus.PENDING_PAYMENT, OrderStatus.PAID].includes(to)) return false;
|
if ([OrderStatus.PENDING_PAYMENT, OrderStatus.PAID].includes(to)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paymentMethod === PaymentMethodEnum.Online) {
|
if (paymentMethod === PaymentMethodEnum.Online) {
|
||||||
if (to === OrderStatus.CONFIRMED && from !== OrderStatus.PAID) return false;
|
if (to === OrderStatus.CONFIRMED && from !== OrderStatus.PAID) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup job to handle abandoned orders (pending payment for >30 minutes)
|
* Cleanup job to handle abandoned orders (pending payment for >30 minutes)
|
||||||
* Runs every 10 minutes to check for abandoned orders
|
* Runs every 10 minutes to check for abandoned orders
|
||||||
*/
|
*/
|
||||||
@Cron(CronExpression.EVERY_10_MINUTES)
|
// @Cron(CronExpression.EVERY_10_MINUTES)
|
||||||
async cleanupAbandonedOrders() {
|
// async cleanupAbandonedOrders() {
|
||||||
this.logger.log('Starting cleanup of abandoned orders...');
|
// this.logger.log('Starting cleanup of abandoned orders...');
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000);
|
// const thirtyMinutesAgo = new Date(Date.now() - 30 * 60 * 1000);
|
||||||
|
|
||||||
// Find abandoned orders: Pending status, Pending payment, created >30 minutes ago
|
// // Find abandoned orders: Pending status, Pending payment, created >30 minutes ago
|
||||||
const abandonedOrders = await this.em.find(
|
// const abandonedOrders = await this.em.find(
|
||||||
Order,
|
// Order,
|
||||||
{
|
// {
|
||||||
status: OrderStatus.Pending,
|
// status: OrderStatus.PENDING_PAYMENT,
|
||||||
paymentStatus: PaymentStatusEnum.Pending,
|
// paymentStatus: PaymentStatusEnum.Pending,
|
||||||
createdAt: { $lt: thirtyMinutesAgo },
|
// createdAt: { $lt: thirtyMinutesAgo },
|
||||||
},
|
// },
|
||||||
{ populate: ['items', 'items.food'] },
|
// { populate: ['items', 'items.food'] },
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (abandonedOrders.length === 0) {
|
// if (abandonedOrders.length === 0) {
|
||||||
this.logger.log('No abandoned orders found');
|
// this.logger.log('No abandoned orders found');
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.logger.log(`Found ${abandonedOrders.length} abandoned order(s) to cleanup`);
|
// this.logger.log(`Found ${abandonedOrders.length} abandoned order(s) to cleanup`);
|
||||||
|
|
||||||
// Process each abandoned order in a transaction
|
// // Process each abandoned order in a transaction
|
||||||
for (const order of abandonedOrders) {
|
// for (const order of abandonedOrders) {
|
||||||
await this.em.transactional(async em => {
|
// await this.em.transactional(async em => {
|
||||||
// Load order items with food relations
|
// // Load order items with food relations
|
||||||
await order.items.loadItems();
|
// await order.items.loadItems();
|
||||||
|
|
||||||
// Restore stock for each item
|
// // Restore stock for each item
|
||||||
for (const item of order.items) {
|
// for (const item of order.items) {
|
||||||
const food = item.food;
|
// const food = item.food;
|
||||||
if (food) {
|
// if (food) {
|
||||||
food.stock += item.quantity;
|
// food.inventory?.availableStock += item.quantity;
|
||||||
em.persist(food);
|
// em.persist(food);
|
||||||
this.logger.debug(
|
// this.logger.debug(
|
||||||
`Restored ${item.quantity} units of stock for food ${food.id} (${food.title || 'N/A'})`,
|
// `Restored ${item.quantity} units of stock for food ${food.id} (${food.title || 'N/A'})`,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Update order status to Cancelled
|
// // Update order status to Cancelled
|
||||||
order.status = OrderStatus.CancelledBySystem;
|
// order.status = OrderStatus.CANCELED;
|
||||||
em.persist(order);
|
// em.persist(order);
|
||||||
|
|
||||||
await em.flush();
|
// await em.flush();
|
||||||
this.logger.log(`Cancelled abandoned order ${order.id}`);
|
// this.logger.log(`Cancelled abandoned order ${order.id}`);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
this.logger.log(`Successfully cleaned up ${abandonedOrders.length} abandoned order(s)`);
|
// this.logger.log(`Successfully cleaned up ${abandonedOrders.length} abandoned order(s)`);
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
// const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||||
const errorStack = error instanceof Error ? error.stack : undefined;
|
// const errorStack = error instanceof Error ? error.stack : undefined;
|
||||||
this.logger.error(`Error during cleanup of abandoned orders: ${errorMessage}`, errorStack);
|
// this.logger.error(`Error during cleanup of abandoned orders: ${errorMessage}`, errorStack);
|
||||||
throw error;
|
// throw error;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { PaymentMethodEnum, PaymentGatewayEnum } from '../../modules/payments/interface/payment';
|
import { PaymentMethodEnum, PaymentGatewayEnum } from '../../modules/payments/interface/payment';
|
||||||
|
|
||||||
export interface PaymentMethodData {
|
export interface PaymentMethodData {
|
||||||
title: string;
|
|
||||||
method: PaymentMethodEnum;
|
method: PaymentMethodEnum;
|
||||||
description: string;
|
description: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
@@ -12,15 +11,14 @@ export interface PaymentMethodData {
|
|||||||
|
|
||||||
export const paymentMethodsData: PaymentMethodData[] = [
|
export const paymentMethodsData: PaymentMethodData[] = [
|
||||||
{
|
{
|
||||||
title: 'پرداخت در محل',
|
method: PaymentMethodEnum.Online,
|
||||||
method: PaymentMethodEnum.CardOnDelivery,
|
description: 'درگاه پرداخت زرینپال',
|
||||||
description: 'پرداخت در محل تحویل',
|
|
||||||
enabled: true,
|
enabled: true,
|
||||||
order: 1,
|
order: 1,
|
||||||
gateway: null,
|
gateway: PaymentGatewayEnum.ZarinPal,
|
||||||
|
merchantId: 'b6f55bd0-6eae-4045-aeb8-07d084fa8f73',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'نقدی',
|
|
||||||
method: PaymentMethodEnum.Cash,
|
method: PaymentMethodEnum.Cash,
|
||||||
description: 'پرداخت نقدی',
|
description: 'پرداخت نقدی',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -28,20 +26,10 @@ export const paymentMethodsData: PaymentMethodData[] = [
|
|||||||
gateway: null,
|
gateway: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'زرینپال',
|
|
||||||
method: PaymentMethodEnum.Online,
|
|
||||||
description: 'درگاه پرداخت زرینپال',
|
|
||||||
enabled: true,
|
|
||||||
order: 3,
|
|
||||||
gateway: PaymentGatewayEnum.ZarinPal,
|
|
||||||
merchantId: 'b6f55bd0-6eae-4045-aeb8-07d084fa8f73',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'کیف پول',
|
|
||||||
method: PaymentMethodEnum.Wallet,
|
method: PaymentMethodEnum.Wallet,
|
||||||
description: 'پرداخت از کیف پول',
|
description: 'پرداخت از کیف پول',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
order: 4,
|
order: 3,
|
||||||
gateway: null,
|
gateway: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ export class FoodsSeeder {
|
|||||||
restaurant,
|
restaurant,
|
||||||
category,
|
category,
|
||||||
isActive: foodData.isActive,
|
isActive: foodData.isActive,
|
||||||
stock: foodData.stock,
|
|
||||||
stockDefault: foodData.stockDefault,
|
|
||||||
sat: false,
|
sat: false,
|
||||||
sun: false,
|
sun: false,
|
||||||
mon: false,
|
mon: false,
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export class PaymentMethodsSeeder {
|
|||||||
if (!existing) {
|
if (!existing) {
|
||||||
const paymentMethod = em.create(PaymentMethod, {
|
const paymentMethod = em.create(PaymentMethod, {
|
||||||
restaurant,
|
restaurant,
|
||||||
title: methodData.title,
|
|
||||||
method: methodData.method,
|
method: methodData.method,
|
||||||
description: methodData.description,
|
description: methodData.description,
|
||||||
enabled: methodData.enabled,
|
enabled: methodData.enabled,
|
||||||
@@ -29,12 +28,10 @@ export class PaymentMethodsSeeder {
|
|||||||
} else {
|
} else {
|
||||||
// Update existing payment method if needed
|
// Update existing payment method if needed
|
||||||
if (
|
if (
|
||||||
existing.title !== methodData.title ||
|
|
||||||
existing.description !== methodData.description ||
|
existing.description !== methodData.description ||
|
||||||
existing.enabled !== methodData.enabled ||
|
existing.enabled !== methodData.enabled ||
|
||||||
existing.order !== methodData.order
|
existing.order !== methodData.order
|
||||||
) {
|
) {
|
||||||
existing.title = methodData.title;
|
|
||||||
existing.description = methodData.description;
|
existing.description = methodData.description;
|
||||||
existing.enabled = methodData.enabled;
|
existing.enabled = methodData.enabled;
|
||||||
existing.order = methodData.order;
|
existing.order = methodData.order;
|
||||||
|
|||||||
Reference in New Issue
Block a user