update status
This commit is contained in:
@@ -32,21 +32,21 @@ type ValidatedCartForOrder = {
|
||||
orderItemsData: OrderItemData[];
|
||||
};
|
||||
|
||||
export type StatusTransitionRef = 'user' | 'admin';
|
||||
|
||||
@Injectable()
|
||||
export class OrdersService {
|
||||
private readonly logger = new Logger(OrdersService.name);
|
||||
|
||||
private static readonly STATUS_TRANSITIONS: Record<OrderStatus, readonly OrderStatus[]> = {
|
||||
[OrderStatus.PENDING_PAYMENT]: [OrderStatus.PAID, OrderStatus.FAILED, OrderStatus.CANCELED, OrderStatus.CONFIRMED],
|
||||
[OrderStatus.PAID]: [OrderStatus.CONFIRMED, OrderStatus.REFUNDED],
|
||||
[OrderStatus.PENDING_PAYMENT]: [OrderStatus.PAID, OrderStatus.CANCELED, OrderStatus.CONFIRMED],
|
||||
[OrderStatus.PAID]: [OrderStatus.CONFIRMED, OrderStatus.CANCELED],
|
||||
[OrderStatus.CONFIRMED]: [OrderStatus.PREPARING, OrderStatus.CANCELED],
|
||||
[OrderStatus.PREPARING]: [OrderStatus.READY, OrderStatus.SHIPPED, OrderStatus.CANCELED],
|
||||
[OrderStatus.READY]: [OrderStatus.COMPLETED, OrderStatus.FAILED],
|
||||
[OrderStatus.SHIPPED]: [OrderStatus.COMPLETED, OrderStatus.FAILED],
|
||||
[OrderStatus.COMPLETED]: [],
|
||||
[OrderStatus.READY]: [OrderStatus.COMPLETED, OrderStatus.CANCELED],
|
||||
[OrderStatus.SHIPPED]: [OrderStatus.COMPLETED, OrderStatus.CANCELED],
|
||||
[OrderStatus.COMPLETED]: [OrderStatus.CANCELED],
|
||||
[OrderStatus.CANCELED]: [],
|
||||
[OrderStatus.FAILED]: [],
|
||||
[OrderStatus.REFUNDED]: [],
|
||||
};
|
||||
|
||||
constructor(
|
||||
@@ -80,6 +80,7 @@ export class OrdersService {
|
||||
description: cart.description,
|
||||
tableNumber: cart.tableNumber,
|
||||
status: OrderStatus.PENDING_PAYMENT,
|
||||
history: [{ status: OrderStatus.PENDING_PAYMENT, changedAt: new Date() }],
|
||||
});
|
||||
|
||||
em.persist(order);
|
||||
@@ -220,69 +221,84 @@ export class OrdersService {
|
||||
return order;
|
||||
}
|
||||
|
||||
async confirmOrder(orderId: string, restId: string) {
|
||||
return this.changeOrderStatus(orderId, restId, OrderStatus.CONFIRMED);
|
||||
}
|
||||
// async confirmOrder(orderId: string, restId: string) {
|
||||
// return this.changeOrderStatus(orderId, restId, OrderStatus.CONFIRMED);
|
||||
// }
|
||||
|
||||
async prepareOrder(orderId: string, restId: string) {
|
||||
return this.changeOrderStatus(orderId, restId, OrderStatus.PREPARING);
|
||||
}
|
||||
// async prepareOrder(orderId: string, restId: string) {
|
||||
// return this.changeOrderStatus(orderId, restId, OrderStatus.PREPARING);
|
||||
// }
|
||||
|
||||
// just admin can reject the order any time
|
||||
async rejectOrder(orderId: string, restId: string) {
|
||||
return this.changeOrderStatus(orderId, restId, OrderStatus.CANCELED, { skipTransitionValidation: true });
|
||||
}
|
||||
// async rejectOrder(orderId: string, restId: string) {
|
||||
// return this.changeOrderStatus(orderId, restId, OrderStatus.CANCELED, { skipTransitionValidation: true });
|
||||
// }
|
||||
|
||||
async readyForDelivery(orderId: string, restId: string) {
|
||||
return this.changeOrderStatus(orderId, restId, OrderStatus.READY);
|
||||
}
|
||||
// async readyForDelivery(orderId: string, restId: string) {
|
||||
// return this.changeOrderStatus(orderId, restId, OrderStatus.READY);
|
||||
// }
|
||||
|
||||
async cancelOrderAsUser(orderId: string, restId: string) {
|
||||
return this.changeOrderStatus(orderId, restId, OrderStatus.CANCELED);
|
||||
}
|
||||
// async cancelOrderAsUser(orderId: string, restId: string) {
|
||||
// return this.changeOrderStatus(orderId, restId, OrderStatus.CANCELED);
|
||||
// }
|
||||
|
||||
async markAsDelivered(orderId: string, restId: string) {
|
||||
return this.changeOrderStatus(orderId, restId, OrderStatus.COMPLETED);
|
||||
}
|
||||
// async markAsDelivered(orderId: string, restId: string) {
|
||||
// return this.changeOrderStatus(orderId, restId, OrderStatus.COMPLETED);
|
||||
// }
|
||||
|
||||
async updateStatus(orderId: string, status: OrderStatus, restId: string) {
|
||||
return this.changeOrderStatus(orderId, restId, status);
|
||||
}
|
||||
// async updateStatus(orderId: string, restId: string, status: OrderStatus, ref: StatusTransitionRef) {
|
||||
// return this.changeOrderStatus(orderId, restId, status, ref);
|
||||
// }
|
||||
|
||||
private async changeOrderStatus(
|
||||
async changeOrderStatus(
|
||||
orderId: string,
|
||||
restId: string,
|
||||
toStatus: OrderStatus,
|
||||
options?: { skipTransitionValidation?: boolean },
|
||||
ref: StatusTransitionRef,
|
||||
): Promise<Order> {
|
||||
const order = await this.getOrderOrFail(orderId, restId);
|
||||
|
||||
if (!options?.skipTransitionValidation) {
|
||||
this.assertStatusTransitionAllowed(order, toStatus);
|
||||
}
|
||||
this.assertStatusTransitionAllowed(order, toStatus, ref);
|
||||
|
||||
order.status = toStatus;
|
||||
await this.em.persistAndFlush(order);
|
||||
return order;
|
||||
}
|
||||
|
||||
private assertStatusTransitionAllowed(order: Order, to: OrderStatus) {
|
||||
private assertStatusTransitionAllowed(order: Order, to: OrderStatus, ref: StatusTransitionRef) {
|
||||
const paymentMethod = order.paymentMethod?.method;
|
||||
if (!paymentMethod) {
|
||||
throw new BadRequestException('Order payment method is missing');
|
||||
}
|
||||
|
||||
if (!this.canTransition(order.status, to, paymentMethod)) {
|
||||
if (!this.canTransition(order.status, to, paymentMethod, ref)) {
|
||||
throw new BadRequestException(`Invalid status transition: ${order.status} -> ${to}`);
|
||||
}
|
||||
}
|
||||
|
||||
private canTransition(from: OrderStatus, to: OrderStatus, paymentMethod: PaymentMethodEnum) {
|
||||
private canTransition(from: OrderStatus, to: OrderStatus, paymentMethod: PaymentMethodEnum, ref: 'user' | 'admin') {
|
||||
if (!OrdersService.STATUS_TRANSITIONS[from]?.includes(to)) return false;
|
||||
|
||||
if (paymentMethod === PaymentMethodEnum.Cash) {
|
||||
if ([OrderStatus.PENDING_PAYMENT, OrderStatus.PAID].includes(to)) return false;
|
||||
if (to === OrderStatus.CANCELED) {
|
||||
// only allow orders with status of PENDING_PAYMENT and PAID are allowed to be canceled by user
|
||||
if (ref === 'user' && ![OrderStatus.PENDING_PAYMENT, OrderStatus.PAID, OrderStatus.CONFIRMED].includes(from)) {
|
||||
return false;
|
||||
} else if (ref === 'admin') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// only allow orders with status of PENDING_PAYMENT and payment
|
||||
// method of cash are allowed to move to CONFIRMED directly
|
||||
if (
|
||||
from == OrderStatus.PENDING_PAYMENT &&
|
||||
to == OrderStatus.CONFIRMED &&
|
||||
paymentMethod !== PaymentMethodEnum.Cash
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// if (paymentMethod === PaymentMethodEnum.Cash) {
|
||||
// if ([OrderStatus.PENDING_PAYMENT, OrderStatus.PAID].includes(to)) return false;
|
||||
// }
|
||||
|
||||
if (paymentMethod === PaymentMethodEnum.Online) {
|
||||
if (to === OrderStatus.CONFIRMED && from !== OrderStatus.PAID) return false;
|
||||
|
||||
Reference in New Issue
Block a user