docs: improved commerce modules [4/n] (#9517)

Improve pricing, product, and promotion modules docs

[4/n]
This commit is contained in:
Shahed Nasser authored and GitHub committed 2024-10-16 09:34:36 +00:00
1 parent eb364834de
commit f6d3453e6d
27 files changed
+1767 -341

No files matched your search

@@ -4,11 +4,13 @@ export const metadata = {
# {metadata.title}
In this document, you’ll learn about the main concepts in the Pricing Module, and how data is stored and related.
In this document, you’ll learn about the main concepts in the Pricing Module.
## Price Set
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 [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)
@@ -16,6 +18,8 @@ A [PriceSet](/references/pricing/models/PriceSet) represents a collection of pri
## Price List
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` properties, 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` properties that indicate the date range in which a price list can be applied.
Its associated prices are represented by the `Price` data model.
@@ -13,16 +13,15 @@ In this document, you’ll find common examples of how you can use the Pricing M
<CodeTabs groupId="app-type">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IPricingModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
export async function POST(
request: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const pricingModuleService: IPricingModuleService = request.scope.resolve(
const pricingModuleService = request.scope.resolve(
Modules.PRICING
)
@@ -84,16 +83,15 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IPricingModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
export async function GET(
request: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const pricingModuleService: IPricingModuleService = request.scope.resolve(
const pricingModuleService = request.scope.resolve(
Modules.PRICING
)
@@ -130,16 +128,15 @@ export async function GET(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IPricingModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
export async function GET(
request: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const pricingModuleService: IPricingModuleService = request.scope.resolve(
const pricingModuleService = request.scope.resolve(
Modules.PRICING
)
@@ -176,16 +173,15 @@ export async function GET(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IPricingModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
export async function POST(
request: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const pricingModuleService: IPricingModuleService = request.scope.resolve(
const pricingModuleService = request.scope.resolve(
Modules.PRICING
)
@@ -255,7 +251,7 @@ export async function POST(
request: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const pricingModuleService: IPricingModuleService = request.scope.resolve(
const pricingModuleService = request.scope.resolve(
Modules.PRICING
)
@@ -330,16 +326,15 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab value="medusa" label="Medusa API Router">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IPricingModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
export async function POST(
request: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const pricingModuleService: IPricingModuleService = request.scope.resolve(
const pricingModuleService = request.scope.resolve(
Modules.PRICING
)
@@ -0,0 +1,29 @@
export const metadata = {
title: `Links between Pricing Module and Other Modules`,
}
# {metadata.title}
This document showcases the module links defined between the Pricing Module and other commerce modules.
## Fulfillment Module
The Fulfillment Module provides fulfillment-related functionalities, including shipping options that the customer chooses from when they place their order. However, it doesn't provide pricing-related functionalities for these options.
Medusa defines a link between the `PriceSet` and `ShippingOption` data models. A shipping option's price is stored as a price set.
![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
The Product Module doesn't store or manage the prices of product variants.
Medusa defines a link between the `ProductVariant` and the `PriceSet`. A product variant’s prices are stored as prices belonging to a price set.
![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.
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
The Pricing Module is the `@medusajs/medusa/pricing` NPM package that provides pricing-related features in your Medusa and Node.js applications.
The Pricing Module provides pricing-related features in your Medusa and Node.js applications.
## How to Use Pricing Module's Service
@@ -15,18 +15,33 @@ You can use the Pricing Module's main service by resolving from the Medusa conta
For example:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const pricingModuleService = container.resolve(
Modules.PRICING
)
const priceSets = await pricingModuleService.listPriceSets()
})
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IPricingModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
export async function GET(
request: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const pricingModuleService: IPricingModuleService = request.scope.resolve(
const pricingModuleService = request.scope.resolve(
Modules.PRICING
)
@@ -39,35 +54,17 @@ export async function GET(
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { IPricingModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function subscriberHandler({ container }: SubscriberArgs) {
const pricingModuleService: IPricingModuleService = container.resolve(
const pricingModuleService = container.resolve(
Modules.PRICING
)
const priceSets = await pricingModuleService.listPriceSets()
}
```
</CodeTab>
<CodeTab label="Workflow Step" value="workflow-step">
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { IPricingModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const pricingModuleService: IPricingModuleService = container.resolve(
Modules.PRICING
)
const priceSets = await pricingModuleService.listPriceSets()
})
```
</CodeTab>
@@ -79,7 +76,7 @@ const step1 = createStep("step-1", async (_, { container }) => {
### Price Management
With the Pricing Module, store the prices of a resource and manage them through the main service's methods.
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.
@@ -122,7 +119,9 @@ const priceSet = await pricingModuleService.addPrices({
### 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.
Group prices and apply them only in specific conditions with price lists.
You can also use them to override existing prices for specified conditions, or create a sale.
```ts
const priceList = await pricingModuleService.createPriceLists([
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
In this document, you'll learn how prices are calculated when you use the `calculatePrices` method of the Pricing Module's main service.
In this document, you'll learn how prices are calculated when you use the [calculatePrices method](/references/pricing/calculatePrices) of the Pricing Module's main service.
## calculatePrices Method
@@ -14,9 +14,7 @@ The [calculatePrices method](/references/pricing/calculatePrices) accepts as par
It returns a price object with the best matching price for each price set.
---
## Calculation Context
### Calculation Context
The calculation context is an optional object passed as a second parameter to the `calculatePrices` method. It accepts rules to restrict the selected prices in the price set.
@@ -34,16 +32,18 @@ const price = await pricingModuleService.calculatePrices(
)
```
---
In this example, you retrieve the prices in a price set for the specified currency code and region ID.
## Returned Price Object
### Returned Price Object
For each price set, the method selects two prices:
For each price set, the `calculatePrices` method selects two prices:
- The calculated price: Either the best context-matching price that belongs to a price list or the same as the original price.
- The original price: Either the same as the calculated price if its price list is of type `override`, or the best context-matching price that doesn't belong to a price list.
- A calculated price: Either a price that belongs to a price list and best matches the specified context, or the same as the original price.
- An original price, which is either:
- The same price as the calculated price if the price list it belongs to is of type `override`;
- Or a price that doesn't belong to a price list and best matches the specified context.
Both prices are returned in an object along with the following properties:
Both prices are returned in an object that has the following properties:
<TypeList
types={[
@@ -8,24 +8,26 @@ In this document, you'll learn about price rules for price sets and price lists.
## Price Rule
Prices can be restricted by rules. Each rule of a price is represented by the [PriceRule data model](/references/pricing/models/PriceRule).
You can restrict prices by rules. Each rule of a price is represented by the [PriceRule data model](/references/pricing/models/PriceRule).
The `Price` data model has a `rules_count` property, which indicates how many rules, represented by `PriceRule`, are applied to the price.
![A diagram showcasing the relation between the PriceRule and Price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648772/Medusa%20Resources/price-rule-1_vy8bn9.jpg)
For exmaple, you create a price restricted to `10557` zip codes.
A price can have multiple price rules. For example:
![A diagram showcasing the relation between the PriceRule and Price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648772/Medusa%20Resources/price-rule-1_vy8bn9.jpg)
![A diagram showcasing the relation between the PriceRule and Price with multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649296/Medusa%20Resources/price-rule-3_pwpocz.jpg)
A price can have multiple price rules.
For example, a price can be restricted by a region and a zip code.
![A diagram showcasing the relation between the PriceRule and Price with multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649296/Medusa%20Resources/price-rule-3_pwpocz.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` property of a `PriceList` indicates how many rules are applied to it.
Rules applied to a price list are represented by the [PriceListRule data model](/references/pricing/models/PriceListRule).
The `rules_count` property of a `PriceList` indicates how many rules are applied to it.
![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)
@@ -1,23 +0,0 @@
export const metadata = {
title: `Relations between Pricing Module and Other Modules`,
}
# {metadata.title}
This document showcases the link modules defined 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 variant’s 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 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.
@@ -4,19 +4,19 @@ export const metadata = {
# {metadata.title}
In this document, you’ll learn about tax-inclusive pricing and how it's used during prices calculation.
In this document, you’ll learn about tax-inclusive pricing and how it's used when calculating prices.
## What is Tax-Inclusive Pricing?
A tax-inclusive price is a price that includes taxes. The tax amount is calculated from the price rather than added to it.
A tax-inclusive price is a price of a resource that includes taxes. Medusa calculates the tax amount from the price rather than adds the amount to it.
For example, if a product’s price is $50 and the tax rate is 2%, then the tax-inclusive price is $49, and the applied tax amount is $1.
For example, if a product’s price is $50, the tax rate is 2%, and tax-inclusive pricing is enabled, then the product's price is $49, and the applied tax amount is $1.
---
## How is Tax-Inclusive Pricing Set?
The `PricePreference` data model holds the tax-inclusive setting for a context. It has two properties that indicate the context:
The [PricePreference data model](/references/pricing/PricePreference) holds the tax-inclusive setting for a context. It has two properties that indicate the context:
- `attribute`: The name of the attribute to compare against. For example, `region_id` or `currency_code`.
- `value`: The attribute’s value. For example, `reg_123` or `usd`.
@@ -55,6 +55,12 @@ To get accurate tax results, pass the `region_id` and / or `currency_code` in th
The `calculatePrices` method returns two properties related to tax-inclusivity:
<Note title="Tip">
Learn more about the returned properties in [this guide](../price-calculation/page.mdx#returned-price-object).
</Note>
- `is_calculated_price_tax_inclusive`: Whether the selected `calculated_price` is tax-inclusive.
- `is_original_price_tax_inclusive` : Whether the selected `original_price` is tax-inclusive.
@@ -65,10 +71,8 @@ A price is considered tax-inclusive if:
### Tax Context Precedence
If:
A region’s price preference’s `is_tax_inclusive`'s value takes higher precedence in determining whether a price is tax-inclusive if:
- both the `region_id` and `currency_code` are provided in the calculation context;
- the selected price belongs to the region;
- and the region has a price preference
Then, the region’s price preference’s `is_tax_inclusive`'s value takes higher precedence in determining whether a price is tax-inclusive.