Skip to content

[Bug]: Buy-get promotion ignores application_method.type, fixed amount discount calculates as percentage #14934

@anh20168620

Description

@anh20168620

Bug Report

Description

The buy-get (buyget) promotion engine ignores the application_method.type field and always calculates the discount as a percentage, even when the type is set to "fixed". This causes wildly incorrect discount amounts when using fixed amount discounts with buyget promotions.

Expected Behavior

When a buyget promotion has application_method.type: "fixed" and value: 50000 (e.g., 50,000 VND), the target item should receive a fixed discount of 50,000 VND per unit.

This is consistent with:

  • The official documentation example which shows buyget with type: "fixed"
  • The ApplicationMethod data model which states: "If it's fixed, the promotion discounts a fixed amount"
  • How standard promotions handle type via getPromotionValue() in @medusajs/utils/dist/totals/promotion/index.js

Actual Behavior

The engine always calculates: amount = applicableAmount × value / 100

So a value: 50000 with type: "fixed" is treated as 50,000% discount instead of a fixed 50,000 VND — resulting in an astronomically large discount.

Root Cause

In packages/modules/promotion/src/utils/compute-actions/buy-get.ts, the applyPromotionToTargetItems function hardcodes percentage calculation:

const applicablePercentage = promotion.application_method?.value ?? 100
const amount = MathBN.mult(applicableAmount, applicablePercentage).div(100)

It never reads promotion.application_method?.type to determine whether to apply a fixed or percentage calculation.

Compare with standard promotions in @medusajs/utils/src/totals/promotion/index.ts:

function getPromotionValue(promotion, lineItemAmount, lineItemsAmount, lineItem) {
  if (promotion.type === ApplicationMethodType.PERCENTAGE) {
    return getPromotionValueForPercentage(promotion, lineItemAmount)
  }
  return getPromotionValueForFixed(promotion, lineItemAmount, lineItemsAmount, lineItem)
}

Standard promotions correctly branch on type. Buyget does not.

Steps to Reproduce

  1. Create a buyget promotion with type: "fixed" and a numeric value (e.g., 50000):
await promotionModuleService.createPromotions({
  code: "BUY1GET1-50K-OFF",
  type: "buyget",
  status: "active",
  application_method: {
    type: "fixed",
    target_type: "items",
    value: 50000,
    buy_rules_min_quantity: 1,
    apply_to_quantity: 1,
    max_quantity: 1,
    buy_rules: [{ attribute: "products.id", operator: "in", values: ["prod_buy"] }],
    target_rules: [{ attribute: "products.id", operator: "in", values: ["prod_target"] }],
  },
})
  1. Add qualifying buy and target items to a cart
  2. Observe the computed discount amount — it will be itemPrice × 50000 / 100 instead of 50000

Additional Context

  • PR fix(promotion): percentage value is accounted for in buyget promotions #11799 (merged 2025-03-13) fixed percentage support for buyget but did not address fixed amount support. The PR also changed all buyget tests from type: "fixed" to type: "percentage", confirming that fixed was not working.
  • The admin dashboard edit form allows changing buyget promotions to type: "fixed", and the value is correctly saved to the database — but the compute engine ignores it.

Environment

  • Medusa version: 2.13.1
  • @medusajs/promotion version: 2.13.1

Suggested Fix

In buy-get.ts, check promotion.application_method?.type before calculating the amount:

let amount
if (promotion.application_method?.type === "fixed") {
  amount = MathBN.min(
    MathBN.mult(promotion.application_method.value, multiplier),
    applicableAmount
  )
} else {
  const applicablePercentage = promotion.application_method?.value ?? 100
  amount = MathBN.mult(applicableAmount, applicablePercentage).div(100)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions