From 7e344fd695583ed0f3710e0aa2dbe0e65d75dc67 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sat, 27 Sep 2025 23:32:27 +0330 Subject: [PATCH 1/5] 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 }; + } //####################################### //####################################### From 521533dd046d6ea939692546a08209002d943d04 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 28 Sep 2025 11:20:03 +0330 Subject: [PATCH 2/5] feat: add get brand detail --- src/modules/admin/controllers/brand.controller.ts | 12 +++++++++++- src/modules/brand/brand.service.ts | 9 +++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/modules/admin/controllers/brand.controller.ts b/src/modules/admin/controllers/brand.controller.ts index 719a73a..91fc884 100644 --- a/src/modules/admin/controllers/brand.controller.ts +++ b/src/modules/admin/controllers/brand.controller.ts @@ -1,6 +1,6 @@ import rateLimit from "express-rate-limit"; import { inject } from "inversify"; -import { controller, httpDelete, httpPatch, httpPost, requestBody, requestParam } from "inversify-express-utils"; +import { controller, httpDelete, httpGet, httpPatch, httpPost, requestBody, requestParam } from "inversify-express-utils"; import { HttpStatus } from "../../../common"; import { BaseController } from "../../../common/base/controller"; @@ -86,4 +86,14 @@ export class AdminBrandController extends BaseController { const data = await this.brandService.deleteById(id); return this.response({ data }); } + + @ApiOperation("get brand detail") + @ApiResponse("successful", HttpStatus.Ok) + @ApiParam("id", "id of brand") + @ApiAuth() + @httpGet("/:id", Guard.authAdmin()) + public async getBrand(@requestParam("id") id: string) { + const data = await this.brandService.getBrandDetail(+id); + return this.response({ data }); + } } diff --git a/src/modules/brand/brand.service.ts b/src/modules/brand/brand.service.ts index 9b4a899..fa8bc0a 100644 --- a/src/modules/brand/brand.service.ts +++ b/src/modules/brand/brand.service.ts @@ -117,6 +117,15 @@ class BrandService { async getPendingBrandsCount() { return await this.brandRepo.getPendingBrandsCount(); } + + async getBrandDetail(id: number) { + const brand = await this.brandRepo.model.findOne({ _id: id }); + if (brand) throw new BadRequestError(BrandMessage.Exist); + + return { + brand, + }; + } } export { BrandService }; From 9155bcc762694abd3a09665b80c2514c60e7d088 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 28 Sep 2025 11:32:16 +0330 Subject: [PATCH 3/5] feat: add get warranty detail --- src/modules/admin/controllers/warranty.controller.ts | 10 ++++++++++ src/modules/warranty/warranty.service.ts | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/src/modules/admin/controllers/warranty.controller.ts b/src/modules/admin/controllers/warranty.controller.ts index 60372f0..f33e7e4 100644 --- a/src/modules/admin/controllers/warranty.controller.ts +++ b/src/modules/admin/controllers/warranty.controller.ts @@ -59,4 +59,14 @@ export class AdminWarrantyController extends BaseController { const data = await this.warrantyService.deleteById(+id); return this.response(data); } + + @ApiOperation("get warranty with its id") + @ApiResponse("successful", HttpStatus.Ok) + @ApiParam("id", "id of warranty") + @ApiAuth() + @httpDelete("/:id", Guard.authAdmin()) + public async getWarranty(@requestParam("id") id: string) { + const data = await this.warrantyService.getWarranty(+id); + return this.response(data); + } } diff --git a/src/modules/warranty/warranty.service.ts b/src/modules/warranty/warranty.service.ts index 17033a1..84e5349 100644 --- a/src/modules/warranty/warranty.service.ts +++ b/src/modules/warranty/warranty.service.ts @@ -61,6 +61,15 @@ class WarrantyService { async getPendingWarranties() { return await this.warrantyRepo.pendingWarranties(); } + + async getWarranty(id: number) { + const warranty = await this.warrantyRepo.model.findById(id); + if (!warranty) throw new BadRequestError(CommonMessage.NotValidId); + + return { + warranty, + }; + } } export { WarrantyService }; From 55ef63196c2c56558f29ca8bb6eca8464073ba0f Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 28 Sep 2025 11:44:59 +0330 Subject: [PATCH 4/5] fix: warranty and brand detail --- src/modules/admin/controllers/brand.controller.ts | 2 +- src/modules/admin/controllers/warranty.controller.ts | 2 +- src/modules/brand/brand.service.ts | 6 +++--- src/modules/warranty/warranty.service.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/modules/admin/controllers/brand.controller.ts b/src/modules/admin/controllers/brand.controller.ts index 91fc884..8e2ed11 100644 --- a/src/modules/admin/controllers/brand.controller.ts +++ b/src/modules/admin/controllers/brand.controller.ts @@ -93,7 +93,7 @@ export class AdminBrandController extends BaseController { @ApiAuth() @httpGet("/:id", Guard.authAdmin()) public async getBrand(@requestParam("id") id: string) { - const data = await this.brandService.getBrandDetail(+id); + const data = await this.brandService.getBrandDetail(id); return this.response({ data }); } } diff --git a/src/modules/admin/controllers/warranty.controller.ts b/src/modules/admin/controllers/warranty.controller.ts index f33e7e4..fea2d84 100644 --- a/src/modules/admin/controllers/warranty.controller.ts +++ b/src/modules/admin/controllers/warranty.controller.ts @@ -66,7 +66,7 @@ export class AdminWarrantyController extends BaseController { @ApiAuth() @httpDelete("/:id", Guard.authAdmin()) public async getWarranty(@requestParam("id") id: string) { - const data = await this.warrantyService.getWarranty(+id); + const data = await this.warrantyService.getWarranty(id); return this.response(data); } } diff --git a/src/modules/brand/brand.service.ts b/src/modules/brand/brand.service.ts index fa8bc0a..ecfa5a6 100644 --- a/src/modules/brand/brand.service.ts +++ b/src/modules/brand/brand.service.ts @@ -118,9 +118,9 @@ class BrandService { return await this.brandRepo.getPendingBrandsCount(); } - async getBrandDetail(id: number) { - const brand = await this.brandRepo.model.findOne({ _id: id }); - if (brand) throw new BadRequestError(BrandMessage.Exist); + async getBrandDetail(id: string) { + const brand = await this.brandRepo.model.findOne({ _id: id, deleted: false }).populate("category"); + if (!brand) throw new BadRequestError(BrandMessage.NotFound); return { brand, diff --git a/src/modules/warranty/warranty.service.ts b/src/modules/warranty/warranty.service.ts index 84e5349..08270ac 100644 --- a/src/modules/warranty/warranty.service.ts +++ b/src/modules/warranty/warranty.service.ts @@ -62,8 +62,8 @@ class WarrantyService { return await this.warrantyRepo.pendingWarranties(); } - async getWarranty(id: number) { - const warranty = await this.warrantyRepo.model.findById(id); + async getWarranty(id: string) { + const warranty = await this.warrantyRepo.model.findOne({ _id: id, deleted: false }); if (!warranty) throw new BadRequestError(CommonMessage.NotValidId); return { From 7821a11b94c4fb3ee1f2c388dafc8f16867056a4 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 28 Sep 2025 12:26:36 +0330 Subject: [PATCH 5/5] fix: rename serive in order detail --- src/modules/admin/controllers/order.controller.ts | 4 ++-- src/modules/order/order.service.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/admin/controllers/order.controller.ts b/src/modules/admin/controllers/order.controller.ts index b0ef5dc..f928199 100644 --- a/src/modules/admin/controllers/order.controller.ts +++ b/src/modules/admin/controllers/order.controller.ts @@ -235,7 +235,7 @@ export class AdminOrderController extends BaseController { @ApiAuth() @httpGet("/:orderId/user", Guard.authAdmin(), Guard.checkAdminPermissions([PermissionEnum.ORDER])) public async getUserOrderDetail(@requestParam("orderId") orderId: string) { - const data = await this.orderService.getAdminUserOrderDetail(+orderId); + const data = await this.orderService.getUserOrderDetailAsAdmin(+orderId); return this.response(data); } @@ -245,7 +245,7 @@ export class AdminOrderController extends BaseController { @ApiAuth() @httpGet("/:orderId/seller", Guard.authAdmin(), Guard.checkAdminPermissions([PermissionEnum.ORDER])) public async getSellerOrderDetail(@requestParam("orderId") orderId: string) { - const data = await this.orderService.getAdminSellerOrderDetail(+orderId); + const data = await this.orderService.getSellerOrderDetailAsAdmin(+orderId); return this.response(data); } } diff --git a/src/modules/order/order.service.ts b/src/modules/order/order.service.ts index c1e4972..4f716aa 100644 --- a/src/modules/order/order.service.ts +++ b/src/modules/order/order.service.ts @@ -323,7 +323,7 @@ class OrderService { //####################################################### //####################################################### - async getAdminUserOrderDetail(orderId: number) { + async getUserOrderDetailAsAdmin(orderId: number) { const { order } = await this.validateAdminUserOrder(orderId); const doc = await this.orderRepo.getUserOrderDetail(orderId, order.user._id.toString()); @@ -334,7 +334,7 @@ class OrderService { //####################################################### //####################################################### - async getAdminSellerOrderDetail(orderId: number) { + async getSellerOrderDetailAsAdmin(orderId: number) { const { shopIds } = await this.validateAdminSellerOrder(orderId); const doc = await this.orderItemRepo.getSellerOrderDetailForAdminPanel(orderId, shopIds);