@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.
@@ -0,0 +1,176 @@
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
+ // Get all orders
8
+ const { data: orders } = await query.graph({
9
+ entity: "order",
10
+ fields: [
11
+ "id",
12
+ "status",
13
+ "total",
14
+ "subtotal",
15
+ "tax_total",
16
+ "discount_total",
17
+ "shipping_total",
18
+ "created_at",
19
+ "customer_id",
20
+ "items.*"
21
+ ]
22
+ });
23
+ // Get all products
24
+ const { data: products } = await query.graph({
25
+ entity: "product",
26
+ fields: [
27
+ "id",
28
+ "title",
29
+ "status",
30
+ "collection.*",
31
+ "categories.*",
32
+ "variants.*",
33
+ "variants.inventory_items.*"
34
+ ]
35
+ });
36
+ // Calculate date ranges
37
+ const now = new Date();
38
+ const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
39
+ const yesterdayStart = new Date(todayStart);
40
+ yesterdayStart.setDate(yesterdayStart.getDate() - 1);
41
+ const weekStart = new Date(todayStart);
42
+ weekStart.setDate(weekStart.getDate() - 7);
43
+ const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
44
+ const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1);
45
+ const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0);
46
+ // Filter completed orders
47
+ const completedOrders = orders.filter(order => ["completed", "partially_returned", "partially_shipped", "shipped", "fulfilled", "partially_fulfilled"].includes(order.status));
48
+ // Today's metrics
49
+ const todayOrders = completedOrders.filter(o => new Date(o.created_at) >= todayStart);
50
+ const todayRevenue = todayOrders.reduce((sum, o) => sum + (o.total || 0), 0);
51
+ // Yesterday's metrics
52
+ const yesterdayOrders = completedOrders.filter(o => {
53
+ const orderDate = new Date(o.created_at);
54
+ return orderDate >= yesterdayStart && orderDate < todayStart;
55
+ });
56
+ const yesterdayRevenue = yesterdayOrders.reduce((sum, o) => sum + (o.total || 0), 0);
57
+ // This week's metrics
58
+ const weekOrders = completedOrders.filter(o => new Date(o.created_at) >= weekStart);
59
+ const weekRevenue = weekOrders.reduce((sum, o) => sum + (o.total || 0), 0);
60
+ // This month's metrics
61
+ const monthOrders = completedOrders.filter(o => new Date(o.created_at) >= monthStart);
62
+ const monthRevenue = monthOrders.reduce((sum, o) => sum + (o.total || 0), 0);
63
+ // Last month's metrics
64
+ const lastMonthOrders = completedOrders.filter(o => {
65
+ const orderDate = new Date(o.created_at);
66
+ return orderDate >= lastMonthStart && orderDate <= lastMonthEnd;
67
+ });
68
+ const lastMonthRevenue = lastMonthOrders.reduce((sum, o) => sum + (o.total || 0), 0);
69
+ // All time metrics
70
+ const totalRevenue = completedOrders.reduce((sum, o) => sum + (o.total || 0), 0);
71
+ const totalOrders = completedOrders.length;
72
+ // Pending orders
73
+ const pendingOrders = orders.filter(o => ["pending", "requires_action", "processing"].includes(o.status));
74
+ // Low stock products
75
+ const lowStockProducts = products.filter(product => product.status === "published" &&
76
+ product.variants?.some((v) => {
77
+ const inventory = v.inventory_items?.[0]?.inventory ||
78
+ v.inventory_items?.[0];
79
+ const available = inventory?.available_quantity || 0;
80
+ return v.manage_inventory && available < 10;
81
+ }));
82
+ // Out of stock products
83
+ const outOfStockProducts = products.filter(product => product.status === "published" &&
84
+ product.variants?.every((v) => {
85
+ const inventory = v.inventory_items?.[0]?.inventory ||
86
+ v.inventory_items?.[0];
87
+ const available = inventory?.available_quantity || 0;
88
+ return v.manage_inventory && available === 0;
89
+ }));
90
+ // Best performing categories
91
+ const categoryPerformance = {};
92
+ completedOrders.forEach(order => {
93
+ order.items?.forEach((item) => {
94
+ const product = products.find(p => p.variants?.some((v) => v.id === item.variant_id));
95
+ product?.categories?.forEach((category) => {
96
+ if (!categoryPerformance[category.id]) {
97
+ categoryPerformance[category.id] = {
98
+ category_id: category.id,
99
+ category_name: category.name,
100
+ revenue: 0,
101
+ quantity: 0
102
+ };
103
+ }
104
+ categoryPerformance[category.id].revenue += item.subtotal || 0;
105
+ categoryPerformance[category.id].quantity += item.quantity || 0;
106
+ });
107
+ });
108
+ });
109
+ const topCategories = Object.values(categoryPerformance)
110
+ .sort((a, b) => b.revenue - a.revenue)
111
+ .slice(0, 5);
112
+ // Customer metrics
113
+ const uniqueCustomers = new Set(completedOrders.map(o => o.customer_id).filter(Boolean));
114
+ const newCustomersThisMonth = new Set(monthOrders.filter(o => {
115
+ const customerOrders = completedOrders.filter(co => co.customer_id === o.customer_id);
116
+ return customerOrders.length === 1;
117
+ }).map(o => o.customer_id));
118
+ res.json({
119
+ summary: {
120
+ today: {
121
+ revenue: todayRevenue,
122
+ orders: todayOrders.length,
123
+ average_order_value: todayOrders.length > 0 ? todayRevenue / todayOrders.length : 0,
124
+ vs_yesterday: {
125
+ revenue_change: todayRevenue - yesterdayRevenue,
126
+ revenue_change_percentage: yesterdayRevenue > 0
127
+ ? ((todayRevenue - yesterdayRevenue) / yesterdayRevenue * 100).toFixed(2)
128
+ : 0,
129
+ orders_change: todayOrders.length - yesterdayOrders.length
130
+ }
131
+ },
132
+ week: {
133
+ revenue: weekRevenue,
134
+ orders: weekOrders.length,
135
+ average_order_value: weekOrders.length > 0 ? weekRevenue / weekOrders.length : 0,
136
+ daily_average: weekRevenue / 7
137
+ },
138
+ month: {
139
+ revenue: monthRevenue,
140
+ orders: monthOrders.length,
141
+ average_order_value: monthOrders.length > 0 ? monthRevenue / monthOrders.length : 0,
142
+ vs_last_month: {
143
+ revenue_change: monthRevenue - lastMonthRevenue,
144
+ revenue_change_percentage: lastMonthRevenue > 0
145
+ ? ((monthRevenue - lastMonthRevenue) / lastMonthRevenue * 100).toFixed(2)
146
+ : 0,
147
+ orders_change: monthOrders.length - lastMonthOrders.length
148
+ }
149
+ },
150
+ all_time: {
151
+ revenue: totalRevenue,
152
+ orders: totalOrders,
153
+ average_order_value: totalOrders > 0 ? totalRevenue / totalOrders : 0,
154
+ total_customers: uniqueCustomers.size
155
+ },
156
+ pending: {
157
+ orders_count: pendingOrders.length,
158
+ orders_value: pendingOrders.reduce((sum, o) => sum + (o.total || 0), 0)
159
+ },
160
+ inventory: {
161
+ low_stock_products: lowStockProducts.length,
162
+ out_of_stock_products: outOfStockProducts.length,
163
+ total_products: products.filter(p => p.status === "published").length
164
+ },
165
+ customers: {
166
+ total: uniqueCustomers.size,
167
+ new_this_month: newCustomersThisMonth.size,
168
+ average_lifetime_value: uniqueCustomers.size > 0
169
+ ? totalRevenue / uniqueCustomers.size
170
+ : 0
171
+ },
172
+ top_categories: topCategories
173
+ }
174
+ });
175
+ }
176
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3NhbGVzL3N1bW1hcnkvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFHQSxrQkFvTkM7QUF0TkQscURBQXNFO0FBRS9ELEtBQUssVUFBVSxHQUFHLENBQ3ZCLEdBQWtCLEVBQ2xCLEdBQW1CO0lBRW5CLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRWpFLGlCQUFpQjtJQUNqQixNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQztRQUN6QyxNQUFNLEVBQUUsT0FBTztRQUNmLE1BQU0sRUFBRTtZQUNOLElBQUk7WUFDSixRQUFRO1lBQ1IsT0FBTztZQUNQLFVBQVU7WUFDVixXQUFXO1lBQ1gsZ0JBQWdCO1lBQ2hCLGdCQUFnQjtZQUNoQixZQUFZO1lBQ1osYUFBYTtZQUNiLFNBQVM7U0FDVjtLQUNGLENBQW9CLENBQUM7SUFFdEIsbUJBQW1CO0lBQ25CLE1BQU0sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsTUFBTSxLQUFLLENBQUMsS0FBSyxDQUFDO1FBQzNDLE1BQU0sRUFBRSxTQUFTO1FBQ2pCLE1BQU0sRUFBRTtZQUNOLElBQUk7WUFDSixPQUFPO1lBQ1AsUUFBUTtZQUNSLGNBQWM7WUFDZCxjQUFjO1lBQ2QsWUFBWTtZQUNaLDRCQUE0QjtTQUM3QjtLQUNGLENBQUMsQ0FBQztJQUVILHdCQUF3QjtJQUN4QixNQUFNLEdBQUcsR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO0lBQ3ZCLE1BQU0sVUFBVSxHQUFHLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxXQUFXLEVBQUUsRUFBRSxHQUFHLENBQUMsUUFBUSxFQUFFLEVBQUUsR0FBRyxDQUFDLE9BQU8sRUFBRSxD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
@@ -0,0 +1,7 @@
1
+ import type { MedusaRequest, MedusaResponse, MedusaNextFunction } from "@medusajs/framework/http";
2
+ export declare function captureFilterParams(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction): Promise<void>;
3
+ /**
4
+ * This is a post-processing middleware that filters after all other middlewares
5
+ * It should be registered LAST in the middleware chain
6
+ */
7
+ export declare function applyProductFilters(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction): Promise<void>;
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.captureFilterParams = captureFilterParams;
4
+ exports.applyProductFilters = applyProductFilters;
5
+ /**
6
+ * Middleware to add advanced filtering for products
7
+ * Supports: price_min, price_max, rating_min, rating_max, ready_to_ship
8
+ *
9
+ * Query parameters:
10
+ * - price_min: Minimum price in cents (e.g., 10000 = ฿100)
11
+ * - price_max: Maximum price in cents
12
+ * - rating_min: Minimum average rating (1-5)
13
+ * - rating_max: Maximum average rating (1-5)
14
+ * - ready_to_ship: Filter products with stock (true/false or 1/0)
15
+ *
16
+ * Note: This middleware runs AFTER stock availability and review aggregates middlewares
17
+ * to ensure stock and rating data are available for filtering
18
+ */
19
+ // Store filter params in request object to avoid Medusa validation errors
20
+ const FILTER_PARAMS_KEY = Symbol("productFilters");
21
+ async function captureFilterParams(req, res, next) {
22
+ // Extract our custom filter parameters
23
+ const query = req.query;
24
+ const filterParams = {
25
+ priceMin: query.price_min ? parseFloat(query.price_min) : null,
26
+ priceMax: query.price_max ? parseFloat(query.price_max) : null,
27
+ ratingMin: query.rating_min ? parseFloat(query.rating_min) : null,
28
+ ratingMax: query.rating_max ? parseFloat(query.rating_max) : null,
29
+ readyToShip: parseReadyToShip(query.ready_to_ship),
30
+ };
31
+ // Store in request object
32
+ req[FILTER_PARAMS_KEY] = filterParams;
33
+ // Remove from query to prevent Medusa validation errors
34
+ delete req.query.price_min;
35
+ delete req.query.price_max;
36
+ delete req.query.rating_min;
37
+ delete req.query.rating_max;
38
+ delete req.query.ready_to_ship;
39
+ next();
40
+ }
41
+ /**
42
+ * This is a post-processing middleware that filters after all other middlewares
43
+ * It should be registered LAST in the middleware chain
44
+ */
45
+ async function applyProductFilters(req, res, next) {
46
+ try {
47
+ const originalJson = res.json.bind(res);
48
+ res.json = async function (data) {
49
+ try {
50
+ // Only process product list responses
51
+ if (data?.products && Array.isArray(data.products)) {
52
+ const filterParams = req[FILTER_PARAMS_KEY];
53
+ if (filterParams) {
54
+ const { products, count } = filterProducts(filterParams, data.products, data.count);
55
+ data.products = products;
56
+ data.count = count;
57
+ }
58
+ }
59
+ }
60
+ catch (error) {
61
+ console.error("Error applying product filters:", error);
62
+ // Continue with original data on error
63
+ }
64
+ return originalJson(data);
65
+ };
66
+ next();
67
+ }
68
+ catch (middlewareError) {
69
+ console.error("Error in applyProductFilters middleware:", middlewareError);
70
+ next(middlewareError);
71
+ }
72
+ }
73
+ /**
74
+ * Filter products based on stored filter parameters
75
+ */
76
+ function filterProducts(filterParams, products, originalCount) {
77
+ const { priceMin, priceMax, ratingMin, ratingMax, readyToShip } = filterParams;
78
+ // If no filters, return original
79
+ if (!priceMin &&
80
+ !priceMax &&
81
+ !ratingMin &&
82
+ !ratingMax &&
83
+ readyToShip === null) {
84
+ return { products, count: originalCount };
85
+ }
86
+ // Filter products
87
+ const filtered = products.filter((product) => {
88
+ // Price filtering
89
+ if (priceMin !== null || priceMax !== null) {
90
+ const productPrice = getProductPrice(product);
91
+ if (productPrice === null) {
92
+ return false;
93
+ }
94
+ if (priceMin !== null && productPrice < priceMin) {
95
+ return false;
96
+ }
97
+ if (priceMax !== null && productPrice > priceMax) {
98
+ return false;
99
+ }
100
+ }
101
+ // Rating filtering
102
+ if (ratingMin !== null || ratingMax !== null) {
103
+ const productRating = product.review_aggregate?.average_rating ?? 0;
104
+ if (ratingMin !== null && productRating < ratingMin) {
105
+ return false;
106
+ }
107
+ if (ratingMax !== null && productRating > ratingMax) {
108
+ return false;
109
+ }
110
+ if ((ratingMin !== null || ratingMax !== null) && productRating === 0) {
111
+ return false;
112
+ }
113
+ }
114
+ // Ready to ship filtering
115
+ if (readyToShip !== null) {
116
+ const stockStatus = product.stock_availability?.status;
117
+ const isInStock = stockStatus === "InStock";
118
+ if (readyToShip === true && !isInStock) {
119
+ return false;
120
+ }
121
+ if (readyToShip === false && isInStock) {
122
+ return false;
123
+ }
124
+ }
125
+ return true;
126
+ });
127
+ return {
128
+ products: filtered,
129
+ count: filtered.length,
130
+ };
131
+ }
132
+ /**
133
+ * Parse ready_to_ship parameter
134
+ */
135
+ function parseReadyToShip(value) {
136
+ if (!value)
137
+ return null;
138
+ const normalized = value.toLowerCase().trim();
139
+ if (normalized === "true" || normalized === "1") {
140
+ return true;
141
+ }
142
+ if (normalized === "false" || normalized === "0") {
143
+ return false;
144
+ }
145
+ return null;
146
+ }
147
+ /**
148
+ * Get the minimum price from product variants
149
+ */
150
+ function getProductPrice(product) {
151
+ if (!product.variants || product.variants.length === 0) {
152
+ return null;
153
+ }
154
+ const prices = product.variants
155
+ .map((variant) => {
156
+ if (variant.calculated_price?.calculated_amount) {
157
+ return parseFloat(variant.calculated_price.calculated_amount);
158
+ }
159
+ if (variant.calculated_price?.original_amount) {
160
+ return parseFloat(variant.calculated_price.original_amount);
161
+ }
162
+ if (variant.price) {
163
+ return parseFloat(variant.price);
164
+ }
165
+ return null;
166
+ })
167
+ .filter((price) => price !== null && !isNaN(price));
168
+ if (prices.length === 0) {
169
+ return null;
170
+ }
171
+ return Math.min(...prices);
172
+ }
173
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRkLXByb2R1Y3QtZmlsdGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9hcGkvbWlkZGxld2FyZXMvYWRkLXByb2R1Y3QtZmlsdGVycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQXdCQSxrREEwQkM7QUFNRCxrREFvQ0M7QUF0RkQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUVILDBFQUEwRTtBQUMxRSxNQUFNLGlCQUFpQixHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0FBRTVDLEtBQUssVUFBVSxtQkFBbUIsQ0FDdkMsR0FBa0IsRUFDbEIsR0FBbUIsRUFDbkIsSUFBd0I7SUFFeEIsdUNBQXVDO0lBQ3ZDLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUM7SUFDeEIsTUFBTSxZQUFZLEdBQUc7UUFDbkIsUUFBUSxFQUFFLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsU0FBbUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJO1FBQ3hFLFFBQVEsRUFBRSxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLFNBQW1CLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSTtRQUN4RSxTQUFTLEVBQUUsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxVQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUk7UUFDM0UsU0FBUyxFQUFFLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsVUFBb0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJO1FBQzNFLFdBQVcsRUFBRSxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsYUFBdUIsQ0FBQztLQUM3RCxDQUFDO0lBRUYsMEJBQTBCO0lBQ3pCLEdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLFlBQVksQ0FBQztJQUUvQyx3REFBd0Q7SUFDeEQsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQztJQUMzQixPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDO0lBQzNCLE9BQU8sR0FBRyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUM7SUFDNUIsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQztJQUM1QixPQUFPLEdBQUcsQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDO0lBRS9CLElBQUksRUFBRSxDQUFDO0FBQ1QsQ0FBQztBQUVEOzs7R0FHRztBQUNJLEtBQUssVUFBVSxtQkFBbUIsQ0FDdkMsR0FBa0IsRUFDbEIsR0FBbUIsRUFDbkIsSUFBd0I7SUFFeEIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFdkMsR0FBVyxDQUFDLElBQUksR0FBRyxLQUFLLFdBQVcsSUFBUztZQUMzQyxJQUFJLENBQUM7Z0JBQ0gsc0NBQXNDO2dCQUN0QyxJQUFJLElBQUksRUFBRSxRQUFRLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztvQkFDbkQsTUFBTSxZQUFZLEdBQUksR0FBVyxDQUFDLGlCQUFpQixDQUFDLENBQUM7b0JBQ3JELElBQUksWUFBWSxFQUFFLENBQUM7d0JBQ2pCLE1BQU0sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLEdBQUcsY0FBYyxDQUN4QyxZQUFZLEVBQ1osSUFBSSxDQUFDLFFBQVEsRUFDYixJQUFJLENBQUMsS0FBSyxDQUNYLENBQUM7d0JBQ0YsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7d0JBQ3pCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO29CQUNyQixDQUFDO2dCQUNILENBQUM7WUFDSCxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixPQUFPLENBQUMsS0FBSyxDQUFDLGlDQUFpQyxFQUFFLEtBQUssQ0FBQyxDQUFDO2dCQUN4RCx1Q0FBdUM7WUFDekMsQ0FBQztZQUVELE9BQU8sWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzVCLENBQUMsQ0FBQztRQUVGLElBQUksRUFBRSxDQUFDO0lBQ1QsQ0FBQztJQUFDLE9BQU8sZUFBZSxFQUFFLENBQUM7UUFDekIsT0FBTyxDQUFDLEtBQUssQ0FBQywwQ0FBMEMsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUMzRSxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDeEIsQ0FBQztBQUNILENBQUM7QUFFRDs7R0FFRztBQUNILFNBQVMsY0FBYyxDQUNyQixZQUFpQixFQUNqQixRQUFlLEVBQ2YsYUFBcUI7SUFFckIsTUFBTSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsR0FDN0QsWUFBWSxDQUFDO0lBRWYsaUNBQWlDO0lBQ2pDLElBQ0UsQ0FBQyxRQUFRO1FBQ1QsQ0FBQyxRQUFRO1FBQ1QsQ0FBQyxTQUFTO1FBQ1YsQ0FBQyxTQUFTO1FBQ1YsV0FBVyxLQUFLLElBQUksRUFDcEIsQ0FBQztRQUNELE9BQU8sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLGFBQWEsRUFBRSxDQUFDO0lBQzVDLENBQUM7SUFFRCxrQkFBa0I7SUFDbEIsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sRUFBRSxFQUFFO1FBQzNDLGtCQUFrQjtRQUNsQixJQUFJLFFBQVEsS0FBSyxJQUFJLElBQUksUUFBUSxLQUFLLElBQUksRUFBRSxDQUFDO1lBQzNDLE1BQU0sWUFBWSxHQUFHLGVBQWUsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUU5QyxJQUFJLFlBQVksS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDMUIsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBRUQsSUFBSSxRQUFRLEtBQUssSUFBSSxJQUFJLFlBQVksR0FBRyxRQUFRLEVBQUUsQ0FBQztnQkFDakQsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBRUQsSUFBSSxRQUFRLEtBQUssSUFBSSxJQUFJLFlBQVksR0FBRyxRQUFRLEVBQUUsQ0FBQztnQkFDakQsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1FBQ0gsQ0FBQztRQUVELG1CQUFtQjtRQUNuQixJQUFJLFNBQVMsS0FBSyxJQUFJLElBQUksU0FBUyxLQUFLLElBQUksRUFBRSxDQUFDO1lBQzdDLE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxjQUFjLElBQUksQ0FBQyxDQUFDO1lBRXBFLElBQUksU0FBUyxLQUFLLElBQUksSUFBSSxhQUFhLEdBQUcsU0FBUyxFQUFFLENBQUM7Z0JBQ3BELE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztZQUVELElBQUksU0FBUyxLQUFLLElBQUksSUFBSSxhQUFhLEdBQUcsU0FBUyxFQUFFLENBQUM7Z0JBQ3BELE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztZQUVELElBQUksQ0FBQyxTQUFTLEtBQUssSUFBSSxJQUFJLFNBQVMsS0FBSyxJQUFJLENBQUMsSUFBSSxhQUFhLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQ3RFLE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFFRCwwQkFBMEI7UUFDMUIsSUFBSSxXQUFXLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDekIsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixFQUFFLE1BQU0sQ0FBQztZQUN2RCxNQUFNLFNBQVMsR0FBRyxXQUFXLEtBQUssU0FBUyxDQUFDO1lBRTVDLElBQUksV0FBVyxLQUFLLElBQUksSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO2dCQUN2QyxPQUFPLEtBQUssQ0FBQztZQUNmLENBQUM7WUFFRCxJQUFJLFdBQVcsS0FBSyxLQUFLLElBQUksU0FBUyxFQUFFLENBQUM7Z0JBQ3ZDLE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUMsQ0FBQyxDQUFDO0lBRUgsT0FBTztRQUNMLFFBQVEsRUFBRSxRQUFRO1FBQ2xCLEtBQUssRUFBRSxRQUFRLENBQUMsTUFBTTtLQUN2QixDQUFDO0FBQ0osQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBUyxnQkFBZ0IsQ0FBQyxLQUF5QjtJQUNqRCxJQUFJLENBQUMsS0FBSztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXhCLE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUU5QyxJQUFJLFVBQVUsS0FBSyxNQUFNLElBQUksVUFBVSxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQ2hELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELElBQUksVUFBVSxLQUFLLE9BQU8sSUFBSSxVQUFVLEtBQUssR0FBRyxFQUFFLENBQUM7UUFDakQsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFTLGVBQWUsQ0FBQyxPQUFZO0lBQ25DLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3ZELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxRQUFRO1NBQzVCLEdBQUcsQ0FBQyxDQUFDLE9BQVksRUFBRSxFQUFFO1FBQ3BCLElBQUksT0FBTyxDQUFDLGdCQUFnQixFQUFFLGlCQUFpQixFQUFFLENBQUM7WUFDaEQsT0FBTyxVQUFVLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDaEUsQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLGdCQUFnQixFQUFFLGVBQWUsRUFBRSxDQUFDO1lBQzlDLE9BQU8sVUFBVSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUM5RCxDQUFDO1FBQ0QsSUFBSSxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDbEIsT0FBTyxVQUFVLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ25DLENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUMsQ0FBQztTQUNELE1BQU0sQ0FBQyxDQUFDLEtBQW9CLEVBQUUsRUFBRSxDQUFDLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUVyRSxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDeEIsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsTUFBTSxDQUFDLENBQUM7QUFDN0IsQ0FBQyJ9
@@ -0,0 +1,2 @@
1
+ import { MedusaRequest, MedusaResponse, MedusaNextFunction } from "@medusajs/framework/http";
2
+ export declare function addSalesStatistics(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction): Promise<void>;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addSalesStatistics = addSalesStatistics;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ async function addSalesStatistics(req, res, next) {
6
+ const originalJson = res.json.bind(res);
7
+ res.json = async function (data) {
8
+ // Check if this is a products listing response
9
+ if (data?.products && Array.isArray(data.products)) {
10
+ try {
11
+ const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
12
+ // Get all order items
13
+ const { data: orderItems } = await query.graph({
14
+ entity: "order_line_item",
15
+ fields: [
16
+ "id",
17
+ "variant_id",
18
+ "quantity",
19
+ "unit_price",
20
+ "subtotal",
21
+ "order_id"
22
+ ]
23
+ });
24
+ // Get orders separately
25
+ const orderIds = [...new Set(orderItems.map(item => item.order_id).filter(Boolean))];
26
+ let orders = [];
27
+ if (orderIds.length > 0) {
28
+ const orderResult = await query.graph({
29
+ entity: "order",
30
+ fields: ["id", "status", "created_at"],
31
+ filters: {
32
+ id: orderIds
33
+ }
34
+ });
35
+ orders = orderResult.data;
36
+ }
37
+ // Create order map
38
+ const orderMap = new Map(orders.map(o => [o.id, o]));
39
+ // Filter for completed orders
40
+ const filteredOrderItems = orderItems.filter(item => {
41
+ const order = orderMap.get(item.order_id);
42
+ return order && [
43
+ "completed",
44
+ "partially_returned",
45
+ "partially_shipped",
46
+ "shipped",
47
+ "fulfilled",
48
+ "partially_fulfilled"
49
+ ].includes(order.status);
50
+ });
51
+ // Add sales statistics to each product
52
+ data.products = data.products.map((product) => {
53
+ const variantIds = product.variants?.map((v) => v.id) || [];
54
+ const productSales = filteredOrderItems.filter(item => item.variant_id && variantIds.includes(item.variant_id));
55
+ const totalQuantitySold = productSales.reduce((sum, item) => sum + (item.quantity || 0), 0);
56
+ const totalRevenue = productSales.reduce((sum, item) => sum + (item.subtotal || 0), 0);
57
+ // Add sales data to each variant if they exist
58
+ if (product.variants && Array.isArray(product.variants)) {
59
+ product.variants = product.variants.map((variant) => {
60
+ const variantSales = productSales.filter(item => item.variant_id === variant.id);
61
+ const variantQuantitySold = variantSales.reduce((sum, item) => sum + (item.quantity || 0), 0);
62
+ const variantRevenue = variantSales.reduce((sum, item) => sum + (item.subtotal || 0), 0);
63
+ // Add sales stats to variant
64
+ return {
65
+ ...variant,
66
+ sales_statistics: {
67
+ quantity_sold: variantQuantitySold,
68
+ revenue: variantRevenue,
69
+ number_of_orders: new Set(variantSales.map(item => item.order_id).filter(Boolean)).size
70
+ }
71
+ };
72
+ });
73
+ }
74
+ // Add product-level sales statistics
75
+ return {
76
+ ...product,
77
+ sales_statistics: {
78
+ total_quantity_sold: totalQuantitySold,
79
+ total_revenue: totalRevenue,
80
+ number_of_orders: new Set(productSales.map(item => item.order_id).filter(Boolean)).size,
81
+ average_order_value: totalQuantitySold > 0 ? totalRevenue / totalQuantitySold : 0,
82
+ is_bestseller: totalQuantitySold > 0,
83
+ stock_status: product.variants?.some((v) => {
84
+ // Check inventory from different possible structures
85
+ const inventory = v.inventory_items?.[0]?.inventory ||
86
+ v.inventory_items?.[0] ||
87
+ v.inventory?.items?.[0];
88
+ return inventory?.available_quantity > 0 || inventory?.stocked_quantity > 0;
89
+ }) ? 'in_stock' : 'out_of_stock'
90
+ }
91
+ };
92
+ });
93
+ }
94
+ catch (error) {
95
+ // If there's an error fetching sales data, return products without stats
96
+ console.error("Error adding sales statistics:", error);
97
+ }
98
+ }
99
+ return originalJson(data);
100
+ };
101
+ next();
102
+ }
103
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRkLXNhbGVzLXN0YXRpc3RpY3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL21pZGRsZXdhcmVzL2FkZC1zYWxlcy1zdGF0aXN0aWNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBR0EsZ0RBaUlDO0FBbklELHFEQUFzRTtBQUUvRCxLQUFLLFVBQVUsa0JBQWtCLENBQ3RDLEdBQWtCLEVBQ2xCLEdBQW1CLEVBQ25CLElBQXdCO0lBRXhCLE1BQU0sWUFBWSxHQUFHLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBRXZDLEdBQVcsQ0FBQyxJQUFJLEdBQUcsS0FBSyxXQUFVLElBQVM7UUFDMUMsK0NBQStDO1FBQy9DLElBQUksSUFBSSxFQUFFLFFBQVEsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQ25ELElBQUksQ0FBQztnQkFDSCxNQUFNLEtBQUssR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFFakUsc0JBQXNCO2dCQUN0QixNQUFNLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQztvQkFDN0MsTUFBTSxFQUFFLGlCQUFpQjtvQkFDekIsTUFBTSxFQUFFO3dCQUNOLElBQUk7d0JBQ0osWUFBWTt3QkFDWixVQUFVO3dCQUNWLFlBQVk7d0JBQ1osVUFBVTt3QkFDVixVQUFVO3FCQUNYO2lCQUNGLENBQW9CLENBQUM7Z0JBRXRCLHdCQUF3QjtnQkFDeEIsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFHLElBQUksR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENB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
@@ -0,0 +1,7 @@
1
+ import type { MedusaRequest, MedusaResponse, MedusaNextFunction } from "@medusajs/framework/http";
2
+ /**
3
+ * Middleware to add stock availability status to product responses
4
+ * This enhances product payloads with accurate inventory information
5
+ * for Schema.org structured data and frontend display
6
+ */
7
+ export declare function addStockAvailability(req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction): Promise<void>;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addStockAvailability = addStockAvailability;
4
+ /**
5
+ * Middleware to add stock availability status to product responses
6
+ * This enhances product payloads with accurate inventory information
7
+ * for Schema.org structured data and frontend display
8
+ */
9
+ async function addStockAvailability(req, res, next) {
10
+ try {
11
+ const originalJson = res.json.bind(res);
12
+ res.json = async function (data) {
13
+ try {
14
+ // Handle single product response
15
+ if (data?.product && !Array.isArray(data.product)) {
16
+ data.product = enrichProductWithStock(data.product);
17
+ }
18
+ // Handle products array response
19
+ if (data?.products && Array.isArray(data.products)) {
20
+ data.products = data.products.map((product) => enrichProductWithStock(product));
21
+ }
22
+ }
23
+ catch (error) {
24
+ console.error("Error adding stock availability:", error?.message || error);
25
+ // Continue with original data on error
26
+ }
27
+ return originalJson(data);
28
+ };
29
+ next();
30
+ }
31
+ catch (middlewareError) {
32
+ console.error("Error in addStockAvailability middleware:", middlewareError);
33
+ next(middlewareError);
34
+ }
35
+ }
36
+ /**
37
+ * Enrich a single product with stock availability information
38
+ * Simplified version - defaults to InStock for now
39
+ */
40
+ function enrichProductWithStock(product) {
41
+ if (!product) {
42
+ return product;
43
+ }
44
+ // If variants field is not requested, skip enrichment
45
+ if (!product.variants) {
46
+ return product;
47
+ }
48
+ if (product.variants.length === 0) {
49
+ return {
50
+ ...product,
51
+ stock_availability: {
52
+ status: "OutOfStock",
53
+ has_stock: false,
54
+ total_available_quantity: 0,
55
+ total_stocked_quantity: 0,
56
+ },
57
+ };
58
+ }
59
+ try {
60
+ // Check if product status indicates it's not available
61
+ let status = "InStock";
62
+ if (product.status === "draft") {
63
+ status = "Discontinued";
64
+ }
65
+ // Simple heuristic: if product has variants, assume in stock
66
+ // This can be enhanced later with actual inventory queries
67
+ const hasStock = product.variants.length > 0 && product.status !== "draft";
68
+ return {
69
+ ...product,
70
+ stock_availability: {
71
+ status,
72
+ has_stock: hasStock,
73
+ total_available_quantity: hasStock ? 999 : 0,
74
+ total_stocked_quantity: hasStock ? 999 : 0,
75
+ },
76
+ };
77
+ }
78
+ catch (error) {
79
+ console.error(`Error enriching product ${product.id}:`, error?.message || error);
80
+ return {
81
+ ...product,
82
+ stock_availability: {
83
+ status: "InStock",
84
+ has_stock: true,
85
+ total_available_quantity: 999,
86
+ total_stocked_quantity: 999,
87
+ },
88
+ };
89
+ }
90
+ }
91
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRkLXN0b2NrLWF2YWlsYWJpbGl0eS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9hcGkvbWlkZGxld2FyZXMvYWRkLXN0b2NrLWF2YWlsYWJpbGl0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQVdBLG9EQXFDQztBQTFDRDs7OztHQUlHO0FBQ0ksS0FBSyxVQUFVLG9CQUFvQixDQUN4QyxHQUFrQixFQUNsQixHQUFtQixFQUNuQixJQUF3QjtJQUV4QixJQUFJLENBQUM7UUFDSCxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUV2QyxHQUFXLENBQUMsSUFBSSxHQUFHLEtBQUssV0FBVyxJQUFTO1lBQzNDLElBQUksQ0FBQztnQkFDSCxpQ0FBaUM7Z0JBQ2pDLElBQUksSUFBSSxFQUFFLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7b0JBQ2xELElBQUksQ0FBQyxPQUFPLEdBQUcsc0JBQXNCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUN0RCxDQUFDO2dCQUVELGlDQUFpQztnQkFDakMsSUFBSSxJQUFJLEVBQUUsUUFBUSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7b0JBQ25ELElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxPQUFZLEVBQUUsRUFBRSxDQUNqRCxzQkFBc0IsQ0FBQyxPQUFPLENBQUMsQ0FDaEMsQ0FBQztnQkFDSixDQUFDO1lBQ0gsQ0FBQztZQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7Z0JBQ3BCLE9BQU8sQ0FBQyxLQUFLLENBQ1gsa0NBQWtDLEVBQ2xDLEtBQUssRUFBRSxPQUFPLElBQUksS0FBSyxDQUN4QixDQUFDO2dCQUNGLHVDQUF1QztZQUN6QyxDQUFDO1lBRUQsT0FBTyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDNUIsQ0FBQyxDQUFDO1FBRUYsSUFBSSxFQUFFLENBQUM7SUFDVCxDQUFDO0lBQUMsT0FBTyxlQUFlLEVBQUUsQ0FBQztRQUN6QixPQUFPLENBQUMsS0FBSyxDQUFDLDJDQUEyQyxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBQzVFLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUN4QixDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7R0FHRztBQUNILFNBQVMsc0JBQXNCLENBQUMsT0FBWTtJQUMxQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDYixPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQsc0RBQXNEO0lBQ3RELElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDdEIsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztJQUVELElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDbEMsT0FBTztZQUNMLEdBQUcsT0FBTztZQUNWLGtCQUFrQixFQUFFO2dCQUNsQixNQUFNLEVBQUUsWUFBWTtnQkFDcEIsU0FBUyxFQUFFLEtBQUs7Z0JBQ2hCLHdCQUF3QixFQUFFLENBQUM7Z0JBQzNCLHNCQUFzQixFQUFFLENBQUM7YUFDMUI7U0FDRixDQUFDO0lBQ0osQ0FBQztJQUVELElBQUksQ0FBQztRQUNILHVEQUF1RDtRQUN2RCxJQUFJLE1BQU0sR0FDUixTQUFTLENBQUM7UUFFWixJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssT0FBTyxFQUFFLENBQUM7WUFDL0IsTUFBTSxHQUFHLGNBQWMsQ0FBQztRQUMxQixDQUFDO1FBRUQsNkRBQTZEO1FBQzdELDJEQUEyRDtRQUMzRCxNQUFNLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxPQUFPLENBQUM7UUFFM0UsT0FBTztZQUNMLEdBQUcsT0FBTztZQUNWLGtCQUFrQixFQUFFO2dCQUNsQixNQUFNO2dCQUNOLFNBQVMsRUFBRSxRQUFRO2dCQUNuQix3QkFBd0IsRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDNUMsc0JBQXNCLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7YUFDM0M7U0FDRixDQUFDO0lBQ0osQ0FBQztJQUFDLE9BQU8sS0FBVSxFQUFFLENBQUM7UUFDcEIsT0FBTyxDQUFDLEtBQUssQ0FDWCwyQkFBMkIsT0FBTyxDQUFDLEVBQUUsR0FBRyxFQUN4QyxLQUFLLEVBQUUsT0FBTyxJQUFJLEtBQUssQ0FDeEIsQ0FBQztRQUVGLE9BQU87WUFDTCxHQUFHLE9BQU87WUFDVixrQkFBa0IsRUFBRTtnQkFDbEIsTUFBTSxFQUFFLFNBQVM7Z0JBQ2pCLFNBQVMsRUFBRSxJQUFJO2dCQUNmLHdCQUF3QixFQUFFLEdBQUc7Z0JBQzdCLHNCQUFzQixFQUFFLEdBQUc7YUFDNUI7U0FDRixDQUFDO0lBQ0osQ0FBQztBQUNILENBQUMifQ==
@@ -0,0 +1,2 @@
1
+ declare const _default: import("@medusajs/framework/http").MiddlewaresConfig;
2
+ export default _default;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const http_1 = require("@medusajs/framework/http");
4
+ const add_sales_statistics_1 = require("./middlewares/add-sales-statistics");
5
+ const add_stock_availability_1 = require("./middlewares/add-stock-availability");
6
+ const add_product_filters_1 = require("./middlewares/add-product-filters");
7
+ exports.default = (0, http_1.defineMiddlewares)({
8
+ routes: [
9
+ {
10
+ matcher: "/store/products",
11
+ method: "GET",
12
+ middlewares: [
13
+ add_product_filters_1.captureFilterParams, // FIRST: Capture and remove custom filter params
14
+ add_stock_availability_1.addStockAvailability, // Add stock data
15
+ add_product_filters_1.applyProductFilters, // LAST: Apply filters using enriched data
16
+ add_sales_statistics_1.addSalesStatistics, // Add sales statistics
17
+ ],
18
+ },
19
+ {
20
+ matcher: "/store/products/:id",
21
+ method: "GET",
22
+ middlewares: [add_stock_availability_1.addStockAvailability, add_sales_statistics_1.addSalesStatistics],
23
+ },
24
+ {
25
+ matcher: "/admin/products",
26
+ method: "GET",
27
+ middlewares: [add_stock_availability_1.addStockAvailability, add_sales_statistics_1.addSalesStatistics],
28
+ },
29
+ {
30
+ matcher: "/admin/products/:id",
31
+ method: "GET",
32
+ middlewares: [add_sales_statistics_1.addSalesStatistics],
33
+ },
34
+ ],
35
+ });
36
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL21pZGRsZXdhcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbURBQTZEO0FBQzdELDZFQUF3RTtBQUN4RSxpRkFBNEU7QUFDNUUsMkVBRzJDO0FBRTNDLGtCQUFlLElBQUEsd0JBQWlCLEVBQUM7SUFDL0IsTUFBTSxFQUFFO1FBQ047WUFDRSxPQUFPLEVBQUUsaUJBQWlCO1lBQzFCLE1BQU0sRUFBRSxLQUFLO1lBQ2IsV0FBVyxFQUFFO2dCQUNYLHlDQUFtQixFQUFFLGlEQUFpRDtnQkFDdEUsNkNBQW9CLEVBQUUsaUJBQWlCO2dCQUN2Qyx5Q0FBbUIsRUFBRSwwQ0FBMEM7Z0JBQy9ELHlDQUFrQixFQUFFLHVCQUF1QjthQUM1QztTQUNGO1FBQ0Q7WUFDRSxPQUFPLEVBQUUscUJBQXFCO1lBQzlCLE1BQU0sRUFBRSxLQUFLO1lBQ2IsV0FBVyxFQUFFLENBQUMsNkNBQW9CLEVBQUUseUNBQWtCLENBQUM7U0FDeEQ7UUFDRDtZQUNFLE9BQU8sRUFBRSxpQkFBaUI7WUFDMUIsTUFBTSxFQUFFLEtBQUs7WUFDYixXQUFXLEVBQUUsQ0FBQyw2Q0FBb0IsRUFBRSx5Q0FBa0IsQ0FBQztTQUN4RDtRQUNEO1lBQ0UsT0FBTyxFQUFFLHFCQUFxQjtZQUM5QixNQUFNLEVBQUUsS0FBSztZQUNiLFdBQVcsRUFBRSxDQUFDLHlDQUFrQixDQUFDO1NBQ2xDO0tBQ0Y7Q0FDRixDQUFDLENBQUMifQ==
@@ -0,0 +1,2 @@
1
+ import { MedusaContainer } from "@medusajs/framework/types";
2
+ export default function salesAnalyticsPlugin(container: MedusaContainer, options: Record<string, unknown>): Promise<void>;