@lodashventure/medusa-sales-analytics 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/api/admin/analytics/sales/route.d.ts +2 -0
- package/.medusa/server/api/admin/analytics/sales/route.js +177 -0
- package/.medusa/server/api/admin/products/[id]/sales-history/route.d.ts +2 -0
- package/.medusa/server/api/admin/products/[id]/sales-history/route.js +204 -0
- package/.medusa/server/api/admin/products/sales-statistics/route.d.ts +2 -0
- package/.medusa/server/api/admin/products/sales-statistics/route.js +97 -0
- package/.medusa/server/api/admin/sales/summary/route.d.ts +2 -0
- package/.medusa/server/api/admin/sales/summary/route.js +176 -0
- package/.medusa/server/api/middlewares/add-product-filters.d.ts +7 -0
- package/.medusa/server/api/middlewares/add-product-filters.js +173 -0
- package/.medusa/server/api/middlewares/add-sales-statistics.d.ts +2 -0
- package/.medusa/server/api/middlewares/add-sales-statistics.js +103 -0
- package/.medusa/server/api/middlewares/add-stock-availability.d.ts +7 -0
- package/.medusa/server/api/middlewares/add-stock-availability.js +91 -0
- package/.medusa/server/api/middlewares.d.ts +2 -0
- package/.medusa/server/api/middlewares.js +36 -0
- package/.medusa/server/index.d.ts +2 -0
- package/.medusa/server/index.js +8 -0
- package/README.md +60 -0
- package/package.json +48 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
async function GET(req, res) {
|
|
6
|
+
const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
7
|
+
// Parse query parameters
|
|
8
|
+
const startDate = req.query.start_date ? new Date(req.query.start_date) : new Date(new Date().setMonth(new Date().getMonth() - 1));
|
|
9
|
+
const endDate = req.query.end_date ? new Date(req.query.end_date) : new Date();
|
|
10
|
+
const groupBy = req.query.group_by || "day"; // day, week, month, year
|
|
11
|
+
const productId = req.query.product_id;
|
|
12
|
+
const collectionId = req.query.collection_id;
|
|
13
|
+
const categoryId = req.query.category_id;
|
|
14
|
+
// Build filters
|
|
15
|
+
let filters = {};
|
|
16
|
+
if (productId)
|
|
17
|
+
filters.product_id = productId;
|
|
18
|
+
if (collectionId)
|
|
19
|
+
filters.collection_id = collectionId;
|
|
20
|
+
if (categoryId)
|
|
21
|
+
filters.category_id = categoryId;
|
|
22
|
+
// Get products based on filters
|
|
23
|
+
const { data: products } = await query.graph({
|
|
24
|
+
entity: "product",
|
|
25
|
+
fields: ["id", "title", "variants.*", "collection.*", "categories.*"],
|
|
26
|
+
filters: filters
|
|
27
|
+
});
|
|
28
|
+
// Get all order items in date range
|
|
29
|
+
const { data: orderItems } = await query.graph({
|
|
30
|
+
entity: "order_line_item",
|
|
31
|
+
fields: [
|
|
32
|
+
"id",
|
|
33
|
+
"variant_id",
|
|
34
|
+
"product_id",
|
|
35
|
+
"quantity",
|
|
36
|
+
"unit_price",
|
|
37
|
+
"subtotal",
|
|
38
|
+
"order.*"
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
// Filter for completed orders and date range
|
|
42
|
+
const completedOrderItems = orderItems.filter(item => {
|
|
43
|
+
if (!item.order)
|
|
44
|
+
return false;
|
|
45
|
+
const orderDate = new Date(item.order.created_at);
|
|
46
|
+
return orderDate >= startDate && orderDate <= endDate && [
|
|
47
|
+
"completed",
|
|
48
|
+
"partially_returned",
|
|
49
|
+
"partially_shipped",
|
|
50
|
+
"shipped",
|
|
51
|
+
"fulfilled",
|
|
52
|
+
"partially_fulfilled"
|
|
53
|
+
].includes(item.order.status);
|
|
54
|
+
});
|
|
55
|
+
// Group sales by period
|
|
56
|
+
const salesByPeriod = {};
|
|
57
|
+
completedOrderItems.forEach(item => {
|
|
58
|
+
const orderDate = new Date(item.order.created_at);
|
|
59
|
+
let periodKey;
|
|
60
|
+
switch (groupBy) {
|
|
61
|
+
case "day":
|
|
62
|
+
periodKey = orderDate.toISOString().split("T")[0];
|
|
63
|
+
break;
|
|
64
|
+
case "week":
|
|
65
|
+
const weekStart = new Date(orderDate);
|
|
66
|
+
weekStart.setDate(orderDate.getDate() - orderDate.getDay());
|
|
67
|
+
periodKey = weekStart.toISOString().split("T")[0];
|
|
68
|
+
break;
|
|
69
|
+
case "month":
|
|
70
|
+
periodKey = `${orderDate.getFullYear()}-${String(orderDate.getMonth() + 1).padStart(2, '0')}`;
|
|
71
|
+
break;
|
|
72
|
+
case "year":
|
|
73
|
+
periodKey = orderDate.getFullYear().toString();
|
|
74
|
+
break;
|
|
75
|
+
default:
|
|
76
|
+
periodKey = orderDate.toISOString().split("T")[0];
|
|
77
|
+
}
|
|
78
|
+
if (!salesByPeriod[periodKey]) {
|
|
79
|
+
salesByPeriod[periodKey] = {
|
|
80
|
+
date: periodKey,
|
|
81
|
+
revenue: 0,
|
|
82
|
+
quantity: 0,
|
|
83
|
+
orders: new Set(),
|
|
84
|
+
products_sold: new Set()
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
salesByPeriod[periodKey].revenue += item.subtotal || 0;
|
|
88
|
+
salesByPeriod[periodKey].quantity += item.quantity || 0;
|
|
89
|
+
salesByPeriod[periodKey].orders.add(item.order.id);
|
|
90
|
+
salesByPeriod[periodKey].products_sold.add(item.product_id);
|
|
91
|
+
});
|
|
92
|
+
// Convert to array and calculate final metrics
|
|
93
|
+
const chartData = Object.keys(salesByPeriod)
|
|
94
|
+
.sort()
|
|
95
|
+
.map(key => ({
|
|
96
|
+
date: key,
|
|
97
|
+
revenue: salesByPeriod[key].revenue,
|
|
98
|
+
quantity: salesByPeriod[key].quantity,
|
|
99
|
+
order_count: salesByPeriod[key].orders.size,
|
|
100
|
+
unique_products: salesByPeriod[key].products_sold.size,
|
|
101
|
+
average_order_value: salesByPeriod[key].orders.size > 0
|
|
102
|
+
? salesByPeriod[key].revenue / salesByPeriod[key].orders.size
|
|
103
|
+
: 0
|
|
104
|
+
}));
|
|
105
|
+
// Calculate summary statistics
|
|
106
|
+
const totalRevenue = completedOrderItems.reduce((sum, item) => sum + (item.subtotal || 0), 0);
|
|
107
|
+
const totalQuantity = completedOrderItems.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
|
108
|
+
const uniqueOrders = new Set(completedOrderItems.map(item => item.order?.id).filter(Boolean));
|
|
109
|
+
const uniqueCustomers = new Set(completedOrderItems.map(item => item.order?.customer_id).filter(Boolean));
|
|
110
|
+
// Top products
|
|
111
|
+
const productSales = {};
|
|
112
|
+
completedOrderItems.forEach(item => {
|
|
113
|
+
const product = products.find(p => p.variants?.some((v) => v.id === item.variant_id));
|
|
114
|
+
if (product) {
|
|
115
|
+
if (!productSales[product.id]) {
|
|
116
|
+
productSales[product.id] = {
|
|
117
|
+
product_id: product.id,
|
|
118
|
+
product_title: product.title,
|
|
119
|
+
quantity_sold: 0,
|
|
120
|
+
revenue: 0
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
productSales[product.id].quantity_sold += item.quantity || 0;
|
|
124
|
+
productSales[product.id].revenue += item.subtotal || 0;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const topProducts = Object.values(productSales)
|
|
128
|
+
.sort((a, b) => b.revenue - a.revenue)
|
|
129
|
+
.slice(0, 10);
|
|
130
|
+
res.json({
|
|
131
|
+
analytics: {
|
|
132
|
+
period: {
|
|
133
|
+
start_date: startDate,
|
|
134
|
+
end_date: endDate,
|
|
135
|
+
group_by: groupBy
|
|
136
|
+
},
|
|
137
|
+
summary: {
|
|
138
|
+
total_revenue: totalRevenue,
|
|
139
|
+
total_quantity_sold: totalQuantity,
|
|
140
|
+
total_orders: uniqueOrders.size,
|
|
141
|
+
unique_customers: uniqueCustomers.size,
|
|
142
|
+
average_order_value: uniqueOrders.size > 0 ? totalRevenue / uniqueOrders.size : 0,
|
|
143
|
+
average_items_per_order: uniqueOrders.size > 0 ? totalQuantity / uniqueOrders.size : 0
|
|
144
|
+
},
|
|
145
|
+
chart_data: chartData,
|
|
146
|
+
top_products: topProducts,
|
|
147
|
+
growth: calculateGrowth(chartData)
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function calculateGrowth(data) {
|
|
152
|
+
if (data.length < 2)
|
|
153
|
+
return null;
|
|
154
|
+
const current = data[data.length - 1];
|
|
155
|
+
const previous = data[data.length - 2];
|
|
156
|
+
return {
|
|
157
|
+
revenue: {
|
|
158
|
+
value: current.revenue - previous.revenue,
|
|
159
|
+
percentage: previous.revenue > 0
|
|
160
|
+
? ((current.revenue - previous.revenue) / previous.revenue * 100).toFixed(2)
|
|
161
|
+
: 0
|
|
162
|
+
},
|
|
163
|
+
quantity: {
|
|
164
|
+
value: current.quantity - previous.quantity,
|
|
165
|
+
percentage: previous.quantity > 0
|
|
166
|
+
? ((current.quantity - previous.quantity) / previous.quantity * 100).toFixed(2)
|
|
167
|
+
: 0
|
|
168
|
+
},
|
|
169
|
+
orders: {
|
|
170
|
+
value: current.order_count - previous.order_count,
|
|
171
|
+
percentage: previous.order_count > 0
|
|
172
|
+
? ((current.order_count - previous.order_count) / previous.order_count * 100).toFixed(2)
|
|
173
|
+
: 0
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2FuYWx5dGljcy9zYWxlcy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUdBLGtCQW1LQztBQXJLRCxxREFBc0U7QUFFL0QsS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFFbkIsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFakUseUJBQXlCO0lBQ3pCLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQW9CLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxRQUFRLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzdJLE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQztJQUN6RixNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQWtCLElBQUksS0FBSyxDQUFDLENBQUMseUJBQXlCO0lBQ2hGLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsVUFBb0IsQ0FBQztJQUNqRCxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLGFBQXVCLENBQUM7SUFDdkQsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxXQUFxQixDQUFDO0lB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
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
async function GET(req, res) {
|
|
6
|
+
const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
7
|
+
const productId = req.params.id;
|
|
8
|
+
// Parse query parameters
|
|
9
|
+
const startDate = req.query.start_date ? new Date(req.query.start_date) : new Date(new Date().setMonth(new Date().getMonth() - 3));
|
|
10
|
+
const endDate = req.query.end_date ? new Date(req.query.end_date) : new Date();
|
|
11
|
+
const variantId = req.query.variant_id;
|
|
12
|
+
// Get product details
|
|
13
|
+
const { data: products } = await query.graph({
|
|
14
|
+
entity: "product",
|
|
15
|
+
fields: [
|
|
16
|
+
"*",
|
|
17
|
+
"variants.*",
|
|
18
|
+
"variants.inventory_items.*",
|
|
19
|
+
"variants.calculated_price.*",
|
|
20
|
+
"collection.*",
|
|
21
|
+
"categories.*"
|
|
22
|
+
],
|
|
23
|
+
filters: {
|
|
24
|
+
id: productId
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
if (!products || products.length === 0) {
|
|
28
|
+
return res.status(404).json({ error: "Product not found" });
|
|
29
|
+
}
|
|
30
|
+
const product = products[0];
|
|
31
|
+
const variantIds = variantId
|
|
32
|
+
? [variantId]
|
|
33
|
+
: product.variants?.map((v) => v.id) || [];
|
|
34
|
+
// Get order items for this product
|
|
35
|
+
const { data: orderItems } = await query.graph({
|
|
36
|
+
entity: "order_line_item",
|
|
37
|
+
fields: [
|
|
38
|
+
"id",
|
|
39
|
+
"variant_id",
|
|
40
|
+
"quantity",
|
|
41
|
+
"unit_price",
|
|
42
|
+
"subtotal",
|
|
43
|
+
"created_at",
|
|
44
|
+
"order.*",
|
|
45
|
+
"order.customer.*"
|
|
46
|
+
],
|
|
47
|
+
filters: {
|
|
48
|
+
variant_id: variantIds
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Group sales by date
|
|
52
|
+
const salesByDate = {};
|
|
53
|
+
const salesByMonth = {};
|
|
54
|
+
const salesByVariant = {};
|
|
55
|
+
const customerPurchases = {};
|
|
56
|
+
orderItems.forEach(item => {
|
|
57
|
+
if (!item.order)
|
|
58
|
+
return;
|
|
59
|
+
const orderDate = new Date(item.order.created_at);
|
|
60
|
+
if (orderDate < startDate || orderDate > endDate)
|
|
61
|
+
return;
|
|
62
|
+
if (![
|
|
63
|
+
"completed",
|
|
64
|
+
"partially_returned",
|
|
65
|
+
"partially_shipped",
|
|
66
|
+
"shipped",
|
|
67
|
+
"fulfilled",
|
|
68
|
+
"partially_fulfilled"
|
|
69
|
+
].includes(item.order.status)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Daily sales
|
|
73
|
+
const dateKey = new Date(item.order.created_at).toISOString().split("T")[0];
|
|
74
|
+
if (!salesByDate[dateKey]) {
|
|
75
|
+
salesByDate[dateKey] = {
|
|
76
|
+
date: dateKey,
|
|
77
|
+
quantity: 0,
|
|
78
|
+
revenue: 0,
|
|
79
|
+
orders: new Set()
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
salesByDate[dateKey].quantity += item.quantity || 0;
|
|
83
|
+
salesByDate[dateKey].revenue += item.subtotal || 0;
|
|
84
|
+
salesByDate[dateKey].orders.add(item.order.id);
|
|
85
|
+
// Monthly sales
|
|
86
|
+
const monthKey = new Date(item.order.created_at).toISOString().substring(0, 7);
|
|
87
|
+
if (!salesByMonth[monthKey]) {
|
|
88
|
+
salesByMonth[monthKey] = {
|
|
89
|
+
month: monthKey,
|
|
90
|
+
quantity: 0,
|
|
91
|
+
revenue: 0,
|
|
92
|
+
orders: new Set()
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
salesByMonth[monthKey].quantity += item.quantity || 0;
|
|
96
|
+
salesByMonth[monthKey].revenue += item.subtotal || 0;
|
|
97
|
+
salesByMonth[monthKey].orders.add(item.order.id);
|
|
98
|
+
// Sales by variant
|
|
99
|
+
if (!salesByVariant[item.variant_id]) {
|
|
100
|
+
const variant = product.variants?.find((v) => v.id === item.variant_id);
|
|
101
|
+
salesByVariant[item.variant_id] = {
|
|
102
|
+
variant_id: item.variant_id,
|
|
103
|
+
variant_title: variant?.title || "Unknown",
|
|
104
|
+
sku: variant?.sku,
|
|
105
|
+
quantity: 0,
|
|
106
|
+
revenue: 0,
|
|
107
|
+
orders: 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
salesByVariant[item.variant_id].quantity += item.quantity || 0;
|
|
111
|
+
salesByVariant[item.variant_id].revenue += item.subtotal || 0;
|
|
112
|
+
salesByVariant[item.variant_id].orders += 1;
|
|
113
|
+
// Customer purchases
|
|
114
|
+
const customerId = item.order.customer_id;
|
|
115
|
+
if (customerId) {
|
|
116
|
+
if (!customerPurchases[customerId]) {
|
|
117
|
+
customerPurchases[customerId] = {
|
|
118
|
+
customer_id: customerId,
|
|
119
|
+
customer_email: item.order.customer?.email,
|
|
120
|
+
customer_name: `${item.order.customer?.first_name || ''} ${item.order.customer?.last_name || ''}`.trim(),
|
|
121
|
+
total_purchases: 0,
|
|
122
|
+
total_spent: 0,
|
|
123
|
+
first_purchase: item.order.created_at,
|
|
124
|
+
last_purchase: item.order.created_at
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
customerPurchases[customerId].total_purchases += item.quantity || 0;
|
|
128
|
+
customerPurchases[customerId].total_spent += item.subtotal || 0;
|
|
129
|
+
if (new Date(item.order.created_at) < new Date(customerPurchases[customerId].first_purchase)) {
|
|
130
|
+
customerPurchases[customerId].first_purchase = item.order.created_at;
|
|
131
|
+
}
|
|
132
|
+
if (new Date(item.order.created_at) > new Date(customerPurchases[customerId].last_purchase)) {
|
|
133
|
+
customerPurchases[customerId].last_purchase = item.order.created_at;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
// Convert to arrays and sort
|
|
138
|
+
const dailySales = Object.keys(salesByDate)
|
|
139
|
+
.sort()
|
|
140
|
+
.map(key => ({
|
|
141
|
+
...salesByDate[key],
|
|
142
|
+
order_count: salesByDate[key].orders.size
|
|
143
|
+
}));
|
|
144
|
+
const monthlySales = Object.keys(salesByMonth)
|
|
145
|
+
.sort()
|
|
146
|
+
.map(key => ({
|
|
147
|
+
...salesByMonth[key],
|
|
148
|
+
order_count: salesByMonth[key].orders.size
|
|
149
|
+
}));
|
|
150
|
+
const variantSales = Object.values(salesByVariant)
|
|
151
|
+
.sort((a, b) => b.revenue - a.revenue);
|
|
152
|
+
const topCustomers = Object.values(customerPurchases)
|
|
153
|
+
.sort((a, b) => b.total_spent - a.total_spent)
|
|
154
|
+
.slice(0, 10);
|
|
155
|
+
// Calculate summary
|
|
156
|
+
const totalQuantity = orderItems.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
|
157
|
+
const totalRevenue = orderItems.reduce((sum, item) => sum + (item.subtotal || 0), 0);
|
|
158
|
+
// Get current inventory
|
|
159
|
+
const currentInventory = product.variants?.reduce((total, variant) => {
|
|
160
|
+
const inventory = variant.inventory_items?.[0]?.inventory ||
|
|
161
|
+
variant.inventory_items?.[0];
|
|
162
|
+
return total + (inventory?.available_quantity || 0);
|
|
163
|
+
}, 0) || 0;
|
|
164
|
+
res.json({
|
|
165
|
+
product: {
|
|
166
|
+
id: product.id,
|
|
167
|
+
title: product.title,
|
|
168
|
+
handle: product.handle,
|
|
169
|
+
collection: product.collection,
|
|
170
|
+
categories: product.categories
|
|
171
|
+
},
|
|
172
|
+
sales_history: {
|
|
173
|
+
period: {
|
|
174
|
+
start_date: startDate,
|
|
175
|
+
end_date: endDate
|
|
176
|
+
},
|
|
177
|
+
summary: {
|
|
178
|
+
total_quantity_sold: totalQuantity,
|
|
179
|
+
total_revenue: totalRevenue,
|
|
180
|
+
unique_customers: Object.keys(customerPurchases).length,
|
|
181
|
+
current_inventory: currentInventory,
|
|
182
|
+
average_sale_price: totalQuantity > 0 ? totalRevenue / totalQuantity : 0
|
|
183
|
+
},
|
|
184
|
+
daily_sales: dailySales,
|
|
185
|
+
monthly_sales: monthlySales,
|
|
186
|
+
variant_breakdown: variantSales,
|
|
187
|
+
top_customers: topCustomers,
|
|
188
|
+
sales_velocity: calculateVelocity(dailySales),
|
|
189
|
+
inventory_turnover: currentInventory > 0 ? totalQuantity / currentInventory : 0
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function calculateVelocity(dailySales) {
|
|
194
|
+
if (dailySales.length === 0)
|
|
195
|
+
return 0;
|
|
196
|
+
const totalDays = dailySales.length;
|
|
197
|
+
const totalQuantity = dailySales.reduce((sum, day) => sum + day.quantity, 0);
|
|
198
|
+
return {
|
|
199
|
+
daily_average: totalQuantity / totalDays,
|
|
200
|
+
weekly_average: (totalQuantity / totalDays) * 7,
|
|
201
|
+
monthly_average: (totalQuantity / totalDays) * 30
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3Byb2R1Y3RzL1tpZF0vc2FsZXMtaGlzdG9yeS9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUdBLGtCQW9OQztBQXRORCxxREFBc0U7QUFFL0QsS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFFbkIsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsaUNBQXlCLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDakUsTUFBTSxTQUFTLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7SUFFaEMseUJBQXlCO0lBQ3pCLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQW9CLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxRQUFRLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzdJLE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQWtCLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQztJQUN6RixNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQW9CLENBQUM7SUFFakQsc0JBQXNCO0lBQ3RCLE1BQU0sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDO1FBQzNDLE1BQU0sRUFBRSxTQUFTO1FBQ2pCLE1BQU0sRUFB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
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
async function GET(req, res) {
|
|
6
|
+
const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
7
|
+
const { data: products } = await query.graph({
|
|
8
|
+
entity: "product",
|
|
9
|
+
fields: [
|
|
10
|
+
"*",
|
|
11
|
+
"categories.*",
|
|
12
|
+
"collection.*",
|
|
13
|
+
"tags.*",
|
|
14
|
+
"type.*",
|
|
15
|
+
"images.*",
|
|
16
|
+
"variants.*",
|
|
17
|
+
"variants.inventory_items.*",
|
|
18
|
+
"variants.calculated_price.*",
|
|
19
|
+
"variants.prices.*",
|
|
20
|
+
"options.*",
|
|
21
|
+
"options.values.*"
|
|
22
|
+
],
|
|
23
|
+
filters: req.query.filters || {}
|
|
24
|
+
});
|
|
25
|
+
// First get all order items with order details
|
|
26
|
+
const { data: orderItems } = await query.graph({
|
|
27
|
+
entity: "order_line_item",
|
|
28
|
+
fields: [
|
|
29
|
+
"id",
|
|
30
|
+
"variant_id",
|
|
31
|
+
"quantity",
|
|
32
|
+
"unit_price",
|
|
33
|
+
"subtotal",
|
|
34
|
+
"order.*"
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
// Filter for completed orders
|
|
38
|
+
const filteredOrderItems = orderItems.filter(item => item.order && [
|
|
39
|
+
"completed",
|
|
40
|
+
"partially_returned",
|
|
41
|
+
"partially_shipped",
|
|
42
|
+
"shipped"
|
|
43
|
+
].includes(item.order.status));
|
|
44
|
+
const salesByProduct = products.map(product => {
|
|
45
|
+
const variantIds = product.variants?.map(v => v.id) || [];
|
|
46
|
+
const productSales = filteredOrderItems.filter(item => item.variant_id && variantIds.includes(item.variant_id));
|
|
47
|
+
const totalQuantitySold = productSales.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
|
48
|
+
const totalRevenue = productSales.reduce((sum, item) => sum + (item.subtotal || 0), 0);
|
|
49
|
+
const salesByVariant = product.variants?.map(variant => {
|
|
50
|
+
const variantSales = productSales.filter(item => item.variant_id === variant.id);
|
|
51
|
+
const variantQuantitySold = variantSales.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
|
52
|
+
const variantRevenue = variantSales.reduce((sum, item) => sum + (item.subtotal || 0), 0);
|
|
53
|
+
return {
|
|
54
|
+
variant_id: variant.id,
|
|
55
|
+
variant_title: variant.title,
|
|
56
|
+
sku: variant.sku,
|
|
57
|
+
barcode: variant.barcode,
|
|
58
|
+
quantity_sold: variantQuantitySold,
|
|
59
|
+
revenue: variantRevenue,
|
|
60
|
+
stock_quantity: variant.inventory_items?.[0]?.inventory?.stocked_quantity || 0,
|
|
61
|
+
available_quantity: variant.inventory_items?.[0]?.inventory?.available_quantity || 0,
|
|
62
|
+
reserved_quantity: variant.inventory_items?.[0]?.inventory?.reserved_quantity || 0,
|
|
63
|
+
calculated_price: variant.calculated_price,
|
|
64
|
+
prices: variant.prices,
|
|
65
|
+
weight: variant.weight,
|
|
66
|
+
length: variant.length,
|
|
67
|
+
height: variant.height,
|
|
68
|
+
width: variant.width,
|
|
69
|
+
material: variant.material,
|
|
70
|
+
metadata: variant.metadata,
|
|
71
|
+
manage_inventory: variant.manage_inventory,
|
|
72
|
+
allow_backorder: variant.allow_backorder
|
|
73
|
+
};
|
|
74
|
+
}) || [];
|
|
75
|
+
return {
|
|
76
|
+
...product,
|
|
77
|
+
total_quantity_sold: totalQuantitySold,
|
|
78
|
+
total_revenue: totalRevenue,
|
|
79
|
+
number_of_orders: new Set(productSales.map((item) => item.order?.id).filter(Boolean)).size,
|
|
80
|
+
variants_with_sales: salesByVariant,
|
|
81
|
+
average_order_value: totalQuantitySold > 0 ? totalRevenue / totalQuantitySold : 0,
|
|
82
|
+
is_bestseller: totalQuantitySold > 0,
|
|
83
|
+
stock_status: product.variants?.some((v) => v.inventory_items?.[0]?.inventory?.available_quantity > 0) ? 'in_stock' : 'out_of_stock'
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
salesByProduct.sort((a, b) => b.total_quantity_sold - a.total_quantity_sold);
|
|
87
|
+
res.json({
|
|
88
|
+
statistics: salesByProduct,
|
|
89
|
+
summary: {
|
|
90
|
+
total_products: products.length,
|
|
91
|
+
products_with_sales: salesByProduct.filter(p => p.total_quantity_sold > 0).length,
|
|
92
|
+
grand_total_revenue: salesByProduct.reduce((sum, p) => sum + p.total_revenue, 0),
|
|
93
|
+
grand_total_quantity: salesByProduct.reduce((sum, p) => sum + p.total_quantity_sold, 0)
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3Byb2R1Y3RzL3NhbGVzLXN0YXRpc3RpY3Mvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFHQSxrQkE0SEM7QUE5SEQscURBQXNFO0FBRS9ELEtBQUssVUFBVSxHQUFHLENBQ3ZCLEdBQWtCLEVBQ2xCLEdBQW1CO0lBRW5CLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRWpFLE1BQU0sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDO1FBQzNDLE1BQU0sRUFBRSxTQUFTO1FBQ2pCLE1BQU0sRUFBRTtZQUNOLEdBQUc7WUFDSCxjQUFjO1lBQ2QsY0FBYztZQUNkLFFBQVE7WUFDUixRQUFRO1lBQ1IsVUFBVTtZQUNWLFlBQVk7WUFDWiw0QkFBNEI7WUFDNUIsNkJBQTZCO1lBQzdCLG1CQUFtQjtZQUNuQixXQUFXO1lBQ1gsa0JBQWtCO1NBQ25CO1FBQ0QsT0FBTyxFQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBZSxJQUFJLEVBQUU7S0FDMUMsQ0FBQyxDQUFDO0lBRUgsK0NBQStDO0lBQy9DLE1BQU0sRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLEdBQUcsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDO1FBQzdDLE1BQU0sRUFBRSxpQkFBaUI7UUFDekIsTUFBTSxFQUFFO1lBQ04sSUFBSTtZQUNKLFlBQVk7WUFDWixVQUFVO1lBQ1YsWUFBWTtZQUNaLFVBQVU7WUFDVixTQUFTO1NBQ1Y7S0FDRixDQUFvQixDQUFDO0lBRXRCLDhCQUE4QjtJQUM5QixNQUFNLGtCQUFrQixHQUFHLFVBQVUsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDbEQsSUFBSSxDQUFDLEtBQUssSUFB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
|