Chore(): localised improvement to promotion module (#13446)

**What**
- Remove overserialization withing transaction and rely more on internal service or protected method instead which does not serialize and work with Infered entity type
- Slightly rework loop complexity

Overall, this gives a good spare of resources and time spent for serialization
This commit is contained in:
Adrien de Peretti
2025-09-10 12:20:31 +02:00
committed by GitHub
parent d978749603
commit ac09b3cbef
9 changed files with 129 additions and 67 deletions

View File

@@ -1,6 +1,7 @@
import {
BigNumberInput,
ComputeActionItemLine,
InferEntityType,
PromotionTypes,
} from "@medusajs/framework/types"
import {
@@ -12,6 +13,7 @@ import {
} from "@medusajs/framework/utils"
import { areRulesValidForContext } from "../validations"
import { computeActionForBudgetExceeded } from "./usage"
import { Promotion } from "@models"
export type EligibleItem = {
item_id: string
@@ -23,7 +25,7 @@ function sortByPrice(a: ComputeActionItemLine, b: ComputeActionItemLine) {
}
function isValidPromotionContext(
promotion: PromotionTypes.PromotionDTO,
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>,
itemsContext: ComputeActionItemLine[]
): boolean {
if (!itemsContext) {
@@ -52,7 +54,7 @@ function isValidPromotionContext(
}
function normalizePromotionApplicationConfiguration(
promotion: PromotionTypes.PromotionDTO
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>
) {
const minimumBuyQuantity = MathBN.convert(
promotion.application_method?.buy_rules_min_quantity ?? 0
@@ -275,7 +277,7 @@ function applyPromotionToTargetItems(
targetItems: EligibleItem[],
itemIdPromotionAmountMap: Map<string, BigNumberInput>,
methodIdPromoValueMap: Map<string, BigNumberInput>,
promotion: PromotionTypes.PromotionDTO,
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>,
itemsMap: Map<string, ComputeActionItemLine>,
applicationConfig: PromotionConfig
): {
@@ -456,7 +458,7 @@ function filterItemsByPromotionRules(
}
export function getComputedActionsForBuyGet(
promotion: PromotionTypes.PromotionDTO,
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>,
itemsContext: ComputeActionItemLine[],
methodIdPromoValueMap: Map<string, BigNumberInput>,
eligibleBuyItemMap: Map<string, EligibleItem[]>,

View File

@@ -1,4 +1,9 @@
import { ApplicationMethodAllocationValues, BigNumberInput, PromotionTypes, } from "@medusajs/framework/types"
import {
ApplicationMethodAllocationValues,
BigNumberInput,
InferEntityType,
PromotionTypes,
} from "@medusajs/framework/types"
import {
ApplicationMethodAllocation,
ApplicationMethodTargetType,
@@ -10,6 +15,7 @@ import {
} from "@medusajs/framework/utils"
import { areRulesValidForContext } from "../validations"
import { computeActionForBudgetExceeded } from "./usage"
import { Promotion } from "@models"
function validateContext(
contextKey: string,
@@ -24,7 +30,7 @@ function validateContext(
}
export function getComputedActionsForItems(
promotion: PromotionTypes.PromotionDTO,
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>,
items: PromotionTypes.ComputeActionContext[TargetType.ITEMS],
appliedPromotionsMap: Map<string, number>,
allocationOverride?: ApplicationMethodAllocationValues
@@ -40,7 +46,7 @@ export function getComputedActionsForItems(
}
function applyPromotionToItems(
promotion: PromotionTypes.PromotionDTO,
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>,
items: PromotionTypes.ComputeActionContext[TargetType.ITEMS],
appliedPromotionsMap: Map<string, BigNumberInput>,
allocationOverride?: ApplicationMethodAllocationValues
@@ -149,7 +155,7 @@ function getValidItemsForPromotion(
items:
| PromotionTypes.ComputeActionContext[TargetType.ITEMS]
| PromotionTypes.ComputeActionContext[TargetType.SHIPPING_METHODS],
promotion: PromotionTypes.PromotionDTO
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>
) {
if (!items?.length || !promotion?.application_method) {
return []

View File

@@ -1,4 +1,8 @@
import { BigNumberInput, PromotionTypes } from "@medusajs/framework/types"
import {
BigNumberInput,
InferEntityType,
PromotionTypes,
} from "@medusajs/framework/types"
import {
ApplicationMethodAllocation,
ApplicationMethodTargetType,
@@ -9,9 +13,10 @@ import {
} from "@medusajs/framework/utils"
import { areRulesValidForContext } from "../validations"
import { computeActionForBudgetExceeded } from "./usage"
import { Promotion } from "@models"
export function getComputedActionsForShippingMethods(
promotion: PromotionTypes.PromotionDTO,
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>,
shippingMethodApplicationContext: PromotionTypes.ComputeActionContext[ApplicationMethodTargetType.SHIPPING_METHODS],
methodIdPromoValueMap: Map<string, number>
): PromotionTypes.ComputeActions[] {
@@ -47,7 +52,7 @@ export function getComputedActionsForShippingMethods(
}
export function applyPromotionToShippingMethods(
promotion: PromotionTypes.PromotionDTO,
promotion: PromotionTypes.PromotionDTO | InferEntityType<typeof Promotion>,
shippingMethods: PromotionTypes.ComputeActionContext[ApplicationMethodTargetType.SHIPPING_METHODS],
methodIdPromoValueMap: Map<string, BigNumberInput>
): PromotionTypes.ComputeActions[] {
@@ -126,10 +131,16 @@ export function applyPromotionToShippingMethods(
const appliedPromoValue = methodIdPromoValueMap.get(method.id) ?? 0
const applicableTotal = MathBN.sub(method.subtotal, appliedPromoValue)
let applicablePromotionValue = MathBN.mult(MathBN.div(applicableTotal, totalApplicableValue), promotionValue)
let applicablePromotionValue = MathBN.mult(
MathBN.div(applicableTotal, totalApplicableValue),
promotionValue
)
if (applicationMethod?.type === ApplicationMethodType.PERCENTAGE) {
applicablePromotionValue = MathBN.mult(MathBN.div(promotionValue, 100), applicableTotal)
applicablePromotionValue = MathBN.mult(
MathBN.div(promotionValue, 100),
applicableTotal
)
}
const amount = MathBN.min(applicablePromotionValue, applicableTotal)

View File

@@ -1,6 +1,7 @@
import {
BigNumberInput,
CampaignBudgetExceededAction,
InferEntityType,
PromotionDTO,
} from "@medusajs/framework/types"
import {
@@ -8,9 +9,10 @@ import {
ComputedActions,
MathBN,
} from "@medusajs/framework/utils"
import { Promotion } from "@models"
export function computeActionForBudgetExceeded(
promotion: PromotionDTO,
promotion: PromotionDTO | InferEntityType<typeof Promotion>,
amount: BigNumberInput
): CampaignBudgetExceededAction | void {
const campaignBudget = promotion.campaign?.budget

View File

@@ -1,5 +1,6 @@
import {
ApplicationMethodTargetTypeValues,
InferEntityType,
PromotionRuleDTO,
PromotionRuleOperatorValues,
} from "@medusajs/framework/types"
@@ -12,6 +13,7 @@ import {
isString,
pickValueFromObject,
} from "@medusajs/framework/utils"
import { PromotionRule } from "@models"
import { CreatePromotionRuleDTO } from "@types"
export function validatePromotionRuleAttributes(
@@ -51,7 +53,7 @@ export function validatePromotionRuleAttributes(
}
export function areRulesValidForContext(
rules: PromotionRuleDTO[],
rules: PromotionRuleDTO[] | InferEntityType<typeof PromotionRule>[],
context: Record<string, any>,
contextScope: ApplicationMethodTargetTypeValues
): boolean {
@@ -63,14 +65,17 @@ export function areRulesValidForContext(
const isShippingScope =
contextScope === ApplicationMethodTargetType.SHIPPING_METHODS
return rules.every((rule) => {
return ("initialized" in rules ? [...rules] : rules).every((rule) => {
if (!rule.attribute || !rule.values?.length) {
return false
}
const validRuleValues = rule.values
.filter((v) => isString(v.value))
.map((v) => v.value as string)
const validRuleValues: string[] = []
for (const value of rule.values) {
if (isString(value.value)) {
validRuleValues.push(value.value as string)
}
}
if (!validRuleValues.length) {
return false