feat(dashboard,core,modules): free shipping promotion in dashboard (#13263)

* feat(dashboard,core,modules): free shipping promotion in dashboard

* self-review

* adapt for edit to work

* changeset

* integration tests

* across for each

* remove only from tests

* remove console log

* revert to across

* update wording for shipping promotions

* modify changeset

* suggestion frane

* fix i18n schema
This commit is contained in:
William Bouchard
2025-08-22 16:30:30 -04:00
committed by GitHub
parent 92e3b2b432
commit 486621383a
22 changed files with 474 additions and 331 deletions
@@ -0,0 +1,22 @@
import { ModuleJoinerConfig } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
export const CartShippingOption: ModuleJoinerConfig = {
isLink: true,
isReadOnlyLink: true,
extends: [
{
serviceName: Modules.CART,
entity: "ShippingMethod",
relationship: {
serviceName: Modules.FULFILLMENT,
primaryKey: "id",
foreignKey: "shipping_option_id",
alias: "shipping_option",
args: {
methodSuffix: "ShippingOptions",
},
},
},
],
}
@@ -2,6 +2,7 @@ export * from "./cart-customer"
export * from "./cart-product"
export * from "./cart-region"
export * from "./cart-sales-channel"
export * from "./cart-shipping-option"
export * from "./inventory-level-stock-location"
export * from "./line-item-adjustment-promotion"
export * from "./order-customer"
@@ -1,10 +1,5 @@
import { IPromotionModuleService } from "@medusajs/framework/types"
import {
ApplicationMethodType,
Modules,
PromotionStatus,
PromotionType,
} from "@medusajs/framework/utils"
import { ApplicationMethodType, Modules, PromotionStatus, PromotionType, } from "@medusajs/framework/utils"
import { moduleIntegrationTestRunner, SuiteOptions } from "@medusajs/test-utils"
import { createCampaigns } from "../../../__fixtures__/campaigns"
import { createDefaultPromotion } from "../../../__fixtures__/promotion"
@@ -1,3 +1,4 @@
export * from "./buy-get"
export * from "./line-items"
export * from "./shipping-methods"
export * from "./usage"
@@ -6,11 +6,11 @@ import {
import {
ApplicationMethodAllocation,
ApplicationMethodTargetType,
ApplicationMethodTargetType as TargetType,
calculateAdjustmentAmountFromPromotion,
ComputedActions,
MathBN,
MedusaError,
ApplicationMethodTargetType as TargetType,
calculateAdjustmentAmountFromPromotion,
} from "@medusajs/framework/utils"
import { areRulesValidForContext } from "../validations"
import { computeActionForBudgetExceeded } from "./usage"
@@ -43,29 +43,6 @@ export function getComputedActionsForItems(
)
}
export function getComputedActionsForShippingMethods(
promotion: PromotionTypes.PromotionDTO,
shippingMethods: PromotionTypes.ComputeActionContext[TargetType.SHIPPING_METHODS],
appliedPromotionsMap: Map<string, number>
): PromotionTypes.ComputeActions[] {
validateContext("shipping_methods", shippingMethods)
return applyPromotionToItems(promotion, shippingMethods, appliedPromotionsMap)
}
export function getComputedActionsForOrder(
promotion: PromotionTypes.PromotionDTO,
itemApplicationContext: PromotionTypes.ComputeActionContext,
methodIdPromoValueMap: Map<string, number>
): PromotionTypes.ComputeActions[] {
return getComputedActionsForItems(
promotion,
itemApplicationContext[TargetType.ITEMS],
methodIdPromoValueMap,
ApplicationMethodAllocation.ACROSS
)
}
function applyPromotionToItems(
promotion: PromotionTypes.PromotionDTO,
items:
@@ -123,20 +123,13 @@ export function applyPromotionToShippingMethods(
}
const promotionValue = applicationMethod?.value ?? 0
const applicableTotal = method.subtotal
const appliedPromoValue = methodIdPromoValueMap.get(method.id) ?? 0
const applicableTotal = MathBN.sub(method.subtotal, appliedPromoValue)
const div = MathBN.eq(totalApplicableValue, 0) ? 1 : totalApplicableValue
let applicablePromotionValue = MathBN.sub(
MathBN.mult(MathBN.div(applicableTotal, div), promotionValue),
appliedPromoValue
)
let applicablePromotionValue = MathBN.mult(MathBN.div(applicableTotal, totalApplicableValue), promotionValue)
if (applicationMethod?.type === ApplicationMethodType.PERCENTAGE) {
applicablePromotionValue = MathBN.sub(
MathBN.mult(MathBN.div(promotionValue, 100), applicableTotal),
appliedPromoValue
)
applicablePromotionValue = MathBN.mult(MathBN.div(promotionValue, 100), applicableTotal)
}
const amount = MathBN.min(applicablePromotionValue, applicableTotal)