medusa-product-helper 0.0.16 → 0.0.18

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