From 7e344fd695583ed0f3710e0aa2dbe0e65d75dc67 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 27 Sep 2025 23:32:27 +0330 Subject: [PATCH] feat(order): add order detail --- .../admin/controllers/order.controller.ts | 20 ++ src/modules/order/order.repository.ts | 198 ++++++++++++++++++ src/modules/order/order.service.ts | 47 +++++ 3 files changed, 265 insertions(+) diff --git a/src/modules/admin/controllers/order.controller.ts b/src/modules/admin/controllers/order.controller.ts index 7b45e01..b0ef5dc 100644 --- a/src/modules/admin/controllers/order.controller.ts +++ b/src/modules/admin/controllers/order.controller.ts @@ -228,4 +228,24 @@ export class AdminOrderController extends BaseController { const { pager } = this.paginate(data.count); return this.response({ pager, orders: data.orders, priceRange }); } + + @ApiOperation("get an order details of user") + @ApiResponse("successful", HttpStatus.Ok) + @ApiParam("orderId", "id of order", true) + @ApiAuth() + @httpGet("/:orderId/user", Guard.authAdmin(), Guard.checkAdminPermissions([PermissionEnum.ORDER])) + public async getUserOrderDetail(@requestParam("orderId") orderId: string) { + const data = await this.orderService.getAdminUserOrderDetail(+orderId); + return this.response(data); + } + + @ApiOperation("get an order details of seller") + @ApiResponse("successful", HttpStatus.Ok) + @ApiParam("orderId", "id of order", true) + @ApiAuth() + @httpGet("/:orderId/seller", Guard.authAdmin(), Guard.checkAdminPermissions([PermissionEnum.ORDER])) + public async getSellerOrderDetail(@requestParam("orderId") orderId: string) { + const data = await this.orderService.getAdminSellerOrderDetail(+orderId); + return this.response(data); + } } diff --git a/src/modules/order/order.repository.ts b/src/modules/order/order.repository.ts index b62606b..91a95f8 100644 --- a/src/modules/order/order.repository.ts +++ b/src/modules/order/order.repository.ts @@ -1641,6 +1641,204 @@ class OrderItemRepo extends BaseRepository { ]); } + async getSellerOrderDetailForAdminPanel(orderId: number, shopIds: Types.ObjectId[]) { + return this.model.aggregate([ + { + $match: { + order: orderId, + shop: { $in: shopIds }, + }, + }, + { + $lookup: { + from: "orders", + localField: "order", + foreignField: "_id", + as: "order", + }, + }, + { + $unwind: "$order", + }, + { + $lookup: { + from: "cartpayments", + localField: "order.payment", + foreignField: "_id", + as: "order.payment", + }, + }, + { + $unwind: "$order.payment", + }, + { + $lookup: { + from: "paymentmethods", + localField: "order.payment.payment_method", + foreignField: "_id", + as: "order.payment.payment_method", + }, + }, + { + $unwind: "$order.payment.payment_method", + }, + { + $lookup: { + from: "users", + localField: "order.user", + foreignField: "_id", + as: "order.user", + }, + }, + { + $unwind: "$order.user", + }, + { + $unwind: "$shipmentItems", + }, + { + $lookup: { + from: "products", + localField: "shipmentItems.product", + foreignField: "_id", + as: "shipmentItems.product", + }, + }, + { + $unwind: "$shipmentItems.product", + }, + { + $lookup: { + from: "productvariants", + localField: "shipmentItems.variant", + foreignField: "_id", + as: "shipmentItems.variant", + }, + }, + { + $unwind: "$shipmentItems.variant", + }, + { + $lookup: { + from: "warranties", + localField: "shipmentItems.variant.warranty", + foreignField: "_id", + as: "shipmentItems.variant.warranty", + }, + }, + { + $unwind: { + path: "$shipmentItems.variant.warranty", + preserveNullAndEmptyArrays: true, + }, + }, + { + $lookup: { + from: "colors", + localField: "shipmentItems.variant.color", + foreignField: "_id", + as: "shipmentItems.variant.color", + }, + }, + { + $unwind: { + path: "$shipmentItems.variant.color", + preserveNullAndEmptyArrays: true, + }, + }, + { + $lookup: { + from: "sizes", + localField: "shipmentItems.variant.size", + foreignField: "_id", + as: "shipmentItems.variant.size", + }, + }, + { + $unwind: { + path: "$shipmentItems.variant.size", + preserveNullAndEmptyArrays: true, + }, + }, + { + $lookup: { + from: "meterages", + localField: "shipmentItems.variant.meterage", + foreignField: "_id", + as: "shipmentItems.variant.meterage", + }, + }, + { + $unwind: { + path: "$shipmentItems.variant.meterage", + preserveNullAndEmptyArrays: true, + }, + }, + { + $lookup: { + from: "shipments", + localField: "shipper", + foreignField: "_id", + as: "shipper", + }, + }, + { + $unwind: "$shipper", + }, + { + $group: { + _id: "$order._id", + user: { $first: "$order.user" }, + payment: { $first: "$order.payment" }, + shipmentAddress: { $first: "$order.shipmentAddress" }, + orderStatus: { $first: "$order.orderStatus" }, + createdAt: { $first: "$order.createdAt" }, + orderItems: { + $first: { + _id: "$_id", + order_id: "$order._id", + shop: "$shop", + shipper: "$shipper", + totalSellingPrice: "$totalSellingPrice", + totalRetailPrice: "$totalRetailPrice", + totalShipmentCost: "$totalShipmentCost", + totalPaymentPrice: "$totalPaymentPrice", + itemsCount: "$itemsCount", + trackCode: "$trackCode", + postingDate: "$postingDate", + postingReceipt: "$postingReceipt", + status: "$status", + }, + }, + shipmentItems: { + $push: { + _id: "$shipmentItems._id", + product: "$shipmentItems.product", + variant: "$shipmentItems.variant", + quantity: "$shipmentItems.quantity", + cancelled_quantity: "$shipmentItems.cancelled_quantity", + returned_quantity: "$shipmentItems.returned_quantity", + selling_price: "$shipmentItems.selling_price", + retail_price: "$shipmentItems.retail_price", + discount_percent: "$shipmentItems.discount_percent", + shipmentCost: "$shipmentItems.shipmentCost", + }, + }, + // totalSellingPrice: { $sum: { $multiply: ["$shipmentItems.selling_price", "$shipmentItems.quantity"] } }, + // totalRetailPrice: { $sum: { $multiply: ["$shipmentItems.retail_price", "$shipmentItems.quantity"] } }, + // totalShipmentCost: { $sum: "$shipmentItems.shipmentCost" }, + // itemsCount: { $sum: "$shipmentItems.quantity" }, + }, + }, + { + $addFields: { + "orderItems.shipmentItems": "$shipmentItems", + // totalPaymentPrice: { $add: ["$totalSellingPrice", "$totalShipmentCost"] }, + }, + }, + ]); + } + async getSellerSalesStats(shopId: string, queries: { start?: string; end?: string }) { // Parse the date range from queries, converting from Persian to Gregorian if necessary const startDate = queries.start ? new Date(TimeService.convertPersianToGregorian(queries.start)) : null; diff --git a/src/modules/order/order.service.ts b/src/modules/order/order.service.ts index 015aa2a..c1e4972 100644 --- a/src/modules/order/order.service.ts +++ b/src/modules/order/order.service.ts @@ -322,6 +322,29 @@ class OrderService { } //####################################################### //####################################################### + + async getAdminUserOrderDetail(orderId: number) { + const { order } = await this.validateAdminUserOrder(orderId); + + const doc = await this.orderRepo.getUserOrderDetail(orderId, order.user._id.toString()); + const cleanedOrder = SellerOrdersDTO.transformOrder(doc[0]); + + return { order: cleanedOrder }; + } + //####################################################### + //####################################################### + + async getAdminSellerOrderDetail(orderId: number) { + const { shopIds } = await this.validateAdminSellerOrder(orderId); + + const doc = await this.orderItemRepo.getSellerOrderDetailForAdminPanel(orderId, shopIds); + + const cleanedOrder = SellerOrdersDTO.transformOrder(doc[0]); + + return { order: cleanedOrder }; + } + //####################################################### + //####################################################### async getSellerSalesStats(sellerId: string, queries: { start: string; end: string }) { const shop = await this.shopRepo.model.findOne({ owner: sellerId }); if (!shop) throw new BadRequestError(ShopMessage.ShopNotFound); @@ -365,6 +388,30 @@ class OrderService { return { order, orderItems }; } + //####################################################### + //####################################################### + private async validateAdminUserOrder(orderId: number) { + const order = await this.orderRepo.model.findOne({ _id: orderId }); + if (!order) throw new BadRequestError(OrderMessage.NotFound); + + const orderItems = await this.orderItemRepo.model.find({ order: order._id }); + if (!orderItems || orderItems.length === 0) throw new BadRequestError(OrderMessage.OrderItemsNotFound); + + return { order, orderItems }; + } + //####################################################### + //####################################################### + private async validateAdminSellerOrder(orderId: number) { + const order = await this.orderRepo.model.findOne({ _id: orderId }).lean(); + if (!order) throw new BadRequestError(OrderMessage.NotFound); + + const orderItems = await this.orderItemRepo.model.find({ order: orderId }).lean(); + const shopIds = orderItems.map((item) => item.shop._id); + + const orderItem = await this.orderItemRepo.model.findOne({ shop: { in: shopIds }, order: order._id }); + if (!orderItem) throw new BadRequestError(OrderMessage.OrderItemsNotFound); + return { order, orderItem, shopIds }; + } //####################################### //#######################################