first
This commit is contained in:
@@ -0,0 +1,363 @@
|
||||
import { inject, injectable } from "inversify";
|
||||
import { isValidObjectId, startSession } from "mongoose";
|
||||
|
||||
import { CancelOrderItemRepo, CancelOrderRepo, CancelReasonRepo } from "./cancel.repository";
|
||||
import { CancelOrderDTO, CreateCancelDTO } from "./DTO/cancel.dto";
|
||||
import { CancelReasonDTO } from "./DTO/cancelReason.dto";
|
||||
import { CommonMessage } from "../../common/enums/message.enum";
|
||||
import { OrderItemsStatus } from "../../common/enums/order.enum";
|
||||
import { AdminCancelsQueries } from "../../common/types/query.type";
|
||||
import { BadRequestError } from "../../core/app/app.errors";
|
||||
import { IOCTYPES } from "../../IOC/ioc.types";
|
||||
import { OrderItemRefEnum, ReasonRefEnum } from "../fine/models/fine.model";
|
||||
import { FineRepo } from "../fine/repository/fine.repository";
|
||||
import { OrderItemRepo } from "../order/order.repository";
|
||||
import { CancellerRefEnum } from "./models/cancel.model";
|
||||
import { FineRuleRepo } from "../fine/repository/fineRule.repository";
|
||||
import { NotificationService } from "../notification/notification.service";
|
||||
|
||||
@injectable()
|
||||
class CancelService {
|
||||
@inject(IOCTYPES.OrderItemRepo) private orderItemRepo: OrderItemRepo;
|
||||
@inject(IOCTYPES.CancelOrderRepo) private cancelOrderRepo: CancelOrderRepo;
|
||||
@inject(IOCTYPES.CancelOrderItemRepo) private cancelOrderItemRepo: CancelOrderItemRepo;
|
||||
@inject(IOCTYPES.CancelReasonRepo) private cancelReasonRepo: CancelReasonRepo;
|
||||
@inject(IOCTYPES.FineRuleRepo) private fineRuleRepo: FineRuleRepo;
|
||||
@inject(IOCTYPES.FineRepo) private fineRepo: FineRepo;
|
||||
@inject(IOCTYPES.NotificationService) notificationService: NotificationService;
|
||||
|
||||
//#######################################################
|
||||
|
||||
async getCancelOrders(userId: string) {
|
||||
const cancelOrders = await this.cancelOrderRepo.model.find({ canceller: userId }).sort({ createdAt: -1 });
|
||||
return {
|
||||
cancelOrders,
|
||||
};
|
||||
}
|
||||
|
||||
async getAllUserCancelOrders(userId: string) {
|
||||
const cancelOrders = await this.cancelOrderRepo.getAllCancelOrdersForUser(userId);
|
||||
return {
|
||||
cancelOrders,
|
||||
};
|
||||
}
|
||||
|
||||
//#######################################################
|
||||
async getAllCancelOrders(queries: AdminCancelsQueries) {
|
||||
const { docs, count } = await this.cancelOrderRepo.getAllCancelsForAdmin(queries);
|
||||
const cancelsOrders = docs.map((doc: any) => CancelOrderDTO.transformCancelOrder(doc));
|
||||
return {
|
||||
cancelsOrders,
|
||||
count,
|
||||
};
|
||||
}
|
||||
|
||||
//#######################################################
|
||||
//#######################################################
|
||||
async createCancelOrderByUser(userId: string, createCancelDto: CreateCancelDTO) {
|
||||
const session = await startSession();
|
||||
session.startTransaction();
|
||||
try {
|
||||
// create Cancel Order
|
||||
let total_cancel_price = 0;
|
||||
const cancelOrder = await this.cancelOrderRepo.model.create(
|
||||
[
|
||||
{
|
||||
cancellerRef: CancellerRefEnum.User,
|
||||
canceller: userId,
|
||||
order: createCancelDto.orderId,
|
||||
status: OrderItemsStatus.cancelled_user,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
const cancelOrderDoc = cancelOrder[0];
|
||||
|
||||
// Create cancelOrderItems
|
||||
for (const item of createCancelDto.items) {
|
||||
const orderItem = await this.orderItemRepo.model.findById(item.orderItemId).session(session);
|
||||
if (!orderItem) {
|
||||
throw new BadRequestError(`OrderItem with ID ${item.orderItemId} not found.`);
|
||||
}
|
||||
if (orderItem.status === OrderItemsStatus.Shipped || orderItem.status === OrderItemsStatus.Delivered) {
|
||||
throw new BadRequestError(`OrderItem with ID ${item.orderItemId} cannot cancel in this position`);
|
||||
}
|
||||
const shipmentItem = orderItem.shipmentItems.find((shipItem) => shipItem._id?.equals(item.shipmentItemId));
|
||||
if (!shipmentItem) {
|
||||
throw new BadRequestError(`ShipmentItem with ID ${item.shipmentItemId} not found in OrderItem ${item.orderItemId}.`);
|
||||
}
|
||||
|
||||
if (item.quantity > shipmentItem.quantity - shipmentItem.cancelled_quantity! - shipmentItem.returned_quantity!) {
|
||||
throw new BadRequestError(`cancel quantity for ShipmentItem ${item.shipmentItemId} exceeds the available quantity.`);
|
||||
}
|
||||
|
||||
total_cancel_price += item.quantity * shipmentItem.selling_price;
|
||||
|
||||
await this.cancelOrderItemRepo.model.create(
|
||||
[
|
||||
{
|
||||
cancelOrderId: cancelOrderDoc._id,
|
||||
orderItem: item.orderItemId,
|
||||
shipmentItem: item.shipmentItemId,
|
||||
quantity: item.quantity,
|
||||
comment: item.comment,
|
||||
reason: item.reason,
|
||||
status: OrderItemsStatus.cancelled_user,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
shipmentItem.cancelled_quantity = item.quantity;
|
||||
orderItem.status = OrderItemsStatus.cancelled_user;
|
||||
orderItem.save({ session });
|
||||
}
|
||||
cancelOrderDoc.total_price = total_cancel_price;
|
||||
await cancelOrderDoc.save({ session });
|
||||
|
||||
await session.commitTransaction();
|
||||
await session.endSession();
|
||||
|
||||
return { cancelOrder: cancelOrder[0] };
|
||||
} catch (error) {
|
||||
await session.abortTransaction();
|
||||
await session.endSession();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################################
|
||||
//#######################################################
|
||||
|
||||
async createCancelOrderBySeller(sellerId: string, createCancelDto: CreateCancelDTO) {
|
||||
const session = await startSession();
|
||||
session.startTransaction();
|
||||
try {
|
||||
// create Cancel Order
|
||||
let total_cancel_price = 0;
|
||||
const cancelOrder = await this.cancelOrderRepo.model.create(
|
||||
[
|
||||
{
|
||||
cancellerRef: CancellerRefEnum.Seller,
|
||||
canceller: sellerId,
|
||||
order: createCancelDto.orderId,
|
||||
status: OrderItemsStatus.cancelled_shop,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
const cancelOrderDoc = cancelOrder[0];
|
||||
|
||||
// Create cancelOrderItems
|
||||
for (const item of createCancelDto.items) {
|
||||
const orderItem = await this.orderItemRepo.model.findById(item.orderItemId).session(session);
|
||||
if (!orderItem) throw new BadRequestError(`آیتم سفارش با شناسه ${item.orderItemId} یافت نشد.`);
|
||||
|
||||
if (orderItem.status === OrderItemsStatus.Shipped || orderItem.status === OrderItemsStatus.Delivered)
|
||||
throw new BadRequestError(`آیتم سفارش با شناسه ${item.orderItemId} در این وضعیت قابل لغو نیست.`);
|
||||
|
||||
const shipmentItem = orderItem.shipmentItems.find((shipItem) => shipItem._id?.equals(item.shipmentItemId));
|
||||
if (!shipmentItem)
|
||||
throw new BadRequestError(`آیتم حمل با شناسه ${item.shipmentItemId} در آیتم سفارش ${item.orderItemId} یافت نشد.`);
|
||||
|
||||
const cancelReason = await this.cancelReasonRepo.model.findById(item.reason);
|
||||
if (!cancelReason) {
|
||||
throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
}
|
||||
|
||||
const cancelledQuantity = shipmentItem.cancelled_quantity || 0;
|
||||
const returnedQuantity = shipmentItem.returned_quantity || 0;
|
||||
if (item.quantity > shipmentItem.quantity - cancelledQuantity - returnedQuantity) {
|
||||
throw new BadRequestError(`Return quantity for ShipmentItem ${item.shipmentItemId} exceeds the available quantity.`);
|
||||
}
|
||||
|
||||
total_cancel_price += item.quantity * shipmentItem.selling_price;
|
||||
|
||||
const cancelOrderItem = await this.cancelOrderItemRepo.model.create(
|
||||
[
|
||||
{
|
||||
cancelOrderId: cancelOrderDoc._id,
|
||||
orderItem: item.orderItemId,
|
||||
shipmentItem: item.shipmentItemId,
|
||||
quantity: item.quantity,
|
||||
comment: item.comment,
|
||||
reason: item.reason,
|
||||
status: OrderItemsStatus.cancelled_shop,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
const fineRule = await this.fineRuleRepo.findById(cancelReason.fineRule.toString());
|
||||
if (fineRule) {
|
||||
const fineAmount = (fineRule.fine_percentage * total_cancel_price) / 100;
|
||||
await this.fineRepo.model.create(
|
||||
[
|
||||
{
|
||||
seller: sellerId,
|
||||
orderItem: cancelOrderItem[0]._id,
|
||||
orderItemRef: OrderItemRefEnum.CancelOrderItem,
|
||||
fine_rule: fineRule._id,
|
||||
reason: item.reason,
|
||||
reasonRef: ReasonRefEnum.Cancel,
|
||||
fine_amount: fineAmount,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
await this.notificationService.notifyFine(sellerId, fineAmount, session);
|
||||
//TODO : decrease balance from seller wallet
|
||||
}
|
||||
|
||||
shipmentItem.cancelled_quantity! += item.quantity;
|
||||
if (shipmentItem.cancelled_quantity === shipmentItem.quantity) orderItem.status = OrderItemsStatus.cancelled_shop;
|
||||
|
||||
await orderItem.save({ session });
|
||||
}
|
||||
|
||||
cancelOrderDoc.total_price = total_cancel_price;
|
||||
await cancelOrderDoc.save({ session });
|
||||
|
||||
await session.commitTransaction();
|
||||
await session.endSession();
|
||||
|
||||
return { cancelOrder: cancelOrder[0] };
|
||||
} catch (error) {
|
||||
await session.abortTransaction();
|
||||
await session.endSession();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################################
|
||||
//#######################################################
|
||||
|
||||
async createCancelOrderByAdmin(adminId: string, createCancelDto: CreateCancelDTO) {
|
||||
const session = await startSession();
|
||||
session.startTransaction();
|
||||
try {
|
||||
// create Cancel Order
|
||||
let total_cancel_price = 0;
|
||||
const cancelOrder = await this.cancelOrderRepo.model.create(
|
||||
[
|
||||
{
|
||||
cancellerRef: CancellerRefEnum.Admin,
|
||||
canceller: adminId,
|
||||
order: createCancelDto.orderId,
|
||||
status: OrderItemsStatus.cancelled_system,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
const cancelOrderDoc = cancelOrder[0];
|
||||
|
||||
// Create cancelOrderItems
|
||||
for (const item of createCancelDto.items) {
|
||||
const orderItem = await this.orderItemRepo.model.findById(item.orderItemId).session(session);
|
||||
if (!orderItem) throw new BadRequestError(`آیتم سفارش با شناسه ${item.orderItemId} یافت نشد.`);
|
||||
|
||||
if (orderItem.status === OrderItemsStatus.Shipped || orderItem.status === OrderItemsStatus.Delivered)
|
||||
throw new BadRequestError(`آیتم سفارش با شناسه ${item.orderItemId} در این وضعیت قابل لغو نیست.`);
|
||||
|
||||
const shipmentItem = orderItem.shipmentItems.find((shipItem) => shipItem._id?.equals(item.shipmentItemId));
|
||||
if (!shipmentItem)
|
||||
throw new BadRequestError(`آیتم حمل با شناسه ${item.shipmentItemId} در آیتم سفارش ${item.orderItemId} یافت نشد.`);
|
||||
|
||||
const cancelReason = await this.cancelReasonRepo.model.findById(item.reason);
|
||||
if (!cancelReason) {
|
||||
throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
}
|
||||
|
||||
const cancelledQuantity = shipmentItem.cancelled_quantity || 0;
|
||||
const returnedQuantity = shipmentItem.returned_quantity || 0;
|
||||
if (item.quantity > shipmentItem.quantity - cancelledQuantity - returnedQuantity) {
|
||||
throw new BadRequestError(`Return quantity for ShipmentItem ${item.shipmentItemId} exceeds the available quantity.`);
|
||||
}
|
||||
|
||||
total_cancel_price += item.quantity * shipmentItem.selling_price;
|
||||
|
||||
await this.cancelOrderItemRepo.model.create(
|
||||
[
|
||||
{
|
||||
cancelOrderId: cancelOrderDoc._id,
|
||||
orderItem: item.orderItemId,
|
||||
shipmentItem: item.shipmentItemId,
|
||||
quantity: item.quantity,
|
||||
comment: item.comment,
|
||||
reason: item.reason,
|
||||
status: OrderItemsStatus.cancelled_shop,
|
||||
},
|
||||
],
|
||||
{ session },
|
||||
);
|
||||
|
||||
shipmentItem.cancelled_quantity! += item.quantity;
|
||||
if (shipmentItem.cancelled_quantity === shipmentItem.quantity) orderItem.status = OrderItemsStatus.cancelled_shop;
|
||||
|
||||
await orderItem.save({ session });
|
||||
}
|
||||
|
||||
cancelOrderDoc.total_price = total_cancel_price;
|
||||
await cancelOrderDoc.save({ session });
|
||||
|
||||
await session.commitTransaction();
|
||||
await session.endSession();
|
||||
|
||||
return { cancelOrder: cancelOrder[0] };
|
||||
} catch (error) {
|
||||
await session.abortTransaction();
|
||||
await session.endSession();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//#######################################################
|
||||
//#######################################################
|
||||
async getCancelReasons() {
|
||||
const reasons = await this.cancelReasonRepo.model.find({ deleted: false }).populate("fineRule");
|
||||
return { reasons };
|
||||
}
|
||||
//#######################################################
|
||||
//#######################################################
|
||||
async createCancelReason(cancelReasonDto: CancelReasonDTO) {
|
||||
const fineRule = await this.fineRuleRepo.model.findById(cancelReasonDto.fineRule);
|
||||
if (!fineRule) {
|
||||
throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
}
|
||||
const newReason = await this.cancelReasonRepo.model.create({
|
||||
title: cancelReasonDto.title,
|
||||
fineRule: fineRule._id,
|
||||
});
|
||||
return {
|
||||
message: CommonMessage.Created,
|
||||
newReason,
|
||||
};
|
||||
}
|
||||
|
||||
async deleteCancelReason(reasonId: string) {
|
||||
if (!isValidObjectId(reasonId)) throw new BadRequestError(CommonMessage.NotValidId);
|
||||
await this.cancelReasonRepo.model.findByIdAndUpdate(reasonId, { deleted: true }, { new: true });
|
||||
return {
|
||||
message: CommonMessage.Deleted,
|
||||
};
|
||||
}
|
||||
//#######################################################
|
||||
//#######################################################
|
||||
// async updateReturnReason(reasonId: string, updateReasonDto: UpdateReasonDTO) {
|
||||
// if (!isValidObjectId(reasonId)) throw new BadRequestError(CommonMessage.NotValidId);
|
||||
|
||||
// const updatedReason = await this.returnReasonRep.model.findByIdAndUpdate(reasonId, updateReasonDto, { new: true });
|
||||
// if (!updatedReason) throw new BadRequestError(CommonMessage.NotFoundById);
|
||||
// return {
|
||||
// message: CommonMessage.Updated,
|
||||
// updatedReason,
|
||||
// };
|
||||
// }
|
||||
//#######################################################
|
||||
//#######################################################
|
||||
}
|
||||
|
||||
export { CancelService };
|
||||
Reference in New Issue
Block a user