Generated the following references: - `file` - `fulfillment` - `inventory` - `js_client` - `medusa` - `medusa_config` - `medusa_react` - `modules` - `notification` - `payment` - `price_selection` - `pricing` - `product` - `services` - `stock_location` - `tax` - `tax_calculation` - `types` - `workflows` Co-authored-by: Shahed Nasser <27354907+shahednasser@users.noreply.github.com>
292 lines
15 KiB
Plaintext
292 lines
15 KiB
Plaintext
---
|
|
displayed_sidebar: homepage
|
|
---
|
|
|
|
import ParameterTypes from "@site/src/components/ParameterTypes"
|
|
|
|
# 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:
|
|
|
|
```ts title="src/strategies/price.ts"
|
|
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
|
|
|
|
```ts
|
|
// ...
|
|
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
|
|
|
|
<ParameterTypes parameters={[{"name":"container","type":"`Record<string, unknown>`","description":"An instance of `MedusaContainer` that allows you to access other resources, such as services, in your Medusa backend.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"config","type":"`Record<string, unknown>`","description":"If this price-selection strategy is created in a plugin, the plugin's options are passed in this parameter.","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="new AbstractPriceSelectionStrategy"/>
|
|
|
|
___
|
|
|
|
## Properties
|
|
|
|
<ParameterTypes parameters={[{"name":"container","type":"`Record<string, unknown>`","description":"An instance of `MedusaContainer` that allows you to access other resources, such as services, in your Medusa backend.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"manager_","type":"`EntityManager`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionManager_","type":"`undefined` \\| `EntityManager`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"__container__","type":"`any`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"config","type":"`Record<string, unknown>`","description":"If this price-selection strategy is created in a plugin, the plugin's options are passed in this parameter.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"__configModule__","type":"`Record<string, unknown>`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"__moduleDeclaration__","type":"`Record<string, unknown>`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="AbstractPriceSelectionStrategy"/>
|
|
|
|
___
|
|
|
|
## Accessors
|
|
|
|
### activeManager\_
|
|
|
|
#### Returns
|
|
|
|
<ParameterTypes parameters={[{"name":"EntityManager","type":"`EntityManager`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="activeManager_"/>
|
|
|
|
___
|
|
|
|
## Methods
|
|
|
|
### 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:
|
|
|
|
```ts
|
|
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
|
|
|
|
<ParameterTypes parameters={[{"name":"data","type":"`object`[]","description":"The necessary data to perform the price selection for each variant ID.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"variantId","type":"`string`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"quantity","type":"`number`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"context","type":"[PriceSelectionContext](../interfaces/medusa.PriceSelectionContext.mdx)","description":"The context of the price selection.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"cart_id","type":"`string`","description":"The cart's ID. This is used when the prices are being retrieved for the variant of a line item,\nas it is used to determine the current region and currency code of the context.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"customer_id","type":"`string`","description":"The ID of the customer viewing the variant.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"region_id","type":"`string`","description":"The region's ID.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"quantity","type":"`number`","description":"The quantity of the item in the cart. This is used to filter out price lists that have\n`min_quantity` or `max_quantity` conditions set.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"currency_code","type":"`string`","description":"The currency code the customer is using.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"include_discount_prices","type":"`boolean`","description":"Whether the price list's prices should be retrieved or not.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"tax_rates","type":"[TaxServiceRate](../types/medusa.TaxServiceRate.mdx)[]","description":"The tax rates to be applied. This is only used for\n[Tax-Inclusive Pricing](https://docs.medusajs.com/modules/taxes/inclusive-pricing).","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"ignore_cache","type":"`boolean`","description":"Whether to calculate the prices even if the value of an earlier price calculation\nis available in the cache.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="calculateVariantPrice"/>
|
|
|
|
#### Returns
|
|
|
|
<ParameterTypes parameters={[{"name":"Promise","type":"Promise<Map<string, [PriceSelectionResult](../interfaces/medusa.PriceSelectionResult.mdx)>>","optional":false,"defaultValue":"","description":"A map, each key is an ID of a variant, and its value is an object holding the price selection result.","expandable":false,"children":[{"name":"Map","type":"Map<string, [PriceSelectionResult](../interfaces/medusa.PriceSelectionResult.mdx)>","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="calculateVariantPrice"/>
|
|
|
|
### 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:
|
|
|
|
```ts
|
|
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}:*`)
|
|
)
|
|
)
|
|
}
|
|
|
|
// ...
|
|
}
|
|
```
|
|
|
|
:::note
|
|
|
|
Learn more about the cache service in [this documentation](https://docs.medusajs.com/development/cache/overview).
|
|
|
|
:::
|
|
|
|
#### Parameters
|
|
|
|
<ParameterTypes parameters={[{"name":"variantIds","type":"`string`[]","description":"The IDs of the updated variants.","optional":false,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="onVariantsPricesUpdate"/>
|
|
|
|
#### Returns
|
|
|
|
<ParameterTypes parameters={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"Resolves after any necessary actions are performed.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="onVariantsPricesUpdate"/>
|
|
|
|
### withTransaction
|
|
|
|
#### Parameters
|
|
|
|
<ParameterTypes parameters={[{"name":"transactionManager","type":"`EntityManager`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="withTransaction"/>
|
|
|
|
#### Returns
|
|
|
|
<ParameterTypes parameters={[{"name":"this","type":"`this`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="withTransaction"/>
|
|
|
|
### shouldRetryTransaction\_
|
|
|
|
#### Parameters
|
|
|
|
<ParameterTypes parameters={[{"name":"err","type":"`Record<string, unknown>` \\| `object`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="shouldRetryTransaction_"/>
|
|
|
|
#### Returns
|
|
|
|
<ParameterTypes parameters={[{"name":"boolean","type":"`boolean`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="shouldRetryTransaction_"/>
|
|
|
|
### atomicPhase\_
|
|
|
|
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
|
|
|
|
<ParameterTypes parameters={[{"name":"TResult","type":"`object`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"TError","type":"`object`","description":"","optional":false,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="atomicPhase_"/>
|
|
|
|
#### Parameters
|
|
|
|
<ParameterTypes parameters={[{"name":"work","type":"(`transactionManager`: `EntityManager`) => Promise<TResult>","description":"the transactional work to be done","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationOrErrorHandler","type":"`IsolationLevel` \\| (`error`: TError) => Promise<void \\| TResult>","description":"the isolation level to be used for the work.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"maybeErrorHandlerOrDontFail","type":"(`error`: TError) => Promise<void \\| TResult>","description":"Potential error handler","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="atomicPhase_"/>
|
|
|
|
#### Returns
|
|
|
|
<ParameterTypes parameters={[{"name":"Promise","type":"Promise<TResult>","optional":false,"defaultValue":"","description":"the result of the transactional work","expandable":false,"children":[{"name":"TResult","type":"TResult","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/development/entities/repositories#retrieving-a-list-of-records" sectionTitle="atomicPhase_"/>
|