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.
Files changed (26) hide show
  1. package/.medusa/server/src/admin/index.js +59 -117
  2. package/.medusa/server/src/admin/index.mjs +59 -117
  3. package/.medusa/server/src/api/store/product-helper/products/route.js +137 -7
  4. package/.medusa/server/src/api/store/product-helper/products/validators.js +11 -1
  5. package/.medusa/server/src/providers/filter-providers/availability-provider.js +53 -67
  6. package/.medusa/server/src/providers/filter-providers/base-filter-provider.js +1 -19
  7. package/.medusa/server/src/providers/filter-providers/base-product-provider.js +37 -100
  8. package/.medusa/server/src/providers/filter-providers/category-provider.js +15 -34
  9. package/.medusa/server/src/providers/filter-providers/collection-provider.js +15 -32
  10. package/.medusa/server/src/providers/filter-providers/index.js +13 -49
  11. package/.medusa/server/src/providers/filter-providers/metadata-provider.js +43 -58
  12. package/.medusa/server/src/providers/filter-providers/price-range-provider.js +66 -79
  13. package/.medusa/server/src/providers/filter-providers/promotion-provider.js +106 -169
  14. package/.medusa/server/src/providers/filter-providers/promotion-window-provider.js +53 -93
  15. package/.medusa/server/src/providers/filter-providers/rating-provider.js +47 -70
  16. package/.medusa/server/src/services/dynamic-filter-service.js +822 -736
  17. package/.medusa/server/src/services/filter-provider-loader.js +91 -139
  18. package/.medusa/server/src/services/filter-provider-registry.js +8 -107
  19. package/.medusa/server/src/services/product-filter-service.js +183 -172
  20. package/.medusa/server/src/shared/product-metadata/utils.js +66 -116
  21. package/.medusa/server/src/utils/query-builders/product-filters.js +89 -111
  22. package/.medusa/server/src/utils/query-parser.js +24 -76
  23. package/.medusa/server/src/workflows/add-to-wishlist.js +12 -26
  24. package/.medusa/server/src/workflows/get-wishlist.js +53 -51
  25. package/.medusa/server/src/workflows/remove-from-wishlist.js +3 -8
  26. 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
- // Try to resolve registry, create if not found
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 = {}, } = options;
88
- // Build filter context
89
- const filterContext = {
90
- ...context,
91
- options: pluginOptions,
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
- // Start with empty filters object
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
- // Apply each filter using its provider
91
+ let metadataFilter;
98
92
  for (const [identifier, value] of Object.entries(filterParams)) {
99
- // Skip undefined/null values
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
- // Log warning but don't fail - allow unknown filters to be ignored
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
- // Validate if provider has validation method
113
- if (provider.validate) {
114
- const validationResult = provider.validate(value);
115
- if (validationResult && Array.isArray(validationResult)) {
116
- // Provider returned validation errors
117
- const errors = validationResult
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
- // Extract promotion filter for post-query filtering
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
- // Fail-fast: throw immediately on provider errors
146
- const errorMessage = error instanceof Error ? error.message : String(error);
147
- throw new Error(`Error applying filter "${identifier}": ${errorMessage}`);
148
- }
149
- }
150
- // Build query options for Query.graph()
151
- // Only include filters if they're not empty
152
- // Important: Check if fields array is empty or undefined, not just truthy
153
- let fields = (projection?.fields && projection.fields.length > 0)
154
- ? [...projection.fields]
155
- : ["*"];
156
- // Ensure essential relations are included to match Medusa's official format
157
- // Even when using ["*"], we need to explicitly ensure relations are included
158
- // Medusa's RemoteQuery may not automatically include nested relations with "*"
159
- if (fields.includes("*")) {
160
- // When using "*", explicitly add relations to ensure they're included
161
- // This ensures variants, options, images, collection, and type are returned
162
- fields = [
163
- "*",
164
- "variants.*",
165
- "variants.prices.*",
166
- "options.*",
167
- "options.values.*",
168
- "images.*",
169
- "collection.*",
170
- "type.*",
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
- // If specific fields are requested, ensure essential relations are included
175
- const hasVariants = fields.some(f => f.startsWith("variants"));
176
- const hasOptions = fields.some(f => f.startsWith("options"));
177
- const hasImages = fields.some(f => f.startsWith("images"));
178
- const hasCollection = fields.includes("collection") || fields.some(f => f.startsWith("collection"));
179
- const hasType = fields.includes("type") || fields.some(f => f.startsWith("type"));
180
- // Add missing essential relations
181
- if (!hasVariants) {
182
- fields.push("variants.*", "variants.prices.*");
183
- }
184
- if (!hasOptions) {
185
- fields.push("options.*", "options.values.*");
186
- }
187
- if (!hasImages)
188
- fields.push("images.*");
189
- if (!hasCollection)
190
- fields.push("collection.*");
191
- if (!hasType)
192
- fields.push("type.*");
193
- }
194
- // Build query context for pricing (similar to Medusa's official route)
195
- const queryContext = {};
196
- // Add pricing context if available
197
- if (context && typeof context === "object" && "pricingContext" in context) {
198
- const pricingContext = context.pricingContext;
199
- if (pricingContext) {
200
- queryContext.variants = {
201
- calculated_price: (0, utils_1.QueryContext)(pricingContext),
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
- const queryOptions = {
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(queryFilters).length > 0 && { filters: queryFilters }),
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
- // Debug: Log query structure when metadata filters are present
218
- if (Object.keys(queryFilters).some((k) => k === "metadata")) {
219
- console.log("[DynamicFilterService] Query filters:", JSON.stringify(queryFilters, null, 2));
220
- console.log("[DynamicFilterService] Query fields:", fields);
221
- console.log("[DynamicFilterService] Query context:", JSON.stringify(queryContext, null, 2));
222
- }
223
- // Execute query with pagination
224
- // Note: query.graph() returns { data: Product[], metadata: { count, skip, take } }
225
- const result = await this.query.graph(queryOptions);
226
- // Extract products from result.data
227
- // Following the pattern used in other Medusa routes (e.g., medusa-review-rating)
228
- // query.graph() returns { data: Product[], metadata: { count, skip, take } }
229
- const { data: productsRaw = [], metadata } = result;
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
- // Log unexpected structure for debugging
237
- console.warn("[DynamicFilterService] Unexpected data structure from query.graph():", {
238
- dataType: typeof productsRaw,
239
- isArray: Array.isArray(productsRaw),
240
- hasData: !!productsRaw,
241
- sample: productsRaw,
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
- // Post-query filtering for price range
245
- // This is done after the query because Medusa's query API doesn't support
246
- // price range filtering directly - we need to filter based on variant prices
247
- if (priceRangeFilter && products.length > 0) {
248
- products = products.filter((product) => {
249
- // A product matches if at least one variant has a price within the range
250
- if (!product.variants || !Array.isArray(product.variants) || product.variants.length === 0) {
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
- // Check each variant to see if it has a price within the range
254
- return product.variants.some((variant) => {
255
- // Try calculated_price first (if pricing context was provided)
256
- let price;
257
- let currencyCode;
258
- if (variant.calculated_price?.calculated_amount !== undefined) {
259
- price = variant.calculated_price.calculated_amount;
260
- currencyCode = variant.calculated_price.currency_code;
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
- // If no price found, exclude this variant
269
- if (price === undefined) {
270
- return false;
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
- // If currency_code is specified in filter, check it matches
273
- if (priceRangeFilter.currency_code && currencyCode !== priceRangeFilter.currency_code) {
274
- return false;
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
- // Check if price is within range
277
- const min = priceRangeFilter.min;
278
- const max = priceRangeFilter.max;
279
- if (min !== undefined && price < min) {
280
- return false;
350
+ else {
351
+ // Both are single values - direct comparison
352
+ if (!this.metadataValuesMatch(productValue, filterValue)) {
353
+ return false;
354
+ }
281
355
  }
282
- if (max !== undefined && price > max) {
283
- return false;
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
- // Update metadata count after filtering
289
- if (metadata) {
290
- metadata.count = products.length;
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
- // Post-query filtering for promotions
294
- // This is done after the query because promotion-product relationships
295
- // are complex and require querying the promotion module
296
- if (promotionFilter && products.length > 0) {
297
- try {
298
- // Query all promotions (we'll filter in memory)
299
- // This ensures we don't miss promotions due to query filter limitations
300
- const promotionQueryOptions = {
301
- entity: "promotion",
302
- fields: [
303
- "id",
304
- "code",
305
- "type",
306
- "application_method.*",
307
- "campaign.*",
308
- "rules.*",
309
- "rules.values.*",
310
- "starts_at",
311
- "ends_at",
312
- "status",
313
- ],
314
- };
315
- // Add basic filters if specified
316
- const promotionQueryFilters = {};
317
- if (promotionFilter.status) {
318
- promotionQueryFilters.status = promotionFilter.status;
319
- }
320
- if (promotionFilter.promotion_type) {
321
- promotionQueryFilters.type = promotionFilter.promotion_type;
322
- }
323
- if (Object.keys(promotionQueryFilters).length > 0) {
324
- promotionQueryOptions.filters = promotionQueryFilters;
325
- }
326
- const promotionResult = await this.query.graph(promotionQueryOptions);
327
- let allPromotions = Array.isArray(promotionResult.data)
328
- ? promotionResult.data
329
- : [];
330
- // Debug: Log all promotions found
331
- console.log(`[DynamicFilterService] Found ${allPromotions.length} total promotions`);
332
- // Filter promotions by date range in memory
333
- const now = new Date();
334
- const promotions = allPromotions.filter((promotion) => {
335
- // Filter by date range if specified
336
- if (promotionFilter.starts_at || promotionFilter.ends_at) {
337
- const startsAt = promotion.starts_at
338
- ? new Date(promotion.starts_at)
339
- : null;
340
- const endsAt = promotion.ends_at ? new Date(promotion.ends_at) : null;
341
- if (promotionFilter.starts_at) {
342
- const filterStart = new Date(promotionFilter.starts_at);
343
- if (endsAt && endsAt < filterStart) {
344
- return false; // Promotion ends before filter start
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
- if (promotionFilter.ends_at) {
348
- const filterEnd = new Date(promotionFilter.ends_at);
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
- // Default: only include promotions active now
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
- // Debug: Log filtered promotions
375
- console.log(`[DynamicFilterService] Found ${promotions.length} promotions after date filtering`);
376
- if (promotions.length > 0) {
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
- // Extract product IDs from promotion rules/targets
481
- // (productIdsWithPromotions and promotionDiscountMap are already declared above)
482
- for (const promotion of promotions) {
483
- // Check if promotion is active now (if date range not specified)
484
- if (!promotionFilter.starts_at && !promotionFilter.ends_at) {
485
- const startsAt = promotion.starts_at
486
- ? new Date(promotion.starts_at)
487
- : null;
488
- const endsAt = promotion.ends_at ? new Date(promotion.ends_at) : null;
489
- const now = new Date();
490
- if (startsAt && startsAt > now) {
491
- continue; // Promotion hasn't started yet
492
- }
493
- if (endsAt && endsAt < now) {
494
- continue; // Promotion has ended
495
- }
496
- }
497
- // Calculate discount percentage based on application_method type
498
- // In Medusa, the discount type is stored in application_method.type, not promotion.type
499
- let discountPercentage;
500
- const applicationMethod = promotion.application_method;
501
- if (applicationMethod &&
502
- typeof applicationMethod === "object" &&
503
- "type" in applicationMethod) {
504
- const appMethodType = applicationMethod.type;
505
- if (appMethodType === "fixed" &&
506
- "target_type" in applicationMethod &&
507
- applicationMethod.target_type === "items") {
508
- // Fixed amount discount on items (amount_off_product)
509
- // Will calculate percentage per product later
510
- discountPercentage = undefined;
511
- }
512
- else if (appMethodType === "percentage" &&
513
- "target_type" in applicationMethod &&
514
- applicationMethod.target_type === "items") {
515
- // Percentage discount on items (percentage_off_product)
516
- if ("value" in applicationMethod) {
517
- discountPercentage =
518
- typeof applicationMethod.value === "number"
519
- ? applicationMethod.value
520
- : Number(applicationMethod.value);
521
- }
522
- }
523
- else if (appMethodType === "percentage" &&
524
- "target_type" in applicationMethod &&
525
- applicationMethod.target_type === "order") {
526
- // Order-level percentage discount
527
- if ("value" in applicationMethod) {
528
- discountPercentage =
529
- typeof applicationMethod.value === "number"
530
- ? applicationMethod.value
531
- : Number(applicationMethod.value);
532
- }
533
- }
534
- else if (appMethodType === "free_shipping") {
535
- // Free shipping - consider as 0% for product filtering
536
- discountPercentage = 0;
537
- }
538
- }
539
- // Fallback: Check promotion.type for backward compatibility
540
- if (discountPercentage === undefined && promotion.type) {
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
- // Debug: Log product IDs found with promotions
668
- console.log(`[DynamicFilterService] Found ${productIdsWithPromotions.size} products with promotions`);
669
- // FALLBACK: If no products found but we have promotions targeting items without rules,
670
- // include all products (this is a workaround for when rules aren't queryable)
671
- if (productIdsWithPromotions.size === 0 && promotions.length > 0) {
672
- const itemsTargetingPromotions = promotions.filter((p) => {
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
- if (productIdsWithPromotions.size > 0) {
711
- const sampleProductIds = Array.from(productIdsWithPromotions).slice(0, 5);
712
- console.log(`[DynamicFilterService] Sample product IDs with promotions:`, sampleProductIds);
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
- // Filter products to only include those with matching promotions
715
- products = products.filter((product) => {
716
- const productId = product?.id;
717
- if (!productId || typeof productId !== "string") {
718
- return false;
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
- // If min_discount_percentage is specified, check discount
725
- if (promotionFilter.min_discount_percentage !== undefined) {
726
- const productDiscount = promotionDiscountMap.get(productId);
727
- // For amount_off_product (fixed amount discount), calculate percentage from product price
728
- if (productDiscount === undefined && product.variants) {
729
- // Try to find promotion with fixed amount discount on items
730
- // and calculate percentage from product price
731
- const productPrice = product.variants?.[0]?.prices?.[0]?.amount;
732
- if (productPrice && typeof productPrice === "number") {
733
- // Find matching promotion with amount discount
734
- for (const promotion of promotions) {
735
- const applicationMethod = promotion.application_method;
736
- const isAmountDiscount = (applicationMethod &&
737
- typeof applicationMethod === "object" &&
738
- "type" in applicationMethod &&
739
- applicationMethod.type === "fixed" &&
740
- "target_type" in applicationMethod &&
741
- applicationMethod.target_type === "items") ||
742
- promotion.type === "amount_off_product";
743
- if (isAmountDiscount) {
744
- if (applicationMethod &&
745
- typeof applicationMethod === "object" &&
746
- "value" in applicationMethod) {
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
- catch (error) {
774
- // Log error but don't fail - return products without promotion filtering
775
- console.warn("[DynamicFilterService] Error filtering by promotions:", error instanceof Error ? error.message : String(error));
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
- // Get count from metadata if available, otherwise query separately
779
- let count = metadata?.count;
780
- if (count === undefined) {
781
- // Query for count without pagination
782
- const countQueryOptions = {
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
- const countResult = await this.query.graph(countQueryOptions);
788
- // Extract count from result - following same pattern as main query
789
- const { data: countData = [], metadata: countMetadata } = countResult;
790
- if (countMetadata?.count !== undefined) {
791
- count = countMetadata.count;
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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