feat(core-flows,dashboard,js-sdk,promotion,medusa,types,utils): limit promotion usage per customer (#13451)
**What** - implement promotion usage limits per customer/email - fix registering spend usage over the limit - fix type errors in promotion module tests **How** - introduce a new type of campaign budget that can be defined by an attribute such as customer id or email - add `CampaignBudgetUsage` entity to keep track of the number of uses per attribute value - update `registerUsage` and `computeActions` in the promotion module to work with the new type - update `core-flows` to pass context needed for usage calculation to the promotion module **Breaking** - registering promotion usage now throws (and cart complete fails) if the budget limit is exceeded or if the cart completion would result in a breached limit --- CLOSES CORE-1172 CLOSES CORE-1173 CLOSES CORE-1174 CLOSES CORE-1175 Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// Always ensure that cartFieldsForCalculateShippingOptionsPrices is present in cartFieldsForRefreshSteps
|
||||
export const cartFieldsForRefreshSteps = [
|
||||
"id",
|
||||
"email",
|
||||
"currency_code",
|
||||
"quantity",
|
||||
"subtotal",
|
||||
|
||||
@@ -338,7 +338,13 @@ export const completeCartWorkflow = createWorkflow(
|
||||
})
|
||||
}
|
||||
|
||||
return promotionUsage
|
||||
return {
|
||||
computedActions: promotionUsage,
|
||||
registrationContext: {
|
||||
customer_id: cart.customer?.id || null,
|
||||
customer_email: cart.email || null,
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
CampaignBudgetUsageContext,
|
||||
IPromotionModuleService,
|
||||
UsageComputedActions,
|
||||
} from "@medusajs/framework/types"
|
||||
@@ -6,26 +7,37 @@ import { Modules } from "@medusajs/framework/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
export const registerUsageStepId = "register-usage"
|
||||
|
||||
type RegisterUsageStepInput = {
|
||||
computedActions: UsageComputedActions[]
|
||||
registrationContext: CampaignBudgetUsageContext
|
||||
}
|
||||
/**
|
||||
* This step registers usage for a promotion.
|
||||
*/
|
||||
export const registerUsageStep = createStep(
|
||||
registerUsageStepId,
|
||||
async (data: UsageComputedActions[], { container }) => {
|
||||
if (!data.length) {
|
||||
return new StepResponse(null, [])
|
||||
async (data: RegisterUsageStepInput, { container }) => {
|
||||
if (!data.computedActions.length) {
|
||||
return new StepResponse(null, {
|
||||
computedActions: [],
|
||||
registrationContext: data.registrationContext,
|
||||
})
|
||||
}
|
||||
|
||||
const promotionModule = container.resolve<IPromotionModuleService>(
|
||||
Modules.PROMOTION
|
||||
)
|
||||
|
||||
await promotionModule.registerUsage(data)
|
||||
await promotionModule.registerUsage(
|
||||
data.computedActions,
|
||||
data.registrationContext
|
||||
)
|
||||
|
||||
return new StepResponse(null, data)
|
||||
},
|
||||
async (revertData, { container }) => {
|
||||
if (!revertData?.length) {
|
||||
if (!revertData?.computedActions.length) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -33,6 +45,9 @@ export const registerUsageStep = createStep(
|
||||
Modules.PROMOTION
|
||||
)
|
||||
|
||||
await promotionModule.revertUsage(revertData)
|
||||
await promotionModule.revertUsage(
|
||||
revertData.computedActions,
|
||||
revertData.registrationContext
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user