chore: reorganize docs apps (#7228)
* reorganize docs apps * add README * fix directory * add condition for old docs
This commit is contained in:
1 parent
224ebb2154
commit
4fe28f5a95
6187 files changed
+601447
-598226
No files matched your search
@@ -0,0 +1,75 @@
|
||||
export const metadata = {
|
||||
title: `Pricing Concepts`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you’ll learn about the main concepts in the Pricing Module, and how data is stored and related.
|
||||
|
||||
## Price
|
||||
|
||||
The `Price` data model represents any price.
|
||||
|
||||
Prices can be conditioned by the `min_quantity` and `max_quantity` fields, which are helpful when calculating the price for a specific quantity.
|
||||
|
||||
If a price has its `min_quantity` or `max_quantity` fields set, they’re only considered for the price calculation if they have a lower `min_quantity` or a higher `max_quantity` than the quantity specified for calculation.
|
||||
|
||||
---
|
||||
|
||||
## Price Set
|
||||
|
||||
A `PriceSet` represents a collection of prices that are linked to a resource (for example, a product or a shipping option). Each of these prices are represented by the `Price` data module.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Prices with Rules
|
||||
|
||||
### Rule Type
|
||||
|
||||
Each price within a price set can be applied for different conditions. These conditions are represented as rule types.
|
||||
|
||||
A `RuleType` defines custom conditions. Each rule type has a unique `rule_field`, referenced in rule values, such as when setting a rule of a price.
|
||||
|
||||
### Price Rule
|
||||
|
||||
Each rule of a price within a price set is represented by the `PriceRule` data model, which holds the value of a rule type. The `Price` data model has a `rules_count` field, which indicates how many rules, represented by `PriceRule`, are applied to the price.
|
||||
|
||||

|
||||
|
||||
For example, you create a `zip_code` rule type. Then, a price within the price set can have the rule value `10557`, indicating that the price can only be applied within the `10557` zip code.
|
||||
|
||||
Each price within the price set can have different values for the same rule type.
|
||||
|
||||
For example:
|
||||
|
||||

|
||||
|
||||
Each price can have multiple rules applied to it as well.
|
||||
|
||||
For example, a price can have the rules `zip_code` and `region_id` applied to it. In this case, the value of each rule is represented by a `PriceRule`.
|
||||
|
||||

|
||||
|
||||
### PriceSetRuleType
|
||||
|
||||
The `PriceSetRuleType` data model indicates what rules the prices can have within a price set. It creates a relation between the `PriceSet` and `RuleType` entities.
|
||||
|
||||
For example, to use the `zip_code` rule type on a price in a price set, the rule type must first be enabled on the price set through the `PriceSetRuleType`.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Price List
|
||||
|
||||
A `PriceList` is a group of prices only enabled if their rules are satisfied. A price list has optional `start_date` and `end_date` fields, which indicate the date range in which a price list can be applied.
|
||||
|
||||
Its associated prices are represented by the `Price` data model.
|
||||
|
||||
Each rule that can be applied to a price list is represented by the `PriceListRule` data model. The `rules_count` field of a `PriceList` indicates how many rules are applied to it.
|
||||
|
||||
Each rule of a price list can have more than one value, representing its values by the `PriceListRuleValue` data model.
|
||||
|
||||

|
||||
@@ -0,0 +1,489 @@
|
||||
import { CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Examples of the Pricing Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you’ll find common examples of how you can use the Pricing Module in your application.
|
||||
|
||||
## Create a Price Set
|
||||
|
||||
<CodeTabs groupId="app-type">
|
||||
<CodeTab label="medusa" value="Medusa API Router">
|
||||
|
||||
```ts
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export async function POST(
|
||||
request: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
request.scope.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
// A rule type with \`rule_field=region_id\` should
|
||||
// already be present in the database
|
||||
const priceSet = await pricingModuleService.create([
|
||||
{
|
||||
rules: [{ rule_field: "region_id" }],
|
||||
prices: [
|
||||
{
|
||||
currency_code: request.body.currency_code,
|
||||
amount: request.body.amount,
|
||||
rules: {
|
||||
region_id: request.body.region_id,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
res.json({ price_set: priceSet })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="nextjs" value="Next.js App Router">
|
||||
|
||||
```ts
|
||||
import { NextResponse } from "next/server"
|
||||
|
||||
import {
|
||||
initialize as initializePricingModule,
|
||||
} from "@medusajs/pricing"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const pricingModuleService = await initializePricingModule()
|
||||
const body = await request.json()
|
||||
|
||||
// A rule type with \`rule_field=region_i\` should
|
||||
// already be present in the database
|
||||
const priceSet = await pricingModuleService.create([
|
||||
{
|
||||
rules: [{ rule_field: "region_id" }],
|
||||
prices: [
|
||||
{
|
||||
currency_code: body.currency_code,
|
||||
amount: body.amount,
|
||||
rules: {
|
||||
region_id: body.region_id,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
return NextResponse.json({ price_set: priceSet })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
---
|
||||
|
||||
## List Price Sets
|
||||
|
||||
<CodeTabs groupId="app-type">
|
||||
<CodeTab label="medusa" value="Medusa API Router">
|
||||
|
||||
```ts
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export async function GET(
|
||||
request: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
request.scope.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
const priceSets = await pricingModuleService.list()
|
||||
|
||||
res.json({ price_sets: priceSets })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="nextjs" value="Next.js App Router">
|
||||
|
||||
```ts
|
||||
import { NextResponse } from "next/server"
|
||||
|
||||
import {
|
||||
initialize as initializePricingModule,
|
||||
} from "@medusajs/pricing"
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const pricingModuleService = await initializePricingModule()
|
||||
|
||||
const priceSets = await pricingModuleService.list()
|
||||
|
||||
return NextResponse.json({ price_sets: priceSets })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
---
|
||||
|
||||
## Retrieve a Price Set by its ID
|
||||
|
||||
<CodeTabs groupId="app-type">
|
||||
<CodeTab label="medusa" value="Medusa API Router">
|
||||
|
||||
```ts
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export async function GET(
|
||||
request: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
request.scope.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
const priceSet = await pricingModuleService.retrieve(
|
||||
request.params.id
|
||||
)
|
||||
|
||||
res.json({ price_set: priceSet })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="nextjs" value="Next.js App Router">
|
||||
|
||||
```ts
|
||||
import { NextResponse } from "next/server"
|
||||
|
||||
import {
|
||||
initialize as initializePricingModule,
|
||||
} from "@medusajs/pricing"
|
||||
|
||||
type ContextType = {
|
||||
params: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: ContextType
|
||||
) {
|
||||
const pricingModuleService = await initializePricingModule()
|
||||
|
||||
const priceSet = await pricingModuleService.retrieve(
|
||||
params.id
|
||||
)
|
||||
|
||||
return NextResponse.json({ price_set: priceSet })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
---
|
||||
|
||||
## Create a Rule Type
|
||||
|
||||
<CodeTabs groupId="app-type">
|
||||
<CodeTab label="medusa" value="Medusa API Router">
|
||||
|
||||
```ts
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export async function POST(
|
||||
request: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
request.scope.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
const priceSet = await pricingModuleService.retrieve(
|
||||
request.params.id
|
||||
)
|
||||
|
||||
res.json({ price_set: priceSet })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="nextjs" value="Next.js App Router">
|
||||
|
||||
```ts
|
||||
import { NextResponse } from "next/server"
|
||||
|
||||
import {
|
||||
initialize as initializePricingModule,
|
||||
} from "@medusajs/pricing"
|
||||
|
||||
type ContextType = {
|
||||
params: {
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: ContextType
|
||||
) {
|
||||
const pricingModuleService = await initializePricingModule()
|
||||
|
||||
const priceSet = await pricingModuleService.retrieve(
|
||||
params.id
|
||||
)
|
||||
|
||||
return NextResponse.json({ price_set: priceSet })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
---
|
||||
|
||||
## Add Prices with Rules
|
||||
|
||||
<CodeTabs groupId="app-type">
|
||||
<CodeTab label="medusa" value="Medusa API Router">
|
||||
|
||||
```ts
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export async function POST(
|
||||
request: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
request.scope.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
const priceSet = await pricingModuleService.addPrices({
|
||||
priceSetId: request.body.price_set_id,
|
||||
prices: [
|
||||
{
|
||||
amount: 500,
|
||||
currency_code: "USD",
|
||||
rules: {
|
||||
region_id: request.body.region_id,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
res.json({ price_set: priceSet })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="nextjs" value="Next.js App Router">
|
||||
|
||||
```ts
|
||||
import { NextResponse } from "next/server"
|
||||
|
||||
import {
|
||||
initialize as initializePricingModule,
|
||||
} from "@medusajs/pricing"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const pricingModuleService = await initializePricingModule()
|
||||
const body = await request.json()
|
||||
|
||||
const priceSet = await pricingModuleService.addPrices({
|
||||
priceSetId: body.price_set_id,
|
||||
prices: [
|
||||
{
|
||||
amount: 500,
|
||||
currency_code: "USD",
|
||||
rules: {
|
||||
region_id: body.region_id,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
return NextResponse.json({ price_set: priceSet })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
---
|
||||
|
||||
## Create Price List
|
||||
|
||||
<CodeTabs groupId="app-type">
|
||||
<CodeTab label="medusa" value="Medusa API Router">
|
||||
|
||||
```ts
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import {
|
||||
IPricingModuleService,
|
||||
PriceListType,
|
||||
} from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export async function POST(
|
||||
request: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
request.scope.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
const priceLists =
|
||||
await pricingModuleService.createPriceLists([
|
||||
{
|
||||
title: "My Sale",
|
||||
description: "This is my sale",
|
||||
type: PriceListType.SALE,
|
||||
starts_at: Date.parse("01/10/2023").toString(),
|
||||
ends_at: Date.parse("31/10/2023").toString(),
|
||||
rules: {
|
||||
region_id: ["DE", "DK"],
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
currency_code: "EUR",
|
||||
price_set_id: "pset_124",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
res.json({ price_lists: priceLists })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="nextjs" value="Next.js App Router">
|
||||
|
||||
```ts
|
||||
import { NextResponse } from "next/server"
|
||||
import { PriceListType } from "@medusajs/medusa"
|
||||
|
||||
import {
|
||||
initialize as initializePricingModule,
|
||||
} from "@medusajs/pricing"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const pricingModuleService = await initializePricingModule()
|
||||
|
||||
const priceLists =
|
||||
await pricingModuleService.createPriceLists([
|
||||
{
|
||||
title: "My Sale",
|
||||
description: "This is my sale",
|
||||
type: PriceListType.SALE,
|
||||
starts_at: Date.parse("01/10/2023").toString(),
|
||||
ends_at: Date.parse("31/10/2023").toString(),
|
||||
rules: {
|
||||
region_id: ["DE", "DK"],
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
currency_code: "EUR",
|
||||
price_set_id: "pset_124",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
return NextResponse.json({ price_lists: priceLists })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
---
|
||||
|
||||
## Calculate Prices For a Currency
|
||||
|
||||
<CodeTabs groupId="app-type">
|
||||
<CodeTab label="medusa" value="Medusa API Router">
|
||||
|
||||
```ts
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export async function POST(
|
||||
request: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
request.scope.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
const price = await pricingModuleService.calculatePrices(
|
||||
{
|
||||
id: [request.params.id],
|
||||
},
|
||||
{
|
||||
context: {
|
||||
currency_code: request.params.currency_code,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
res.json({ price })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="nextjs" value="Next.js App Router">
|
||||
|
||||
```ts
|
||||
import { NextResponse } from "next/server"
|
||||
|
||||
import {
|
||||
initialize as initializePricingModule,
|
||||
} from "@medusajs/pricing"
|
||||
|
||||
type ContextType = {
|
||||
params: {
|
||||
id: string
|
||||
currency_code: string
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: ContextType
|
||||
) {
|
||||
const pricingModuleService = await initializePricingModule()
|
||||
|
||||
const price = await pricingModuleService.calculatePrices({
|
||||
id: [params.id],
|
||||
}, {
|
||||
context: {
|
||||
currency_code: params.currency_code,
|
||||
},
|
||||
})
|
||||
|
||||
return NextResponse.json({ price })
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
---
|
||||
|
||||
## More Examples
|
||||
|
||||
The [module interface reference](/references/pricing) provides a reference to all the methods available for use with examples for each.
|
||||
@@ -0,0 +1,185 @@
|
||||
import { CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Pricing Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
The Pricing Module is the `@medusajs/pricing` NPM package that provides pricing-related features in your Medusa and Node.js applications.
|
||||
|
||||
## Features
|
||||
|
||||
### Price Management
|
||||
|
||||
With the Pricing Module, store the prices of a resource and manage them through the main interface's methods.
|
||||
|
||||
Prices are grouped in a price set, allowing you to add more than one price for a resource based on different conditions, such as currency code.
|
||||
|
||||
```ts
|
||||
const priceSet = await pricingModuleService.create({
|
||||
rules: [],
|
||||
prices: [
|
||||
{
|
||||
amount: 500,
|
||||
currency_code: "USD",
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
amount: 400,
|
||||
currency_code: "EUR",
|
||||
min_quantity: 0,
|
||||
max_quantity: 4,
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
### Advanced Rule Engine
|
||||
|
||||
Create custom rules and apply them to prices. This gives you more flexibility in how you condition prices, filter them, and ensure the best prices are retrieved for custom contexts.
|
||||
|
||||
```ts
|
||||
const ruleTypes = await pricingModuleService.createRuleTypes([
|
||||
{
|
||||
name: "Region",
|
||||
rule_field: "region_id",
|
||||
},
|
||||
])
|
||||
|
||||
const priceSet = await pricingModuleService.addPrices({
|
||||
priceSetId,
|
||||
prices: [
|
||||
{
|
||||
amount: 500,
|
||||
currency_code: "EUR",
|
||||
rules: {
|
||||
region_id: "PL",
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
### Price Lists
|
||||
|
||||
Price lists allow you to group prices and apply them only in specific conditions. You can also use them to override existing prices for the specified conditions.
|
||||
|
||||
```ts
|
||||
const priceList = await pricingModuleService.createPriceLists({
|
||||
title: "My Sale",
|
||||
type: "sale",
|
||||
starts_at: Date.parse("01/10/2023"),
|
||||
ends_at: Date.parse("31/10/2023"),
|
||||
rules: {
|
||||
region_id: ["DE", "DK"],
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
currency_code: "EUR",
|
||||
price_set_id: priceSet.id,
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
### Price Calculation Strategy
|
||||
|
||||
The module’s main service provides a `calculatePrices` method to retrieve the best price for a given context.
|
||||
|
||||
You can use your custom rules here to find the best price for the specified rule values.
|
||||
|
||||
```ts
|
||||
const price = await pricingModuleService.calculatePrices(
|
||||
{ id: [priceSetId] },
|
||||
{
|
||||
context: {
|
||||
currency_code: "EUR",
|
||||
region_id: "PL",
|
||||
},
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configure Pricing Module
|
||||
|
||||
After installing the `@medusajs/pricing` package in your Medusa application, add it to the `modules` object in `medusa-config.js`:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
const modules = {
|
||||
// ...
|
||||
pricingService: {
|
||||
resolve: "@medusajs/pricing",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How to Use Pricing Module's Service
|
||||
|
||||
You can use the Pricing Module's main service by resolving from the Medusa container the resource `ModuleRegistrationName.PRICING` imported from `@medusajs/modules-sdk`.
|
||||
|
||||
For example:
|
||||
|
||||
<CodeTabs groupId="resource-type">
|
||||
<CodeTab label="API Route" value="api-route">
|
||||
|
||||
```ts title="src/api/store/custom/route.ts"
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export async function GET(
|
||||
request: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
request.scope.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
res.json({
|
||||
price_sets: await pricingModuleService.list(),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="Subscriber" value="subscribers">
|
||||
|
||||
```ts title="src/subscribers/custom-handler.ts"
|
||||
import { SubscriberArgs } from "@medusajs/medusa"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export default async function subscriberHandler({
|
||||
container,
|
||||
}: SubscriberArgs) {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
container.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
const priceSets = await pricingModuleService.list()
|
||||
}
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
<CodeTab label="Workflow Step" value="workflow-step">
|
||||
|
||||
```ts title="src/workflows/hello-world/step1.ts"
|
||||
import { createStep } from "@medusajs/workflows-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
const step1 = createStep("step-1", async (_, context) => {
|
||||
const pricingModuleService: IPricingModuleService =
|
||||
context.container.resolve(ModuleRegistrationName.PRICING)
|
||||
|
||||
const priceSets = await pricingModuleService.list()
|
||||
})
|
||||
```
|
||||
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
@@ -0,0 +1,503 @@
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent, TabsContentWrapper } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Prices Calculation`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
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:
|
||||
|
||||
```ts
|
||||
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_field` of a rule type and its value being the rule's value.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
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
|
||||
|
||||

|
||||
|
||||
- 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
|
||||
|
||||

|
||||
|
||||
- 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` field 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
|
||||
|
||||
```ts
|
||||
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:
|
||||
|
||||
```ts
|
||||
const ruleTypes = await pricingModuleService.createRuleTypes([
|
||||
{
|
||||
name: "Region",
|
||||
rule_field: "region_id",
|
||||
},
|
||||
{
|
||||
name: "City",
|
||||
rule_field: "city",
|
||||
},
|
||||
])
|
||||
|
||||
const priceSet = await pricingModuleService.create({
|
||||
rules: [
|
||||
{ rule_field: "region_id" },
|
||||
{ rule_field: "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
|
||||
|
||||
<Tabs defaultValue="code">
|
||||
<TabsList>
|
||||
<TabsTrigger value="code">Code</TabsTrigger>
|
||||
<TabsTrigger value="result">Result</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContentWrapper>
|
||||
<TabsContent value="code">
|
||||
|
||||
```ts
|
||||
const price = await pricingModuleService.calculatePrices(
|
||||
{ id: [priceSet.id] },
|
||||
{
|
||||
context: {
|
||||
currency_code: "EUR"
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="result">
|
||||
|
||||
The returned price is:
|
||||
|
||||
```ts
|
||||
const price = {
|
||||
id: "<PRICE_SET_ID>",
|
||||
is_calculated_price_price_list: false,
|
||||
calculated_amount: 500,
|
||||
|
||||
is_original_price_price_list: false,
|
||||
original_amount: 500,
|
||||
|
||||
currency_code: "EUR",
|
||||
|
||||
calculated_price: {
|
||||
price_id: "<DEFAULT_PRICE_ID>",
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
|
||||
original_price: {
|
||||
price_id: "<DEFAULT_PRICE_ID>",
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Original price selection: since there are no provided rules in the context, the original price is the default price.
|
||||
- Calculated price selection: since there are no associated price lists, the calculated price is set to the original price.
|
||||
|
||||
</TabsContent>
|
||||
</TabsContentWrapper>
|
||||
</Tabs>
|
||||
|
||||
### Exact Match Rule in Context
|
||||
|
||||
<Tabs defaultValue="code">
|
||||
<TabsList>
|
||||
<TabsTrigger value="code">Code</TabsTrigger>
|
||||
<TabsTrigger value="result">Result</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContentWrapper>
|
||||
<TabsContent value="code">
|
||||
|
||||
```ts
|
||||
const price = await pricingModuleService.calculatePrices(
|
||||
{ id: [priceSet.id] },
|
||||
{
|
||||
context: {
|
||||
currency_code: "EUR",
|
||||
region_id: "PL"
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="result">
|
||||
|
||||
The returned price is:
|
||||
|
||||
```ts
|
||||
const price = {
|
||||
id: "<PRICE_SET_ID>",
|
||||
is_calculated_price_price_list: false,
|
||||
calculated_amount: 400,
|
||||
|
||||
is_original_price_price_list: false,
|
||||
original_amount: 400,
|
||||
|
||||
currency_code: "EUR",
|
||||
|
||||
calculated_price: {
|
||||
price_id: "<SECOND_PRICE_ID>",
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
|
||||
original_price: {
|
||||
price_id: "<SECOND_PRICE_ID>",
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Original price selection: Since the second price of the price set has the same context as the provided one, it's selected as the original price.
|
||||
- Calculated price selection: since there are no associated price lists, the calculated price is set to the original price.
|
||||
|
||||
</TabsContent>
|
||||
</TabsContentWrapper>
|
||||
</Tabs>
|
||||
|
||||
### Context without Exact Matching Rules
|
||||
|
||||
<Tabs defaultValue="code">
|
||||
<TabsList>
|
||||
<TabsTrigger value="code">Code</TabsTrigger>
|
||||
<TabsTrigger value="result">Result</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContentWrapper>
|
||||
<TabsContent value="code">
|
||||
|
||||
```ts
|
||||
const price = await pricingModuleService.calculatePrices(
|
||||
{ id: [priceSet.id] },
|
||||
{
|
||||
context: {
|
||||
currency_code: "EUR",
|
||||
region_id: "PL",
|
||||
city: "krakow"
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="result">
|
||||
|
||||
The returned price is:
|
||||
|
||||
```ts
|
||||
const price = {
|
||||
id: "<PRICE_SET_ID>",
|
||||
is_calculated_price_price_list: false,
|
||||
calculated_amount: 500,
|
||||
|
||||
is_original_price_price_list: false,
|
||||
original_amount: 500,
|
||||
|
||||
currency_code: "EUR",
|
||||
|
||||
calculated_price: {
|
||||
price_id: "<FOURTH_PRICE_ID>",
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
|
||||
original_price: {
|
||||
price_id: "<FOURTH_PRICE_ID>",
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Original price selection: The fourth price in the price list is selected based on the following process:
|
||||
- There are no prices having the same rules as the context.
|
||||
- Retrieve all prices matching some combination of the rules in the context.
|
||||
- Sort the prices in descending order by their number of rules, each rule type's default priority, and the priority of the rule values.
|
||||
- All rule types, in this case, don't have a priority. So, the sorting depends on the number of rules each price has.
|
||||
- The fourth price has two rules, and the second and third have one rule. So, the fourth price is selected as the original price.
|
||||
- Calculated price selection: since there are no associated price lists, the calculated price is set to the original price.
|
||||
|
||||
</TabsContent>
|
||||
</TabsContentWrapper>
|
||||
</Tabs>
|
||||
|
||||
### Price Selection with Price List
|
||||
|
||||
<Tabs defaultValue="code">
|
||||
<TabsList>
|
||||
<TabsTrigger value="code">Code</TabsTrigger>
|
||||
<TabsTrigger value="result">Result</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContentWrapper>
|
||||
<TabsContent value="code">
|
||||
|
||||
```ts
|
||||
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"
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="result">
|
||||
|
||||
The returned price is:
|
||||
|
||||
```ts
|
||||
const price = {
|
||||
id: "<PRICE_SET_ID>",
|
||||
is_calculated_price_price_list: true,
|
||||
calculated_amount: 400,
|
||||
|
||||
is_original_price_price_list: false,
|
||||
original_amount: 500,
|
||||
|
||||
currency_code: "EUR",
|
||||
|
||||
calculated_price: {
|
||||
price_id: "<FOURTH_PRICE_ID>",
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
|
||||
original_price: {
|
||||
price_id: "<PL_PRICE_ID_1>",
|
||||
price_list_id: "<PRICE_LIST_ID>",
|
||||
price_list_type: "sale",
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Original price selection: The process is explained in the Context without Exact Matching Rules section.
|
||||
- Calculated price selection: The first price of the price list is selected based on the following process:
|
||||
- The price set has a price list associated with it. So, retrieve its prices and sort them in ascending order by their amount.
|
||||
- Since the first price of the price list has the lowest amount, it's selected as the calculated price.
|
||||
|
||||
</TabsContent>
|
||||
</TabsContentWrapper>
|
||||
</Tabs>
|
||||
@@ -0,0 +1,17 @@
|
||||
export const metadata = {
|
||||
title: `Relations between Pricing Module and Other Modules`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
When Commerce Modules are used together in a Medusa application, the Medusa application handles building the relations between these modules.
|
||||
|
||||
This document showcases the relation between the Pricing Module and other Commerce Modules.
|
||||
|
||||
## Product Module
|
||||
|
||||
A product variant’s prices are stored as prices belonging to a price set. The Medusa application forms a relation between the `ProductVariant` and the `PriceSet`.
|
||||
|
||||

|
||||
|
||||
So, when you want to add prices for a product variant, you create a price set and add the prices to it. You can then benefit from adding rules to prices or using the `calculatePrices` method to retrieve the price of a product variant within a specified context.
|
||||
Reference in new issue
Block a user