AbstractPriceSelectionStrategy
Overview
The price selection strategy retrieves the best price for a product variant for a specific context such as selected region, taxes applied, the quantity in cart, and more.
Medusa provides a default price selection strategy, but you can override it. A price selecion strategy is a TypeScript or JavaScript file in the src/strategies
directory of your Medusa backend project. It exports a class that extends the AbstractPriceSelectionStrategy
class.
For example:
import {
AbstractPriceSelectionStrategy,
PriceSelectionContext,
PriceSelectionResult,
} from "@medusajs/medusa"
export default class MyStrategy extends
AbstractPriceSelectionStrategy {
async calculateVariantPrice(
data: {
variantId: string;
quantity?: number
}[],
context: PriceSelectionContext
): Promise<Map<string, PriceSelectionResult>> {
throw new Error("Method not implemented.")
}
}
constructor
You can use the constructor
of your price-selection strategy to access the different services in Medusa through dependency injection.
Example
// ...
import {
AbstractPriceSelectionStrategy,
CustomerService,
} from "@medusajs/medusa"
type InjectedDependencies = {
customerService: CustomerService
}
class MyStrategy extends
AbstractPriceSelectionStrategy {
protected customerService_: CustomerService
constructor(container: InjectedDependencies) {
super(container)
this.customerService_ = container.customerService
}
// ...
}
export default MyStrategy
Parameters
container
Record<string, unknown>RequiredMedusaContainer
that allows you to access other resources, such as services, in your Medusa backend.config
Record<string, unknown>Properties
container
Record<string, unknown>RequiredMedusaContainer
that allows you to access other resources, such as services, in your Medusa backend.manager_
EntityManagerRequiredtransactionManager_
undefined | EntityManagerRequired__container__
anyRequiredconfig
Record<string, unknown>__configModule__
Record<string, unknown>__moduleDeclaration__
Record<string, unknown>Accessors
activeManager_
Returns
EntityManager
EntityManagerRequiredMethods
calculateVariantPrice
This method retrieves one or more product variants' prices. It's used when retrieving product variants or their associated line items. It's also used when retrieving other entities that product variants and line items belong to, such as products and carts respectively.
Example
For example, here's a snippet of how the price selection strategy is implemented in the Medusa backend:
import {
AbstractPriceSelectionStrategy,
CustomerService,
PriceSelectionContext,
PriceSelectionResult,
} from "@medusajs/medusa"
type InjectedDependencies = {
customerService: CustomerService
}
export default class MyStrategy extends
AbstractPriceSelectionStrategy {
async calculateVariantPrice(
data: {
variantId: string
quantity?: number
}[],
context: PriceSelectionContext
): Promise<Map<string, PriceSelectionResult>> {
const dataMap = new Map(data.map((d) => [d.variantId, d]))
const cacheKeysMap = new Map(
data.map(({ variantId, quantity }) => [
variantId,
this.getCacheKey(variantId, { ...context, quantity }),
])
)
const nonCachedData: {
variantId: string
quantity?: number
}[] = []
const variantPricesMap = new Map<string, PriceSelectionResult>()
if (!context.ignore_cache) {
const cacheHits = await promiseAll(
[...cacheKeysMap].map(async ([, cacheKey]) => {
return await this.cacheService_.get<PriceSelectionResult>(cacheKey)
})
)
if (!cacheHits.length) {
nonCachedData.push(...dataMap.values())
}
for (const [index, cacheHit] of cacheHits.entries()) {
const variantId = data[index].variantId
if (cacheHit) {
variantPricesMap.set(variantId, cacheHit)
continue
}
nonCachedData.push(dataMap.get(variantId)!)
}
} else {
nonCachedData.push(...dataMap.values())
}
let results: Map<string, PriceSelectionResult> = new Map()
if (
this.featureFlagRouter_.isFeatureEnabled(
TaxInclusivePricingFeatureFlag.key
)
) {
results = await this.calculateVariantPrice_new(nonCachedData, context)
} else {
results = await this.calculateVariantPrice_old(nonCachedData, context)
}
await promiseAll(
[...results].map(async ([variantId, prices]) => {
variantPricesMap.set(variantId, prices)
if (!context.ignore_cache) {
await this.cacheService_.set(cacheKeysMap.get(variantId)!, prices)
}
})
)
return variantPricesMap
}
// ...
}
Parameters
data
object[]RequiredThe necessary data to perform the price selection for each variant ID.
data
object[]RequiredThe context of the price selection.
Returns
A map, each key is an ID of a variant, and its value is an object holding the price selection result.
onVariantsPricesUpdate
This method is called when prices of product variants have changed. You can use it to invalidate prices stored in the cache.
Example
For example, this is how this method is implemented in the Medusa backend's default price selection strategy:
import {
AbstractPriceSelectionStrategy,
CustomerService,
} from "@medusajs/medusa"
import { promiseAll } from "@medusajs/utils"
type InjectedDependencies = {
customerService: CustomerService
}
export default class MyStrategy extends
AbstractPriceSelectionStrategy {
public async onVariantsPricesUpdate(variantIds: string[]): Promise<void> {
await promiseAll(
variantIds.map(
async (id: string) => await this.cacheService_.invalidate(`ps:${id}:*`)
)
)
}
// ...
}
Learn more about the cache service in this documentation.
Parameters
variantIds
string[]RequiredReturns
Promise
Promise<void>RequiredwithTransaction
Parameters
transactionManager
EntityManagerReturns
this
thisRequiredshouldRetryTransaction_
Parameters
err
Record<string, unknown> | objectRequiredReturns
boolean
booleanRequiredatomicPhase_
Wraps some work within a transactional block. If the service already has a transaction manager attached this will be reused, otherwise a new transaction manager is created.
Type Parameters
TResult
objectRequiredTError
objectRequiredParameters
work
(transactionManager: EntityManager) => Promise<TResult>RequiredisolationOrErrorHandler
IsolationLevel | (error: TError) => Promise<void | TResult>maybeErrorHandlerOrDontFail
(error: TError) => Promise<void | TResult>Returns
Promise
Promise<TResult>Requiredthe result of the transactional work
Promise
Promise<TResult>Required