feat(utils,types): added item/shipping adjustments for order/items/shipping_methods (#6050)

what:

- adds compute actions for the following cases:
  - items => each & across
  - shipping_method => each & across
  - order
- adds a remove compute actions when code is no longer present in adjustments array

RESOLVES CORE-1625
RESOLVES CORE-1626
RESOLVES CORE-1627
RESOLVES CORE-1628
RESOLVES CORE-1585
This commit is contained in:
Riqwan Thamir
2024-01-12 13:00:06 +00:00
committed by GitHub
parent 192bc336cc
commit b782d3bcb7
24 changed files with 2638 additions and 81 deletions
@@ -0,0 +1,3 @@
export * from "./items"
export * from "./order"
export * from "./shipping-methods"
@@ -0,0 +1,131 @@
import {
ApplicationMethodAllocationValues,
PromotionTypes,
} from "@medusajs/types"
import {
ApplicationMethodAllocation,
ApplicationMethodTargetType,
MedusaError,
} from "@medusajs/utils"
import { areRulesValidForContext } from "../validations"
export function getComputedActionsForItems(
promotion: PromotionTypes.PromotionDTO,
itemApplicationContext: PromotionTypes.ComputeActionContext[ApplicationMethodTargetType.ITEMS],
methodIdPromoValueMap: Map<string, number>,
allocationOverride?: ApplicationMethodAllocationValues
): PromotionTypes.ComputeActions[] {
const applicableItems: PromotionTypes.ComputeActionContext[ApplicationMethodTargetType.ITEMS] =
[]
if (!itemApplicationContext) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`"items" should be present as an array in the context for computeActions`
)
}
for (const itemContext of itemApplicationContext) {
const isPromotionApplicableToItem = areRulesValidForContext(
promotion?.application_method?.target_rules!,
itemContext
)
if (!isPromotionApplicableToItem) {
continue
}
applicableItems.push(itemContext)
}
return applyPromotionToItems(
promotion,
applicableItems,
methodIdPromoValueMap,
allocationOverride
)
}
export function applyPromotionToItems(
promotion: PromotionTypes.PromotionDTO,
items: PromotionTypes.ComputeActionContext[ApplicationMethodTargetType.ITEMS],
methodIdPromoValueMap: Map<string, number>,
allocationOverride?: ApplicationMethodAllocationValues
): PromotionTypes.ComputeActions[] {
const { application_method: applicationMethod } = promotion
const allocation = applicationMethod?.allocation!
const computedActions: PromotionTypes.ComputeActions[] = []
if (
[allocation, allocationOverride].includes(ApplicationMethodAllocation.EACH)
) {
for (const method of items!) {
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
const promotionValue = parseFloat(applicationMethod!.value!)
const applicableTotal =
method.unit_price *
Math.min(method.quantity, applicationMethod?.max_quantity!) -
appliedPromoValue
const amount = Math.min(promotionValue, applicableTotal)
if (amount <= 0) {
continue
}
methodIdPromoValueMap.set(method.id, appliedPromoValue + amount)
computedActions.push({
action: "addItemAdjustment",
item_id: method.id,
amount,
code: promotion.code!,
})
}
}
if (
[allocation, allocationOverride].includes(
ApplicationMethodAllocation.ACROSS
)
) {
const totalApplicableValue = items!.reduce((acc, method) => {
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
return (
acc +
method.unit_price *
Math.min(method.quantity, applicationMethod?.max_quantity!) -
appliedPromoValue
)
}, 0)
for (const method of items!) {
const promotionValue = parseFloat(applicationMethod!.value!)
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
const applicableTotal =
method.unit_price *
Math.min(method.quantity, applicationMethod?.max_quantity!) -
appliedPromoValue
// TODO: should we worry about precision here?
const applicablePromotionValue =
(applicableTotal / totalApplicableValue) * promotionValue
const amount = Math.min(applicablePromotionValue, applicableTotal)
if (amount <= 0) {
continue
}
computedActions.push({
action: "addItemAdjustment",
item_id: method.id,
amount,
code: promotion.code!,
})
}
}
return computedActions
}
@@ -0,0 +1,19 @@
import { PromotionTypes } from "@medusajs/types"
import {
ApplicationMethodAllocation,
ApplicationMethodTargetType,
} from "@medusajs/utils"
import { getComputedActionsForItems } from "./items"
export function getComputedActionsForOrder(
promotion: PromotionTypes.PromotionDTO,
itemApplicationContext: PromotionTypes.ComputeActionContext,
methodIdPromoValueMap: Map<string, number>
): PromotionTypes.ComputeActions[] {
return getComputedActionsForItems(
promotion,
itemApplicationContext[ApplicationMethodTargetType.ITEMS],
methodIdPromoValueMap,
ApplicationMethodAllocation.ACROSS
)
}
@@ -0,0 +1,114 @@
import { PromotionTypes } from "@medusajs/types"
import {
ApplicationMethodAllocation,
ApplicationMethodTargetType,
MedusaError,
} from "@medusajs/utils"
import { areRulesValidForContext } from "../validations"
export function getComputedActionsForShippingMethods(
promotion: PromotionTypes.PromotionDTO,
shippingMethodApplicationContext: PromotionTypes.ComputeActionContext[ApplicationMethodTargetType.SHIPPING_METHODS],
methodIdPromoValueMap: Map<string, number>
): PromotionTypes.ComputeActions[] {
const applicableShippingItems: PromotionTypes.ComputeActionContext[ApplicationMethodTargetType.SHIPPING_METHODS] =
[]
if (!shippingMethodApplicationContext) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`"shipping_methods" should be present as an array in the context for computeActions`
)
}
for (const shippingMethodContext of shippingMethodApplicationContext) {
const isPromotionApplicableToItem = areRulesValidForContext(
promotion.application_method?.target_rules!,
shippingMethodContext
)
if (!isPromotionApplicableToItem) {
continue
}
applicableShippingItems.push(shippingMethodContext)
}
return applyPromotionToShippingMethods(
promotion,
applicableShippingItems,
methodIdPromoValueMap
)
}
export function applyPromotionToShippingMethods(
promotion: PromotionTypes.PromotionDTO,
shippingMethods: PromotionTypes.ComputeActionContext[ApplicationMethodTargetType.SHIPPING_METHODS],
methodIdPromoValueMap: Map<string, number>
): PromotionTypes.ComputeActions[] {
const { application_method: applicationMethod } = promotion
const allocation = applicationMethod?.allocation!
const computedActions: PromotionTypes.ComputeActions[] = []
if (allocation === ApplicationMethodAllocation.EACH) {
for (const method of shippingMethods!) {
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
const promotionValue = parseFloat(applicationMethod!.value!)
const applicableTotal = method.unit_price - appliedPromoValue
const amount = Math.min(promotionValue, applicableTotal)
if (amount <= 0) {
continue
}
methodIdPromoValueMap.set(method.id, appliedPromoValue + amount)
computedActions.push({
action: "addShippingMethodAdjustment",
shipping_method_id: method.id,
amount,
code: promotion.code!,
})
}
}
if (allocation === ApplicationMethodAllocation.ACROSS) {
const totalApplicableValue = shippingMethods!.reduce((acc, method) => {
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
return acc + method.unit_price - appliedPromoValue
}, 0)
if (totalApplicableValue <= 0) {
return computedActions
}
for (const method of shippingMethods!) {
const promotionValue = parseFloat(applicationMethod!.value!)
const applicableTotal = method.unit_price
const appliedPromoValue = methodIdPromoValueMap.get(method.id) || 0
// TODO: should we worry about precision here?
const applicablePromotionValue =
(applicableTotal / totalApplicableValue) * promotionValue -
appliedPromoValue
const amount = Math.min(applicablePromotionValue, applicableTotal)
if (amount <= 0) {
continue
}
methodIdPromoValueMap.set(method.id, appliedPromoValue + amount)
computedActions.push({
action: "addShippingMethodAdjustment",
shipping_method_id: method.id,
amount,
code: promotion.code!,
})
}
}
return computedActions
}