Merge pull request #52 from Danakcorp/mrtz

Mrtz
This commit is contained in:
morteza-mortezai
2025-11-05 23:40:29 +03:30
committed by GitHub
10 changed files with 110 additions and 58 deletions
+1
View File
@@ -257,6 +257,7 @@ export const enum OrderMessage {
TrackingEmailSent = "ایمیل حاوی اطلاعات سفارس و کد رهگیری برای شما ارسال شد", TrackingEmailSent = "ایمیل حاوی اطلاعات سفارس و کد رهگیری برای شما ارسال شد",
ItemAlreadyReceived = "سفارش شما قبلا تحویل گرفته شده است", ItemAlreadyReceived = "سفارش شما قبلا تحویل گرفته شده است",
ItemNotShipped = "سفارش هنوز ارسال نشده است", ItemNotShipped = "سفارش هنوز ارسال نشده است",
CancelCompleted = "فرآیند لغو کالا تکمیل شد",
} }
export const enum TicketMessage { export const enum TicketMessage {
+1 -1
View File
@@ -100,4 +100,4 @@ export type NotificationQuery = {
since: number; since: number;
}; };
export type OrderStatusQuery = "Delivered" | "Processing" | "Cancelled" | "Returned"; export type OrderStatusQuery = "Delivered" | "Processing" | "Cancelled" | "Returned" | "Shipped";
@@ -166,6 +166,17 @@ export class AdminOrderController extends BaseController {
return this.response(data); return this.response(data);
} }
@ApiOperation("complete a cancell order with cancel order id")
@ApiResponse("successful", HttpStatus.Ok)
@ApiParam("cancellOrderId", "id of return order", true)
@ApiModel(CompeleteReturnRequestDTO)
@ApiAuth()
@httpPost("/cancel/:cancelOrderId/complete", Guard.authAdmin(), Guard.checkAdminPermissions([PermissionEnum.ADMIN]))
public async completeCancelOrder(@requestParam("cancelOrderId") cancelOrderId: string, @requestBody() dto: CompeleteReturnRequestDTO) {
const data = await this.cancelService.completeCancelOrder(cancelOrderId, dto);
return this.response(data);
}
//get orders for admin panel //get orders for admin panel
@ApiOperation("get all native shop orders") @ApiOperation("get all native shop orders")
@ApiResponse("successfully", HttpStatus.Ok) @ApiResponse("successfully", HttpStatus.Ok)
+22 -1
View File
@@ -1,5 +1,5 @@
import { Expose, Transform, Type, plainToClass } from "class-transformer"; import { Expose, Transform, Type, plainToClass } from "class-transformer";
import { ArrayMinSize, IsArray, IsNotEmpty, IsNumber, IsString, Min, ValidateNested } from "class-validator"; import { ArrayMinSize, IsArray, IsNotEmpty, IsNumber, IsOptional, IsString, Min, ValidateNested } from "class-validator";
import { ApiProperty } from "../../../common/decorator/swggerDocs"; import { ApiProperty } from "../../../common/decorator/swggerDocs";
import { IsValidId } from "../../../common/decorator/validation.decorator"; import { IsValidId } from "../../../common/decorator/validation.decorator";
@@ -113,3 +113,24 @@ export class CreateCancelDTO {
}) })
items: CancelOrderItemsDTO[]; items: CancelOrderItemsDTO[];
} }
export class CompeleteCancelOrderDTO {
@Expose()
@IsNotEmpty()
@IsString()
@ApiProperty({ type: "string", description: "Date", example: "1404/02/02" })
refundDate: string;
@Expose()
@IsNotEmpty()
@IsString()
@ApiProperty({ type: "string", description: "comment", example: "متن دلخواه" })
refundComment: string;
@Expose()
@IsOptional()
@IsNotEmpty()
@IsString()
@ApiProperty({ type: "string", description: "the image of receipt if mandatory ", example: "https://cdn.com" })
refundReceipt?: string;
}
+23 -2
View File
@@ -2,9 +2,9 @@ import { inject, injectable } from "inversify";
import { isValidObjectId, startSession } from "mongoose"; import { isValidObjectId, startSession } from "mongoose";
import { CancelOrderItemRepo, CancelOrderRepo, CancelReasonRepo } from "./cancel.repository"; import { CancelOrderItemRepo, CancelOrderRepo, CancelReasonRepo } from "./cancel.repository";
import { CancelOrderDTO, CreateCancelDTO } from "./DTO/cancel.dto"; import { CancelOrderDTO, CompeleteCancelOrderDTO, CreateCancelDTO } from "./DTO/cancel.dto";
import { CancelReasonDTO } from "./DTO/cancelReason.dto"; import { CancelReasonDTO } from "./DTO/cancelReason.dto";
import { CommonMessage } from "../../common/enums/message.enum"; import { CommonMessage, OrderMessage } from "../../common/enums/message.enum";
import { OrderItemsStatus } from "../../common/enums/order.enum"; import { OrderItemsStatus } from "../../common/enums/order.enum";
import { AdminCancelsQueries } from "../../common/types/query.type"; import { AdminCancelsQueries } from "../../common/types/query.type";
import { BadRequestError } from "../../core/app/app.errors"; import { BadRequestError } from "../../core/app/app.errors";
@@ -13,6 +13,7 @@ import { OrderItemRefEnum, ReasonRefEnum } from "../fine/models/fine.model";
import { FineRepo } from "../fine/repository/fine.repository"; import { FineRepo } from "../fine/repository/fine.repository";
import { OrderItemRepo } from "../order/order.repository"; import { OrderItemRepo } from "../order/order.repository";
import { CancellerRefEnum } from "./models/cancel.model"; import { CancellerRefEnum } from "./models/cancel.model";
import { StatusEnum } from "../../common/enums/status.enum";
import { FineRuleRepo } from "../fine/repository/fineRule.repository"; import { FineRuleRepo } from "../fine/repository/fineRule.repository";
import { NotificationService } from "../notification/notification.service"; import { NotificationService } from "../notification/notification.service";
@@ -344,6 +345,26 @@ class CancelService {
message: CommonMessage.Deleted, message: CommonMessage.Deleted,
}; };
} }
async completeCancelOrder(cancelRequestId: string, dto: CompeleteCancelOrderDTO) {
const { refundComment, refundDate, refundReceipt } = dto;
const cancelOrder = await this.cancelOrderRepo.model.findByIdAndUpdate(cancelRequestId, {
status: StatusEnum.Completed,
refundComment,
refundDate,
refundReceipt,
});
if (!cancelOrder) throw new BadRequestError(OrderMessage.NotFound);
return {
message: OrderMessage.CancelCompleted,
};
}
//#######################################################
//#######################################################
//####################################################### //#######################################################
//####################################################### //#######################################################
// async updateReturnReason(reasonId: string, updateReasonDto: UpdateReasonDTO) { // async updateReturnReason(reasonId: string, updateReasonDto: UpdateReasonDTO) {
@@ -1,12 +1,14 @@
import { Types } from "mongoose"; import { Types } from "mongoose";
import { OrderItemsStatus } from "../../../../common/enums/order.enum"; import { CancelStatusEnum, CancellerRefEnum } from "../cancel.model";
import { CancellerRefEnum } from "../cancel.model";
export interface ICancelOrder { export interface ICancelOrder {
order: number; order: number;
canceller: Types.ObjectId; canceller: Types.ObjectId;
cancellerRef: CancellerRefEnum; cancellerRef: CancellerRefEnum;
status: OrderItemsStatus; status: CancelStatusEnum;
total_price: number; total_price: number;
cancellReceipt: string;
cancellComment: string;
cancellDate: Date;
} }
+9 -2
View File
@@ -1,7 +1,6 @@
import { Schema, model } from "mongoose"; import { Schema, model } from "mongoose";
import { ICancelOrder } from "./Abstraction/ICancel"; import { ICancelOrder } from "./Abstraction/ICancel";
import { OrderItemsStatus } from "../../../common/enums/order.enum";
export enum CancellerRefEnum { export enum CancellerRefEnum {
User = "User", User = "User",
@@ -9,13 +8,21 @@ export enum CancellerRefEnum {
Admin = "Admin", Admin = "Admin",
} }
export enum CancelStatusEnum {
Pending = "Pending",
Completed = "Completed",
}
const CancelOrderSchema = new Schema<ICancelOrder>( const CancelOrderSchema = new Schema<ICancelOrder>(
{ {
order: { type: Number, ref: "Order", required: true }, order: { type: Number, ref: "Order", required: true },
canceller: { type: Schema.Types.ObjectId, refPath: "cancellerRef", required: true }, canceller: { type: Schema.Types.ObjectId, refPath: "cancellerRef", required: true },
cancellerRef: { type: String, enum: CancellerRefEnum, required: true }, cancellerRef: { type: String, enum: CancellerRefEnum, required: true },
status: { type: String, enum: OrderItemsStatus, default: OrderItemsStatus.cancelled_system }, status: { type: String, enum: CancelStatusEnum, default: CancelStatusEnum.Pending },
total_price: { type: Number, default: 0 }, total_price: { type: Number, default: 0 },
cancellReceipt: { type: String, default: null },
cancellComment: { type: String, default: null },
cancellDate: { type: Date, default: null },
}, },
{ timestamps: true, toJSON: { versionKey: false, virtuals: true }, id: false }, { timestamps: true, toJSON: { versionKey: false, virtuals: true }, id: false },
); );
+34 -45
View File
@@ -18,9 +18,7 @@ class OrderRepository extends BaseRepository<IOrder> {
async getUserOrderCount(userId: string) { async getUserOrderCount(userId: string) {
const pipeline = [ const pipeline = [
{ {
$match: { $match: { user: new Types.ObjectId(userId) },
user: new Types.ObjectId(userId),
},
}, },
{ {
$lookup: { $lookup: {
@@ -30,9 +28,7 @@ class OrderRepository extends BaseRepository<IOrder> {
as: "orderItems", as: "orderItems",
}, },
}, },
{ { $unwind: "$orderItems" },
$unwind: "$orderItems",
},
{ {
$group: { $group: {
_id: { _id: {
@@ -49,48 +45,25 @@ class OrderRepository extends BaseRepository<IOrder> {
$sum: { $sum: {
$cond: [ $cond: [
{ {
$or: [ $or: [{ $eq: ["$_id.itemStatus", OrderItemsStatus.Processing] }],
{ $eq: ["$_id.orderStatus", OrdersStatus.process_by_seller] },
{ $eq: ["$_id.orderStatus", OrdersStatus.wait_payment] },
],
},
{
$cond: [
{
$or: [
{ $eq: ["$_id.itemStatus", OrderItemsStatus.Processing] },
{ $eq: ["$_id.itemStatus", OrderItemsStatus.Shipped] },
],
}, },
1, 1,
0, 0,
], ],
}, },
0,
],
},
}, },
delivered: { delivered: {
$sum: { $sum: {
$cond: [ $cond: [{ $eq: ["$_id.itemStatus", OrderItemsStatus.Delivered] }, 1, 0],
{
$and: [
{ $eq: ["$_id.orderStatus", OrdersStatus.process_by_seller] },
{ $eq: ["$_id.itemStatus", OrderItemsStatus.Delivered] },
],
},
1,
0,
],
}, },
}, },
cancelled: { cancelled: {
$sum: { $sum: {
$cond: [ $cond: [
{ {
$and: [ $in: [
{ $eq: ["$_id.orderStatus", OrdersStatus.cancelled_system] }, "$_id.itemStatus",
{ $eq: ["$_id.itemStatus", OrderItemsStatus.cancelled_system] }, [OrderItemsStatus.cancelled_system, OrderItemsStatus.cancelled_user, OrderItemsStatus.cancelled_shop],
], ],
}, },
1, 1,
@@ -98,6 +71,16 @@ class OrderRepository extends BaseRepository<IOrder> {
], ],
}, },
}, },
returned: {
$sum: {
$cond: [{ $eq: ["$_id.itemStatus", OrderItemsStatus.Returned] }, 1, 0],
},
},
shipped: {
$sum: {
$cond: [{ $eq: ["$_id.itemStatus", OrderItemsStatus.Shipped] }, 1, 0],
},
},
}, },
}, },
{ {
@@ -106,24 +89,33 @@ class OrderRepository extends BaseRepository<IOrder> {
processing: 1, processing: 1,
delivered: 1, delivered: 1,
cancelled: 1, cancelled: 1,
returned: 1,
shipped: 1,
}, },
}, },
]; ];
const result = await this.model.aggregate(pipeline); const [result] = await this.model.aggregate(pipeline);
return result[0] || { processing: 0, delivered: 0, cancelled: 0 }; return (
result || {
processing: 0,
delivered: 0,
cancelled: 0,
returned: 0,
shipped: 0,
}
);
} }
async getUserOrders(userId: string, statusQuery: OrderStatusQuery) { async getUserOrders(userId: string, statusQuery: OrderStatusQuery) {
const matchQuery: { [k: string]: unknown } = { const matchQuery: { [k: string]: unknown } = {
user: new Types.ObjectId(userId), user: new Types.ObjectId(userId),
$or: [], // $or: [],
}; };
const orderItemsMatchQuery: { [k: string]: unknown } = {}; const orderItemsMatchQuery: { [k: string]: unknown } = {};
switch (statusQuery) { switch (statusQuery) {
case "Cancelled": case "Cancelled":
matchQuery.$or = [{ orderStatus: OrdersStatus.cancelled_system }, { orderStatus: OrdersStatus.Cancelled }];
orderItemsMatchQuery.$or = [ orderItemsMatchQuery.$or = [
{ "orderItems.status": OrderItemsStatus.cancelled_system }, { "orderItems.status": OrderItemsStatus.cancelled_system },
{ "orderItems.status": OrderItemsStatus.cancelled_shop }, { "orderItems.status": OrderItemsStatus.cancelled_shop },
@@ -131,20 +123,17 @@ class OrderRepository extends BaseRepository<IOrder> {
]; ];
break; break;
case "Processing": case "Processing":
matchQuery.$or = [{ orderStatus: OrdersStatus.process_by_seller }, { orderStatus: OrdersStatus.wait_payment }]; orderItemsMatchQuery["orderItems.status"] = OrderItemsStatus.Processing;
orderItemsMatchQuery.$or = [
{ "orderItems.status": OrderItemsStatus.Processing },
{ "orderItems.status": OrderItemsStatus.Shipped },
];
break; break;
case "Delivered": case "Delivered":
matchQuery.orderStatus = OrdersStatus.process_by_seller;
orderItemsMatchQuery["orderItems.status"] = OrderItemsStatus.Delivered; orderItemsMatchQuery["orderItems.status"] = OrderItemsStatus.Delivered;
break; break;
case "Returned": case "Returned":
matchQuery.orderStatus = OrdersStatus.process_by_seller;
orderItemsMatchQuery["orderItems.status"] = OrderItemsStatus.Returned; orderItemsMatchQuery["orderItems.status"] = OrderItemsStatus.Returned;
break; break;
case "Shipped":
orderItemsMatchQuery["orderItems.status"] = OrderItemsStatus.Shipped;
break;
default: default:
break; break;
} }
+1 -1
View File
@@ -45,7 +45,7 @@ export class ReturnRequestItemsDTO {
@Expose() @Expose()
@IsOptional() @IsOptional()
@IsNotEmpty() @IsNotEmpty({ message: "عکس نمیتواند خالی باشد" })
@IsString() @IsString()
@ApiProperty({ type: "string", description: "the image of return if mandatory ", example: "https://cdn.com" }) @ApiProperty({ type: "string", description: "the image of return if mandatory ", example: "https://cdn.com" })
imagesUrl?: string; imagesUrl?: string;
+1 -1
View File
@@ -181,7 +181,7 @@ class UserController extends BaseController {
//order //order
@ApiOperation("get current logged in user order ") @ApiOperation("get current logged in user order ")
@ApiResponse("successful", HttpStatus.Ok) @ApiResponse("successful", HttpStatus.Ok)
@ApiQuery("status", "the status of order ==> can be = Delivered | Cancelled | Processing") @ApiQuery("status", "the status of order ==> can be = Delivered | Cancelled | Processing | Returned | Shipped")
@ApiAuth() @ApiAuth()
@httpGet("/profile/orders", Guard.authUser()) @httpGet("/profile/orders", Guard.authUser())
public async getUserOrders(@request() req: Request, @queryParam("status") status: string) { public async getUserOrders(@request() req: Request, @queryParam("status") status: string) {