Skip to main content
Skip to main content

Prices Calculation

In this document, you'll learn how prices are calculated under the hood when you use the calculatePrices method of the Pricing Module interface.

Overview

The calculatePrices method accepts the ID of one or more price sets and a context. For each price set, it selects two types of prices that best match the context; one belongs to a price list, and one doesn't.

Then, it returns an array of price objects, each price for every supplied price set ID.


Calculation Context

The context is an object passed to the calculatePrices method. It must contain at least the currency_code. Only prices in the price sets with the same currency code are considered for the price selection.

For example:

import {
initialize as initializePricingModule,
} from "@medusajs/pricing"

async function calculatePrice(
priceSetId: string,
currencyCode: string
) {
const pricingModuleService = await initializePricingModule()

const price = await pricingModuleService.calculatePrices(
{ id: [priceSetId] },
{
context: {
currency_code: currencyCode,
},
}
)
}

The context object can also contain any custom rules, with the key being the rule_attribute of a rule type and its value being the rule's value.

For example:

import {
initialize as initializePricingModule,
} from "@medusajs/pricing"

async function calculatePrice(
priceSetId: string,
currencyCode: string
) {
const pricingModuleService = await initializePricingModule()

const price = await pricingModuleService.calculatePrices(
{ id: [priceSetId] },
{
context: {
currency_code: currencyCode,
region_id: "US",
},
}
)
}

Prices Selection

For each price set, the method selects two prices:

  • The calculated price: a price that belongs to a price list. If there are no prices associated with a price list, it’ll be the same as the original price.
  • The original price: a price that doesn't belong to a price list.

Calculated Price Selection Process

A diagram showcasing the calculated price selection process.

  • Find the price set’s associated valid price lists. A price list is considered valid if:
    • The current date is between its start and end dates.
    • The price list's rules satisfy the context's rules.
  • If valid price lists are found, the prices within them are sorted by their amount in ascending order. The one having the lowest amount is selected as the calculated price.
  • If no valid price list is found, the selected calculated price will be the same as the original price.

Original Price Selection Process

A diagram showcasing the original price selection process.

  • If the price list associated with the calculated price is of type override, the selected original price is set to the calculated price.
  • If no rules are provided in the context other than the currency_code, the default price is selected as the original price. The default price is a price having no rules applied to it.
  • Otherwise, if a price exists in any price set with the same rules provided in the context, it's selected as the original price.
  • If no price exists with the same rules as the context, all prices satisfying any combination of the provided rules are retrieved.
    • The prices are sorted in descending order by the associated Price's rules_count, the default_priority of the rule types, and the priority of the associated PriceRule. The priority attribute has a higher precedence than the default_priority.
    • The highest price sorted is selected as the original price since it's considered the best price.

Returned Calculated Price

After the original and calculated prices are selected, the method will use them to create the following price object for each pr

const price = {
id: priceSetId,
is_calculated_price_price_list:
!!calculatedPrice?.price_list_id,
calculated_amount: parseInt(
calculatedPrice?.amount || ""
) || null,

is_original_price_price_list:
!!originalPrice?.price_list_id,
original_amount: parseInt(
originalPrice?.amount || ""
) || null,

currency_code: calculatedPrice?.currency_code || null,

calculated_price: {
price_id: calculatedPrice?.id || null,
price_list_id: calculatedPrice?.price_list_id || null,
price_list_type: calculatedPrice?.price_list_type || null,
min_quantity: parseInt(
calculatedPrice?.min_quantity || ""
) || null,
max_quantity: parseInt(
calculatedPrice?.max_quantity || ""
) || null,
},

original_price: {
price_id: originalPrice?.id || null,
price_list_id: originalPrice?.price_list_id || null,
price_list_type: originalPrice?.price_list_type || null,
min_quantity: parseInt(
originalPrice?.min_quantity || ""
) || null,
max_quantity: parseInt(
originalPrice?.max_quantity || ""
) || null,
},
}

Where:

  • id: The ID of the price set from which the price was selected.
  • is_calculated_price_price_list: whether the calculated price belongs to a price list. As mentioned earlier, if no valid price list is found, the calculated price is set to the original price, which doesn't belong to a price list.
  • calculated_amount: The amount of the calculated price, or null if there isn't a calculated price.
  • is_original_price_price_list: whether the original price belongs to a price list. As mentioned earlier, if the price list of the calculated price is of type override, the original price will be the same as the calculated price.
  • original_amount: The amount of the original price, or null if there isn't an original price.
  • currency_code: The currency code of the calculated price, or null if there isn't a calculated price.
  • calculated_price: An object containing the calculated price's price details and potentially its associated price list.
  • original_price: An object containing the original price's price details and potentially its associated price list.

The method returns an array of these price objects.


Example

Consider the following rule types and price sets:

const ruleTypes = await pricingModuleService.createRuleTypes([
{
name: "Region",
rule_attribute: "region_id",
},
{
name: "City",
rule_attribute: "city",
},
])

const priceSet = await pricingModuleService.create({
rules: [
{ rule_attribute: "region_id" },
{ rule_attribute: "city" },
],
prices: [
//default
{
amount: 500,
currency_code: "EUR",
rules: {},
},
// prices with rules
{
amount: 400,
currency_code: "EUR",
rules: {
region_id: "PL",
},
},
{
amount: 450,
currency_code: "EUR",
rules: {
city: "krakow",
},
},
{
amount: 500,
currency_code: "EUR",
rules: {
city: "warsaw",
region_id: "PL",
},
},
],
})

Default Price Selection

const price = await pricingModuleService.calculatePrices(
{ id: [priceSet.id] },
{
context: {
currency_code: "EUR"
}
}
)

Exact Match Rule in Context

const price = await pricingModuleService.calculatePrices(
{ id: [priceSet.id] },
{
context: {
currency_code: "EUR",
region_id: "PL"
}
}
)

Context without Exact Matching Rules

const price = await pricingModuleService.calculatePrices(
{ id: [priceSet.id] },
{
context: {
currency_code: "EUR",
region_id: "PL",
city: "krakow"
}
}
)

Price Selection with Price List

const priceList = pricingModuleService.createPriceList({
name: "Test Price List",
starts_at: Date.parse("01/10/2023"),
ends_at: Date.parse("31/10/2023"),
rules: {
region_id: ['PL']
},
type: "sale"
prices: [
{
amount: 400,
currency_code: "EUR",
price_set_id: priceSet.id,
},
{
amount: 450,
currency_code: "EUR",
price_set_id: priceSet.id,
},
],
});

const price = await pricingModuleService.calculatePrices(
{ id: [priceSet.id] },
{
context: {
currency_code: "EUR",
region_id: "PL",
city: "krakow"
}
}
)
Was this section helpful?