diff --git a/integration-tests/modules/__tests__/cart/store/add-promotions-to-cart.spec.ts b/integration-tests/modules/__tests__/cart/store/add-promotions-to-cart.spec.ts index da3d227272..450e88fc50 100644 --- a/integration-tests/modules/__tests__/cart/store/add-promotions-to-cart.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/add-promotions-to-cart.spec.ts @@ -56,7 +56,7 @@ medusaIntegrationTestRunner({ max_quantity: 1, target_rules: [ { - attribute: "product_id", + attribute: "items.product_id", operator: "eq", values: "prod_tshirt", }, diff --git a/packages/modules/promotion/src/services/promotion-module.ts b/packages/modules/promotion/src/services/promotion-module.ts index bf4cbeee6e..6406473491 100644 --- a/packages/modules/promotion/src/services/promotion-module.ts +++ b/packages/modules/promotion/src/services/promotion-module.ts @@ -362,7 +362,8 @@ export default class PromotionModuleService const isPromotionApplicable = areRulesValidForContext( promotionRules, - applicationContext + applicationContext, + ApplicationMethodTargetType.ORDER ) if (!isPromotionApplicable) { diff --git a/packages/modules/promotion/src/utils/compute-actions/buy-get.ts b/packages/modules/promotion/src/utils/compute-actions/buy-get.ts index 3028da49bc..92f0eb4a70 100644 --- a/packages/modules/promotion/src/utils/compute-actions/buy-get.ts +++ b/packages/modules/promotion/src/utils/compute-actions/buy-get.ts @@ -36,7 +36,13 @@ export function getComputedActionsForBuyGet( const validQuantity = MathBN.sum( ...itemsContext - .filter((item) => areRulesValidForContext(buyRules, item)) + .filter((item) => + areRulesValidForContext( + buyRules, + item, + ApplicationMethodTargetType.ITEMS + ) + ) .map((item) => item.quantity) ) @@ -49,7 +55,13 @@ export function getComputedActionsForBuyGet( } const validItemsForTargetRules = itemsContext - .filter((item) => areRulesValidForContext(targetRules, item)) + .filter((item) => + areRulesValidForContext( + targetRules, + item, + ApplicationMethodTargetType.ITEMS + ) + ) .filter((item) => isPresent(item.subtotal) && isPresent(item.quantity)) .sort((a, b) => { const aPrice = MathBN.div(a.subtotal, a.quantity) diff --git a/packages/modules/promotion/src/utils/compute-actions/line-items.ts b/packages/modules/promotion/src/utils/compute-actions/line-items.ts index 6570279b49..4ae977044b 100644 --- a/packages/modules/promotion/src/utils/compute-actions/line-items.ts +++ b/packages/modules/promotion/src/utils/compute-actions/line-items.ts @@ -5,6 +5,7 @@ import { } from "@medusajs/types" import { ApplicationMethodAllocation, + ApplicationMethodTargetType, ComputedActions, MathBN, MedusaError, @@ -172,7 +173,8 @@ function getValidItemsForPromotion( const isQuantityPresent = "quantity" in item const isPromotionApplicableToItem = areRulesValidForContext( promotion?.application_method?.target_rules!, - item + item, + ApplicationMethodTargetType.ITEMS ) return ( diff --git a/packages/modules/promotion/src/utils/compute-actions/shipping-methods.ts b/packages/modules/promotion/src/utils/compute-actions/shipping-methods.ts index f74d00e891..319043d168 100644 --- a/packages/modules/promotion/src/utils/compute-actions/shipping-methods.ts +++ b/packages/modules/promotion/src/utils/compute-actions/shipping-methods.ts @@ -28,7 +28,8 @@ export function getComputedActionsForShippingMethods( for (const shippingMethodContext of shippingMethodApplicationContext) { const isPromotionApplicableToItem = areRulesValidForContext( promotion.application_method?.target_rules!, - shippingMethodContext + shippingMethodContext, + ApplicationMethodTargetType.SHIPPING_METHODS ) if (!isPromotionApplicableToItem) { diff --git a/packages/modules/promotion/src/utils/validations/promotion-rule.ts b/packages/modules/promotion/src/utils/validations/promotion-rule.ts index decd93333a..4dcaea86bc 100644 --- a/packages/modules/promotion/src/utils/validations/promotion-rule.ts +++ b/packages/modules/promotion/src/utils/validations/promotion-rule.ts @@ -1,5 +1,10 @@ -import { PromotionRuleDTO, PromotionRuleOperatorValues } from "@medusajs/types" import { + ApplicationMethodTargetTypeValues, + PromotionRuleDTO, + PromotionRuleOperatorValues, +} from "@medusajs/types" +import { + ApplicationMethodTargetType, MedusaError, PromotionRuleOperator, isPresent, @@ -46,7 +51,8 @@ export function validatePromotionRuleAttributes( export function areRulesValidForContext( rules: PromotionRuleDTO[], - context: Record + context: Record, + contextScope: ApplicationMethodTargetTypeValues ): boolean { return rules.every((rule) => { const validRuleValues = rule.values?.map((ruleValue) => ruleValue.value) @@ -55,7 +61,10 @@ export function areRulesValidForContext( return false } - const valuesToCheck = pickValueFromObject(rule.attribute, context) + const valuesToCheck = pickValueFromObject( + fetchRuleAttributeForContext(rule.attribute, contextScope), + context + ) return evaluateRuleValueCondition( validRuleValues.filter(isString), @@ -65,6 +74,38 @@ export function areRulesValidForContext( }) } +/* + The context here can either be either: + - a cart context + - an item context under a cart + - a shipping method context under a cart + + The rule's attributes are set from the perspective of the cart context. For example: items.product.id + + When the context here is item or shipping_method, we need to drop the "items."" or "shipping_method." + from the rule attribute string to accurate pick the values from the context. +*/ +function fetchRuleAttributeForContext( + ruleAttribute: string, + contextScope: ApplicationMethodTargetTypeValues +): string { + if (contextScope === ApplicationMethodTargetType.ITEMS) { + ruleAttribute = ruleAttribute.replace( + `${ApplicationMethodTargetType.ITEMS}.`, + "" + ) + } + + if (contextScope === ApplicationMethodTargetType.SHIPPING_METHODS) { + ruleAttribute = ruleAttribute.replace( + `${ApplicationMethodTargetType.SHIPPING_METHODS}.`, + "" + ) + } + + return ruleAttribute +} + export function evaluateRuleValueCondition( ruleValues: string[], operator: string,