fix(promotion): validate rules accurately when attribute is scoped by context (#8655)
This commit is contained in:
@@ -56,7 +56,7 @@ medusaIntegrationTestRunner({
|
||||
max_quantity: 1,
|
||||
target_rules: [
|
||||
{
|
||||
attribute: "product_id",
|
||||
attribute: "items.product_id",
|
||||
operator: "eq",
|
||||
values: "prod_tshirt",
|
||||
},
|
||||
|
||||
@@ -362,7 +362,8 @@ export default class PromotionModuleService
|
||||
|
||||
const isPromotionApplicable = areRulesValidForContext(
|
||||
promotionRules,
|
||||
applicationContext
|
||||
applicationContext,
|
||||
ApplicationMethodTargetType.ORDER
|
||||
)
|
||||
|
||||
if (!isPromotionApplicable) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<string, any>
|
||||
context: Record<string, any>,
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user