medusa-product-helper 0.0.13 → 0.0.18
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/src/admin/index.js +59 -117
- package/.medusa/server/src/admin/index.mjs +59 -117
- package/.medusa/server/src/api/store/product-helper/products/route.js +112 -0
- package/.medusa/server/src/api/store/product-helper/products/validators.js +3 -1
- package/.medusa/server/src/config/product-helper-options.js +15 -1
- package/.medusa/server/src/index.js +101 -0
- package/.medusa/server/src/providers/filter-providers/availability-provider.js +82 -0
- package/.medusa/server/src/providers/filter-providers/base-filter-provider.js +14 -0
- package/.medusa/server/src/providers/filter-providers/base-product-provider.js +59 -0
- package/.medusa/server/src/providers/filter-providers/category-provider.js +36 -0
- package/.medusa/server/src/providers/filter-providers/collection-provider.js +36 -0
- package/.medusa/server/src/providers/filter-providers/index.js +58 -0
- package/.medusa/server/src/providers/filter-providers/metadata-provider.js +69 -0
- package/.medusa/server/src/providers/filter-providers/price-range-provider.js +95 -0
- package/.medusa/server/src/providers/filter-providers/promotion-provider.js +134 -0
- package/.medusa/server/src/providers/filter-providers/promotion-window-provider.js +85 -0
- package/.medusa/server/src/providers/filter-providers/rating-provider.js +69 -0
- package/.medusa/server/src/services/dynamic-filter-service.js +525 -0
- package/.medusa/server/src/services/filter-provider-loader.js +107 -0
- package/.medusa/server/src/services/filter-provider-registry.js +43 -0
- package/.medusa/server/src/services/product-filter-service.js +183 -0
- package/.medusa/server/src/shared/product-metadata/utils.js +66 -116
- package/.medusa/server/src/utils/query-builders/product-filters.js +89 -111
- package/.medusa/server/src/utils/query-parser.js +51 -0
- package/.medusa/server/src/workflows/add-to-wishlist.js +12 -26
- package/.medusa/server/src/workflows/get-wishlist.js +53 -51
- package/.medusa/server/src/workflows/remove-from-wishlist.js +3 -8
- package/README.md +89 -0
- package/package.json +3 -3
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DynamicFilterService = void 0;
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const filter_provider_registry_1 = require("./filter-provider-registry");
|
|
6
|
+
class DynamicFilterService {
|
|
7
|
+
constructor(container) {
|
|
8
|
+
this.registry = this.resolveRegistry(container);
|
|
9
|
+
this.query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
10
|
+
}
|
|
11
|
+
getRegistry() {
|
|
12
|
+
return this.registry;
|
|
13
|
+
}
|
|
14
|
+
async applyFilters(options) {
|
|
15
|
+
const { filterParams, options: pluginOptions, pagination, projection, context = {} } = options;
|
|
16
|
+
const filterContext = { ...context, options: pluginOptions };
|
|
17
|
+
const { queryFilters, priceRangeFilter, promotionFilter } = await this.processFilters(filterParams, filterContext);
|
|
18
|
+
const fields = this.buildFields(projection?.fields);
|
|
19
|
+
const queryContext = this.buildQueryContext(context);
|
|
20
|
+
const queryOptions = this.buildQueryOptions(queryFilters, fields, pagination, queryContext);
|
|
21
|
+
const result = await this.query.graph(queryOptions);
|
|
22
|
+
const { data: productsRaw = [], metadata } = result;
|
|
23
|
+
let products = this.normalizeProducts(productsRaw);
|
|
24
|
+
products = await this.applyPostQueryFilters(products, priceRangeFilter, promotionFilter);
|
|
25
|
+
// Count should reflect filtered products, not initial query count
|
|
26
|
+
// If post-query filters were applied, use filtered products length
|
|
27
|
+
// Otherwise, use metadata count or products length
|
|
28
|
+
const hasPostQueryFilters = priceRangeFilter || promotionFilter;
|
|
29
|
+
const count = hasPostQueryFilters
|
|
30
|
+
? products.length
|
|
31
|
+
: await this.getCount(queryFilters, metadata, products.length);
|
|
32
|
+
return {
|
|
33
|
+
products,
|
|
34
|
+
count,
|
|
35
|
+
metadata: {
|
|
36
|
+
count,
|
|
37
|
+
skip: pagination?.offset,
|
|
38
|
+
take: pagination?.limit || (hasPostQueryFilters ? undefined : 20),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
resolveRegistry(container) {
|
|
43
|
+
try {
|
|
44
|
+
return container.resolve("filterProviderRegistry");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
const registry = new filter_provider_registry_1.FilterProviderRegistry();
|
|
48
|
+
this.autoRegisterBuiltInProviders(registry);
|
|
49
|
+
return registry;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
autoRegisterBuiltInProviders(registry) {
|
|
53
|
+
try {
|
|
54
|
+
const { registerBuiltInProviders } = require("../providers/filter-providers");
|
|
55
|
+
registerBuiltInProviders(registry);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.warn("[DynamicFilterService] Failed to auto-register built-in providers:", error);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async processFilters(filterParams, filterContext) {
|
|
62
|
+
let queryFilters = {};
|
|
63
|
+
let priceRangeFilter;
|
|
64
|
+
let promotionFilter;
|
|
65
|
+
for (const [identifier, value] of Object.entries(filterParams)) {
|
|
66
|
+
if (value == null)
|
|
67
|
+
continue;
|
|
68
|
+
const provider = this.registry.get(identifier);
|
|
69
|
+
if (!provider) {
|
|
70
|
+
console.warn(`[DynamicFilterService] No filter provider found for: "${identifier}"`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
this.validateWithProvider(provider, value);
|
|
75
|
+
const result = await Promise.resolve(provider.apply(queryFilters, value, filterContext));
|
|
76
|
+
queryFilters = result;
|
|
77
|
+
priceRangeFilter = queryFilters.__price_range_filter__;
|
|
78
|
+
promotionFilter = queryFilters.__promotion_filter__;
|
|
79
|
+
delete queryFilters.__price_range_filter__;
|
|
80
|
+
delete queryFilters.__promotion_filter__;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw new Error(`Error applying filter "${identifier}": ${error}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { queryFilters, priceRangeFilter, promotionFilter };
|
|
87
|
+
}
|
|
88
|
+
validateWithProvider(provider, value) {
|
|
89
|
+
if (provider.validate) {
|
|
90
|
+
const validationResult = provider.validate(value);
|
|
91
|
+
if (Array.isArray(validationResult)) {
|
|
92
|
+
const errors = validationResult.map(err => `${err.path ? `${err.path}: ` : ""}${err.message}`).join(", ");
|
|
93
|
+
throw new Error(errors);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
buildFields(requestedFields) {
|
|
98
|
+
const essentialRelations = [
|
|
99
|
+
"variants.*", "variants.prices.*",
|
|
100
|
+
"options.*", "options.values.*",
|
|
101
|
+
"images.*", "collection.*", "type.*"
|
|
102
|
+
];
|
|
103
|
+
if (!requestedFields?.length || requestedFields.includes("*")) {
|
|
104
|
+
return ["*", ...essentialRelations];
|
|
105
|
+
}
|
|
106
|
+
const fields = [...requestedFields];
|
|
107
|
+
const has = (prefix) => fields.some(f => f.startsWith(prefix));
|
|
108
|
+
if (!has("variants"))
|
|
109
|
+
fields.push("variants.*", "variants.prices.*");
|
|
110
|
+
if (!has("options"))
|
|
111
|
+
fields.push("options.*", "options.values.*");
|
|
112
|
+
if (!has("images"))
|
|
113
|
+
fields.push("images.*");
|
|
114
|
+
if (!has("collection"))
|
|
115
|
+
fields.push("collection.*");
|
|
116
|
+
if (!has("type"))
|
|
117
|
+
fields.push("type.*");
|
|
118
|
+
return fields;
|
|
119
|
+
}
|
|
120
|
+
buildQueryContext(context) {
|
|
121
|
+
if (context?.pricingContext) {
|
|
122
|
+
return { variants: { calculated_price: context.pricingContext } };
|
|
123
|
+
}
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
buildQueryOptions(filters, fields, pagination, queryContext) {
|
|
127
|
+
const options = {
|
|
128
|
+
entity: "product",
|
|
129
|
+
fields,
|
|
130
|
+
...(Object.keys(filters).length > 0 && { filters }),
|
|
131
|
+
};
|
|
132
|
+
if (pagination) {
|
|
133
|
+
options.pagination = {};
|
|
134
|
+
// Ensure default limit if limit is 0 or undefined
|
|
135
|
+
if (pagination.limit !== undefined && pagination.limit > 0) {
|
|
136
|
+
options.pagination.take = pagination.limit;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// Default limit when limit is 0 or undefined
|
|
140
|
+
options.pagination.take = 20;
|
|
141
|
+
}
|
|
142
|
+
if (pagination.offset !== undefined) {
|
|
143
|
+
options.pagination.skip = pagination.offset;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// If no pagination provided, set default
|
|
148
|
+
options.pagination = {
|
|
149
|
+
take: 20,
|
|
150
|
+
skip: 0
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (queryContext && Object.keys(queryContext).length > 0) {
|
|
154
|
+
options.context = queryContext;
|
|
155
|
+
}
|
|
156
|
+
return options;
|
|
157
|
+
}
|
|
158
|
+
normalizeProducts(productsRaw) {
|
|
159
|
+
if (Array.isArray(productsRaw)) {
|
|
160
|
+
return productsRaw.filter(p => p != null);
|
|
161
|
+
}
|
|
162
|
+
console.warn("[DynamicFilterService] Unexpected data structure from query.graph()");
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
async applyPostQueryFilters(products, priceRangeFilter, promotionFilter) {
|
|
166
|
+
let filteredProducts = products;
|
|
167
|
+
if (priceRangeFilter && filteredProducts.length > 0) {
|
|
168
|
+
filteredProducts = this.filterByPriceRange(filteredProducts, priceRangeFilter);
|
|
169
|
+
}
|
|
170
|
+
if (promotionFilter && filteredProducts.length > 0) {
|
|
171
|
+
filteredProducts = await this.filterByPromotion(filteredProducts, promotionFilter);
|
|
172
|
+
}
|
|
173
|
+
return filteredProducts;
|
|
174
|
+
}
|
|
175
|
+
filterByPriceRange(products, priceRangeFilter) {
|
|
176
|
+
return products.filter(product => {
|
|
177
|
+
if (!product.variants?.length)
|
|
178
|
+
return false;
|
|
179
|
+
return product.variants.some((variant) => {
|
|
180
|
+
const priceInfo = this.getPriceInfo(variant);
|
|
181
|
+
if (!priceInfo.price)
|
|
182
|
+
return false;
|
|
183
|
+
if (priceRangeFilter.currency_code && priceInfo.currency !== priceRangeFilter.currency_code) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
const { min, max } = priceRangeFilter;
|
|
187
|
+
if (min !== undefined && priceInfo.price < min)
|
|
188
|
+
return false;
|
|
189
|
+
if (max !== undefined && priceInfo.price > max)
|
|
190
|
+
return false;
|
|
191
|
+
return true;
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
getPriceInfo(variant) {
|
|
196
|
+
if (variant.calculated_price?.calculated_amount !== undefined) {
|
|
197
|
+
return {
|
|
198
|
+
price: variant.calculated_price.calculated_amount,
|
|
199
|
+
currency: variant.calculated_price.currency_code
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (variant.prices?.[0]) {
|
|
203
|
+
const price = variant.prices[0];
|
|
204
|
+
return { price: price.amount, currency: price.currency_code };
|
|
205
|
+
}
|
|
206
|
+
return {};
|
|
207
|
+
}
|
|
208
|
+
async filterByPromotion(products, promotionFilter) {
|
|
209
|
+
try {
|
|
210
|
+
const promotions = await this.fetchPromotions(promotionFilter);
|
|
211
|
+
// Debug logging
|
|
212
|
+
console.log(`[DynamicFilterService] Found ${promotions.length} promotions matching filter`);
|
|
213
|
+
if (promotions.length === 0) {
|
|
214
|
+
console.warn("[DynamicFilterService] No promotions found - returning empty array");
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
const productPromotionData = await this.extractProductPromotionData(promotions);
|
|
218
|
+
// Debug logging
|
|
219
|
+
console.log(`[DynamicFilterService] Extracted ${productPromotionData.productIds.size} unique product IDs from promotions`);
|
|
220
|
+
console.log(`[DynamicFilterService] Found discounts for ${productPromotionData.discounts.size} products`);
|
|
221
|
+
console.log(`[DynamicFilterService] Has universal promotions (no rules): ${productPromotionData.hasUniversalPromotions}`);
|
|
222
|
+
// If we have universal promotions (no rules = applies to all products)
|
|
223
|
+
// or if we have product IDs, filter accordingly
|
|
224
|
+
const hasProductIds = productPromotionData.productIds.size > 0;
|
|
225
|
+
const hasUniversalPromotions = productPromotionData.hasUniversalPromotions;
|
|
226
|
+
if (!hasProductIds && !hasUniversalPromotions) {
|
|
227
|
+
console.warn("[DynamicFilterService] No product IDs found and no universal promotions - returning empty array");
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
const filtered = products.filter(product => {
|
|
231
|
+
const productId = product?.id;
|
|
232
|
+
if (!productId)
|
|
233
|
+
return false;
|
|
234
|
+
// If promotion has no rules (universal), it applies to all products
|
|
235
|
+
// Otherwise, check if product is in the promotion's product list
|
|
236
|
+
if (hasProductIds && !productPromotionData.productIds.has(productId)) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
// Get discount for this product (from specific promotion or universal promotion)
|
|
240
|
+
let discount = productPromotionData.discounts.get(productId) || 0;
|
|
241
|
+
// If no specific discount found but we have universal promotions, use the universal discount
|
|
242
|
+
if (discount === 0 && hasUniversalPromotions) {
|
|
243
|
+
// Use the discount from the first universal promotion
|
|
244
|
+
discount = productPromotionData.universalDiscount || 0;
|
|
245
|
+
}
|
|
246
|
+
const meetsCriteria = this.meetsDiscountCriteria(discount, promotionFilter);
|
|
247
|
+
if (!meetsCriteria) {
|
|
248
|
+
console.log(`[DynamicFilterService] Product ${productId} discount ${discount}% does not meet criteria (min: ${promotionFilter.min_discount_percentage}, max: ${promotionFilter.max_discount_percentage})`);
|
|
249
|
+
}
|
|
250
|
+
return meetsCriteria;
|
|
251
|
+
});
|
|
252
|
+
console.log(`[DynamicFilterService] Filtered ${products.length} products down to ${filtered.length} matching promotion criteria`);
|
|
253
|
+
return filtered;
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
console.warn("[DynamicFilterService] Error filtering by promotions:", error);
|
|
257
|
+
return products;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async fetchPromotions(promotionFilter) {
|
|
261
|
+
const queryOptions = {
|
|
262
|
+
entity: "promotion",
|
|
263
|
+
fields: [
|
|
264
|
+
"id", "code", "type", "application_method.*", "campaign.*",
|
|
265
|
+
"rules.*", "rules.values.*", "starts_at", "ends_at", "status", "metadata"
|
|
266
|
+
],
|
|
267
|
+
};
|
|
268
|
+
const filters = {};
|
|
269
|
+
// Status filter (default to active if not specified)
|
|
270
|
+
if (promotionFilter.status) {
|
|
271
|
+
filters.status = promotionFilter.status;
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
// Default to active promotions
|
|
275
|
+
filters.status = "active";
|
|
276
|
+
}
|
|
277
|
+
if (promotionFilter.promotion_type) {
|
|
278
|
+
filters.type = promotionFilter.promotion_type;
|
|
279
|
+
}
|
|
280
|
+
if (Object.keys(filters).length > 0) {
|
|
281
|
+
queryOptions.filters = filters;
|
|
282
|
+
}
|
|
283
|
+
// Fetch all promotions matching status/type filters
|
|
284
|
+
// Date filtering will be done in code since Medusa query API
|
|
285
|
+
// may not support complex date range queries
|
|
286
|
+
const result = await this.query.graph(queryOptions);
|
|
287
|
+
let promotions = Array.isArray(result.data) ? result.data : [];
|
|
288
|
+
// Filter by date ranges in code
|
|
289
|
+
const now = new Date();
|
|
290
|
+
promotions = promotions.filter((promo) => {
|
|
291
|
+
// Check starts_at
|
|
292
|
+
if (promo.starts_at) {
|
|
293
|
+
const startsAt = new Date(promo.starts_at);
|
|
294
|
+
if (promotionFilter.starts_at) {
|
|
295
|
+
// If specific starts_at filter provided, check if promotion starts at or before it
|
|
296
|
+
const filterStartsAt = new Date(promotionFilter.starts_at);
|
|
297
|
+
if (startsAt > filterStartsAt)
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
// Default: only include promotions that have started
|
|
302
|
+
if (startsAt > now)
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Check ends_at
|
|
307
|
+
if (promo.ends_at) {
|
|
308
|
+
const endsAt = new Date(promo.ends_at);
|
|
309
|
+
if (promotionFilter.ends_at) {
|
|
310
|
+
// If specific ends_at filter provided, check if promotion ends at or after it
|
|
311
|
+
const filterEndsAt = new Date(promotionFilter.ends_at);
|
|
312
|
+
if (endsAt < filterEndsAt)
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
// Default: only include promotions that haven't ended yet
|
|
317
|
+
if (endsAt < now)
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// Open-ended promotion (no ends_at)
|
|
323
|
+
// Include if include_open_ended is true (default) or not explicitly false
|
|
324
|
+
if (promotionFilter.include_open_ended === false) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return true;
|
|
329
|
+
});
|
|
330
|
+
return promotions;
|
|
331
|
+
}
|
|
332
|
+
async extractProductPromotionData(promotions) {
|
|
333
|
+
const productIds = new Set();
|
|
334
|
+
const discounts = new Map();
|
|
335
|
+
let hasUniversalPromotions = false;
|
|
336
|
+
let universalDiscount = 0;
|
|
337
|
+
for (const promotion of promotions) {
|
|
338
|
+
const discount = this.calculateDiscountPercentage(promotion);
|
|
339
|
+
// Debug: Log full promotion structure for first promotion
|
|
340
|
+
if (promotions.indexOf(promotion) === 0) {
|
|
341
|
+
console.log(`[DynamicFilterService] Promotion structure:`, JSON.stringify({
|
|
342
|
+
id: promotion.id,
|
|
343
|
+
code: promotion.code,
|
|
344
|
+
type: promotion.type,
|
|
345
|
+
rules: promotion.rules,
|
|
346
|
+
application_method: promotion.application_method,
|
|
347
|
+
metadata: promotion.metadata
|
|
348
|
+
}, null, 2));
|
|
349
|
+
}
|
|
350
|
+
// Check if promotion has no rules (universal promotion - applies to all products)
|
|
351
|
+
const hasNoRules = !promotion.rules || promotion.rules.length === 0;
|
|
352
|
+
if (hasNoRules) {
|
|
353
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code} has no rules - applies to all products`);
|
|
354
|
+
hasUniversalPromotions = true;
|
|
355
|
+
if (discount !== undefined && discount > universalDiscount) {
|
|
356
|
+
universalDiscount = discount;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const ids = await this.extractProductIdsFromPromotion(promotion);
|
|
360
|
+
// Debug logging for first promotion
|
|
361
|
+
if (promotions.indexOf(promotion) === 0) {
|
|
362
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: discount=${discount}%, productIds=${ids.length}, universal=${hasNoRules}`);
|
|
363
|
+
if (ids.length > 0) {
|
|
364
|
+
console.log(`[DynamicFilterService] Sample product IDs: ${ids.slice(0, 3).join(", ")}`);
|
|
365
|
+
}
|
|
366
|
+
else if (!hasNoRules) {
|
|
367
|
+
console.warn(`[DynamicFilterService] No product IDs extracted from promotion ${promotion.id || promotion.code}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Only add product IDs if promotion has rules
|
|
371
|
+
// Universal promotions (no rules) apply to all products
|
|
372
|
+
if (!hasNoRules) {
|
|
373
|
+
ids.forEach(id => {
|
|
374
|
+
productIds.add(id);
|
|
375
|
+
if (discount !== undefined) {
|
|
376
|
+
const currentMax = discounts.get(id) || 0;
|
|
377
|
+
discounts.set(id, Math.max(currentMax, discount));
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return { productIds, discounts, hasUniversalPromotions, universalDiscount };
|
|
383
|
+
}
|
|
384
|
+
calculateDiscountPercentage(promotion) {
|
|
385
|
+
const appMethod = promotion.application_method;
|
|
386
|
+
if (!appMethod || typeof appMethod !== "object") {
|
|
387
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: No application_method`);
|
|
388
|
+
return undefined;
|
|
389
|
+
}
|
|
390
|
+
// Try to get discount from application_method.type === "percentage"
|
|
391
|
+
if (appMethod.type === "percentage" && "value" in appMethod) {
|
|
392
|
+
const value = typeof appMethod.value === "number" ? appMethod.value : Number(appMethod.value);
|
|
393
|
+
if (!Number.isNaN(value)) {
|
|
394
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: Found percentage discount ${value}% from application_method.type=percentage`);
|
|
395
|
+
return value;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Try to get discount from promotion type
|
|
399
|
+
if (promotion.type === "percentage_off_product" || promotion.type === "percentage_off_order") {
|
|
400
|
+
if ("value" in appMethod) {
|
|
401
|
+
const value = typeof appMethod.value === "number" ? appMethod.value : Number(appMethod.value);
|
|
402
|
+
if (!Number.isNaN(value)) {
|
|
403
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: Found percentage discount ${value}% from promotion.type=${promotion.type}`);
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: Could not calculate discount percentage. type=${promotion.type}, appMethod.type=${appMethod.type}, appMethod.value=${appMethod.value}`);
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
async extractProductIdsFromPromotion(promotion) {
|
|
412
|
+
const productIds = new Set();
|
|
413
|
+
// Debug: Log rule structure
|
|
414
|
+
if (promotion.rules?.length) {
|
|
415
|
+
console.log(`[DynamicFilterService] Processing ${promotion.rules.length} rules for promotion ${promotion.id || promotion.code}`);
|
|
416
|
+
for (const rule of promotion.rules) {
|
|
417
|
+
const ruleType = rule.type || rule.attribute || "";
|
|
418
|
+
const normalizedType = String(ruleType).toLowerCase();
|
|
419
|
+
console.log(`[DynamicFilterService] Rule: type=${ruleType}, attribute=${rule.attribute}, values=`, rule.values);
|
|
420
|
+
// Skip rules that definitely don't contain product IDs
|
|
421
|
+
if (normalizedType &&
|
|
422
|
+
(normalizedType === "customer_groups" ||
|
|
423
|
+
normalizedType === "regions" ||
|
|
424
|
+
normalizedType === "currency" ||
|
|
425
|
+
normalizedType === "customer_group" ||
|
|
426
|
+
normalizedType === "region")) {
|
|
427
|
+
console.log(`[DynamicFilterService] Skipping non-product rule: ${ruleType}`);
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
// Process all other rules (including product-related ones)
|
|
431
|
+
// In Medusa v2, rules.values can be:
|
|
432
|
+
// - An array of strings (product IDs)
|
|
433
|
+
// - An array of objects with id/value fields
|
|
434
|
+
// - A single value
|
|
435
|
+
let values = [];
|
|
436
|
+
if (Array.isArray(rule.values)) {
|
|
437
|
+
values = rule.values;
|
|
438
|
+
}
|
|
439
|
+
else if (rule.values !== undefined && rule.values !== null) {
|
|
440
|
+
values = [rule.values];
|
|
441
|
+
}
|
|
442
|
+
console.log(`[DynamicFilterService] Processing ${values.length} values from rule ${ruleType}`);
|
|
443
|
+
for (const value of values) {
|
|
444
|
+
if (typeof value === "string") {
|
|
445
|
+
// String values could be product IDs
|
|
446
|
+
if (value.length > 0) {
|
|
447
|
+
console.log(`[DynamicFilterService] Adding product ID from string: ${value}`);
|
|
448
|
+
productIds.add(value);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
else if (typeof value === "object" && value !== null) {
|
|
452
|
+
// Handle object values - could be { id: "prod_123" } or similar
|
|
453
|
+
console.log(`[DynamicFilterService] Processing object value:`, value);
|
|
454
|
+
if ("id" in value && typeof value.id === "string") {
|
|
455
|
+
console.log(`[DynamicFilterService] Adding product ID from object.id: ${value.id}`);
|
|
456
|
+
productIds.add(value.id);
|
|
457
|
+
}
|
|
458
|
+
else if ("value" in value && typeof value.value === "string") {
|
|
459
|
+
console.log(`[DynamicFilterService] Adding product ID from object.value: ${value.value}`);
|
|
460
|
+
productIds.add(value.value);
|
|
461
|
+
}
|
|
462
|
+
else if ("product_id" in value && typeof value.product_id === "string") {
|
|
463
|
+
console.log(`[DynamicFilterService] Adding product ID from object.product_id: ${value.product_id}`);
|
|
464
|
+
productIds.add(value.product_id);
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
// Try to extract any string field that looks like an ID
|
|
468
|
+
for (const [key, val] of Object.entries(value)) {
|
|
469
|
+
if (typeof val === "string" && val.length > 0 && (val.startsWith("prod_") || key.toLowerCase().includes("id"))) {
|
|
470
|
+
console.log(`[DynamicFilterService] Adding product ID from object.${key}: ${val}`);
|
|
471
|
+
productIds.add(val);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
console.log(`[DynamicFilterService] No rules found in promotion ${promotion.id || promotion.code}`);
|
|
481
|
+
}
|
|
482
|
+
// Also check metadata for product IDs (fallback)
|
|
483
|
+
if (promotion.metadata?.product_ids?.length) {
|
|
484
|
+
console.log(`[DynamicFilterService] Found product IDs in metadata:`, promotion.metadata.product_ids);
|
|
485
|
+
promotion.metadata.product_ids.forEach((id) => {
|
|
486
|
+
if (typeof id === "string") {
|
|
487
|
+
productIds.add(id);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
const result = Array.from(productIds);
|
|
492
|
+
console.log(`[DynamicFilterService] Extracted ${result.length} product IDs:`, result);
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
meetsDiscountCriteria(discount, promotionFilter) {
|
|
496
|
+
if (promotionFilter.min_discount_percentage !== undefined &&
|
|
497
|
+
discount < promotionFilter.min_discount_percentage) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
if (promotionFilter.max_discount_percentage !== undefined &&
|
|
501
|
+
discount > promotionFilter.max_discount_percentage) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
async getCount(queryFilters, metadata, productsLength) {
|
|
507
|
+
if (metadata?.count !== undefined)
|
|
508
|
+
return metadata.count;
|
|
509
|
+
try {
|
|
510
|
+
const countResult = await this.query.graph({
|
|
511
|
+
entity: "product",
|
|
512
|
+
fields: ["id"],
|
|
513
|
+
...(Object.keys(queryFilters).length > 0 && { filters: queryFilters }),
|
|
514
|
+
});
|
|
515
|
+
return countResult.metadata?.count ||
|
|
516
|
+
(Array.isArray(countResult.data) ? countResult.data.length : productsLength);
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
console.warn("[DynamicFilterService] Could not determine count:", error);
|
|
520
|
+
return productsLength;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
exports.DynamicFilterService = DynamicFilterService;
|
|
525
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1maWx0ZXItc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9keW5hbWljLWZpbHRlci1zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHFEQUFxRTtBQUNyRSx5RUFBbUU7QUFrQm5FLE1BQWEsb0JBQW9CO0lBSS9CLFlBQVksU0FBMEI7UUFDcEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBQy9DLElBQUksQ0FBQyxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBUSxpQ0FBeUIsQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUN4RSxDQUFDO0lBRUQsV0FBVztRQUNULE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQTtJQUN0QixDQUFDO0lBRUQsS0FBSyxDQUFDLFlBQVksQ0FBQyxPQUE0QjtRQUM3QyxNQUFNLEVBQUUsWUFBWSxFQUFFLE9BQU8sRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxPQUFPLEdBQUcsRUFBRSxFQUFFLEdBQUcsT0FBTyxDQUFBO1FBQzlGLE1BQU0sYUFBYSxHQUFrQixFQUFFLEdBQUcsT0FBTyxFQUFFLE9BQU8sRUFBRSxhQUFhLEVBQUUsQ0FBQTtRQUUzRSxNQUFNLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGVBQWUsRUFBRSxHQUFHLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxZQUFZLEVBQUUsYUFBYSxDQUFDLENBQUE7UUFFbEgsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsTUFBTSxDQUFDLENBQUE7UUFDbkQsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixDQUFDLE9BQU8sQ0FBQyxDQUFBO1FBQ3BELE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxZQUFZLEVBQUUsTUFBTSxFQUFF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
|