fix(promotion): validate rules accurately when attribute is scoped by context (#8655)

This commit is contained in:
Riqwan Thamir
2024-08-19 15:38:42 +02:00
committed by GitHub
parent a66bd3bd6b
commit 4791d1d775
6 changed files with 66 additions and 9 deletions
@@ -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,