2585 lines
70 KiB
TypeScript
2585 lines
70 KiB
TypeScript
import { Types } from "mongoose";
|
|
|
|
import { IOrder } from "./models/Abstraction/IOrder";
|
|
import { IOrderItem } from "./models/Abstraction/IOrderItem";
|
|
import { OrderModel } from "./models/order.model";
|
|
import { OrderItemModel } from "./models/orderItem.model";
|
|
import { BaseRepository } from "../../common/base/repository";
|
|
import { OrderItemsStatus, OrdersStatus } from "../../common/enums/order.enum";
|
|
import { OrderStatusQuery, SellerOrdersQueries } from "../../common/types/query.type";
|
|
import { TimeService } from "../../utils/time.service";
|
|
export type Gran = "daily" | "weekly" | "monthly";
|
|
|
|
class OrderRepository extends BaseRepository<IOrder> {
|
|
constructor() {
|
|
super(OrderModel);
|
|
}
|
|
|
|
async getUserOrderCount(userId: string) {
|
|
const pipeline = [
|
|
{
|
|
$match: {
|
|
user: new Types.ObjectId(userId),
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "orderitems",
|
|
localField: "_id",
|
|
foreignField: "order",
|
|
as: "orderItems",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems",
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
orderId: "$_id",
|
|
orderStatus: "$orderStatus",
|
|
itemStatus: "$orderItems.status",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
processing: {
|
|
$sum: {
|
|
$cond: [
|
|
{
|
|
$or: [
|
|
{ $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,
|
|
0,
|
|
],
|
|
},
|
|
0,
|
|
],
|
|
},
|
|
},
|
|
delivered: {
|
|
$sum: {
|
|
$cond: [
|
|
{
|
|
$and: [
|
|
{ $eq: ["$_id.orderStatus", OrdersStatus.process_by_seller] },
|
|
{ $eq: ["$_id.itemStatus", OrderItemsStatus.Delivered] },
|
|
],
|
|
},
|
|
1,
|
|
0,
|
|
],
|
|
},
|
|
},
|
|
cancelled: {
|
|
$sum: {
|
|
$cond: [
|
|
{
|
|
$and: [
|
|
{ $eq: ["$_id.orderStatus", OrdersStatus.cancelled_system] },
|
|
{ $eq: ["$_id.itemStatus", OrderItemsStatus.cancelled_system] },
|
|
],
|
|
},
|
|
1,
|
|
0,
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
processing: 1,
|
|
delivered: 1,
|
|
cancelled: 1,
|
|
},
|
|
},
|
|
];
|
|
|
|
const result = await this.model.aggregate(pipeline);
|
|
return result[0] || { processing: 0, delivered: 0, cancelled: 0 };
|
|
}
|
|
|
|
async getUserOrders(userId: string, statusQuery: OrderStatusQuery) {
|
|
const matchQuery: { [k: string]: unknown } = {
|
|
user: new Types.ObjectId(userId),
|
|
$or: [],
|
|
};
|
|
const orderItemsMatchQuery: { [k: string]: unknown } = {};
|
|
|
|
switch (statusQuery) {
|
|
case "Cancelled":
|
|
matchQuery.$or = [{ orderStatus: OrdersStatus.cancelled_system }, { orderStatus: OrdersStatus.Cancelled }];
|
|
orderItemsMatchQuery.$or = [
|
|
{ "orderItems.status": OrderItemsStatus.cancelled_system },
|
|
{ "orderItems.status": OrderItemsStatus.cancelled_shop },
|
|
{ "orderItems.status": OrderItemsStatus.cancelled_user },
|
|
];
|
|
break;
|
|
case "Processing":
|
|
matchQuery.$or = [{ orderStatus: OrdersStatus.process_by_seller }, { orderStatus: OrdersStatus.wait_payment }];
|
|
orderItemsMatchQuery.$or = [
|
|
{ "orderItems.status": OrderItemsStatus.Processing },
|
|
{ "orderItems.status": OrderItemsStatus.Shipped },
|
|
];
|
|
break;
|
|
case "Delivered":
|
|
matchQuery.orderStatus = OrdersStatus.process_by_seller;
|
|
orderItemsMatchQuery["orderItems.status"] = OrderItemsStatus.Delivered;
|
|
break;
|
|
case "Returned":
|
|
matchQuery.orderStatus = OrdersStatus.process_by_seller;
|
|
orderItemsMatchQuery["orderItems.status"] = OrderItemsStatus.Returned;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return this.model.aggregate([
|
|
{
|
|
$match: matchQuery,
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "cartpayments",
|
|
localField: "payment",
|
|
foreignField: "_id",
|
|
as: "payment",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$payment",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "paymentmethods",
|
|
localField: "payment.payment_method",
|
|
foreignField: "_id",
|
|
as: "payment.payment_method",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$payment.payment_method",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "orderitems",
|
|
localField: "_id",
|
|
foreignField: "order",
|
|
as: "orderItems",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems",
|
|
},
|
|
{
|
|
$match: orderItemsMatchQuery,
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "shipments",
|
|
localField: "orderItems.shipper",
|
|
foreignField: "_id",
|
|
as: "orderItems.shipper",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems.shipper",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "shops",
|
|
localField: "orderItems.shop",
|
|
foreignField: "_id",
|
|
as: "orderItems.shop",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems.shop",
|
|
},
|
|
{
|
|
$unwind: "$orderItems.shipmentItems",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "products",
|
|
localField: "orderItems.shipmentItems.product",
|
|
foreignField: "_id",
|
|
as: "shipmentItemProduct",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$shipmentItemProduct",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "productvariants",
|
|
localField: "orderItems.shipmentItems.variant",
|
|
foreignField: "_id",
|
|
as: "shipmentItemVariant",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$shipmentItemVariant",
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$orderItems._id",
|
|
shop: { $first: "$orderItems.shop" },
|
|
shipper: { $first: "$orderItems.shipper" },
|
|
totalSellingPrice: { $first: "$orderItems.totalSellingPrice" },
|
|
totalRetailPrice: { $first: "$orderItems.totalRetailPrice" },
|
|
totalShipmentCost: { $first: "$orderItems.totalShipmentCost" },
|
|
totalPaymentPrice: { $first: "$orderItems.totalPaymentPrice" },
|
|
itemsCount: { $first: "$orderItems.itemsCount" },
|
|
trackCode: { $first: "$orderItems.trackCode" },
|
|
postingDate: { $first: "$orderItems.postingDate" },
|
|
postingReceipt: { $first: "$orderItems.postingReceipt" },
|
|
status: { $first: "$orderItems.status" },
|
|
shipmentItems: {
|
|
$push: {
|
|
product: "$shipmentItemProduct",
|
|
variant: "$shipmentItemVariant",
|
|
quantity: "$orderItems.shipmentItems.quantity",
|
|
cancelled_quantity: "$orderItems.shipmentItems.cancelled_quantity",
|
|
returned_quantity: "$orderItems.shipmentItems.returned_quantity",
|
|
selling_price: "$orderItems.shipmentItems.selling_price",
|
|
retail_price: "$orderItems.shipmentItems.retail_price",
|
|
discount_percent: "$orderItems.shipmentItems.discount_percent",
|
|
shipmentCost: "$orderItems.shipmentItems.shipmentCost",
|
|
},
|
|
},
|
|
|
|
orderId: { $first: "$_id" },
|
|
payment: { $first: "$payment" },
|
|
shipmentAddress: { $first: "$shipmentAddress" },
|
|
orderStatus: { $first: "$orderStatus" },
|
|
createdAt: { $first: "$createdAt" },
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$orderId",
|
|
payment: { $first: "$payment" },
|
|
shipmentAddress: { $first: "$shipmentAddress" },
|
|
orderStatus: { $first: "$orderStatus" },
|
|
createdAt: { $first: "$createdAt" },
|
|
orderItems: {
|
|
$push: {
|
|
_id: "$_id", // orderItem `_id`
|
|
shop: "$shop",
|
|
shipper: "$shipper",
|
|
totalSellingPrice: "$totalSellingPrice",
|
|
totalRetailPrice: "$totalRetailPrice",
|
|
totalShipmentCost: "$totalShipmentCost",
|
|
totalPaymentPrice: "$totalPaymentPrice",
|
|
itemsCount: "$itemsCount",
|
|
trackCode: "$trackCode",
|
|
postingDate: "$postingDate",
|
|
postingReceipt: "$postingReceipt",
|
|
status: "$status",
|
|
shipmentItems: "$shipmentItems",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$sort: {
|
|
createdAt: -1,
|
|
},
|
|
},
|
|
]);
|
|
}
|
|
|
|
async getUserOrderDetail(orderId: number, userId: string) {
|
|
return this.model.aggregate([
|
|
{
|
|
$match: {
|
|
_id: orderId,
|
|
user: new Types.ObjectId(userId),
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "cartpayments",
|
|
localField: "payment",
|
|
foreignField: "_id",
|
|
as: "payment",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$payment",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "paymentmethods",
|
|
localField: "payment.payment_method",
|
|
foreignField: "_id",
|
|
as: "payment.payment_method",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$payment.payment_method",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "users",
|
|
localField: "user",
|
|
foreignField: "_id",
|
|
as: "user",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$user",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "orderitems",
|
|
localField: "_id",
|
|
foreignField: "order",
|
|
as: "orderItems",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "shipments",
|
|
localField: "orderItems.shipper",
|
|
foreignField: "_id",
|
|
as: "orderItems.shipper",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems.shipper",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "shops",
|
|
localField: "orderItems.shop",
|
|
foreignField: "_id",
|
|
as: "orderItems.shop",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems.shop",
|
|
},
|
|
{
|
|
$unwind: "$orderItems.shipmentItems",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "products",
|
|
localField: "orderItems.shipmentItems.product",
|
|
foreignField: "_id",
|
|
as: "orderItems.shipmentItems.product",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems.shipmentItems.product",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "productvariants",
|
|
localField: "orderItems.shipmentItems.variant",
|
|
foreignField: "_id",
|
|
as: "orderItems.shipmentItems.variant",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$orderItems.shipmentItems.variant",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "warranties",
|
|
localField: "orderItems.shipmentItems.variant.warranty",
|
|
foreignField: "_id",
|
|
as: "orderItems.shipmentItems.variant.warranty",
|
|
},
|
|
},
|
|
{
|
|
$unwind: {
|
|
path: "$orderItems.shipmentItems.variant.warranty",
|
|
preserveNullAndEmptyArrays: true,
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "colors",
|
|
localField: "orderItems.shipmentItems.variant.color",
|
|
foreignField: "_id",
|
|
as: "orderItems.shipmentItems.variant.color",
|
|
},
|
|
},
|
|
{
|
|
$unwind: {
|
|
path: "$orderItems.shipmentItems.variant.color",
|
|
preserveNullAndEmptyArrays: true,
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "sizes",
|
|
localField: "orderItems.shipmentItems.variant.size",
|
|
foreignField: "_id",
|
|
as: "orderItems.shipmentItems.variant.size",
|
|
},
|
|
},
|
|
{
|
|
$unwind: {
|
|
path: "$orderItems.shipmentItems.variant.size",
|
|
preserveNullAndEmptyArrays: true,
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "meterages",
|
|
localField: "orderItems.shipmentItems.variant.meterage",
|
|
foreignField: "_id",
|
|
as: "orderItems.shipmentItems.variant.meterage",
|
|
},
|
|
},
|
|
{
|
|
$unwind: {
|
|
path: "$orderItems.shipmentItems.variant.meterage",
|
|
preserveNullAndEmptyArrays: true,
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$orderItems._id",
|
|
shop: { $first: "$orderItems.shop" },
|
|
shipper: { $first: "$orderItems.shipper" },
|
|
status: { $first: "$orderItems.status" },
|
|
totalSellingPrice: { $first: "$orderItems.totalSellingPrice" },
|
|
totalRetailPrice: { $first: "$orderItems.totalRetailPrice" },
|
|
totalShipmentCost: { $first: "$orderItems.totalShipmentCost" },
|
|
totalPaymentPrice: { $first: "$orderItems.totalPaymentPrice" },
|
|
itemsCount: { $first: "$orderItems.itemsCount" },
|
|
trackCode: { $first: "$orderItems.trackCode" },
|
|
postingDate: { $first: "$orderItems.postingDate" },
|
|
postingReceipt: { $first: "$orderItems.postingReceipt" },
|
|
shipmentItems: { $push: "$orderItems.shipmentItems" },
|
|
orderId: { $first: "$_id" },
|
|
user: { $first: "$user" },
|
|
payment: { $first: "$payment" },
|
|
shipmentAddress: { $first: "$shipmentAddress" },
|
|
orderStatus: { $first: "$orderStatus" },
|
|
createdAt: { $first: "$createdAt" },
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$orderId",
|
|
user: { $first: "$user" },
|
|
payment: { $first: "$payment" },
|
|
shipmentAddress: { $first: "$shipmentAddress" },
|
|
orderStatus: { $first: "$orderStatus" },
|
|
createdAt: { $first: "$createdAt" },
|
|
orderItems: {
|
|
$push: {
|
|
_id: "$_id",
|
|
order_id: "$orderId",
|
|
shop: "$shop",
|
|
shipper: "$shipper",
|
|
totalSellingPrice: "$totalSellingPrice",
|
|
totalRetailPrice: "$totalRetailPrice",
|
|
totalShipmentCost: "$totalShipmentCost",
|
|
totalPaymentPrice: "$totalPaymentPrice",
|
|
itemsCount: "$itemsCount",
|
|
trackCode: "$trackCode",
|
|
postingDate: "$postingDate",
|
|
postingReceipt: "$postingReceipt",
|
|
status: "$status",
|
|
shipmentItems: "$shipmentItems",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
|
|
class OrderItemRepo extends BaseRepository<IOrderItem> {
|
|
constructor() {
|
|
super(OrderItemModel);
|
|
}
|
|
|
|
async getOrderTrackingDetails(orderId: number) {
|
|
return this.model.find({ order: orderId, trackCode: { $exists: true } }).populate([{ path: "shipper" }]);
|
|
}
|
|
|
|
async getUserOrderItemsDetail(orderId: number, orderItemId: string) {
|
|
return this.model.aggregate([
|
|
{
|
|
$match: {
|
|
_id: new Types.ObjectId(orderItemId),
|
|
order: orderId,
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "orders",
|
|
localField: "order",
|
|
foreignField: "_id",
|
|
as: "order",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$order",
|
|
},
|
|
{
|
|
$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 getOrdersForSellerPanel(shopId: string, queries: SellerOrdersQueries, orderIds?: string[] | string) {
|
|
const page = queries.page || 1;
|
|
const limit = queries.limit || 10;
|
|
const skip = (page - 1) * limit;
|
|
|
|
const priceQuery: { [k: string]: { [op: string]: number } } = {};
|
|
const orderMatchQuery: any = {};
|
|
|
|
if (queries.minPrice) {
|
|
priceQuery.totalPaymentPrice = { $gte: queries.minPrice };
|
|
}
|
|
|
|
if (queries.maxPrice) {
|
|
if (priceQuery.totalPaymentPrice) {
|
|
priceQuery.totalPaymentPrice.$lte = queries.maxPrice;
|
|
} else {
|
|
priceQuery.totalPaymentPrice = { $lte: queries.maxPrice };
|
|
}
|
|
}
|
|
|
|
if (typeof orderIds === "string") {
|
|
orderMatchQuery.order = +orderIds;
|
|
} else if (orderIds && orderIds.length > 0) {
|
|
orderMatchQuery.order = { $in: orderIds.map((id) => +id) };
|
|
}
|
|
|
|
const docs = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
shop: new Types.ObjectId(shopId),
|
|
...orderMatchQuery,
|
|
},
|
|
},
|
|
{
|
|
$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",
|
|
},
|
|
{
|
|
$match: {
|
|
...(queries.status && {
|
|
"order.payment.paymentStatus": queries.status,
|
|
}),
|
|
...(queries.shipperId && {
|
|
shipper: +queries.shipperId,
|
|
}),
|
|
...(queries.since && {
|
|
"order.createdAt": { $gte: new Date(queries.since) },
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
$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: "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"] },
|
|
},
|
|
},
|
|
{
|
|
$match: priceQuery,
|
|
},
|
|
{
|
|
$sort: {
|
|
"order.createdAt": -1,
|
|
},
|
|
},
|
|
{
|
|
$facet: {
|
|
data: [{ $skip: skip }, { $limit: limit }],
|
|
totalCount: [{ $count: "count" }],
|
|
},
|
|
},
|
|
{
|
|
$addFields: {
|
|
totalCount: { $arrayElemAt: ["$totalCount.count", 0] },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
shipmentItems: 0,
|
|
},
|
|
},
|
|
]);
|
|
|
|
return {
|
|
count: (docs[0]?.totalCount as number) || 0,
|
|
docs: docs[0]?.data || [],
|
|
};
|
|
}
|
|
|
|
async getOrdersForAdminPanel(queries: SellerOrdersQueries) {
|
|
const page = queries.page || 1;
|
|
const limit = queries.limit || 10;
|
|
const skip = (page - 1) * limit;
|
|
|
|
const priceQuery: { [k: string]: { [op: string]: number } } = {};
|
|
|
|
if (queries.minPrice) {
|
|
priceQuery.totalPaymentPrice = { $gte: queries.minPrice };
|
|
}
|
|
|
|
if (queries.maxPrice) {
|
|
if (priceQuery.totalPaymentPrice) {
|
|
priceQuery.totalPaymentPrice.$lte = queries.maxPrice;
|
|
} else {
|
|
priceQuery.totalPaymentPrice = { $lte: queries.maxPrice };
|
|
}
|
|
}
|
|
|
|
const docs = await this.model.aggregate([
|
|
{
|
|
$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",
|
|
},
|
|
{
|
|
$match: {
|
|
...(queries.status && {
|
|
"order.payment.paymentStatus": queries.status,
|
|
}),
|
|
...(queries.shipperId && {
|
|
shipper: +queries.shipperId,
|
|
}),
|
|
...(queries.since && {
|
|
"order.createdAt": { $gte: new Date(queries.since) },
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
$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: "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: {
|
|
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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
{
|
|
$addFields: {
|
|
"orderItems.shipmentItems": "$shipmentItems",
|
|
// totalPaymentPrice: { $add: ["$totalSellingPrice", "$totalShipmentCost"] },
|
|
},
|
|
},
|
|
{
|
|
$match: priceQuery,
|
|
},
|
|
{
|
|
$sort: {
|
|
"order.createdAt": -1,
|
|
},
|
|
},
|
|
{
|
|
$facet: {
|
|
data: [{ $skip: skip }, { $limit: limit }],
|
|
totalCount: [{ $count: "count" }],
|
|
},
|
|
},
|
|
{
|
|
$addFields: {
|
|
totalCount: { $arrayElemAt: ["$totalCount.count", 0] },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
shipmentItems: 0,
|
|
},
|
|
},
|
|
]);
|
|
|
|
return {
|
|
count: (docs[0]?.totalCount as number) || 0,
|
|
docs: docs[0]?.data || [],
|
|
};
|
|
}
|
|
|
|
async getSellerOrdersForAdminPanel(shopIds: Types.ObjectId[], queries: SellerOrdersQueries) {
|
|
const page = queries.page || 1;
|
|
const limit = queries.limit || 10;
|
|
const skip = (page - 1) * limit;
|
|
|
|
const priceQuery: { [k: string]: { [op: string]: number } } = {};
|
|
|
|
if (queries.minPrice) {
|
|
priceQuery.totalPaymentPrice = { $gte: queries.minPrice };
|
|
}
|
|
|
|
if (queries.maxPrice) {
|
|
if (priceQuery.totalPaymentPrice) {
|
|
priceQuery.totalPaymentPrice.$lte = queries.maxPrice;
|
|
} else {
|
|
priceQuery.totalPaymentPrice = { $lte: queries.maxPrice };
|
|
}
|
|
}
|
|
|
|
const docs = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
shop: { $in: shopIds },
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "shops",
|
|
foreignField: "_id",
|
|
localField: "shop",
|
|
as: "shop",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$shop",
|
|
},
|
|
{
|
|
$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",
|
|
},
|
|
{
|
|
$match: {
|
|
...(queries.status && {
|
|
"order.payment.paymentStatus": queries.status,
|
|
}),
|
|
...(queries.shipperId && {
|
|
shipper: +queries.shipperId,
|
|
}),
|
|
...(queries.since && {
|
|
"order.createdAt": { $gte: new Date(queries.since) },
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
$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: "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: {
|
|
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"] },
|
|
},
|
|
},
|
|
{
|
|
$match: priceQuery,
|
|
},
|
|
{
|
|
$sort: {
|
|
"order.createdAt": -1,
|
|
},
|
|
},
|
|
{
|
|
$facet: {
|
|
data: [{ $skip: skip }, { $limit: limit }],
|
|
totalCount: [{ $count: "count" }],
|
|
},
|
|
},
|
|
{
|
|
$addFields: {
|
|
totalCount: { $arrayElemAt: ["$totalCount.count", 0] },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
shipmentItems: 0,
|
|
},
|
|
},
|
|
]);
|
|
|
|
return {
|
|
count: (docs[0]?.totalCount as number) || 0,
|
|
docs: docs[0]?.data || [],
|
|
};
|
|
}
|
|
|
|
async getOrderPriceRangeForAdminPanel() {
|
|
const docs = await this.model.aggregate([
|
|
{
|
|
$lookup: {
|
|
from: "orders",
|
|
localField: "order",
|
|
foreignField: "_id",
|
|
as: "order",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$order",
|
|
},
|
|
{
|
|
$unwind: "$shipmentItems",
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$order._id",
|
|
totalSellingPrice: { $sum: { $multiply: ["$shipmentItems.selling_price", "$shipmentItems.quantity"] } },
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
minPrice: { $min: "$totalSellingPrice" },
|
|
maxPrice: { $max: "$totalSellingPrice" },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
minPrice: 1,
|
|
maxPrice: 1,
|
|
},
|
|
},
|
|
]);
|
|
return docs[0];
|
|
}
|
|
|
|
async getOrderPriceRange(shopId: string) {
|
|
const docs = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
shop: new Types.ObjectId(shopId),
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "orders",
|
|
localField: "order",
|
|
foreignField: "_id",
|
|
as: "order",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$order",
|
|
},
|
|
{
|
|
$unwind: "$shipmentItems",
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$order._id",
|
|
totalSellingPrice: { $sum: { $multiply: ["$shipmentItems.selling_price", "$shipmentItems.quantity"] } },
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
minPrice: { $min: "$totalSellingPrice" },
|
|
maxPrice: { $max: "$totalSellingPrice" },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
minPrice: 1,
|
|
maxPrice: 1,
|
|
},
|
|
},
|
|
]);
|
|
return docs[0];
|
|
}
|
|
async getSellerOrderPriceRange(shopIds: Types.ObjectId[]) {
|
|
const docs = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
shop: { $in: shopIds },
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "shops",
|
|
foreignField: "_id",
|
|
localField: "shop",
|
|
as: "shop",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$shop",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "orders",
|
|
localField: "order",
|
|
foreignField: "_id",
|
|
as: "order",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$order",
|
|
},
|
|
{
|
|
$unwind: "$shipmentItems",
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$order._id",
|
|
totalSellingPrice: { $sum: { $multiply: ["$shipmentItems.selling_price", "$shipmentItems.quantity"] } },
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
minPrice: { $min: "$totalSellingPrice" },
|
|
maxPrice: { $max: "$totalSellingPrice" },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
minPrice: 1,
|
|
maxPrice: 1,
|
|
},
|
|
},
|
|
]);
|
|
return docs[0];
|
|
}
|
|
|
|
async getSellerOrderDetail(orderId: number, shopId: string) {
|
|
return this.model.aggregate([
|
|
{
|
|
$match: {
|
|
order: orderId,
|
|
shop: new Types.ObjectId(shopId),
|
|
},
|
|
},
|
|
{
|
|
$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 getSellerOrderDetailForAdminPanel(orderId: number, shopIds: Types.ObjectId[]) {
|
|
return this.model.aggregate([
|
|
{
|
|
$match: {
|
|
order: orderId,
|
|
shop: { $in: shopIds },
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "shops",
|
|
localField: "shop",
|
|
foreignField: "_id",
|
|
as: "shop",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$shop",
|
|
},
|
|
{
|
|
$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;
|
|
const endDate = queries.end ? new Date(TimeService.convertPersianToGregorian(queries.end)) : new Date();
|
|
|
|
// Build the match query to filter data within the date range and for the specific shop
|
|
const matchQuery: any = {
|
|
shop: new Types.ObjectId(shopId),
|
|
createdAt: {
|
|
...(startDate ? { $gte: startDate } : {}),
|
|
$lte: endDate,
|
|
},
|
|
};
|
|
|
|
const salesData = await this.model.aggregate([
|
|
{ $match: matchQuery },
|
|
{
|
|
$lookup: {
|
|
from: "orders",
|
|
localField: "order",
|
|
foreignField: "_id",
|
|
as: "order",
|
|
},
|
|
},
|
|
{ $unwind: "$order" },
|
|
{
|
|
$group: {
|
|
_id: {
|
|
year: { $year: "$createdAt" },
|
|
month: { $month: "$createdAt" },
|
|
},
|
|
totalSales: { $sum: "$totalSellingPrice" },
|
|
totalNetSales: { $sum: "$totalRetailPrice" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
totalOrders: { $sum: 1 },
|
|
},
|
|
},
|
|
{ $sort: { "_id.year": 1, "_id.month": 1 } },
|
|
]);
|
|
|
|
// Format the aggregated sales data
|
|
const formattedSalesData = salesData.map((item) => {
|
|
const monthString = `${item._id.year}-${String(item._id.month).padStart(2, "0")}`;
|
|
return {
|
|
month: TimeService.PersianMonthName(monthString),
|
|
totalSales: item.totalSales,
|
|
totalNetSales: item.totalNetSales,
|
|
itemsSold: item.itemsSold,
|
|
averageSales: item.totalSales / (item.totalOrders || 1),
|
|
totalOrders: item.totalOrders,
|
|
};
|
|
});
|
|
|
|
// Calculate overall stats
|
|
const totalSales = formattedSalesData.reduce((acc, curr) => acc + curr.totalSales, 0);
|
|
const totalNetSales = formattedSalesData.reduce((acc, curr) => acc + curr.totalNetSales, 0);
|
|
const totalOrders = formattedSalesData.reduce((acc, curr) => acc + curr.totalOrders, 0);
|
|
const itemsSold = formattedSalesData.reduce((acc, curr) => acc + curr.itemsSold, 0);
|
|
|
|
return {
|
|
averageDailySales: totalSales / (formattedSalesData.length * 30 || 1),
|
|
totalSales,
|
|
totalNetSales,
|
|
totalOrders,
|
|
itemsSold,
|
|
startDate: startDate ? TimeService.convertGregorianToPersian(startDate) : null,
|
|
endDate: TimeService.convertGregorianToPersian(endDate),
|
|
totalMonths: formattedSalesData.length,
|
|
statsByMonth: formattedSalesData,
|
|
};
|
|
}
|
|
|
|
async getAllProcessingOrders() {
|
|
return await this.model.find({ status: "Processing" }).countDocuments();
|
|
}
|
|
|
|
async getDailyShipmentSalesReport() {
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
const endOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999);
|
|
const salesData = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
createdAt: {
|
|
$gte: startOfDay,
|
|
$lte: endOfDay,
|
|
},
|
|
status: {
|
|
$nin: ["cancelled_shop", "cancelled_system", "Returned", OrdersStatus.wait_payment],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null, // Since we only want today's stats, grouping by date isn't necessary
|
|
totalShipmentCost: { $sum: "$totalShipmentCost" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
totalOrders: { $sum: 1 },
|
|
},
|
|
},
|
|
]);
|
|
|
|
const defaultData = {
|
|
_id: null,
|
|
totalShipmentCost: 0,
|
|
itemsSold: 0,
|
|
totalOrders: 0,
|
|
};
|
|
return salesData.length > 0 ? salesData[0] : defaultData;
|
|
}
|
|
|
|
async getWeeklyShipmentSalesReport() {
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
const startOfWeek = new Date(today);
|
|
startOfWeek.setDate(today.getDate() - 6); // 7 days ago
|
|
const endOfWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999);
|
|
|
|
const salesData = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
createdAt: {
|
|
$gte: startOfWeek,
|
|
$lte: endOfWeek,
|
|
},
|
|
status: {
|
|
$nin: ["cancelled_shop", "cancelled_system", "Returned", OrdersStatus.wait_payment],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null, // Since we only want today's stats, grouping by date isn't necessary
|
|
totalShipmentCost: { $sum: "$totalShipmentCost" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
totalOrders: { $sum: 1 },
|
|
},
|
|
},
|
|
]);
|
|
|
|
const defaultData = {
|
|
_id: null,
|
|
totalShipmentCost: 0,
|
|
itemsSold: 0,
|
|
totalOrders: 0,
|
|
};
|
|
return salesData.length > 0 ? salesData[0] : defaultData;
|
|
}
|
|
|
|
async getMonthlyShipmentSalesReport() {
|
|
const today = new Date();
|
|
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
|
|
const endOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
|
|
|
|
const salesData = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
createdAt: {
|
|
$gte: startOfMonth,
|
|
$lte: endOfMonth,
|
|
},
|
|
status: {
|
|
$nin: ["cancelled_shop", "cancelled_system", "Returned", OrdersStatus.wait_payment],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null, // Since we only want today's stats, grouping by date isn't necessary
|
|
totalShipmentCost: { $sum: "$totalShipmentCost" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
totalOrders: { $sum: 1 },
|
|
},
|
|
},
|
|
]);
|
|
|
|
const defaultData = {
|
|
_id: null,
|
|
totalShipmentCost: 0,
|
|
itemsSold: 0,
|
|
totalOrders: 0,
|
|
};
|
|
return salesData.length > 0 ? salesData[0] : defaultData;
|
|
}
|
|
|
|
async getAnnualShipmentSalesReport() {
|
|
const today = new Date();
|
|
const startOfYear = new Date(today.getFullYear(), 0, 1);
|
|
const endOfYear = new Date(today.getFullYear() + 1, 0, 1);
|
|
|
|
const salesData = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
createdAt: {
|
|
$gte: startOfYear,
|
|
$lte: endOfYear,
|
|
},
|
|
status: {
|
|
$nin: ["cancelled_shop", "cancelled_system", "Returned", OrdersStatus.wait_payment],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null, // Since we only want today's stats, grouping by date isn't necessary
|
|
totalShipmentCost: { $sum: "$totalShipmentCost" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
totalOrders: { $sum: 1 },
|
|
},
|
|
},
|
|
]);
|
|
|
|
const defaultData = {
|
|
_id: null,
|
|
totalShipmentCost: 0,
|
|
itemsSold: 0,
|
|
totalOrders: 0,
|
|
};
|
|
return salesData.length > 0 ? salesData[0] : defaultData;
|
|
}
|
|
|
|
async getDailySalesReport() {
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
const endOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999);
|
|
const salesData = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
createdAt: {
|
|
$gte: startOfDay,
|
|
$lte: endOfDay,
|
|
},
|
|
status: {
|
|
$nin: ["cancelled_shop", "cancelled_system", OrderItemsStatus.cancelled_user, "Returned"],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null, // Since we only want today's stats, grouping by date isn't necessary
|
|
totalSales: { $sum: "$totalSellingPrice" },
|
|
totalNetSales: { $sum: "$totalRetailPrice" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
totalOrders: { $sum: 1 },
|
|
},
|
|
},
|
|
]);
|
|
|
|
const defaultData = {
|
|
_id: null,
|
|
totalSales: 0,
|
|
totalNetSales: 0,
|
|
itemsSold: 0,
|
|
totalOrders: 0,
|
|
};
|
|
return salesData.length > 0 ? salesData[0] : defaultData;
|
|
}
|
|
|
|
async getWeeklySalesReport() {
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
const startOfWeek = new Date(today);
|
|
startOfWeek.setDate(today.getDate() - 6); // 7 days ago
|
|
const endOfWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999);
|
|
|
|
const salesData = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
createdAt: {
|
|
$gte: startOfWeek,
|
|
$lte: endOfWeek,
|
|
},
|
|
status: {
|
|
$nin: ["cancelled_shop", "cancelled_system", "Returned"],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null, // Grouping by week
|
|
totalSales: { $sum: "$totalSellingPrice" },
|
|
totalNetSales: { $sum: "$totalRetailPrice" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
totalOrders: { $sum: 1 },
|
|
},
|
|
},
|
|
]);
|
|
|
|
const defaultData = {
|
|
_id: null,
|
|
totalSales: 0,
|
|
totalNetSales: 0,
|
|
itemsSold: 0,
|
|
totalOrders: 0,
|
|
};
|
|
return salesData.length > 0 ? salesData[0] : defaultData;
|
|
}
|
|
|
|
async getFiveDaysAgoSalesReport() {
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0); // Reset time to the start of the day
|
|
|
|
const fiveDaysAgo = new Date(today);
|
|
fiveDaysAgo.setDate(today.getDate() - 4); // 5 days ago
|
|
|
|
const salesData = await this.model.aggregate([
|
|
{
|
|
$match: {
|
|
createdAt: {
|
|
$gte: fiveDaysAgo,
|
|
$lt: new Date(today.getTime() + 24 * 60 * 60 * 1000), // Include today
|
|
},
|
|
status: {
|
|
$nin: [
|
|
OrderItemsStatus.cancelled_shop,
|
|
OrderItemsStatus.cancelled_system,
|
|
OrderItemsStatus.Returned,
|
|
OrdersStatus.wait_payment,
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
$dateToString: { format: "%Y-%m-%d", date: "$createdAt" },
|
|
},
|
|
totalSales: { $sum: "$totalSellingPrice" },
|
|
},
|
|
},
|
|
{
|
|
$sort: { _id: 1 }, // Sort by date in ascending order
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
date: "$_id",
|
|
price: "$totalSales",
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
dates: { $push: "$date" },
|
|
prices: { $push: "$price" },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
data: {
|
|
$map: {
|
|
input: { $range: [0, 5] },
|
|
as: "day",
|
|
in: {
|
|
date: {
|
|
$dateToString: {
|
|
format: "%Y-%m-%d",
|
|
date: {
|
|
$dateSubtract: {
|
|
startDate: today,
|
|
unit: "day",
|
|
amount: "$$day",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
price: {
|
|
$let: {
|
|
vars: {
|
|
index: {
|
|
$indexOfArray: [
|
|
"$dates",
|
|
{
|
|
$dateToString: {
|
|
format: "%Y-%m-%d",
|
|
date: { $dateSubtract: { startDate: today, unit: "day", amount: "$$day" } },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
in: {
|
|
$cond: {
|
|
if: { $gte: ["$$index", 0] },
|
|
then: { $arrayElemAt: ["$prices", "$$index"] },
|
|
else: 0,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$data",
|
|
},
|
|
{
|
|
$replaceRoot: { newRoot: "$data" },
|
|
},
|
|
]);
|
|
|
|
return salesData;
|
|
}
|
|
// Shared constants (place near top of file)
|
|
|
|
async getMonthlySalesReport() {
|
|
const EXCLUDED_ORDER_ITEM_STATUSES = [
|
|
OrderItemsStatus.cancelled_shop,
|
|
OrderItemsStatus.cancelled_system,
|
|
OrderItemsStatus.cancelled_user,
|
|
OrderItemsStatus.Returned,
|
|
];
|
|
|
|
const EXCLUDED_ORDER_STATUSES = [OrdersStatus.wait_payment, OrdersStatus.cancelled_system, OrdersStatus.Cancelled];
|
|
|
|
const today = new Date();
|
|
const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
|
|
const endOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
|
|
|
|
const salesData = await this.model.aggregate([
|
|
// filter items by month & item status first
|
|
{
|
|
$match: {
|
|
createdAt: { $gte: startOfMonth, $lt: endOfMonth },
|
|
status: { $nin: EXCLUDED_ORDER_ITEM_STATUSES },
|
|
},
|
|
},
|
|
|
|
// join the parent order
|
|
{
|
|
$lookup: {
|
|
from: "orders",
|
|
localField: "order",
|
|
foreignField: "_id",
|
|
as: "order",
|
|
},
|
|
},
|
|
{ $unwind: "$order" },
|
|
|
|
// filter by parent order status
|
|
{
|
|
$match: {
|
|
"order.orderStatus": { $nin: EXCLUDED_ORDER_STATUSES },
|
|
},
|
|
},
|
|
|
|
// aggregate totals and collect distinct order ids
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
totalSales: { $sum: "$totalSellingPrice" },
|
|
totalNetSales: { $sum: "$totalRetailPrice" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
orderIds: { $addToSet: "$order._id" },
|
|
},
|
|
},
|
|
|
|
// convert orderIds into a number
|
|
{
|
|
$addFields: {
|
|
totalOrders: { $size: "$orderIds" },
|
|
},
|
|
},
|
|
|
|
// optionally remove the array from results
|
|
{
|
|
$project: { orderIds: 0 },
|
|
},
|
|
]);
|
|
|
|
return salesData.length > 0 ? salesData[0] : null;
|
|
}
|
|
|
|
async getOrderChart(granularity: Gran = "daily", from?: string, to?: string) {
|
|
const EXCLUDED_ORDER_ITEM_STATUSES = [
|
|
OrderItemsStatus.cancelled_shop,
|
|
OrderItemsStatus.cancelled_system,
|
|
OrderItemsStatus.cancelled_user,
|
|
OrderItemsStatus.Returned,
|
|
];
|
|
|
|
const EXCLUDED_ORDER_STATUSES = [OrdersStatus.wait_payment, OrdersStatus.cancelled_system, OrdersStatus.Cancelled];
|
|
|
|
const DATE_TRUNC_TIMEZONE = "Asia/Tehran";
|
|
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
const today = new Date();
|
|
|
|
// if user asked for monthly and did not give from/to, use calendar month
|
|
let startDate: Date;
|
|
let endDate: Date;
|
|
|
|
if (granularity === "monthly" && !from && !to) {
|
|
startDate = new Date(today.getFullYear(), today.getMonth(), 1);
|
|
endDate = new Date(today.getFullYear(), today.getMonth() + 1, 1);
|
|
} else {
|
|
endDate = to ? new Date(to) : new Date();
|
|
endDate.setHours(23, 59, 59, 999);
|
|
const defaultStart = new Date(Date.now() - 30 * MS_PER_DAY);
|
|
startDate = from ? new Date(from) : defaultStart;
|
|
startDate.setHours(0, 0, 0, 0);
|
|
}
|
|
|
|
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
|
|
throw new Error("Invalid date format. Expected YYYY-MM-DD or ISO date.");
|
|
}
|
|
|
|
if (startDate > endDate) {
|
|
const tmp = startDate;
|
|
startDate = endDate;
|
|
endDate = tmp;
|
|
}
|
|
|
|
const allowed: Record<Gran, "day" | "week" | "month"> = {
|
|
daily: "day",
|
|
weekly: "week",
|
|
monthly: "month",
|
|
};
|
|
const unit = allowed[granularity] ?? "day";
|
|
|
|
const pipeline: any[] = [
|
|
// 1. filter order items by date and item-status
|
|
{
|
|
$match: {
|
|
createdAt: { $gte: startDate, $lte: endDate },
|
|
status: { $nin: EXCLUDED_ORDER_ITEM_STATUSES },
|
|
},
|
|
},
|
|
|
|
// 2. join parent order and filter by order status
|
|
{
|
|
$lookup: {
|
|
from: "orders",
|
|
localField: "order",
|
|
foreignField: "_id",
|
|
as: "order",
|
|
},
|
|
},
|
|
{ $unwind: "$order" },
|
|
{
|
|
$match: {
|
|
"order.orderStatus": { $nin: EXCLUDED_ORDER_STATUSES },
|
|
},
|
|
},
|
|
|
|
// 3. group by truncated period (include timezone)
|
|
{
|
|
$group: {
|
|
_id: {
|
|
period: {
|
|
$dateTrunc: {
|
|
date: "$createdAt",
|
|
unit: unit,
|
|
binSize: 1,
|
|
timezone: DATE_TRUNC_TIMEZONE,
|
|
},
|
|
},
|
|
},
|
|
periodTotal: { $sum: "$totalSellingPrice" },
|
|
},
|
|
},
|
|
|
|
{ $sort: { "_id.period": 1 } },
|
|
];
|
|
|
|
const aggResults: Array<{ _id: { period: Date }; periodTotal: number }> = await this.model.aggregate(pipeline).exec();
|
|
|
|
// build map and x/y arrays (same logic as you had)
|
|
const map = new Map<string, number>();
|
|
for (const r of aggResults) {
|
|
const d: Date = r._id.period;
|
|
const key = unit === "month" ? d.toISOString().slice(0, 7) : d.toISOString().slice(0, 10);
|
|
map.set(key, r.periodTotal ?? 0);
|
|
}
|
|
|
|
function formatKey(d: Date) {
|
|
return unit === "month" ? d.toISOString().slice(0, 7) : d.toISOString().slice(0, 10);
|
|
}
|
|
function addPeriod(d: Date) {
|
|
const nd = new Date(d);
|
|
if (unit === "day") nd.setDate(nd.getDate() + 1);
|
|
else if (unit === "week") nd.setDate(nd.getDate() + 7);
|
|
else nd.setMonth(nd.getMonth() + 1);
|
|
return nd;
|
|
}
|
|
|
|
const xAxis: string[] = [];
|
|
const yAxis: number[] = [];
|
|
|
|
let cursor = new Date(startDate);
|
|
if (unit === "month") cursor.setDate(1);
|
|
|
|
while (cursor <= endDate) {
|
|
const key = formatKey(cursor);
|
|
xAxis.push(key);
|
|
yAxis.push(map.get(key) ?? 0);
|
|
cursor = addPeriod(cursor);
|
|
}
|
|
|
|
const totalSales = yAxis.reduce((s, v) => s + v, 0);
|
|
|
|
return {
|
|
chartType: "line",
|
|
title: "نمودار کل فروش",
|
|
unit: "تومان",
|
|
granularity,
|
|
xAxis,
|
|
yAxis,
|
|
totalSales,
|
|
_debug: { aggregatedPeriods: aggResults.length },
|
|
};
|
|
}
|
|
|
|
async getAnnualSalesReport() {
|
|
const today = new Date();
|
|
const startOfYear = new Date(today.getFullYear(), 0, 1);
|
|
const endOfYear = new Date(today.getFullYear() + 1, 0, 1);
|
|
|
|
const salesData = await this.model.aggregate([
|
|
{
|
|
$lookup: {
|
|
from: "orders",
|
|
localField: "order",
|
|
foreignField: "_id",
|
|
as: "order",
|
|
},
|
|
},
|
|
{
|
|
$match: {
|
|
createdAt: {
|
|
$gte: startOfYear,
|
|
$lt: endOfYear,
|
|
},
|
|
status: {
|
|
$nin: ["cancelled_shop", "cancelled_system", "Returned", OrdersStatus.wait_payment],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: null,
|
|
totalSales: { $sum: "$totalSellingPrice" },
|
|
totalNetSales: { $sum: "$totalRetailPrice" },
|
|
itemsSold: { $sum: "$itemsCount" },
|
|
totalOrders: { $sum: 1 },
|
|
},
|
|
},
|
|
]);
|
|
|
|
return salesData.length > 0 ? salesData[0] : null;
|
|
}
|
|
|
|
async getTopSellingProducts(limit = 5) {
|
|
const topProducts = await this.model.aggregate([
|
|
{ $unwind: "$shipmentItems" },
|
|
{
|
|
$group: {
|
|
_id: "$shipmentItems.product",
|
|
totalSold: { $sum: "$shipmentItems.quantity" },
|
|
},
|
|
},
|
|
{ $sort: { totalSold: -1 } },
|
|
{ $limit: limit },
|
|
{
|
|
$lookup: {
|
|
from: "products",
|
|
localField: "_id",
|
|
foreignField: "_id",
|
|
as: "productDetails",
|
|
},
|
|
},
|
|
{ $unwind: "$productDetails" },
|
|
{
|
|
$lookup: {
|
|
from: "shops",
|
|
localField: "productDetails.shop",
|
|
foreignField: "_id",
|
|
as: "productDetails.shop",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$productDetails.shop",
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 0,
|
|
productId: "$_id",
|
|
totalSold: 1,
|
|
productDetails: {
|
|
_id: 1,
|
|
title_fa: 1,
|
|
title_en: 1,
|
|
model: 1,
|
|
description: 1,
|
|
shop: {
|
|
_id: 1,
|
|
shopName: 1,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
]);
|
|
|
|
return topProducts;
|
|
}
|
|
}
|
|
//**************************************************** */
|
|
//**************************************************** */
|
|
|
|
function createOrderItemRepo(): OrderItemRepo {
|
|
return new OrderItemRepo();
|
|
}
|
|
|
|
function createOrderRepo(): OrderRepository {
|
|
return new OrderRepository();
|
|
}
|
|
|
|
export { OrderRepository, createOrderRepo, createOrderItemRepo, OrderItemRepo };
|