From 8ddf8b4d764ae359d279498f08d85146d55077f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:29:15 +0100 Subject: [PATCH] fix: skip promotion usage limit checks on edit flows (#14176) * fix: skip promotion usage limit checks on edit flows * feat: add test check --- .changeset/nice-kids-beg.md | 7 +++++++ .../http/__tests__/exchanges/exchanges.spec.ts | 10 ++++++++++ .../order/workflows/compute-adjustments-for-preview.ts | 7 +++++-- .../core/types/src/promotion/common/compute-actions.ts | 7 +++++++ .../modules/promotion/src/services/promotion-module.ts | 8 ++++++-- 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 .changeset/nice-kids-beg.md diff --git a/.changeset/nice-kids-beg.md b/.changeset/nice-kids-beg.md new file mode 100644 index 0000000000..50cdb440c2 --- /dev/null +++ b/.changeset/nice-kids-beg.md @@ -0,0 +1,7 @@ +--- +"@medusajs/promotion": patch +"@medusajs/core-flows": patch +"@medusajs/types": patch +--- + +fix: skip promotion usage limit checks on edit flows diff --git a/integration-tests/http/__tests__/exchanges/exchanges.spec.ts b/integration-tests/http/__tests__/exchanges/exchanges.spec.ts index 53978b4e25..894452044f 100644 --- a/integration-tests/http/__tests__/exchanges/exchanges.spec.ts +++ b/integration-tests/http/__tests__/exchanges/exchanges.spec.ts @@ -1437,6 +1437,16 @@ medusaIntegrationTestRunner({ adminHeaders ) + const promotionModule = getContainer().resolve(Modules.PROMOTION) + + // check that adjustments are computed for promotions that exceeded usage limit (we ignore usage limits on edit flows) + // @ts-ignore + await promotionModule.updatePromotions({ + id: appliedPromotion.id, + limit: 1, + used: 1, + }) + let result = await api.post( "/admin/exchanges", { diff --git a/packages/core/core-flows/src/order/workflows/compute-adjustments-for-preview.ts b/packages/core/core-flows/src/order/workflows/compute-adjustments-for-preview.ts index 756db5a731..c7690bb75a 100644 --- a/packages/core/core-flows/src/order/workflows/compute-adjustments-for-preview.ts +++ b/packages/core/core-flows/src/order/workflows/compute-adjustments-for-preview.ts @@ -29,7 +29,7 @@ export type ComputeAdjustmentsForPreviewWorkflowInput = { /** * The order's details. */ - order: OrderDTO & { + order: OrderDTO & { /** * The promotions applied to the order. */ @@ -52,7 +52,7 @@ export const computeAdjustmentsForPreviewWorkflowId = * * You can use this workflow within your customizations or your own custom workflows, allowing you to compute adjustments * in your custom flows. - * + * * @since v2.12.0 * * @example @@ -110,6 +110,9 @@ export const computeAdjustmentsForPreviewWorkflow = createWorkflow( const actions = getActionsToComputeFromPromotionsStep({ computeActionContext: actionsToComputeItemsInput, promotionCodesToApply: orderPromotions, + options: { + skip_usage_limit_checks: true, + }, }) const { lineItemAdjustmentsToCreate } = diff --git a/packages/core/types/src/promotion/common/compute-actions.ts b/packages/core/types/src/promotion/common/compute-actions.ts index 24e7bc9016..38bc28a79e 100644 --- a/packages/core/types/src/promotion/common/compute-actions.ts +++ b/packages/core/types/src/promotion/common/compute-actions.ts @@ -295,4 +295,11 @@ export interface ComputeActionOptions { * automatically. If not provided, the automatic promotions are applied. */ prevent_auto_promotions?: boolean + + /** + * Whether to skip the usage limit checks. + * Useful when recomputing adjustment for promotions that are already applied as a part of edit/exchange flows. + * + */ + skip_usage_limit_checks?: boolean } diff --git a/packages/modules/promotion/src/services/promotion-module.ts b/packages/modules/promotion/src/services/promotion-module.ts index 19f41663fc..96d9f5b756 100644 --- a/packages/modules/promotion/src/services/promotion-module.ts +++ b/packages/modules/promotion/src/services/promotion-module.ts @@ -634,7 +634,10 @@ export default class PromotionModuleService options: PromotionTypes.ComputeActionOptions = {}, @MedusaContext() sharedContext: Context = {} ): Promise { - const { prevent_auto_promotions: preventAutoPromotions } = options + const { + prevent_auto_promotions: preventAutoPromotions, + skip_usage_limit_checks: skipUsageLimitChecks, + } = options const computedActions: PromotionTypes.ComputeActions[] = [] const { items = [], shipping_methods: shippingMethods = [] } = applicationContext @@ -806,6 +809,7 @@ export default class PromotionModuleService } = promotion if ( + !skipUsageLimitChecks && promotion.campaign?.budget?.type === CampaignBudgetType.USE_BY_ATTRIBUTE ) { const attribute = promotion.campaign?.budget?.attribute! @@ -847,7 +851,7 @@ export default class PromotionModuleService } // Check promotion usage limit - if (typeof promotion.limit === "number") { + if (!skipUsageLimitChecks && typeof promotion.limit === "number") { if ((promotion.used ?? 0) >= promotion.limit) { computedActions.push({ action: ComputedActions.PROMOTION_LIMIT_EXCEEDED,