medusa-product-helper 0.0.16 → 0.0.19
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 +137 -7
- package/.medusa/server/src/api/store/product-helper/products/validators.js +11 -1
- package/.medusa/server/src/providers/filter-providers/availability-provider.js +53 -67
- package/.medusa/server/src/providers/filter-providers/base-filter-provider.js +1 -19
- package/.medusa/server/src/providers/filter-providers/base-product-provider.js +37 -100
- package/.medusa/server/src/providers/filter-providers/category-provider.js +15 -34
- package/.medusa/server/src/providers/filter-providers/collection-provider.js +15 -32
- package/.medusa/server/src/providers/filter-providers/index.js +13 -49
- package/.medusa/server/src/providers/filter-providers/metadata-provider.js +43 -58
- package/.medusa/server/src/providers/filter-providers/price-range-provider.js +66 -79
- package/.medusa/server/src/providers/filter-providers/promotion-provider.js +106 -169
- package/.medusa/server/src/providers/filter-providers/promotion-window-provider.js +53 -93
- package/.medusa/server/src/providers/filter-providers/rating-provider.js +47 -70
- package/.medusa/server/src/services/dynamic-filter-service.js +822 -736
- package/.medusa/server/src/services/filter-provider-loader.js +91 -139
- package/.medusa/server/src/services/filter-provider-registry.js +8 -107
- package/.medusa/server/src/services/product-filter-service.js +183 -172
- 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 +24 -76
- 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/package.json +1 -1
|
@@ -3,812 +3,898 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DynamicFilterService = void 0;
|
|
4
4
|
const utils_1 = require("@medusajs/framework/utils");
|
|
5
5
|
const filter_provider_registry_1 = require("./filter-provider-registry");
|
|
6
|
-
/**
|
|
7
|
-
* Service that orchestrates filter application using filter providers.
|
|
8
|
-
*
|
|
9
|
-
* This service coordinates between filter providers and Medusa's RemoteQuery
|
|
10
|
-
* to build and execute product queries with dynamic filters. It handles:
|
|
11
|
-
* - Resolving providers for filter identifiers
|
|
12
|
-
* - Validating filter values
|
|
13
|
-
* - Applying filters to build query filters
|
|
14
|
-
* - Executing queries with pagination and sorting
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```ts
|
|
18
|
-
* const service = new DynamicFilterService(container)
|
|
19
|
-
* const result = await service.applyFilters({
|
|
20
|
-
* filterParams: {
|
|
21
|
-
* category_id: ["cat_123"],
|
|
22
|
-
* price_range: { min: 10, max: 100 },
|
|
23
|
-
* },
|
|
24
|
-
* options: pluginOptions,
|
|
25
|
-
* pagination: { limit: 20, offset: 0 },
|
|
26
|
-
* })
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
6
|
class DynamicFilterService {
|
|
30
7
|
constructor(container) {
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
this.registry = container.resolve("filterProviderRegistry");
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// Registry not registered, create new one and register built-in providers
|
|
37
|
-
// This allows the service to work even if registry wasn't pre-registered
|
|
38
|
-
this.registry = new filter_provider_registry_1.FilterProviderRegistry();
|
|
39
|
-
// Auto-register built-in providers
|
|
40
|
-
try {
|
|
41
|
-
const { registerBuiltInProviders } = require("../providers/filter-providers");
|
|
42
|
-
registerBuiltInProviders(this.registry);
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
// If registration fails, log but continue
|
|
46
|
-
console.warn("[DynamicFilterService] Failed to auto-register built-in providers:", error instanceof Error ? error.message : String(error));
|
|
47
|
-
}
|
|
48
|
-
}
|
|
8
|
+
this.registry = this.resolveRegistry(container);
|
|
49
9
|
this.query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
50
10
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Get the filter provider registry.
|
|
53
|
-
* Useful for registering providers programmatically.
|
|
54
|
-
*/
|
|
55
11
|
getRegistry() {
|
|
56
12
|
return this.registry;
|
|
57
13
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Apply filters to a product query and return results.
|
|
60
|
-
*
|
|
61
|
-
* This method:
|
|
62
|
-
* 1. Iterates through filter parameters
|
|
63
|
-
* 2. Resolves providers for each filter identifier
|
|
64
|
-
* 3. Validates filter values (if provider has validate method)
|
|
65
|
-
* 4. Applies filters to build the query filters object
|
|
66
|
-
* 5. Executes the query with pagination and sorting
|
|
67
|
-
* 6. Returns products and count
|
|
68
|
-
*
|
|
69
|
-
* @param options - Options for applying filters
|
|
70
|
-
* @returns Products and count matching the filters
|
|
71
|
-
* @throws Error if provider validation fails or provider application fails (fail-fast)
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```ts
|
|
75
|
-
* const result = await service.applyFilters({
|
|
76
|
-
* filterParams: {
|
|
77
|
-
* category_id: ["cat_123", "cat_456"],
|
|
78
|
-
* price_range: { min: 10, max: 100 },
|
|
79
|
-
* },
|
|
80
|
-
* options: pluginOptions,
|
|
81
|
-
* pagination: { limit: 20, offset: 0 },
|
|
82
|
-
* projection: { fields: ["id", "title", "handle"] },
|
|
83
|
-
* })
|
|
84
|
-
* ```
|
|
85
|
-
*/
|
|
86
14
|
async applyFilters(options) {
|
|
87
|
-
const { filterParams, options: pluginOptions, pagination, projection, context = {}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
15
|
+
const { filterParams, options: pluginOptions, pagination, projection, context = {} } = options;
|
|
16
|
+
const filterContext = { ...context, options: pluginOptions };
|
|
17
|
+
console.log(`[DynamicFilterService] applyFilters called with context:`, {
|
|
18
|
+
hasPricingContext: !!context.pricingContext,
|
|
19
|
+
pricingContextKeys: context.pricingContext ? Object.keys(context.pricingContext) : []
|
|
20
|
+
});
|
|
21
|
+
const { queryFilters, priceRangeFilter, promotionFilter, metadataFilter } = await this.processFilters(filterParams, filterContext);
|
|
22
|
+
// Check if pricing context has currency_code before including calculated_price
|
|
23
|
+
const pricingContext = context?.pricingContext;
|
|
24
|
+
console.log(`[DynamicFilterService] Pricing context check:`, {
|
|
25
|
+
hasPricingContext: !!pricingContext,
|
|
26
|
+
currencyCode: pricingContext?.currency_code,
|
|
27
|
+
currencyCodeType: typeof pricingContext?.currency_code
|
|
28
|
+
});
|
|
29
|
+
const hasValidPricingContext = !!pricingContext && typeof pricingContext.currency_code === "string" && pricingContext.currency_code.trim().length > 0;
|
|
30
|
+
console.log(`[DynamicFilterService] Has valid pricing context:`, hasValidPricingContext);
|
|
31
|
+
// Build query context first to verify it's valid
|
|
32
|
+
const queryContext = this.buildQueryContext(context);
|
|
33
|
+
console.log(`[DynamicFilterService] Query context built:`, {
|
|
34
|
+
hasVariants: !!queryContext.variants,
|
|
35
|
+
hasCalculatedPrice: !!queryContext.variants?.calculated_price,
|
|
36
|
+
calculatedPriceKeys: queryContext.variants?.calculated_price ? Object.keys(queryContext.variants.calculated_price) : []
|
|
37
|
+
});
|
|
38
|
+
// Only include calculated_price in fields if query context has the proper structure
|
|
39
|
+
// This ensures we don't request calculated_price if currency_code is missing
|
|
40
|
+
const hasValidQueryContext = !!(queryContext.variants?.calculated_price);
|
|
41
|
+
console.log(`[DynamicFilterService] Has valid query context for calculated_price:`, hasValidQueryContext);
|
|
42
|
+
const fields = this.buildFields(projection?.fields, hasValidQueryContext);
|
|
43
|
+
console.log(`[DynamicFilterService] Fields built, includes calculated_price:`, fields.some(f => f.includes("calculated_price")));
|
|
44
|
+
const queryOptions = this.buildQueryOptions(queryFilters, fields, pagination, queryContext);
|
|
45
|
+
const result = await this.query.graph(queryOptions);
|
|
46
|
+
const { data: productsRaw = [], metadata } = result;
|
|
47
|
+
let products = this.normalizeProducts(productsRaw);
|
|
48
|
+
// Extract currency_code from pricing context for currency filtering
|
|
49
|
+
const currencyCode = pricingContext?.currency_code;
|
|
50
|
+
products = await this.applyPostQueryFilters(products, priceRangeFilter, promotionFilter, metadataFilter, currencyCode);
|
|
51
|
+
// Count should reflect filtered products, not initial query count
|
|
52
|
+
// If post-query filters were applied, use filtered products length
|
|
53
|
+
// Otherwise, use metadata count or products length
|
|
54
|
+
const hasPostQueryFilters = priceRangeFilter || promotionFilter || metadataFilter;
|
|
55
|
+
const count = hasPostQueryFilters
|
|
56
|
+
? products.length
|
|
57
|
+
: await this.getCount(queryFilters, metadata, products.length);
|
|
58
|
+
return {
|
|
59
|
+
products,
|
|
60
|
+
count,
|
|
61
|
+
metadata: {
|
|
62
|
+
count,
|
|
63
|
+
skip: pagination?.offset,
|
|
64
|
+
take: pagination?.limit || (hasPostQueryFilters ? undefined : 20),
|
|
65
|
+
},
|
|
92
66
|
};
|
|
93
|
-
|
|
67
|
+
}
|
|
68
|
+
resolveRegistry(container) {
|
|
69
|
+
try {
|
|
70
|
+
return container.resolve("filterProviderRegistry");
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
const registry = new filter_provider_registry_1.FilterProviderRegistry();
|
|
74
|
+
this.autoRegisterBuiltInProviders(registry);
|
|
75
|
+
return registry;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
autoRegisterBuiltInProviders(registry) {
|
|
79
|
+
try {
|
|
80
|
+
const { registerBuiltInProviders } = require("../providers/filter-providers");
|
|
81
|
+
registerBuiltInProviders(registry);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.warn("[DynamicFilterService] Failed to auto-register built-in providers:", error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async processFilters(filterParams, filterContext) {
|
|
94
88
|
let queryFilters = {};
|
|
95
89
|
let priceRangeFilter;
|
|
96
90
|
let promotionFilter;
|
|
97
|
-
|
|
91
|
+
let metadataFilter;
|
|
98
92
|
for (const [identifier, value] of Object.entries(filterParams)) {
|
|
99
|
-
|
|
100
|
-
if (value === undefined || value === null) {
|
|
93
|
+
if (value == null)
|
|
101
94
|
continue;
|
|
102
|
-
}
|
|
103
95
|
const provider = this.registry.get(identifier);
|
|
104
96
|
if (!provider) {
|
|
105
|
-
|
|
106
|
-
// This enables custom providers to be added without breaking existing code
|
|
107
|
-
console.warn(`[DynamicFilterService] No filter provider found for identifier: "${identifier}". ` +
|
|
108
|
-
`Filter will be ignored. Available providers: ${this.registry.getIdentifiers().join(", ")}`);
|
|
97
|
+
console.warn(`[DynamicFilterService] No filter provider found for: "${identifier}"`);
|
|
109
98
|
continue;
|
|
110
99
|
}
|
|
111
100
|
try {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
.map((err) => `${err.path ? `${err.path}: ` : ""}${err.message}`)
|
|
119
|
-
.join(", ");
|
|
120
|
-
throw new Error(`Filter validation failed for "${identifier}": ${errors}`);
|
|
121
|
-
}
|
|
122
|
-
// If validate() throws, it will be caught below
|
|
123
|
-
}
|
|
124
|
-
// Apply filter
|
|
125
|
-
const result = provider.apply(queryFilters, value, filterContext);
|
|
126
|
-
queryFilters = await Promise.resolve(result);
|
|
127
|
-
// Extract price range filter for post-query filtering
|
|
128
|
-
// Price filtering requires post-query filtering because Medusa's query API
|
|
129
|
-
// doesn't support price range filtering directly
|
|
130
|
-
if (queryFilters.__price_range_filter__) {
|
|
101
|
+
this.validateWithProvider(provider, value);
|
|
102
|
+
const result = await Promise.resolve(provider.apply(queryFilters, value, filterContext));
|
|
103
|
+
queryFilters = result;
|
|
104
|
+
// Extract special filter keys, but preserve existing values if not set by this provider
|
|
105
|
+
// This ensures filters work independently - each filter can be applied alone or with others
|
|
106
|
+
if (queryFilters.__price_range_filter__ !== undefined) {
|
|
131
107
|
priceRangeFilter = queryFilters.__price_range_filter__;
|
|
132
|
-
// Remove from queryFilters so it doesn't get passed to the query
|
|
133
|
-
delete queryFilters.__price_range_filter__;
|
|
134
108
|
}
|
|
135
|
-
|
|
136
|
-
// Promotion filtering requires querying Medusa's promotion module and
|
|
137
|
-
// matching products via promotion rules/targets, which can't be done directly
|
|
138
|
-
if (queryFilters.__promotion_filter__) {
|
|
109
|
+
if (queryFilters.__promotion_filter__ !== undefined) {
|
|
139
110
|
promotionFilter = queryFilters.__promotion_filter__;
|
|
140
|
-
// Remove from queryFilters so it doesn't get passed to the query
|
|
141
|
-
delete queryFilters.__promotion_filter__;
|
|
142
111
|
}
|
|
112
|
+
if (queryFilters.__metadata_filter__ !== undefined) {
|
|
113
|
+
metadataFilter = queryFilters.__metadata_filter__;
|
|
114
|
+
}
|
|
115
|
+
delete queryFilters.__price_range_filter__;
|
|
116
|
+
delete queryFilters.__promotion_filter__;
|
|
117
|
+
delete queryFilters.__metadata_filter__;
|
|
143
118
|
}
|
|
144
119
|
catch (error) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
120
|
+
throw new Error(`Error applying filter "${identifier}": ${error}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { queryFilters, priceRangeFilter, promotionFilter, metadataFilter };
|
|
124
|
+
}
|
|
125
|
+
validateWithProvider(provider, value) {
|
|
126
|
+
if (provider.validate) {
|
|
127
|
+
const validationResult = provider.validate(value);
|
|
128
|
+
if (Array.isArray(validationResult)) {
|
|
129
|
+
const errors = validationResult.map(err => `${err.path ? `${err.path}: ` : ""}${err.message}`).join(", ");
|
|
130
|
+
throw new Error(errors);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
buildFields(requestedFields, hasPricingContext) {
|
|
135
|
+
console.log(`[DynamicFilterService] buildFields called:`, {
|
|
136
|
+
hasPricingContext,
|
|
137
|
+
requestedFieldsCount: requestedFields?.length || 0
|
|
138
|
+
});
|
|
139
|
+
const essentialRelations = [
|
|
140
|
+
"variants.*", "variants.prices.*", // Always include all prices for multi-currency support
|
|
141
|
+
"options.*", "options.values.*",
|
|
142
|
+
"images.*", "collection.*", "type.*"
|
|
143
|
+
];
|
|
144
|
+
// Include calculated_price ONLY if pricing context is available AND valid
|
|
145
|
+
// This is a critical check - we should never include calculated_price without currency_code
|
|
146
|
+
if (hasPricingContext) {
|
|
147
|
+
essentialRelations.push("variants.calculated_price.*");
|
|
148
|
+
console.log(`[DynamicFilterService] Added calculated_price.* to essential relations`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log(`[DynamicFilterService] NOT adding calculated_price.* (hasPricingContext=false)`);
|
|
152
|
+
}
|
|
153
|
+
if (!requestedFields?.length || requestedFields.includes("*")) {
|
|
154
|
+
const result = ["*", ...essentialRelations];
|
|
155
|
+
console.log(`[DynamicFilterService] Returning all fields with essential relations, includes calculated_price:`, result.some(f => f.includes("calculated_price")));
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
const fields = [...requestedFields];
|
|
159
|
+
const has = (prefix) => fields.some(f => f.startsWith(prefix));
|
|
160
|
+
if (!has("variants")) {
|
|
161
|
+
fields.push("variants.*", "variants.prices.*"); // Always include all prices
|
|
162
|
+
if (hasPricingContext) {
|
|
163
|
+
fields.push("variants.calculated_price.*");
|
|
164
|
+
console.log(`[DynamicFilterService] Added calculated_price.* to fields`);
|
|
165
|
+
}
|
|
172
166
|
}
|
|
173
167
|
else {
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
168
|
+
// Even if variants are requested, ensure prices are included
|
|
169
|
+
if (!has("variants.prices")) {
|
|
170
|
+
fields.push("variants.prices.*");
|
|
171
|
+
}
|
|
172
|
+
// Check if calculated_price is already in requested fields
|
|
173
|
+
if (hasPricingContext && !has("variants.calculated_price")) {
|
|
174
|
+
fields.push("variants.calculated_price.*");
|
|
175
|
+
console.log(`[DynamicFilterService] Added calculated_price.* to fields (variants already present)`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!has("options"))
|
|
179
|
+
fields.push("options.*", "options.values.*");
|
|
180
|
+
if (!has("images"))
|
|
181
|
+
fields.push("images.*");
|
|
182
|
+
if (!has("collection"))
|
|
183
|
+
fields.push("collection.*");
|
|
184
|
+
if (!has("type"))
|
|
185
|
+
fields.push("type.*");
|
|
186
|
+
const includesCalculatedPrice = fields.some(f => f.includes("calculated_price"));
|
|
187
|
+
console.log(`[DynamicFilterService] Final fields include calculated_price:`, includesCalculatedPrice);
|
|
188
|
+
return fields;
|
|
189
|
+
}
|
|
190
|
+
buildQueryContext(context) {
|
|
191
|
+
const pricingContext = context?.pricingContext;
|
|
192
|
+
// Only include calculated_price if currency_code is present and valid in pricing context
|
|
193
|
+
// Medusa's calculatePrices method requires currency_code to be a non-empty string
|
|
194
|
+
if (pricingContext) {
|
|
195
|
+
const currencyCode = pricingContext.currency_code;
|
|
196
|
+
if (typeof currencyCode === "string" && currencyCode.trim().length > 0) {
|
|
197
|
+
// Ensure currency_code is trimmed and set in the context we pass to Medusa
|
|
198
|
+
const validPricingContext = {
|
|
199
|
+
...pricingContext,
|
|
200
|
+
currency_code: currencyCode.trim()
|
|
202
201
|
};
|
|
202
|
+
// Double-check currency_code is present before returning
|
|
203
|
+
if (validPricingContext.currency_code) {
|
|
204
|
+
// Use QueryContext() helper to properly wrap the pricing context
|
|
205
|
+
// This is required by Medusa's query.graph() for calculated_price
|
|
206
|
+
const pricingContextForLogging = validPricingContext;
|
|
207
|
+
console.log(`[DynamicFilterService] Wrapping pricing context with QueryContext():`, {
|
|
208
|
+
currency_code: pricingContextForLogging.currency_code,
|
|
209
|
+
region_id: pricingContextForLogging.region_id,
|
|
210
|
+
allKeys: Object.keys(pricingContextForLogging)
|
|
211
|
+
});
|
|
212
|
+
const wrappedContext = (0, utils_1.QueryContext)(validPricingContext);
|
|
213
|
+
console.log(`[DynamicFilterService] QueryContext() returned:`, {
|
|
214
|
+
type: typeof wrappedContext,
|
|
215
|
+
isFunction: typeof wrappedContext === "function",
|
|
216
|
+
keys: typeof wrappedContext === "object" && wrappedContext !== null ? Object.keys(wrappedContext) : "N/A"
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
variants: {
|
|
220
|
+
calculated_price: wrappedContext
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
console.warn("[DynamicFilterService] Pricing context exists but currency_code is missing or invalid:", {
|
|
227
|
+
hasPricingContext: !!pricingContext,
|
|
228
|
+
currencyCodeType: typeof currencyCode,
|
|
229
|
+
currencyCodeValue: currencyCode
|
|
230
|
+
});
|
|
203
231
|
}
|
|
204
232
|
}
|
|
205
|
-
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
buildQueryOptions(filters, fields, pagination, queryContext) {
|
|
236
|
+
console.log(`[DynamicFilterService] buildQueryOptions called`);
|
|
237
|
+
// Note: QueryContext() wraps the pricing context, so we can't directly access currency_code
|
|
238
|
+
// from the wrapped object. We trust that buildQueryContext() already validated it.
|
|
239
|
+
// If queryContext has calculated_price, it means currency_code was validated before wrapping.
|
|
240
|
+
let safeQueryContext = queryContext;
|
|
241
|
+
if (queryContext?.variants?.calculated_price) {
|
|
242
|
+
// QueryContext() wraps the context, so we can't check currency_code here
|
|
243
|
+
// But we know it was validated in buildQueryContext() before wrapping
|
|
244
|
+
console.log(`[DynamicFilterService] Query context includes calculated_price (wrapped with QueryContext())`);
|
|
245
|
+
}
|
|
246
|
+
const options = {
|
|
206
247
|
entity: "product",
|
|
207
248
|
fields,
|
|
208
|
-
...(Object.keys(
|
|
209
|
-
...(pagination && {
|
|
210
|
-
pagination: {
|
|
211
|
-
...(pagination.limit !== undefined && { take: pagination.limit }),
|
|
212
|
-
...(pagination.offset !== undefined && { skip: pagination.offset }),
|
|
213
|
-
},
|
|
214
|
-
}),
|
|
215
|
-
...(Object.keys(queryContext).length > 0 && { context: queryContext }),
|
|
249
|
+
...(Object.keys(filters).length > 0 && { filters }),
|
|
216
250
|
};
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
// Ensure products is always an array and filter out null/undefined
|
|
231
|
-
let products = [];
|
|
232
|
-
if (Array.isArray(productsRaw)) {
|
|
233
|
-
products = productsRaw.filter((p) => p !== null && p !== undefined);
|
|
251
|
+
if (pagination) {
|
|
252
|
+
options.pagination = {};
|
|
253
|
+
// Ensure default limit if limit is 0 or undefined
|
|
254
|
+
if (pagination.limit !== undefined && pagination.limit > 0) {
|
|
255
|
+
options.pagination.take = pagination.limit;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// Default limit when limit is 0 or undefined
|
|
259
|
+
options.pagination.take = 20;
|
|
260
|
+
}
|
|
261
|
+
if (pagination.offset !== undefined) {
|
|
262
|
+
options.pagination.skip = pagination.offset;
|
|
263
|
+
}
|
|
234
264
|
}
|
|
235
265
|
else {
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
266
|
+
// If no pagination provided, set default
|
|
267
|
+
options.pagination = {
|
|
268
|
+
take: 20,
|
|
269
|
+
skip: 0
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (safeQueryContext && Object.keys(safeQueryContext).length > 0) {
|
|
273
|
+
options.context = safeQueryContext;
|
|
274
|
+
console.log(`[DynamicFilterService] Query options include context with calculated_price:`, !!safeQueryContext.variants?.calculated_price);
|
|
243
275
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
276
|
+
else {
|
|
277
|
+
console.log(`[DynamicFilterService] Query options do not include context`);
|
|
278
|
+
}
|
|
279
|
+
console.log(`[DynamicFilterService] Final query options:`, {
|
|
280
|
+
entity: options.entity,
|
|
281
|
+
fieldsCount: options.fields?.length || 0,
|
|
282
|
+
hasContext: !!options.context,
|
|
283
|
+
hasCalculatedPrice: !!options.context?.variants?.calculated_price,
|
|
284
|
+
filtersCount: Object.keys(options.filters || {}).length
|
|
285
|
+
});
|
|
286
|
+
return options;
|
|
287
|
+
}
|
|
288
|
+
normalizeProducts(productsRaw) {
|
|
289
|
+
if (Array.isArray(productsRaw)) {
|
|
290
|
+
return productsRaw.filter(p => p != null);
|
|
291
|
+
}
|
|
292
|
+
console.warn("[DynamicFilterService] Unexpected data structure from query.graph()");
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
async applyPostQueryFilters(products, priceRangeFilter, promotionFilter, metadataFilter, currencyCode) {
|
|
296
|
+
let filteredProducts = products;
|
|
297
|
+
// Apply currency filter first - only include products available in the selected currency
|
|
298
|
+
if (currencyCode && filteredProducts.length > 0) {
|
|
299
|
+
console.log(`[DynamicFilterService] Applying currency filter: ${currencyCode} to ${filteredProducts.length} products`);
|
|
300
|
+
filteredProducts = this.filterByCurrency(filteredProducts, currencyCode);
|
|
301
|
+
console.log(`[DynamicFilterService] Filtered to ${filteredProducts.length} products available in ${currencyCode}`);
|
|
302
|
+
}
|
|
303
|
+
if (metadataFilter && filteredProducts.length > 0) {
|
|
304
|
+
filteredProducts = this.filterByMetadata(filteredProducts, metadataFilter);
|
|
305
|
+
}
|
|
306
|
+
if (priceRangeFilter && filteredProducts.length > 0) {
|
|
307
|
+
console.log(`[DynamicFilterService] Applying price range filter:`, priceRangeFilter, `to ${filteredProducts.length} products`);
|
|
308
|
+
filteredProducts = this.filterByPriceRange(filteredProducts, priceRangeFilter, currencyCode);
|
|
309
|
+
console.log(`[DynamicFilterService] Filtered to ${filteredProducts.length} products`);
|
|
310
|
+
}
|
|
311
|
+
if (promotionFilter && filteredProducts.length > 0) {
|
|
312
|
+
filteredProducts = await this.filterByPromotion(filteredProducts, promotionFilter);
|
|
313
|
+
}
|
|
314
|
+
return filteredProducts;
|
|
315
|
+
}
|
|
316
|
+
filterByMetadata(products, metadataFilter) {
|
|
317
|
+
return products.filter(product => {
|
|
318
|
+
const productMetadata = product.metadata || {};
|
|
319
|
+
// Check each metadata filter criteria
|
|
320
|
+
for (const [key, filterValue] of Object.entries(metadataFilter)) {
|
|
321
|
+
const productValue = productMetadata[key];
|
|
322
|
+
// If product doesn't have this metadata key, it doesn't match
|
|
323
|
+
if (productValue === undefined || productValue === null) {
|
|
251
324
|
return false;
|
|
252
325
|
}
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
//
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
else if (variant.prices && Array.isArray(variant.prices) && variant.prices.length > 0) {
|
|
263
|
-
// Fall back to first price in prices array
|
|
264
|
-
const firstPrice = variant.prices[0];
|
|
265
|
-
price = firstPrice.amount;
|
|
266
|
-
currencyCode = firstPrice.currency_code;
|
|
326
|
+
// Handle array filter values (e.g., metadata[color]=["red","blue"])
|
|
327
|
+
if (Array.isArray(filterValue)) {
|
|
328
|
+
// Product value should be in the filter array
|
|
329
|
+
if (Array.isArray(productValue)) {
|
|
330
|
+
// Both are arrays - check if any product value matches any filter value
|
|
331
|
+
const matches = productValue.some((pv) => filterValue.some((fv) => this.metadataValuesMatch(pv, fv)));
|
|
332
|
+
if (!matches)
|
|
333
|
+
return false;
|
|
267
334
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
335
|
+
else {
|
|
336
|
+
// Product value is single, filter is array - check if product value matches any filter value
|
|
337
|
+
const matches = filterValue.some((fv) => this.metadataValuesMatch(productValue, fv));
|
|
338
|
+
if (!matches)
|
|
339
|
+
return false;
|
|
271
340
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
// Single value filter
|
|
344
|
+
if (Array.isArray(productValue)) {
|
|
345
|
+
// Product value is array, filter is single - check if any product value matches filter
|
|
346
|
+
const matches = productValue.some((pv) => this.metadataValuesMatch(pv, filterValue));
|
|
347
|
+
if (!matches)
|
|
348
|
+
return false;
|
|
275
349
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
350
|
+
else {
|
|
351
|
+
// Both are single values - direct comparison
|
|
352
|
+
if (!this.metadataValuesMatch(productValue, filterValue)) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
281
355
|
}
|
|
282
|
-
|
|
283
|
-
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return true;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
metadataValuesMatch(productValue, filterValue) {
|
|
362
|
+
// Normalize string comparison (case-insensitive)
|
|
363
|
+
if (typeof productValue === "string" && typeof filterValue === "string") {
|
|
364
|
+
return productValue.toLowerCase() === filterValue.toLowerCase();
|
|
365
|
+
}
|
|
366
|
+
// Direct equality for numbers and booleans
|
|
367
|
+
return productValue === filterValue;
|
|
368
|
+
}
|
|
369
|
+
filterByPriceRange(products, priceRangeFilter, currencyCode) {
|
|
370
|
+
const { min, max } = priceRangeFilter;
|
|
371
|
+
// Use currency_code from filter, context, or parameter (priority: filter > parameter > context)
|
|
372
|
+
const targetCurrency = priceRangeFilter.currency_code || currencyCode;
|
|
373
|
+
console.log(`[DynamicFilterService] Filtering ${products.length} products by price range: min=${min}, max=${max}, currency=${targetCurrency}`);
|
|
374
|
+
const filtered = products.filter(product => {
|
|
375
|
+
if (!product.variants?.length)
|
|
376
|
+
return false;
|
|
377
|
+
return product.variants.some((variant) => {
|
|
378
|
+
// Get all prices for this variant
|
|
379
|
+
const prices = variant.prices || [];
|
|
380
|
+
// Find price matching the target currency
|
|
381
|
+
const matchingPrice = prices.find((price) => {
|
|
382
|
+
if (!targetCurrency)
|
|
383
|
+
return true; // If no currency specified, use first price
|
|
384
|
+
return price.currency_code === targetCurrency;
|
|
385
|
+
});
|
|
386
|
+
// If no matching price found and currency is required, exclude this variant
|
|
387
|
+
if (targetCurrency && !matchingPrice) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
// Use calculated_price if available (includes price list overrides)
|
|
391
|
+
let priceToUse;
|
|
392
|
+
let currencyToUse;
|
|
393
|
+
if (variant.calculated_price?.calculated_amount !== undefined) {
|
|
394
|
+
// Only use calculated_price if it matches the target currency
|
|
395
|
+
if (!targetCurrency || variant.calculated_price.currency_code === targetCurrency) {
|
|
396
|
+
priceToUse = variant.calculated_price.calculated_amount;
|
|
397
|
+
currencyToUse = variant.calculated_price.currency_code;
|
|
284
398
|
}
|
|
399
|
+
}
|
|
400
|
+
// Fall back to regular price if calculated_price not available or doesn't match
|
|
401
|
+
if (priceToUse === undefined && matchingPrice) {
|
|
402
|
+
priceToUse = matchingPrice.amount;
|
|
403
|
+
currencyToUse = matchingPrice.currency_code;
|
|
404
|
+
}
|
|
405
|
+
if (priceToUse === undefined)
|
|
406
|
+
return false;
|
|
407
|
+
// Ensure currency matches if target currency is specified
|
|
408
|
+
if (targetCurrency && currencyToUse !== targetCurrency) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
if (min !== undefined && priceToUse < min)
|
|
412
|
+
return false;
|
|
413
|
+
if (max !== undefined && priceToUse > max)
|
|
414
|
+
return false;
|
|
415
|
+
return true;
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
console.log(`[DynamicFilterService] Filtered ${products.length} products down to ${filtered.length} matching price range`);
|
|
419
|
+
return filtered;
|
|
420
|
+
}
|
|
421
|
+
filterByCurrency(products, currencyCode) {
|
|
422
|
+
console.log(`[DynamicFilterService] Filtering ${products.length} products by currency: ${currencyCode}`);
|
|
423
|
+
const filtered = products.filter(product => {
|
|
424
|
+
if (!product.variants?.length)
|
|
425
|
+
return false;
|
|
426
|
+
// Check if at least one variant has a price in the target currency
|
|
427
|
+
return product.variants.some((variant) => {
|
|
428
|
+
// Check calculated_price first (if available and matches currency)
|
|
429
|
+
if (variant.calculated_price?.currency_code === currencyCode) {
|
|
285
430
|
return true;
|
|
286
|
-
}
|
|
431
|
+
}
|
|
432
|
+
// Check regular prices
|
|
433
|
+
const prices = variant.prices || [];
|
|
434
|
+
return prices.some((price) => price.currency_code === currencyCode);
|
|
287
435
|
});
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
436
|
+
});
|
|
437
|
+
console.log(`[DynamicFilterService] Filtered ${products.length} products down to ${filtered.length} available in ${currencyCode}`);
|
|
438
|
+
return filtered;
|
|
439
|
+
}
|
|
440
|
+
getPriceInfo(variant) {
|
|
441
|
+
// Prioritize calculated_price if available (includes price list overrides when pricing context is provided)
|
|
442
|
+
if (variant.calculated_price?.calculated_amount !== undefined) {
|
|
443
|
+
return {
|
|
444
|
+
price: variant.calculated_price.calculated_amount,
|
|
445
|
+
currency: variant.calculated_price.currency_code
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
// Fall back to regular price if calculated_price is not available
|
|
449
|
+
if (variant.prices?.[0]) {
|
|
450
|
+
const price = variant.prices[0];
|
|
451
|
+
return {
|
|
452
|
+
price: price.amount,
|
|
453
|
+
currency: price.currency_code
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
return {};
|
|
457
|
+
}
|
|
458
|
+
async filterByPromotion(products, promotionFilter) {
|
|
459
|
+
try {
|
|
460
|
+
const promotions = await this.fetchPromotions(promotionFilter);
|
|
461
|
+
// Debug logging
|
|
462
|
+
console.log(`[DynamicFilterService] Found ${promotions.length} promotions matching filter`);
|
|
463
|
+
if (promotions.length === 0) {
|
|
464
|
+
console.warn("[DynamicFilterService] No promotions found - returning empty array");
|
|
465
|
+
return [];
|
|
466
|
+
}
|
|
467
|
+
const productPromotionData = await this.extractProductPromotionData(promotions);
|
|
468
|
+
// Debug logging
|
|
469
|
+
console.log(`[DynamicFilterService] Extracted ${productPromotionData.productIds.size} unique product IDs from promotions`);
|
|
470
|
+
console.log(`[DynamicFilterService] Found discounts for ${productPromotionData.discounts.size} products`);
|
|
471
|
+
console.log(`[DynamicFilterService] Has universal promotions (no rules): ${productPromotionData.hasUniversalPromotions}`);
|
|
472
|
+
if (productPromotionData.universalDiscount !== undefined) {
|
|
473
|
+
console.log(`[DynamicFilterService] Universal discount: ${productPromotionData.universalDiscount}%`);
|
|
291
474
|
}
|
|
475
|
+
// Log discount filter criteria
|
|
476
|
+
if (promotionFilter.min_discount_percentage !== undefined || promotionFilter.max_discount_percentage !== undefined) {
|
|
477
|
+
console.log(`[DynamicFilterService] Filtering by discount: min=${promotionFilter.min_discount_percentage}, max=${promotionFilter.max_discount_percentage}`);
|
|
478
|
+
}
|
|
479
|
+
// Log sample discounts for debugging
|
|
480
|
+
if (productPromotionData.discounts.size > 0) {
|
|
481
|
+
const sampleDiscounts = Array.from(productPromotionData.discounts.entries()).slice(0, 5);
|
|
482
|
+
console.log(`[DynamicFilterService] Sample product discounts:`, sampleDiscounts.map(([id, discount]) => `${id.substring(0, 8)}...: ${discount}%`).join(", "));
|
|
483
|
+
}
|
|
484
|
+
// If we have universal promotions (no rules = applies to all products)
|
|
485
|
+
// or if we have product IDs, filter accordingly
|
|
486
|
+
const hasProductIds = productPromotionData.productIds.size > 0;
|
|
487
|
+
const hasUniversalPromotions = productPromotionData.hasUniversalPromotions;
|
|
488
|
+
if (!hasProductIds && !hasUniversalPromotions) {
|
|
489
|
+
console.warn("[DynamicFilterService] No product IDs found and no universal promotions - returning empty array");
|
|
490
|
+
return [];
|
|
491
|
+
}
|
|
492
|
+
const filtered = products.filter(product => {
|
|
493
|
+
const productId = product?.id;
|
|
494
|
+
if (!productId)
|
|
495
|
+
return false;
|
|
496
|
+
// Determine if this product should be included based on promotion rules
|
|
497
|
+
let shouldInclude = false;
|
|
498
|
+
let discount;
|
|
499
|
+
if (hasProductIds) {
|
|
500
|
+
// If promotion has product-specific rules, only include products in the promotion
|
|
501
|
+
if (productPromotionData.productIds.has(productId)) {
|
|
502
|
+
shouldInclude = true;
|
|
503
|
+
// Get discount for this product (from specific promotion)
|
|
504
|
+
// Don't default to 0 - if discount is undefined, the product should be excluded
|
|
505
|
+
discount = productPromotionData.discounts.get(productId);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else if (hasUniversalPromotions) {
|
|
509
|
+
// If promotion is universal (no product-specific rules), it applies to all products
|
|
510
|
+
// Use the universal discount - don't default to 0
|
|
511
|
+
discount = productPromotionData.universalDiscount;
|
|
512
|
+
shouldInclude = true;
|
|
513
|
+
}
|
|
514
|
+
// If product is not in any promotion, exclude it
|
|
515
|
+
if (!shouldInclude) {
|
|
516
|
+
console.log(`[DynamicFilterService] Product ${productId} is not in any promotion`);
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
// If product is in promotion but discount is undefined, exclude it
|
|
520
|
+
// This happens when discount calculation failed or promotion has no valid discount
|
|
521
|
+
if (discount === undefined) {
|
|
522
|
+
console.log(`[DynamicFilterService] Product ${productId} is in promotion but has no valid discount - excluding`);
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
// Check if discount meets the filter criteria
|
|
526
|
+
const meetsCriteria = this.meetsDiscountCriteria(discount, promotionFilter);
|
|
527
|
+
if (!meetsCriteria) {
|
|
528
|
+
console.log(`[DynamicFilterService] Product ${productId} discount ${discount}% does not meet criteria (min: ${promotionFilter.min_discount_percentage}, max: ${promotionFilter.max_discount_percentage})`);
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
console.log(`[DynamicFilterService] Product ${productId} discount ${discount}% meets criteria (min: ${promotionFilter.min_discount_percentage}, max: ${promotionFilter.max_discount_percentage})`);
|
|
532
|
+
}
|
|
533
|
+
return meetsCriteria;
|
|
534
|
+
});
|
|
535
|
+
console.log(`[DynamicFilterService] Filtered ${products.length} products down to ${filtered.length} matching promotion criteria`);
|
|
536
|
+
return filtered;
|
|
292
537
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
538
|
+
catch (error) {
|
|
539
|
+
console.warn("[DynamicFilterService] Error filtering by promotions:", error);
|
|
540
|
+
return products;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
async fetchPromotions(promotionFilter) {
|
|
544
|
+
const queryOptions = {
|
|
545
|
+
entity: "promotion",
|
|
546
|
+
fields: [
|
|
547
|
+
"id", "code", "type", "application_method.*", "application_method.target_rules.*",
|
|
548
|
+
"application_method.target_rules.values.*", "application_method.buy_rules.*",
|
|
549
|
+
"application_method.buy_rules.values.*", "campaign.*",
|
|
550
|
+
"rules.*", "rules.values.*", "starts_at", "ends_at", "status", "metadata"
|
|
551
|
+
],
|
|
552
|
+
};
|
|
553
|
+
const filters = {};
|
|
554
|
+
// Status filter (default to active if not specified)
|
|
555
|
+
if (promotionFilter.status) {
|
|
556
|
+
filters.status = promotionFilter.status;
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
// Default to active promotions
|
|
560
|
+
filters.status = "active";
|
|
561
|
+
}
|
|
562
|
+
if (promotionFilter.promotion_type) {
|
|
563
|
+
filters.type = promotionFilter.promotion_type;
|
|
564
|
+
}
|
|
565
|
+
if (Object.keys(filters).length > 0) {
|
|
566
|
+
queryOptions.filters = filters;
|
|
567
|
+
}
|
|
568
|
+
// Fetch all promotions matching status/type filters
|
|
569
|
+
// Date filtering will be done in code since Medusa query API
|
|
570
|
+
// may not support complex date range queries
|
|
571
|
+
const result = await this.query.graph(queryOptions);
|
|
572
|
+
let promotions = Array.isArray(result.data) ? result.data : [];
|
|
573
|
+
// Filter by date ranges in code
|
|
574
|
+
const now = new Date();
|
|
575
|
+
promotions = promotions.filter((promo) => {
|
|
576
|
+
// Check starts_at
|
|
577
|
+
if (promo.starts_at) {
|
|
578
|
+
const startsAt = new Date(promo.starts_at);
|
|
579
|
+
if (promotionFilter.starts_at) {
|
|
580
|
+
// If specific starts_at filter provided, check if promotion starts at or before it
|
|
581
|
+
const filterStartsAt = new Date(promotionFilter.starts_at);
|
|
582
|
+
if (startsAt > filterStartsAt)
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
// Default: only include promotions that have started
|
|
587
|
+
if (startsAt > now)
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
// Check ends_at
|
|
592
|
+
if (promo.ends_at) {
|
|
593
|
+
const endsAt = new Date(promo.ends_at);
|
|
594
|
+
if (promotionFilter.ends_at) {
|
|
595
|
+
// If specific ends_at filter provided, check if promotion ends at or after it
|
|
596
|
+
const filterEndsAt = new Date(promotionFilter.ends_at);
|
|
597
|
+
if (endsAt < filterEndsAt)
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
// Default: only include promotions that haven't ended yet
|
|
602
|
+
if (endsAt < now)
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
// Open-ended promotion (no ends_at)
|
|
608
|
+
// Include if include_open_ended is true (default) or not explicitly false
|
|
609
|
+
if (promotionFilter.include_open_ended === false) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return true;
|
|
614
|
+
});
|
|
615
|
+
return promotions;
|
|
616
|
+
}
|
|
617
|
+
async extractProductPromotionData(promotions) {
|
|
618
|
+
const productIds = new Set();
|
|
619
|
+
const discounts = new Map();
|
|
620
|
+
let hasUniversalPromotions = false;
|
|
621
|
+
let universalDiscount = undefined;
|
|
622
|
+
for (const promotion of promotions) {
|
|
623
|
+
const discount = this.calculateDiscountPercentage(promotion);
|
|
624
|
+
// Debug: Log full promotion structure for first promotion
|
|
625
|
+
if (promotions.indexOf(promotion) === 0) {
|
|
626
|
+
console.log(`[DynamicFilterService] Promotion structure:`, JSON.stringify({
|
|
627
|
+
id: promotion.id,
|
|
628
|
+
code: promotion.code,
|
|
629
|
+
type: promotion.type,
|
|
630
|
+
rules: promotion.rules,
|
|
631
|
+
application_method: promotion.application_method,
|
|
632
|
+
metadata: promotion.metadata
|
|
633
|
+
}, null, 2));
|
|
634
|
+
}
|
|
635
|
+
// Check if promotion has no rules (universal promotion - applies to all products)
|
|
636
|
+
const hasNoRules = !promotion.rules || promotion.rules.length === 0;
|
|
637
|
+
if (hasNoRules) {
|
|
638
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code} has no rules - applies to all products`);
|
|
639
|
+
hasUniversalPromotions = true;
|
|
640
|
+
// Only update universal discount if it's valid and greater than current
|
|
641
|
+
if (discount !== undefined) {
|
|
642
|
+
if (universalDiscount === undefined || discount > universalDiscount) {
|
|
643
|
+
universalDiscount = discount;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
console.warn(`[DynamicFilterService] Universal promotion ${promotion.id || promotion.code} has no valid discount - products will be excluded if filtering by discount`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const ids = await this.extractProductIdsFromPromotion(promotion);
|
|
651
|
+
// Debug logging for first promotion
|
|
652
|
+
if (promotions.indexOf(promotion) === 0) {
|
|
653
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: discount=${discount}%, productIds=${ids.length}, universal=${hasNoRules}`);
|
|
654
|
+
if (ids.length > 0) {
|
|
655
|
+
console.log(`[DynamicFilterService] Sample product IDs: ${ids.slice(0, 3).join(", ")}`);
|
|
656
|
+
}
|
|
657
|
+
else if (!hasNoRules) {
|
|
658
|
+
console.warn(`[DynamicFilterService] No product IDs extracted from promotion ${promotion.id || promotion.code}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
// If we found product IDs (from rules, target_rules, or buy_rules), this is NOT a universal promotion
|
|
662
|
+
// Only treat as universal if there are no rules AND no product IDs found
|
|
663
|
+
const isActuallyUniversal = hasNoRules && ids.length === 0;
|
|
664
|
+
if (isActuallyUniversal) {
|
|
665
|
+
// Universal promotion - applies to all products
|
|
666
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code} is universal (no rules, no product IDs)`);
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
// Product-specific promotion - only apply to the specified products
|
|
670
|
+
ids.forEach(id => {
|
|
671
|
+
productIds.add(id);
|
|
672
|
+
// Only store discount if it's valid (not undefined)
|
|
673
|
+
// When multiple promotions apply to the same product, use the maximum discount
|
|
674
|
+
if (discount !== undefined) {
|
|
675
|
+
const currentDiscount = discounts.get(id);
|
|
676
|
+
if (currentDiscount === undefined) {
|
|
677
|
+
discounts.set(id, discount);
|
|
346
678
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (startsAt && startsAt > filterEnd) {
|
|
350
|
-
return false; // Promotion starts after filter end
|
|
351
|
-
}
|
|
679
|
+
else {
|
|
680
|
+
discounts.set(id, Math.max(currentDiscount, discount));
|
|
352
681
|
}
|
|
353
682
|
}
|
|
354
683
|
else {
|
|
355
|
-
|
|
356
|
-
const startsAt = promotion.starts_at
|
|
357
|
-
? new Date(promotion.starts_at)
|
|
358
|
-
: null;
|
|
359
|
-
const endsAt = promotion.ends_at ? new Date(promotion.ends_at) : null;
|
|
360
|
-
if (startsAt && startsAt > now) {
|
|
361
|
-
return false; // Promotion hasn't started yet
|
|
362
|
-
}
|
|
363
|
-
if (endsAt && endsAt < now) {
|
|
364
|
-
return false; // Promotion has ended
|
|
365
|
-
}
|
|
366
|
-
// Include open-ended promotions if include_open_ended is true
|
|
367
|
-
if (!endsAt &&
|
|
368
|
-
promotionFilter.include_open_ended === false) {
|
|
369
|
-
return false; // Exclude open-ended promotions
|
|
370
|
-
}
|
|
684
|
+
console.warn(`[DynamicFilterService] Promotion ${promotion.id || promotion.code} has product ${id} but discount is undefined - product will be excluded if filtering by discount`);
|
|
371
685
|
}
|
|
372
|
-
return true;
|
|
373
686
|
});
|
|
374
|
-
//
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
console.log(`[DynamicFilterService] Sample promotion:`, JSON.stringify({
|
|
378
|
-
id: promotions[0].id,
|
|
379
|
-
code: promotions[0].code,
|
|
380
|
-
type: promotions[0].type,
|
|
381
|
-
application_method: promotions[0].application_method,
|
|
382
|
-
rules_count: promotions[0].rules?.length || 0,
|
|
383
|
-
rules: promotions[0].rules,
|
|
384
|
-
}, null, 2));
|
|
385
|
-
}
|
|
386
|
-
// Initialize product tracking sets before we start processing
|
|
387
|
-
const productIdsWithPromotions = new Set();
|
|
388
|
-
const promotionDiscountMap = new Map(); // productId -> max discount percentage
|
|
389
|
-
// If promotions have no rules, try querying promotion rules separately
|
|
390
|
-
// In Medusa v2, rules might be stored as a separate entity or relationship
|
|
391
|
-
const promotionsWithoutRules = promotions.filter((p) => !p.rules || p.rules.length === 0);
|
|
392
|
-
if (promotionsWithoutRules.length > 0) {
|
|
393
|
-
console.log(`[DynamicFilterService] Found ${promotionsWithoutRules.length} promotions without rules, querying rules separately`);
|
|
394
|
-
for (const promotion of promotionsWithoutRules) {
|
|
395
|
-
try {
|
|
396
|
-
// Try querying promotion_rule entity
|
|
397
|
-
const ruleQueryResult = await this.query.graph({
|
|
398
|
-
entity: "promotion_rule",
|
|
399
|
-
fields: ["id", "attribute", "values", "operator", "promotion_id"],
|
|
400
|
-
filters: {
|
|
401
|
-
promotion_id: [promotion.id],
|
|
402
|
-
},
|
|
403
|
-
});
|
|
404
|
-
const rules = Array.isArray(ruleQueryResult.data)
|
|
405
|
-
? ruleQueryResult.data
|
|
406
|
-
: [];
|
|
407
|
-
if (rules.length > 0) {
|
|
408
|
-
console.log(`[DynamicFilterService] Found ${rules.length} rules for promotion ${promotion.id}:`, JSON.stringify(rules, null, 2));
|
|
409
|
-
// Attach rules to promotion object
|
|
410
|
-
promotion.rules = rules;
|
|
411
|
-
}
|
|
412
|
-
else {
|
|
413
|
-
// Try querying with different entity names or structures
|
|
414
|
-
console.log(`[DynamicFilterService] No promotion_rule found for ${promotion.id}, trying alternative queries`);
|
|
415
|
-
// Try querying promotion_rule_value entity
|
|
416
|
-
try {
|
|
417
|
-
const ruleValueQueryResult = await this.query.graph({
|
|
418
|
-
entity: "promotion_rule_value",
|
|
419
|
-
fields: ["id", "value", "promotion_rule_id"],
|
|
420
|
-
filters: {
|
|
421
|
-
promotion_rule: {
|
|
422
|
-
promotion_id: [promotion.id],
|
|
423
|
-
},
|
|
424
|
-
},
|
|
425
|
-
});
|
|
426
|
-
const ruleValues = Array.isArray(ruleValueQueryResult.data)
|
|
427
|
-
? ruleValueQueryResult.data
|
|
428
|
-
: [];
|
|
429
|
-
if (ruleValues.length > 0) {
|
|
430
|
-
console.log(`[DynamicFilterService] Found ${ruleValues.length} rule values for promotion ${promotion.id}`);
|
|
431
|
-
// Extract product IDs from rule values
|
|
432
|
-
for (const ruleValue of ruleValues) {
|
|
433
|
-
if (ruleValue && typeof ruleValue === "object" && "value" in ruleValue) {
|
|
434
|
-
const value = ruleValue.value;
|
|
435
|
-
if (typeof value === "string") {
|
|
436
|
-
// This might be a product ID
|
|
437
|
-
productIdsWithPromotions.add(value);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
catch (error) {
|
|
444
|
-
console.log(`[DynamicFilterService] Could not query promotion_rule_value:`, error instanceof Error ? error.message : String(error));
|
|
445
|
-
}
|
|
446
|
-
// Try querying the promotion again with deeper expansion
|
|
447
|
-
try {
|
|
448
|
-
const deepPromotionQuery = await this.query.graph({
|
|
449
|
-
entity: "promotion",
|
|
450
|
-
fields: [
|
|
451
|
-
"id",
|
|
452
|
-
"rules.*",
|
|
453
|
-
"rules.values.*",
|
|
454
|
-
"rules.batch_rules.*",
|
|
455
|
-
"rules.batch_rules.values.*",
|
|
456
|
-
],
|
|
457
|
-
filters: {
|
|
458
|
-
id: [promotion.id],
|
|
459
|
-
},
|
|
460
|
-
});
|
|
461
|
-
const deepPromotions = Array.isArray(deepPromotionQuery.data)
|
|
462
|
-
? deepPromotionQuery.data
|
|
463
|
-
: [];
|
|
464
|
-
if (deepPromotions.length > 0 && deepPromotions[0].rules) {
|
|
465
|
-
console.log(`[DynamicFilterService] Found rules with deep expansion:`, JSON.stringify(deepPromotions[0].rules, null, 2));
|
|
466
|
-
promotion.rules = deepPromotions[0].rules;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
catch (error) {
|
|
470
|
-
console.log(`[DynamicFilterService] Could not query promotion with deep expansion:`, error instanceof Error ? error.message : String(error));
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
catch (error) {
|
|
475
|
-
// Entity might not exist or have different name - that's okay
|
|
476
|
-
console.log(`[DynamicFilterService] Could not query promotion_rule entity (this is normal if rules are embedded):`, error instanceof Error ? error.message : String(error));
|
|
477
|
-
}
|
|
478
|
-
}
|
|
687
|
+
// If we found product IDs, this is not a universal promotion
|
|
688
|
+
if (ids.length > 0) {
|
|
689
|
+
hasUniversalPromotions = false;
|
|
479
690
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
if (promotion.type === "percentage_off_product") {
|
|
542
|
-
if (applicationMethod &&
|
|
543
|
-
typeof applicationMethod === "object" &&
|
|
544
|
-
"value" in applicationMethod) {
|
|
545
|
-
discountPercentage =
|
|
546
|
-
typeof applicationMethod.value === "number"
|
|
547
|
-
? applicationMethod.value
|
|
548
|
-
: Number(applicationMethod.value);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
else if (promotion.type === "amount_off_product") {
|
|
552
|
-
// Will calculate per product
|
|
553
|
-
discountPercentage = undefined;
|
|
554
|
-
}
|
|
555
|
-
else if (promotion.type === "percentage_off_order") {
|
|
556
|
-
if (applicationMethod &&
|
|
557
|
-
typeof applicationMethod === "object" &&
|
|
558
|
-
"value" in applicationMethod) {
|
|
559
|
-
discountPercentage =
|
|
560
|
-
typeof applicationMethod.value === "number"
|
|
561
|
-
? applicationMethod.value
|
|
562
|
-
: Number(applicationMethod.value);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
// Extract product IDs from promotion rules
|
|
567
|
-
// Promotions use rules to target products (e.g., items, product_id, product_ids, collection_ids, etc.)
|
|
568
|
-
if (promotion.rules && Array.isArray(promotion.rules) && promotion.rules.length > 0) {
|
|
569
|
-
for (const rule of promotion.rules) {
|
|
570
|
-
// Check rule attribute - Medusa uses various attribute names
|
|
571
|
-
// Common ones: "items", "product_id", "product_ids", "product_collection_id", etc.
|
|
572
|
-
const ruleAttribute = rule.attribute;
|
|
573
|
-
if (ruleAttribute === "items" ||
|
|
574
|
-
ruleAttribute === "product_id" ||
|
|
575
|
-
ruleAttribute === "product_ids") {
|
|
576
|
-
// Direct product targeting
|
|
577
|
-
const productIds = Array.isArray(rule.values)
|
|
578
|
-
? rule.values
|
|
579
|
-
: rule.values
|
|
580
|
-
? [rule.values]
|
|
581
|
-
: [];
|
|
582
|
-
for (const productId of productIds) {
|
|
583
|
-
if (typeof productId === "string") {
|
|
584
|
-
productIdsWithPromotions.add(productId);
|
|
585
|
-
// Track maximum discount percentage for this product
|
|
586
|
-
if (discountPercentage !== undefined) {
|
|
587
|
-
const currentMax = promotionDiscountMap.get(productId) || 0;
|
|
588
|
-
promotionDiscountMap.set(productId, Math.max(currentMax, discountPercentage));
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
else if (ruleAttribute === "product_collection_id" ||
|
|
594
|
-
ruleAttribute === "product_collection_ids" ||
|
|
595
|
-
ruleAttribute === "collection_id" ||
|
|
596
|
-
ruleAttribute === "collection_ids") {
|
|
597
|
-
// Collection-based targeting - we'll need to get products from collections
|
|
598
|
-
// For now, we'll query products by collection_id
|
|
599
|
-
const collectionIds = Array.isArray(rule.values)
|
|
600
|
-
? rule.values
|
|
601
|
-
: rule.values
|
|
602
|
-
? [rule.values]
|
|
603
|
-
: [];
|
|
604
|
-
// Query products in these collections
|
|
605
|
-
for (const collectionId of collectionIds) {
|
|
606
|
-
if (typeof collectionId === "string") {
|
|
607
|
-
const collectionProductsResult = await this.query.graph({
|
|
608
|
-
entity: "product",
|
|
609
|
-
fields: ["id"],
|
|
610
|
-
filters: {
|
|
611
|
-
collection_id: [collectionId],
|
|
612
|
-
},
|
|
613
|
-
});
|
|
614
|
-
const collectionProducts = Array.isArray(collectionProductsResult.data)
|
|
615
|
-
? collectionProductsResult.data
|
|
616
|
-
: [];
|
|
617
|
-
for (const product of collectionProducts) {
|
|
618
|
-
if (product && typeof product === "object" && "id" in product) {
|
|
619
|
-
const productId = product.id;
|
|
620
|
-
productIdsWithPromotions.add(productId);
|
|
621
|
-
if (discountPercentage !== undefined) {
|
|
622
|
-
const currentMax = promotionDiscountMap.get(productId) || 0;
|
|
623
|
-
promotionDiscountMap.set(productId, Math.max(currentMax, discountPercentage));
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
// No rules found - if target_type is "items", the promotion might apply to all products
|
|
634
|
-
// OR the products might be stored in a different way (e.g., join table, metadata, etc.)
|
|
635
|
-
const applicationMethod = promotion.application_method;
|
|
636
|
-
if (applicationMethod &&
|
|
637
|
-
typeof applicationMethod === "object" &&
|
|
638
|
-
"target_type" in applicationMethod &&
|
|
639
|
-
applicationMethod.target_type === "items") {
|
|
640
|
-
console.log(`[DynamicFilterService] Promotion ${promotion.id} targets items but has no rules`);
|
|
641
|
-
// Try to find products associated with this promotion through alternative methods
|
|
642
|
-
// Option 1: Check if products are stored in promotion metadata
|
|
643
|
-
if (promotion.metadata && typeof promotion.metadata === "object") {
|
|
644
|
-
const metadata = promotion.metadata;
|
|
645
|
-
if (metadata.product_ids && Array.isArray(metadata.product_ids)) {
|
|
646
|
-
console.log(`[DynamicFilterService] Found product_ids in promotion metadata`);
|
|
647
|
-
const productIds = metadata.product_ids;
|
|
648
|
-
for (const productId of productIds) {
|
|
649
|
-
if (typeof productId === "string") {
|
|
650
|
-
productIdsWithPromotions.add(productId);
|
|
651
|
-
if (discountPercentage !== undefined) {
|
|
652
|
-
const currentMax = promotionDiscountMap.get(productId) || 0;
|
|
653
|
-
promotionDiscountMap.set(productId, Math.max(currentMax, discountPercentage));
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
// Option 2: If no rules and no metadata product_ids,
|
|
660
|
-
// and the promotion is active, we might need to include all products
|
|
661
|
-
// BUT this is risky - only do it if explicitly configured
|
|
662
|
-
// For now, we'll log and skip products for promotions without rules
|
|
663
|
-
console.log(`[DynamicFilterService] Skipping products for promotion ${promotion.id} - no rules found and no product_ids in metadata`);
|
|
664
|
-
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
return { productIds, discounts, hasUniversalPromotions, universalDiscount };
|
|
694
|
+
}
|
|
695
|
+
calculateDiscountPercentage(promotion) {
|
|
696
|
+
const appMethod = promotion.application_method;
|
|
697
|
+
if (!appMethod || typeof appMethod !== "object") {
|
|
698
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: No application_method`);
|
|
699
|
+
return undefined;
|
|
700
|
+
}
|
|
701
|
+
// Try to get discount from application_method.type === "percentage"
|
|
702
|
+
if (appMethod.type === "percentage" && "value" in appMethod) {
|
|
703
|
+
const value = typeof appMethod.value === "number" ? appMethod.value : Number(appMethod.value);
|
|
704
|
+
if (!Number.isNaN(value)) {
|
|
705
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: Found percentage discount ${value}% from application_method.type=percentage`);
|
|
706
|
+
return value;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
// Try to get discount from promotion type
|
|
710
|
+
if (promotion.type === "percentage_off_product" || promotion.type === "percentage_off_order") {
|
|
711
|
+
if ("value" in appMethod) {
|
|
712
|
+
const value = typeof appMethod.value === "number" ? appMethod.value : Number(appMethod.value);
|
|
713
|
+
if (!Number.isNaN(value)) {
|
|
714
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: Found percentage discount ${value}% from promotion.type=${promotion.type}`);
|
|
715
|
+
return value;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
console.log(`[DynamicFilterService] Promotion ${promotion.id || promotion.code}: Could not calculate discount percentage. type=${promotion.type}, appMethod.type=${appMethod.type}, appMethod.value=${appMethod.value}`);
|
|
720
|
+
return undefined;
|
|
721
|
+
}
|
|
722
|
+
extractIdsFromRuleValues(values) {
|
|
723
|
+
const ids = [];
|
|
724
|
+
if (!values)
|
|
725
|
+
return ids;
|
|
726
|
+
let valueArray = [];
|
|
727
|
+
if (Array.isArray(values)) {
|
|
728
|
+
valueArray = values;
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
valueArray = [values];
|
|
732
|
+
}
|
|
733
|
+
for (const value of valueArray) {
|
|
734
|
+
if (typeof value === "string") {
|
|
735
|
+
if (value.length > 0) {
|
|
736
|
+
ids.push(value);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
else if (typeof value === "object" && value !== null) {
|
|
740
|
+
// Prioritize "value" property if it looks like a product ID (starts with "prod_")
|
|
741
|
+
// This handles Medusa v2 promotion rule values where product IDs are in the "value" field
|
|
742
|
+
if ("value" in value && typeof value.value === "string" && value.value.startsWith("prod_")) {
|
|
743
|
+
ids.push(value.value);
|
|
744
|
+
}
|
|
745
|
+
else if ("product_id" in value && typeof value.product_id === "string") {
|
|
746
|
+
ids.push(value.product_id);
|
|
747
|
+
}
|
|
748
|
+
else if ("id" in value && typeof value.id === "string") {
|
|
749
|
+
// Only use "id" if it looks like a product ID, otherwise it might be a rule value ID
|
|
750
|
+
if (value.id.startsWith("prod_")) {
|
|
751
|
+
ids.push(value.id);
|
|
665
752
|
}
|
|
666
753
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
const appMethod = p.application_method;
|
|
674
|
-
return (appMethod &&
|
|
675
|
-
typeof appMethod === "object" &&
|
|
676
|
-
"target_type" in appMethod &&
|
|
677
|
-
appMethod.target_type === "items" &&
|
|
678
|
-
(!p.rules || p.rules.length === 0));
|
|
679
|
-
});
|
|
680
|
-
if (itemsTargetingPromotions.length > 0) {
|
|
681
|
-
console.log(`[DynamicFilterService] FALLBACK: Found ${itemsTargetingPromotions.length} promotions targeting items with no rules. ` +
|
|
682
|
-
`Including all products as fallback (promotion rules may be stored in a way we can't query yet).`);
|
|
683
|
-
// Include all current products and set their discount percentages
|
|
684
|
-
for (const product of products) {
|
|
685
|
-
if (product && typeof product === "object" && "id" in product) {
|
|
686
|
-
const productId = product.id;
|
|
687
|
-
productIdsWithPromotions.add(productId);
|
|
688
|
-
// Calculate and set discount percentage for each promotion
|
|
689
|
-
for (const promo of itemsTargetingPromotions) {
|
|
690
|
-
const appMethod = promo.application_method;
|
|
691
|
-
if (appMethod &&
|
|
692
|
-
typeof appMethod === "object" &&
|
|
693
|
-
"type" in appMethod &&
|
|
694
|
-
appMethod.type === "percentage" &&
|
|
695
|
-
"value" in appMethod) {
|
|
696
|
-
const discount = typeof appMethod.value === "number"
|
|
697
|
-
? appMethod.value
|
|
698
|
-
: Number(appMethod.value);
|
|
699
|
-
if (!Number.isNaN(discount)) {
|
|
700
|
-
const currentMax = promotionDiscountMap.get(productId) || 0;
|
|
701
|
-
promotionDiscountMap.set(productId, Math.max(currentMax, discount));
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
}
|
|
754
|
+
else {
|
|
755
|
+
// Try to extract any string field that looks like a product ID
|
|
756
|
+
for (const [key, val] of Object.entries(value)) {
|
|
757
|
+
if (typeof val === "string" && val.length > 0 && val.startsWith("prod_")) {
|
|
758
|
+
ids.push(val);
|
|
759
|
+
break; // Found product ID, no need to check other fields
|
|
706
760
|
}
|
|
707
|
-
console.log(`[DynamicFilterService] FALLBACK applied: ${productIdsWithPromotions.size} products now included`);
|
|
708
761
|
}
|
|
709
762
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return ids;
|
|
766
|
+
}
|
|
767
|
+
async extractProductIdsFromPromotion(promotion) {
|
|
768
|
+
const productIds = new Set();
|
|
769
|
+
// Check application_method.target_rules for product IDs (Medusa v2 stores product selections here)
|
|
770
|
+
// This is where products are stored when you select "items" in the promotion configuration
|
|
771
|
+
if (promotion.application_method?.target_rules?.length) {
|
|
772
|
+
console.log(`[DynamicFilterService] Processing ${promotion.application_method.target_rules.length} target_rules for promotion ${promotion.id || promotion.code}`);
|
|
773
|
+
for (const rule of promotion.application_method.target_rules) {
|
|
774
|
+
const ruleType = rule.attribute || rule.type || "";
|
|
775
|
+
const normalizedType = String(ruleType).toLowerCase();
|
|
776
|
+
console.log(`[DynamicFilterService] Target rule: attribute=${ruleType}, values=`, rule.values);
|
|
777
|
+
// Extract product IDs from target_rules
|
|
778
|
+
// Attribute can be "items", "items.product.id", "product", etc.
|
|
779
|
+
// If it contains "item" or "product", extract IDs from values
|
|
780
|
+
if (rule.values && (normalizedType.includes("item") || normalizedType.includes("product"))) {
|
|
781
|
+
const extracted = this.extractIdsFromRuleValues(rule.values);
|
|
782
|
+
extracted.forEach(id => productIds.add(id));
|
|
783
|
+
console.log(`[DynamicFilterService] Extracted ${extracted.length} product IDs from target_rules (attribute: ${ruleType})`);
|
|
713
784
|
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
const
|
|
717
|
-
if (
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
// Check if product has a matching promotion
|
|
721
|
-
if (!productIdsWithPromotions.has(productId)) {
|
|
722
|
-
return false;
|
|
785
|
+
else if (rule.values) {
|
|
786
|
+
// Even if attribute doesn't match, try extracting IDs anyway (might be product-related)
|
|
787
|
+
const extracted = this.extractIdsFromRuleValues(rule.values);
|
|
788
|
+
if (extracted.length > 0) {
|
|
789
|
+
console.log(`[DynamicFilterService] Extracted ${extracted.length} product IDs from target_rules (unknown attribute: ${ruleType})`);
|
|
790
|
+
extracted.forEach(id => productIds.add(id));
|
|
723
791
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const discountAmount = typeof applicationMethod.value === "number"
|
|
748
|
-
? applicationMethod.value
|
|
749
|
-
: Number(applicationMethod.value);
|
|
750
|
-
if (!Number.isNaN(discountAmount) && productPrice > 0) {
|
|
751
|
-
const calculatedPercentage = (discountAmount / productPrice) * 100;
|
|
752
|
-
const currentMax = promotionDiscountMap.get(productId) || 0;
|
|
753
|
-
promotionDiscountMap.set(productId, Math.max(currentMax, calculatedPercentage));
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
const finalDiscount = promotionDiscountMap.get(productId) || 0;
|
|
761
|
-
// Check if discount meets minimum threshold
|
|
762
|
-
if (finalDiscount < promotionFilter.min_discount_percentage) {
|
|
763
|
-
return false;
|
|
764
|
-
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
// Check application_method.buy_rules for product IDs (buy-x-get-y promotions)
|
|
796
|
+
if (promotion.application_method?.buy_rules?.length) {
|
|
797
|
+
console.log(`[DynamicFilterService] Processing ${promotion.application_method.buy_rules.length} buy_rules for promotion ${promotion.id || promotion.code}`);
|
|
798
|
+
for (const rule of promotion.application_method.buy_rules) {
|
|
799
|
+
const ruleType = rule.attribute || rule.type || "";
|
|
800
|
+
const normalizedType = String(ruleType).toLowerCase();
|
|
801
|
+
console.log(`[DynamicFilterService] Buy rule: attribute=${ruleType}, values=`, rule.values);
|
|
802
|
+
// Extract product IDs from buy_rules
|
|
803
|
+
// Attribute can be "items", "items.product.id", "product", etc.
|
|
804
|
+
if (rule.values && (normalizedType.includes("item") || normalizedType.includes("product"))) {
|
|
805
|
+
const extracted = this.extractIdsFromRuleValues(rule.values);
|
|
806
|
+
extracted.forEach(id => productIds.add(id));
|
|
807
|
+
console.log(`[DynamicFilterService] Extracted ${extracted.length} product IDs from buy_rules`);
|
|
808
|
+
}
|
|
809
|
+
else if (rule.values) {
|
|
810
|
+
// Even if attribute doesn't match, try extracting IDs anyway
|
|
811
|
+
const extracted = this.extractIdsFromRuleValues(rule.values);
|
|
812
|
+
if (extracted.length > 0) {
|
|
813
|
+
extracted.forEach(id => productIds.add(id));
|
|
814
|
+
console.log(`[DynamicFilterService] Extracted ${extracted.length} product IDs from buy_rules (unknown attribute: ${ruleType})`);
|
|
765
815
|
}
|
|
766
|
-
return true;
|
|
767
|
-
});
|
|
768
|
-
// Update metadata count after filtering
|
|
769
|
-
if (metadata) {
|
|
770
|
-
metadata.count = products.length;
|
|
771
816
|
}
|
|
772
817
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
818
|
+
}
|
|
819
|
+
// Debug: Log rule structure
|
|
820
|
+
if (promotion.rules?.length) {
|
|
821
|
+
console.log(`[DynamicFilterService] Processing ${promotion.rules.length} rules for promotion ${promotion.id || promotion.code}`);
|
|
822
|
+
for (const rule of promotion.rules) {
|
|
823
|
+
const ruleType = rule.type || rule.attribute || "";
|
|
824
|
+
const normalizedType = String(ruleType).toLowerCase();
|
|
825
|
+
console.log(`[DynamicFilterService] Rule: type=${ruleType}, attribute=${rule.attribute}, values=`, rule.values);
|
|
826
|
+
// Skip rules that definitely don't contain product IDs
|
|
827
|
+
if (normalizedType &&
|
|
828
|
+
(normalizedType === "customer_groups" ||
|
|
829
|
+
normalizedType === "regions" ||
|
|
830
|
+
normalizedType === "currency" ||
|
|
831
|
+
normalizedType === "customer_group" ||
|
|
832
|
+
normalizedType === "region")) {
|
|
833
|
+
console.log(`[DynamicFilterService] Skipping non-product rule: ${ruleType}`);
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
// Process all other rules (including product-related ones)
|
|
837
|
+
// Extract product IDs from rule values
|
|
838
|
+
const extracted = this.extractIdsFromRuleValues(rule.values);
|
|
839
|
+
extracted.forEach(id => productIds.add(id));
|
|
840
|
+
console.log(`[DynamicFilterService] Extracted ${extracted.length} product IDs from rule ${ruleType}`);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
console.log(`[DynamicFilterService] No rules found in promotion ${promotion.id || promotion.code}`);
|
|
845
|
+
}
|
|
846
|
+
// Also check metadata for product IDs (fallback)
|
|
847
|
+
if (promotion.metadata?.product_ids?.length) {
|
|
848
|
+
console.log(`[DynamicFilterService] Found product IDs in metadata:`, promotion.metadata.product_ids);
|
|
849
|
+
promotion.metadata.product_ids.forEach((id) => {
|
|
850
|
+
if (typeof id === "string") {
|
|
851
|
+
productIds.add(id);
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
const result = Array.from(productIds);
|
|
856
|
+
console.log(`[DynamicFilterService] Extracted ${result.length} product IDs:`, result);
|
|
857
|
+
return result;
|
|
858
|
+
}
|
|
859
|
+
meetsDiscountCriteria(discount, promotionFilter) {
|
|
860
|
+
// Validate discount is a valid number
|
|
861
|
+
if (typeof discount !== "number" || Number.isNaN(discount)) {
|
|
862
|
+
console.warn(`[DynamicFilterService] Invalid discount value: ${discount}`);
|
|
863
|
+
return false;
|
|
864
|
+
}
|
|
865
|
+
// Check minimum discount percentage
|
|
866
|
+
if (promotionFilter.min_discount_percentage !== undefined) {
|
|
867
|
+
const minDiscount = Number(promotionFilter.min_discount_percentage);
|
|
868
|
+
if (!Number.isNaN(minDiscount) && discount < minDiscount) {
|
|
869
|
+
return false;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
// Check maximum discount percentage
|
|
873
|
+
if (promotionFilter.max_discount_percentage !== undefined) {
|
|
874
|
+
const maxDiscount = Number(promotionFilter.max_discount_percentage);
|
|
875
|
+
if (!Number.isNaN(maxDiscount) && discount > maxDiscount) {
|
|
876
|
+
return false;
|
|
776
877
|
}
|
|
777
878
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
async getCount(queryFilters, metadata, productsLength) {
|
|
882
|
+
if (metadata?.count !== undefined)
|
|
883
|
+
return metadata.count;
|
|
884
|
+
try {
|
|
885
|
+
const countResult = await this.query.graph({
|
|
783
886
|
entity: "product",
|
|
784
887
|
fields: ["id"],
|
|
785
888
|
...(Object.keys(queryFilters).length > 0 && { filters: queryFilters }),
|
|
786
|
-
};
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
else if (Array.isArray(countData)) {
|
|
794
|
-
count = countData.length;
|
|
795
|
-
}
|
|
796
|
-
else {
|
|
797
|
-
// Fallback: use products array length if count query also fails
|
|
798
|
-
console.warn("[DynamicFilterService] Could not determine count from query result, using products array length");
|
|
799
|
-
count = products.length;
|
|
800
|
-
}
|
|
889
|
+
});
|
|
890
|
+
return countResult.metadata?.count ||
|
|
891
|
+
(Array.isArray(countResult.data) ? countResult.data.length : productsLength);
|
|
892
|
+
}
|
|
893
|
+
catch (error) {
|
|
894
|
+
console.warn("[DynamicFilterService] Could not determine count:", error);
|
|
895
|
+
return productsLength;
|
|
801
896
|
}
|
|
802
|
-
return {
|
|
803
|
-
products,
|
|
804
|
-
count: typeof count === "number" ? count : products.length,
|
|
805
|
-
metadata: {
|
|
806
|
-
count: typeof count === "number" ? count : products.length,
|
|
807
|
-
skip: pagination?.offset,
|
|
808
|
-
take: pagination?.limit,
|
|
809
|
-
},
|
|
810
|
-
};
|
|
811
897
|
}
|
|
812
898
|
}
|
|
813
899
|
exports.DynamicFilterService = DynamicFilterService;
|
|
814
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1maWx0ZXItc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9keW5hbWljLWZpbHRlci1zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHFEQUFtRjtBQUVuRix5RUFBbUU7QUE2RG5FOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JHO0FBQ0gsTUFBYSxvQkFBb0I7SUFJL0IsWUFBWSxTQUEwQjtRQUNwQywrQ0FBK0M7UUFDL0MsSUFBSSxDQUFDO1lBQ0gsSUFBSSxDQUFDLFFBQVEsR0FBRyxTQUFTLENBQUMsT0FBTyxDQUMvQix3QkFBd0IsQ0FDekIsQ0FBQTtRQUNILENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCwwRUFBMEU7WUFDMUUseUVBQXlFO1lBQ3pFLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxpREFBc0IsRUFBRSxDQUFBO1lBRTVDLG1DQUFtQztZQUNuQyxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxFQUFFLHdCQUF3QixFQUFFLEdBQUcsT0FBTyxDQUFDLCtCQUErQixDQUFDLENBQUE7Z0JBQzdFLHdCQUF3QixDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQTtZQUN6QyxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZiwwQ0FBMEM7Z0JBQzFDLE9BQU8sQ0FBQyxJQUFJLENBQ1Ysb0VBQW9FLEVBQ3BFLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FDdkQsQ0FBQTtZQUNILENBQUM7UUFDSCxDQUFDO1FBQ0QsSUFBSSxDQUFDLEtBQUssR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFRLGlDQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFBO0lBQ3hFLENBQUM7SUFFRDs7O09BR0c7SUFDSCxXQUFXO1FBQ1QsT0FBTyxJQUFJLENBQUMsUUFB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
|
|
900
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1maWx0ZXItc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9keW5hbWljLWZpbHRlci1zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHFEQUFtRjtBQUNuRix5RUFBbUU7QUFrQm5FLE1BQWEsb0JBQW9CO0lBSS9CLFlBQVksU0FBMEI7UUFDcEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLENBQUMsZUFBZSxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBQy9DLElBQUksQ0FBQyxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBUSxpQ0FBeUIsQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUN4RSxDQUFDO0lBRUQsV0FBVztRQUNULE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQTtJQUN0QixDQUFDO0lBRUQsS0FBSyxDQUFDLFlBQVksQ0FBQyxPQUE0QjtRQUM3QyxNQUFNLEVBQUUsWUFBWSxFQUFFLE9BQU8sRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxPQUFPLEdBQUcsRUFBRSxFQUFFLEdBQUcsT0FBTyxDQUFBO1FBQzlGLE1BQU0sYUFBYSxHQUFrQixFQUFFLEdBQUcsT0FBTyxFQUFFLE9BQU8sRUFBRSxhQUFhLEVBQUUsQ0FBQTtRQUUzRSxPQUFPLENBQUMsR0FBRyxDQUFDLDBEQUEwRCxFQUFFO1lBQ3RFLGlCQUFpQixFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsY0FBYztZQUMzQyxrQkFBa0IsRUFBRSxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRTtTQUN0RixDQUFDLENBQUE7UUFFRixNQUFNLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGVBQWUsRUFBRSxjQUFjLEVBQUUsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENB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
|