docs: edits and fixes to commerce module docs (#7468)

Apply edits and fixes to the commerce modules docs
This commit is contained in:
Shahed Nasser
2024-05-29 11:08:06 +00:00
committed by GitHub
parent 130de74d6d
commit 2c5ba408d4
160 changed files with 6400 additions and 3790 deletions
@@ -6,70 +6,46 @@ export const metadata = {
In this document, youll 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, theyre 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.
A [PriceSet](/references/pricing/models/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](/references/pricing/models/Price).
![A diagram showcasing the relation between the price set and price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648650/Medusa%20Resources/price-set-money-amount_xeees0.jpg)
---
## Prices with Rules
### Rule Type
## 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.
A [RuleType](/references/pricing/models/RuleType) defines custom conditions. A rule type has a unique `rule_attribute` which indicates the field this rule applies on. For example, `region_id`.
### Price Rule
This is referenced when setting a rule of a price. For example:
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.
export const ruleTypeHighlights = [
["8", "region_id", "Reference a rule type by its `rule_attribute`."],
["8", `"PL"`, "The value of this rule."]
]
![A diagram showcasing the relation between the PriceRule, PriceSet, Price, and RuleType.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648772/Medusa%20Resources/price-rule-1_vy8bn9.jpg)
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:
![A diagram showcasing the relation between the PriceRule, PriceSet, Price, and RuleType.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648884/Medusa%20Resources/price-rule-2_b6fuyb.jpg)
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`.
![A diagram showcasing the relation between the PriceRule, PriceSet, Price, and RuleType with multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649296/Medusa%20Resources/price-rule-3_pwpocz.jpg)
### 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`.
![A diagram showcasing the relation between the PriceSet, PriceRule, Price, RuleType, and PriceSetRuleType](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649375/Medusa%20Resources/price-set-rule-type_cqqt0u.jpg)
```ts highlights={ruleTypeHighlights}
const priceSet = await pricingModuleService.addPrices({
priceSetId,
prices: [
{
amount: 500,
currency_code: "EUR",
rules: {
region_id: "PL",
},
},
],
})
```
---
## 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.
A [PriceList](/references/pricing/models/PriceList) is a group of prices only enabled if their conditions and 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.
![A diagram showcasing the relation between the PriceSet, PriceList, Price, RuleType, and PriceListRuleValue](https://res.cloudinary.com/dza7lstvk/image/upload/v1709641999/Medusa%20Resources/price-list_zd10yd.jpg)
Its associated prices are represented by the `Price` data model.
@@ -8,4 +8,10 @@ export const metadata = {
Find in this reference the list of events emitted by the Pricing Module.
<Note type="soon">
Events are still in development, so this reference will change in the future.
</Note>
<EventsTable />
@@ -11,7 +11,7 @@ In this document, youll find common examples of how you can use the Pricing M
## Create a Price Set
<CodeTabs groupId="app-type">
<CodeTab label="medusa" value="Medusa API Router">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
@@ -47,7 +47,7 @@ In this document, youll find common examples of how you can use the Pricing M
```
</CodeTab>
<CodeTab label="nextjs" value="Next.js App Router">
<CodeTab value="nextjs" label="Next.js App Router">
```ts
import { NextResponse } from "next/server"
@@ -89,7 +89,7 @@ In this document, youll find common examples of how you can use the Pricing M
## List Price Sets
<CodeTabs groupId="app-type">
<CodeTab label="medusa" value="Medusa API Router">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
@@ -110,7 +110,7 @@ In this document, youll find common examples of how you can use the Pricing M
```
</CodeTab>
<CodeTab label="nextjs" value="Next.js App Router">
<CodeTab value="nextjs" label="Next.js App Router">
```ts
import { NextResponse } from "next/server"
@@ -136,7 +136,7 @@ In this document, youll find common examples of how you can use the Pricing M
## Retrieve a Price Set by its ID
<CodeTabs groupId="app-type">
<CodeTab label="medusa" value="Medusa API Router">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
@@ -159,7 +159,7 @@ In this document, youll find common examples of how you can use the Pricing M
```
</CodeTab>
<CodeTab label="nextjs" value="Next.js App Router">
<CodeTab value="nextjs" label="Next.js App Router">
```ts
import { NextResponse } from "next/server"
@@ -196,7 +196,7 @@ In this document, youll find common examples of how you can use the Pricing M
## Create a Rule Type
<CodeTabs groupId="app-type">
<CodeTab label="medusa" value="Medusa API Router">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
@@ -210,16 +210,17 @@ In this document, youll find common examples of how you can use the Pricing M
const pricingModuleService: IPricingModuleService =
request.scope.resolve(ModuleRegistrationName.PRICING)
const priceSet = await pricingModuleService.retrieve(
request.params.id
)
const ruleType = await pricingModuleService.createRuleTypes([{
name: "Customer Group",
rule_attribute: "customer_group_id",
}])
res.json({ price_set: priceSet })
res.json({ rule_type: ruleType })
}
```
</CodeTab>
<CodeTab label="nextjs" value="Next.js App Router">
<CodeTab value="nextjs" label="Next.js App Router">
```ts
import { NextResponse } from "next/server"
@@ -228,23 +229,17 @@ In this document, youll find common examples of how you can use the Pricing M
initialize as initializePricingModule,
} from "@medusajs/pricing"
type ContextType = {
params: {
id: string
}
}
export async function GET(
request: Request,
{ params }: ContextType
export async function POST(
request: Request
) {
const pricingModuleService = await initializePricingModule()
const priceSet = await pricingModuleService.retrieve(
params.id
)
const ruleType = await pricingModuleService.createRuleTypes([{
name: "Customer Group",
rule_attribute: "customer_group_id",
}])
return NextResponse.json({ price_set: priceSet })
return NextResponse.json({ rule_type: ruleType })
}
```
@@ -256,7 +251,7 @@ In this document, youll find common examples of how you can use the Pricing M
## Add Prices with Rules
<CodeTabs groupId="app-type">
<CodeTab label="medusa" value="Medusa API Router">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
@@ -288,7 +283,7 @@ In this document, youll find common examples of how you can use the Pricing M
```
</CodeTab>
<CodeTab label="nextjs" value="Next.js App Router">
<CodeTab value="nextjs" label="Next.js App Router">
```ts
import { NextResponse } from "next/server"
@@ -326,7 +321,7 @@ In this document, youll find common examples of how you can use the Pricing M
## Create Price List
<CodeTabs groupId="app-type">
<CodeTab label="medusa" value="Medusa API Router">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
@@ -369,7 +364,7 @@ In this document, youll find common examples of how you can use the Pricing M
```
</CodeTab>
<CodeTab label="nextjs" value="Next.js App Router">
<CodeTab value="nextjs" label="Next.js App Router">
```ts
import { NextResponse } from "next/server"
@@ -415,7 +410,7 @@ In this document, youll find common examples of how you can use the Pricing M
## Calculate Prices For a Currency
<CodeTabs groupId="app-type">
<CodeTab label="medusa" value="Medusa API Router">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
@@ -445,7 +440,7 @@ In this document, youll find common examples of how you can use the Pricing M
```
</CodeTab>
<CodeTab label="nextjs" value="Next.js App Router">
<CodeTab value="nextjs" label="Next.js App Router">
```ts
import { NextResponse } from "next/server"
@@ -486,4 +481,4 @@ In this document, youll find common examples of how you can use the Pricing M
## More Examples
The [module interface reference](/references/pricing) provides a reference to all the methods available for use with examples for each.
The [Pricing Module's main service reference](/references/pricing) provides a reference to all the methods available for use with examples for each.
@@ -12,7 +12,7 @@ The Pricing Module is the `@medusajs/pricing` NPM package that provides pricing-
### Price Management
With the Pricing Module, store the prices of a resource and manage them through the main interface's methods.
With the Pricing Module, store the prices of a resource and manage them through the main service'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.
@@ -23,7 +23,6 @@ const priceSet = await pricingModuleService.create({
{
amount: 500,
currency_code: "USD",
rules: {},
},
{
amount: 400,
@@ -87,9 +86,7 @@ const priceList = await pricingModuleService.createPriceLists({
### Price Calculation Strategy
The modules 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.
Retrieve the best price in a given context and for the specified rule values.
```ts
const price = await pricingModuleService.calculatePrices(
@@ -107,14 +104,16 @@ const price = await pricingModuleService.calculatePrices(
## Configure Pricing Module
After installing the `@medusajs/pricing` package in your Medusa application, add it to the `modules` object in `medusa-config.js`:
To use the Pricing Module, enable it in the `modules` object in `medusa-config.js`:
```js title="medusa-config.js"
const { Modules } = require("@medusajs/modules-sdk")
// ...
const modules = {
// ...
pricingService: {
resolve: "@medusajs/pricing",
},
[Modules.PRICING]: true,
}
```
@@ -173,9 +172,11 @@ For example:
import { IPricingModuleService } from "@medusajs/types"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
const step1 = createStep("step-1", async (_, context) => {
const step1 = createStep(
"step-1",
async (_, { container }) => {
const pricingModuleService: IPricingModuleService =
context.container.resolve(ModuleRegistrationName.PRICING)
container.resolve(ModuleRegistrationName.PRICING)
const priceSets = await pricingModuleService.list()
})
@@ -1,167 +1,164 @@
import { Tabs, TabsList, TabsTrigger, TabsContent, TabsContentWrapper } from "docs-ui"
import { Tabs, TabsList, TabsTrigger, TabsContent, TabsContentWrapper, TypeList } from "docs-ui"
export const metadata = {
title: `Prices Calculation`,
title: `Prices Calculation Strategy`,
}
# {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.
In this document, you'll learn how prices are calculated when you use the `calculatePrices` method of the Pricing Module's main service.
## 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.
The [calculatePrices method](/references/pricing/calculatePrices) accepts the ID of one or more price sets and a context. It returns a price object with the best matching price for each price set.
---
## 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.
The context is an object passed as a second parameter 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,
},
}
)
}
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.
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:
```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",
},
}
)
}
const price = await pricingModuleService.calculatePrices(
{ id: [priceSetId] },
{
context: {
currency_code: currencyCode,
region_id: "US",
},
}
)
```
---
## Prices Selection
## Returned Price Object
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, itll be the same as the original price.
- The original price: a price that doesn't belong to a price list.
- The original price: If the calculated price's price list type is `override`, then the original price will be the same as the calculated price. Otheriwse, a price that doesn't belong to a price list.
### Calculated Price Selection Process
After the original and calculated prices are selected, the method will use them to create a price object for each price set.
![A diagram showcasing the calculated price selection process.](https://res.cloudinary.com/dza7lstvk/image/upload/v1700574799/Medusa%20Docs/Diagrams/calculated-price_vjnx3j.jpg)
The price object has the following properties:
- Find the price sets 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.](https://res.cloudinary.com/dza7lstvk/image/upload/v1700574800/Medusa%20Docs/Diagrams/original-price_i47fso.jpg)
- 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.
<TypeList
types={[
{
name: "id",
type: "`string`",
description: "The ID of the price set from which the price was selected."
},
{
name: "is_calculated_price_price_list",
type: "`boolean`",
description: "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."
},
{
name: "calculated_amount",
type: "`number`",
description: "The amount of the calculated price, or `null` if there isn't a calculated price."
},
{
name: "is_original_price_price_list",
type: "`boolean`",
description: "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."
},
{
name: "original_amount",
type: "`number`",
description: "The amount of the original price, or `null` if there isn't an original price."
},
{
name: "currency_code",
type: "`string`",
description: "The currency code of the calculated price, or `null` if there isn't a calculated price."
},
{
name: "calculated_price",
type: "`object`",
description: "The calculated price's price details and potentially its associated price list.",
children: [
{
name: "price_id",
type: "`string`",
description: "The ID of the price."
},
{
name: "price_list_id",
type: "`string`",
description: "The ID of the associated price list."
},
{
name: "price_list_type",
type: "`string`",
description: "The price list's type."
},
{
name: "min_quantity",
type: "`number`",
description: "The price's min quantity condition."
},
{
name: "max_quantity",
type: "`number`",
description: "The price's max quantity condition."
}
]
},
{
name: "original_price",
type: "`object`",
description: "The original price's price details and potentially its associated price list.",
children: [
{
name: "price_id",
type: "`string`",
description: "The ID of the price."
},
{
name: "price_list_id",
type: "`string`",
description: "The ID of the associated price list."
},
{
name: "price_list_type",
type: "`string`",
description: "The price list's type."
},
{
name: "min_quantity",
type: "`number`",
description: "The price's min quantity condition."
},
{
name: "max_quantity",
type: "`number`",
description: "The price's max quantity condition."
}
]
}
]}
sectionTitle="Returned Calculated Price"
/>
---
@@ -173,18 +170,18 @@ Consider the following rule types and price sets:
const ruleTypes = await pricingModuleService.createRuleTypes([
{
name: "Region",
rule_field: "region_id",
rule_attribute: "region_id",
},
{
name: "City",
rule_field: "city",
rule_attribute: "city",
},
])
const priceSet = await pricingModuleService.create({
rules: [
{ rule_field: "region_id" },
{ rule_field: "city" },
{ rule_attribute: "region_id" },
{ rule_attribute: "city" },
],
prices: [
//default
@@ -0,0 +1,47 @@
export const metadata = {
title: `Price Rules`,
}
# {metadata.title}
In this document, you'll learn about price rules for price sets and price lists.
## Price Rule
Each rule of a price within a price set is represented by the [PriceRule data model](/references/pricing/models/PriceRule), 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.
![A diagram showcasing the relation between the PriceRule, PriceSet, Price, and RuleType.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648772/Medusa%20Resources/price-rule-1_vy8bn9.jpg)
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:
![A diagram showcasing the relation between the PriceRule, PriceSet, Price, and RuleType.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648884/Medusa%20Resources/price-rule-2_b6fuyb.jpg)
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`.
![A diagram showcasing the relation between the PriceRule, PriceSet, Price, and RuleType with multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649296/Medusa%20Resources/price-rule-3_pwpocz.jpg)
---
## Restrict Price Rules
The [PriceSetRuleType data model](/references/pricing/models/PriceSetRuleType) indicates what rules the prices can have within a price set. It creates a relation between the `PriceSet` and `RuleType` data models.
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`.
![A diagram showcasing the relation between the PriceSet, PriceRule, Price, RuleType, and PriceSetRuleType](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649375/Medusa%20Resources/price-set-rule-type_cqqt0u.jpg)
---
## Price List Rules
Rules that can be applied to a price list are represented by the [PriceListRule data model](/references/pricing/models/PriceListRule). 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](/references/pricing/models/PriceListRuleValue).
![A diagram showcasing the relation between the PriceSet, PriceList, Price, RuleType, and PriceListRuleValue](https://res.cloudinary.com/dza7lstvk/image/upload/v1709641999/Medusa%20Resources/price-list_zd10yd.jpg)
@@ -4,14 +4,20 @@ export const metadata = {
# {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 link modules defined between the Pricing Module and other commerce modules.
This document showcases the relation between the Pricing Module and other Commerce Modules.
## Fulfillment Module
A shipping option's price is stored as a price set. Medusa defines a link module that builds a relationship between the `PriceSet` and `ShippingOption` data models.
![A diagram showcasing an example of how data models from the Pricing and Fulfillment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716561747/Medusa%20Resources/pricing-fulfillment_spywwa.jpg)
---
## Product Module
A product variants prices are stored as prices belonging to a price set. The Medusa application forms a relation between the `ProductVariant` and the `PriceSet`.
A product variants prices are stored as prices belonging to a price set. Medusa defines a link module that builds a relationship between the `ProductVariant` and the `PriceSet`.
![A diagram showcasing an example of how resources from the Pricing and Product Module are linked. The PriceSet is linked to the ProductVariant of the Product Module.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709651039/Medusa%20Resources/pricing-product_m4xaut.jpg)
![A diagram showcasing an example of how data models from the Pricing and Product Module are linked. The PriceSet is linked to the ProductVariant of the Product Module.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709651039/Medusa%20Resources/pricing-product_m4xaut.jpg)
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.