docs: docs for next release (#13621)
* docs: docs for next release * changes to opentelemetry dependencies * document plugin env variables * document admin changes * fix vale error * add version notes * document campaign budget updates * document campaign changes in user guide * document chages in cluster mode cli * documented once promotion allocation * document multiple API keys support
This commit is contained in:
@@ -75,12 +75,12 @@ The [ApplicationMethod data model](/references/promotion/models/ApplicationMetho
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
Is the discounted amount applied to each item or split between the applicable items?
|
||||
Is the discounted amount applied to each item, split between the applicable items, or applied on specific number of items?
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`each`, `across`
|
||||
`each`, `across`, `once`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
@@ -113,7 +113,11 @@ In this example, the cart must have two product variants with the SKU `SHIRT` fo
|
||||
|
||||
## Maximum Quantity Restriction
|
||||
|
||||
When the `allocation` property in the `ApplicationMethod` is set to `each`, you can set the `max_quantity` property of `ApplicationMethod` to limit how many item quantities the promotion is applied to.
|
||||
You can restrict how many items the promotion is applied to either at the item level or the cart level.
|
||||
|
||||
### Item Level Restriction
|
||||
|
||||
When the `allocation` property in the `ApplicationMethod` is set to `each`, you can set the `max_quantity` property of `ApplicationMethod` to limit how many quantities of each applicable item the promotion is applied to.
|
||||
|
||||
For example, if the `max_quantity` property is set to `1` and the customer has a line item with quantity two in the cart, the promotion is only applied to one of them.
|
||||
|
||||
@@ -147,4 +151,112 @@ This condition is applied on the quantity of every applicable item in the cart.
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### Cart Level Restriction
|
||||
|
||||
<Note>
|
||||
|
||||
The `once` allocation type is available from [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0).
|
||||
|
||||
</Note>
|
||||
|
||||
When the `allocation` property in the `ApplicationMethod` is set to `once`, you must set the `max_quantity` property of `ApplicationMethod`. It limits how many items in total the promotion is applied to.
|
||||
|
||||
In this scenario, the Promotion Module prioritizes which applicable items the promotion is applied to based on the following rules:
|
||||
|
||||
1. Prioritize items with the lowest price.
|
||||
2. Distribute the promotion sequentially until the `max_quantity` is reached.
|
||||
|
||||
#### Example 1
|
||||
|
||||
Consider:
|
||||
|
||||
- A promotion whose application method has its `allocation` property set to `once` and `max_quantity` set to `2`.
|
||||
- A cart with three items having different prices, each with a quantity of `1`.
|
||||
|
||||
The Promotion Module will apply the promotion to the two items with the lowest price.
|
||||
|
||||
```json title="Example Cart"
|
||||
{
|
||||
"cart": {
|
||||
"items": [
|
||||
{
|
||||
"id": "item_1",
|
||||
"price": 10,
|
||||
"quantity": 1 // The promotion is applied to this item
|
||||
},
|
||||
{
|
||||
"id": "item_2",
|
||||
"price": 20,
|
||||
"quantity": 1 // The promotion is applied to this item
|
||||
},
|
||||
{
|
||||
"id": "item_3",
|
||||
"price": 30,
|
||||
"quantity": 1 // The promotion is NOT applied to this item
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example 2
|
||||
|
||||
Consider:
|
||||
|
||||
- A promotion whose application method has its `allocation` property set to `once` and `max_quantity` set to `2`.
|
||||
- A cart with two items having different prices and quantities greater than `2`.
|
||||
|
||||
The Promotion Module will try to apply the promotion to the item with the lowest price first:
|
||||
|
||||
```json title="Example Cart"
|
||||
{
|
||||
"cart": {
|
||||
"items": [
|
||||
{
|
||||
"id": "item_1",
|
||||
"price": 10,
|
||||
"quantity": 3 // The promotion is applied to 2 of this item
|
||||
},
|
||||
{
|
||||
"id": "item_2",
|
||||
"price": 20,
|
||||
"quantity": 4 // The promotion is NOT applied to this item
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Since that item has a quantity of `3`, the promotion is applied to `2` of that item, reaching the `max_quantity` limit. The promotion is not applied to the other item.
|
||||
|
||||
#### Example 3
|
||||
|
||||
Consider:
|
||||
|
||||
- A promotion whose application method has its `allocation` property set to `once` and `max_quantity` set to `5`.
|
||||
- A cart with two items having different prices and quantities less than `5`.
|
||||
|
||||
The Promotion Module will try to apply the promotion to the item with the lowest price first:
|
||||
|
||||
```json title="Example Cart"
|
||||
{
|
||||
"cart": {
|
||||
"items": [
|
||||
{
|
||||
"id": "item_1",
|
||||
"price": 10,
|
||||
"quantity": 3 // The promotion is applied to all 3 of this item
|
||||
},
|
||||
{
|
||||
"id": "item_2",
|
||||
"price": 20,
|
||||
"quantity": 4 // The promotion is applied to 2 of this item
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The promotion is applied to all `3` quantities of the item with the lowest price. Since the `max_quantity` is `5`, the promotion is applied to `2` quantities of the other item, reaching the `max_quantity` limit.
|
||||
@@ -14,31 +14,76 @@ Refer to this [Medusa Admin User Guide](!user-guide!/promotions/campaigns) to le
|
||||
|
||||
## What is a Campaign?
|
||||
|
||||
A [Campaign](/references/promotion/models/Campaign) combines [promotions](../concepts/page.mdx#what-is-a-promotion) under the same conditions, such as start and end dates.
|
||||
A [Campaign](/references/promotion/models/Campaign) groups [promotions](../concepts/page.mdx#what-is-a-promotion) under the same conditions, such as start and end dates.
|
||||
|
||||
Campaigns are useful for grouping promotions that share the same time frame or target audience. They're also useful for limiting the usage of promotions.
|
||||
Use campaigns to group promotions that share the same time frame or target audience, and to limit promotion usage.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Campaign Limits
|
||||
## Limit Promotion Usage with Campaign Budgets
|
||||
|
||||
Each campaign can have a budget represented by the [CampaignBudget data model](/references/promotion/models/CampaignBudget). The budget limits how many times the promotion can be used.
|
||||
Each campaign can have a budget represented by the [CampaignBudget data model](/references/promotion/models/CampaignBudget). The budget limits how many times a promotion can be used.
|
||||
|
||||
There are two types of budgets:
|
||||
There are three types of budgets: two that are global and one that is based on cart attributes.
|
||||
|
||||
- `spend`: An amount that, when crossed, the promotion becomes unusable.
|
||||
- For example, if the amount limit is set to `$100`, and the total amount of usage of this promotion crosses that threshold, the promotion can no longer be applied.
|
||||
- `usage`: The number of times that a promotion can be used.
|
||||
- For example, if the usage limit is set to `10`, the promotion can be used only 10 times by customers. After that, it can no longer be applied.
|
||||
### Global Budgets
|
||||
|
||||
A global budget limits promotion usage without considering any cart attributes.
|
||||
|
||||
There are two types of global budgets:
|
||||
|
||||
- `spend`: An amount that, when exceeded, makes the promotion unusable.
|
||||
- For example, if the amount limit is `$100` and the total usage of this promotion exceeds that threshold, the promotion can no longer be applied.
|
||||
- `usage`: The number of times a promotion can be used.
|
||||
- For example, if the usage limit is `10`, customers can use the promotion only 10 times. After that, it can no longer be applied.
|
||||
|
||||

|
||||
|
||||
### How Budgets Limit Promotion Usage
|
||||
Global budgets track usage and limits through the following properties of the `CampaignBudget` data model:
|
||||
|
||||
When a customer tries to use a promotion, Medusa checks whether the campaign has a budget and if the budget limit has been reached. If so, the promotion cannot be applied.
|
||||
- `limit`: The maximum amount or number of uses allowed for the promotion.
|
||||
- `used`: The current amount spent or number of times the promotion has been used.
|
||||
|
||||
For example, if a campaign has a budget of type `usage` with a limit of `10`, and the promotion has already been used 10 times, it cannot be applied anymore and is considered expired.
|
||||
### Attribute-based Budgets
|
||||
|
||||
However, once a promotion is applied to a cart, it remains valid until the order is completed, even if the budget limit is reached in the meantime. This ensures that customers who have already applied the promotion can still benefit from it during checkout.
|
||||
<Note>
|
||||
|
||||
Attribute-based budgets were introduced in [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0).
|
||||
|
||||
</Note>
|
||||
|
||||
An attribute-based budget limits promotion usage based on a cart attribute. Use these budget types to have granular control over how many times a promotion can be used based on specific attributes.
|
||||
|
||||
There's one type of attribute-based budget, which is `use_by_attribute`. It allows you to limit the number of times a promotion can be used based on a specific cart attribute.
|
||||
|
||||
#### Allowed Attributes
|
||||
|
||||
There are two attributes that you can limit promotion usage by:
|
||||
|
||||
- `customer_id`: Limits promotion usage based on the unique identifier of a customer.
|
||||
- `customer_email`: Limits promotion usage based on the email address of a customer.
|
||||
|
||||
These attributes are compared against the cart's `customer_id` or `email` to determine how many times the promotion has been used for that specific attribute value, and whether the budget limit has been reached.
|
||||
|
||||
#### Tracking Attribute-based Usage
|
||||
|
||||
The `CampaignBudgetUsage` data model tracks the usage of attribute-based budgets. It tracks how many times a promotion has been used for each unique attribute value. It includes the following properties:
|
||||
|
||||
1. `attribute_value`: The value of the attribute, such as a specific customer ID or email.
|
||||
2. `used`: The number of times the promotion has been used for that attribute value.
|
||||
|
||||
For example, if the attribute is `customer_id`, a new `CampaignBudgetUsage` record is created for each customer that uses the promotion to track their individual usage. Once a customer exceeds the limit set in the `CampaignBudget`, they can no longer use the promotion.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## How Campaign Budgets Limit Promotion Usage
|
||||
|
||||
When a customer tries to use a promotion, Medusa checks whether the campaign has a budget and if the budget limit has been reached. If the limit is reached, the promotion cannot be applied.
|
||||
|
||||
For example, if a campaign has a `usage` budget with a limit of `10` and the promotion has already been used 10 times, it can no longer be applied and is considered expired.
|
||||
|
||||
However, once a promotion is applied to a cart, it remains valid until the order is completed, even if the budget limit is reached in the meantime. This ensures that customers who already applied the promotion can still benefit from it during checkout.
|
||||
@@ -36,6 +36,12 @@ This reference assumes you've already resolved the data model repository, as exp
|
||||
|
||||
## Create Records
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -43,7 +49,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
@@ -35,6 +35,12 @@ This reference assumes you've already resolved the data model repository, as exp
|
||||
|
||||
## Delete Record by ID
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -42,7 +48,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -92,7 +98,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
@@ -106,7 +106,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -149,7 +149,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -213,7 +213,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -285,7 +285,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -349,7 +349,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -403,6 +403,12 @@ The method returns an array of records. The number of records is less than or eq
|
||||
|
||||
## Sort Records
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts title="src/modules/blog/service.ts"
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -410,7 +416,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
+12
-6
@@ -99,6 +99,12 @@ This reference assumes you've already resolved the data model repository, as exp
|
||||
|
||||
To retrieve a list of records matching a set of filters, use the `findAndCount` method of the data model repository:
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts title="src/modules/blog/service.ts"
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -106,7 +112,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -156,7 +162,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -226,7 +232,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -306,7 +312,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -378,7 +384,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -439,7 +445,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
@@ -37,6 +37,12 @@ This reference assumes you've already resolved the data model repository, as exp
|
||||
|
||||
## Restore Record by ID
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -44,7 +50,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -140,7 +146,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -236,7 +242,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
@@ -39,6 +39,12 @@ This reference assumes you've already resolved the data model repository, as exp
|
||||
|
||||
## Soft-Delete Record by ID
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -46,7 +52,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -142,7 +148,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
@@ -238,7 +244,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
@@ -50,6 +50,12 @@ This reference assumes you've already resolved the data model repository, as exp
|
||||
|
||||
## Update Records
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -57,7 +63,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
@@ -35,6 +35,12 @@ This reference assumes you've already resolved the data model repository, as exp
|
||||
|
||||
## Upsert Records
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -42,7 +48,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
+7
-1
@@ -41,6 +41,12 @@ This reference assumes you've already resolved the data model repository, as exp
|
||||
|
||||
## Upsert Records
|
||||
|
||||
<Note>
|
||||
|
||||
As of [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), MikroORM dependencies are included in the `@medusajs/framework` package. If you're using an older version of Medusa, change the import statement to `@mikro-orm/knex`.
|
||||
|
||||
</Note>
|
||||
|
||||
```ts
|
||||
import {
|
||||
InjectTransactionManager,
|
||||
@@ -48,7 +54,7 @@ import {
|
||||
MedusaService,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context, InferTypeOf, DAL } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
import Post from "./models/post"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
|
||||
@@ -1646,7 +1646,7 @@ import {
|
||||
MedusaContext,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
|
||||
class BlogModuleService {
|
||||
// ...
|
||||
@@ -2971,7 +2971,7 @@ To register a resource in the Module's container using a loader, use the `contai
|
||||
import {
|
||||
LoaderOptions,
|
||||
} from "@medusajs/framework/types"
|
||||
import { asValue } from "awilix"
|
||||
import { asValue } from "@medusajs/framework/awilix"
|
||||
|
||||
export default async function helloWorldLoader({
|
||||
container,
|
||||
|
||||
@@ -1484,7 +1484,7 @@ In `src/modules/product-review/service.ts`, add the following methods to the `Pr
|
||||
import { InjectManager, MedusaService, MedusaContext } from "@medusajs/framework/utils"
|
||||
import Review from "./models/review"
|
||||
import { Context } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
|
||||
class ProductReviewModuleService extends MedusaService({
|
||||
Review,
|
||||
|
||||
@@ -175,7 +175,7 @@ export const loaderHighlights = [
|
||||
|
||||
```ts title="src/modules/contentful/loader/create-content-models.ts" highlights={loaderHighlights}
|
||||
import { LoaderOptions } from "@medusajs/framework/types"
|
||||
import { asValue } from "awilix"
|
||||
import { asValue } from "@medusajs/framework/awilix"
|
||||
import { createClient } from "contentful-management"
|
||||
import { MedusaError } from "@medusajs/framework/utils"
|
||||
|
||||
|
||||
@@ -94,15 +94,7 @@ Refer to the [Instrumentation](!docs!/learn/debugging-and-testing/instrumentatio
|
||||
|
||||
### a. Install Instrumentation Dependencies
|
||||
|
||||
To set up instrumentation in Medusa, you need to install the necessary OpenTelemetry dependencies.
|
||||
|
||||
In your Medusa application's directory, run the following command:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/sdk-trace-node @opentelemetry/instrumentation-pg
|
||||
```
|
||||
|
||||
Then, you need to install the dependencies necessary for the monitoring tool you want to use, which is Sentry in this case.
|
||||
To set up instrumentation in Medusa, you need to install the dependencies necessary for the monitoring tool you want to use, which is Sentry in this case.
|
||||
|
||||
So, run the following command to install the necessary Sentry dependencies:
|
||||
|
||||
|
||||
@@ -65,19 +65,66 @@ npx medusa start
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`--cluster <number>`
|
||||
`--cluster <string> [--workers <string>] [--servers <string>]`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
Start Medusa's Node.js server in [cluster mode](https://expressjs.com/en/advanced/best-practice-performance.html#run-your-app-in-a-cluster). Running in cluster mode significantly improves performance as the workload and tasks are distributed among all available instances instead of a single one.
|
||||
Start Medusa in cluster mode. Learn more in the [Starting Medusa in Cluster Mode](#starting-medusa-in-cluster-mode) section.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
Cluster mode is disabled by default. If the option is passed but no number is passed, Medusa will try to consume all available CPU cores.
|
||||
Cluster mode is disabled by default. If the option is passed but no number or percentage is passed, Medusa will try to consume all available CPU cores.
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</Table>
|
||||
|
||||
---
|
||||
|
||||
## Starting Medusa in Cluster Mode
|
||||
|
||||
<Note>
|
||||
|
||||
Prior to [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0), the `--cluster` option accepted a number value only. You can now pass either a number or a percentage value, and you can also specify the number of servers and workers.
|
||||
|
||||
</Note>
|
||||
|
||||
Medusa supports starting the Node.js server in [cluster mode](https://expressjs.com/en/advanced/best-practice-performance.html#run-your-app-in-a-cluster), which significantly improves performance as the workload and tasks are distributed among all available instances instead of a single one.
|
||||
|
||||
Cluster mode is disabled by default. To enable it, pass the `--cluster` option when starting Medusa:
|
||||
|
||||
```bash
|
||||
npx medusa start --cluster
|
||||
```
|
||||
|
||||
When the `--cluster` option is passed without a number or percentage value, Medusa will try to consume all available CPU cores.
|
||||
|
||||
### Specify Number or Percentage of CPU Cores
|
||||
|
||||
You can specify the number or percentage of CPU cores to be used by passing a number or percentage value to the `--cluster` option:
|
||||
|
||||
```bash
|
||||
npx medusa start --cluster 2 # Use 2 CPU cores
|
||||
npx medusa start --cluster 50% # Use 50% of available CPU
|
||||
```
|
||||
|
||||
### Specify Number of Servers and Workers
|
||||
|
||||
When running Medusa in cluster mode, you can specify the number or percentage of instances that are [servers or workers](!docs!/learn/production/worker-mode) by passing the `--servers` and `--workers` options:
|
||||
|
||||
```bash
|
||||
npx medusa start --cluster 4 --servers 25% --workers 75% # Use 4 CPU cores, with 25% as servers and 75% as workers
|
||||
npx medusa start --cluster 4 --servers 1 --workers 3 # Use 4 CPU cores, with 1 as server and 3 as workers
|
||||
npx medusa start --cluster 4 --servers 1 --workers 1 # Use 4 CPU cores, with 1 as server and 1 as worker (the remaining 2 will run in shared mode)
|
||||
```
|
||||
|
||||
When the number or percentage of servers and workers don't add up to the total number of instances in cluster mode, the remaining instances will run in shared mode.
|
||||
|
||||
<Note>
|
||||
|
||||
Learn more in the [Worker Mode](!docs!/learn/production/worker-mode) guide.
|
||||
|
||||
</Note>
|
||||
@@ -1958,7 +1958,7 @@ In `src/modules/wishlist/service.ts`, add the following imports and method:
|
||||
// other imports...
|
||||
import { InjectManager } from "@medusajs/framework/utils"
|
||||
import { Context } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
|
||||
export default class WishlistModuleService extends MedusaService({
|
||||
Wishlist,
|
||||
|
||||
@@ -1133,7 +1133,7 @@ Before adding the step that does this, you'll add a method in the `RestockModule
|
||||
// other imports...
|
||||
import { InjectManager, MedusaContext } from "@medusajs/framework/utils"
|
||||
import { Context } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
import { EntityManager } from "@medusajs/framework/@mikro-orm/knex"
|
||||
|
||||
class RestockModuleService extends MedusaService({
|
||||
RestockSubscription,
|
||||
|
||||
@@ -61,7 +61,9 @@ In this example, you retrieve the product variants' inventory quantity by passin
|
||||
A variant is in stock if:
|
||||
|
||||
1. Its `manage_inventory`'s value is `false`, meaning that Medusa doesn't keep track of its inventory.
|
||||
2. If its `inventory_quantity`'s value is greater than `0`. This property is only available on variants whose `manage_inventory` is `false`.
|
||||
2. If its `inventory_quantity`'s value is greater than `0`.
|
||||
- This property is only available on variants whose `manage_inventory` is `false`.
|
||||
- If the variant doesn't have inventory levels in the stock location associated with the API's scope, its `inventory_quantity` will be `null`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ module.exports = {
|
||||
Then, create the `integration-tests/setup.js` file with the following content:
|
||||
|
||||
```js title="integration-tests/setup.js"
|
||||
const { MetadataStorage } = require("@mikro-orm/core")
|
||||
const { MetadataStorage } = require("@medusajs/framework/@mikro-orm/core")
|
||||
|
||||
MetadataStorage.clear()
|
||||
```
|
||||
|
||||
@@ -68,8 +68,8 @@ export const generatedEditDates = {
|
||||
"app/commerce-modules/promotion/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/commerce-modules/promotion/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/commerce-modules/promotion/actions/page.mdx": "2025-06-27T15:42:19.142Z",
|
||||
"app/commerce-modules/promotion/application-method/page.mdx": "2025-10-01T09:54:44.931Z",
|
||||
"app/commerce-modules/promotion/campaign/page.mdx": "2025-02-26T11:32:24.484Z",
|
||||
"app/commerce-modules/promotion/application-method/page.mdx": "2025-10-14T12:09:22.188Z",
|
||||
"app/commerce-modules/promotion/campaign/page.mdx": "2025-10-13T07:34:59.008Z",
|
||||
"app/commerce-modules/promotion/concepts/page.mdx": "2025-02-26T11:31:54.391Z",
|
||||
"app/commerce-modules/promotion/page.mdx": "2025-04-17T08:48:14.643Z",
|
||||
"app/commerce-modules/region/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
@@ -565,7 +565,7 @@ export const generatedEditDates = {
|
||||
"app/medusa-cli/commands/develop/page.mdx": "2025-09-01T15:31:35.072Z",
|
||||
"app/medusa-cli/commands/exec/page.mdx": "2025-01-16T09:51:17.050Z",
|
||||
"app/medusa-cli/commands/new/page.mdx": "2024-08-28T10:43:34.110Z",
|
||||
"app/medusa-cli/commands/start/page.mdx": "2025-04-08T11:56:15.522Z",
|
||||
"app/medusa-cli/commands/start/page.mdx": "2025-10-13T10:27:25.345Z",
|
||||
"app/medusa-cli/commands/telemtry/page.mdx": "2025-01-16T09:51:24.323Z",
|
||||
"app/medusa-cli/commands/user/page.mdx": "2025-09-01T15:36:38.978Z",
|
||||
"app/recipes/marketplace/examples/restaurant-delivery/page.mdx": "2025-08-25T07:30:37.299Z",
|
||||
@@ -877,7 +877,7 @@ export const generatedEditDates = {
|
||||
"references/promotion/interfaces/promotion.IPromotionModuleService/page.mdx": "2024-11-25T17:49:58.612Z",
|
||||
"references/types/EventBusTypes/interfaces/types.EventBusTypes.IEventBusService/page.mdx": "2025-04-11T09:04:46.063Z",
|
||||
"references/types/TransactionBaseTypes/interfaces/types.TransactionBaseTypes.ITransactionBaseService/page.mdx": "2024-09-06T00:11:08.494Z",
|
||||
"app/storefront-development/products/inventory/page.mdx": "2025-03-27T14:46:51.435Z",
|
||||
"app/storefront-development/products/inventory/page.mdx": "2025-10-13T10:18:19.391Z",
|
||||
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.updateAuthIdentities/page.mdx": "2025-05-20T07:51:40.735Z",
|
||||
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.updateProvider/page.mdx": "2025-05-20T07:51:40.735Z",
|
||||
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.updateProviderIdentities/page.mdx": "2025-05-20T07:51:40.735Z",
|
||||
@@ -2165,7 +2165,7 @@ export const generatedEditDates = {
|
||||
"app/commerce-modules/sales-channel/links-to-other-modules/page.mdx": "2025-04-17T16:00:09.483Z",
|
||||
"app/commerce-modules/stock-location/links-to-other-modules/page.mdx": "2025-04-17T16:02:51.467Z",
|
||||
"app/commerce-modules/store/links-to-other-modules/page.mdx": "2025-04-17T16:03:16.419Z",
|
||||
"app/examples/page.mdx": "2025-07-16T09:53:26.163Z",
|
||||
"app/examples/page.mdx": "2025-09-29T15:36:18.867Z",
|
||||
"app/medusa-cli/commands/build/page.mdx": "2025-09-01T15:30:05.995Z",
|
||||
"app/js-sdk/page.mdx": "2025-08-01T14:17:07.509Z",
|
||||
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.apiKey/page.mdx": "2025-05-20T07:51:40.924Z",
|
||||
@@ -5564,7 +5564,7 @@ export const generatedEditDates = {
|
||||
"references/modules/sales_channel_models/page.mdx": "2024-12-10T14:55:13.205Z",
|
||||
"references/types/DmlTypes/types/types.DmlTypes.KnownDataTypes/page.mdx": "2024-12-17T16:57:19.922Z",
|
||||
"references/types/DmlTypes/types/types.DmlTypes.RelationshipTypes/page.mdx": "2024-12-10T14:54:55.435Z",
|
||||
"app/recipes/commerce-automation/restock-notification/page.mdx": "2025-07-14T09:35:35.226Z",
|
||||
"app/recipes/commerce-automation/restock-notification/page.mdx": "2025-09-29T15:35:49.320Z",
|
||||
"app/integrations/guides/shipstation/page.mdx": "2025-05-20T07:51:40.717Z",
|
||||
"app/nextjs-starter/guides/customize-stripe/page.mdx": "2025-07-15T08:50:51.997Z",
|
||||
"references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflow/page.mdx": "2025-09-18T17:04:38.644Z",
|
||||
@@ -5831,7 +5831,7 @@ export const generatedEditDates = {
|
||||
"references/core_flows/types/core_flows.ThrowUnlessPaymentCollectionNotePaidInput/page.mdx": "2025-06-25T10:11:33.516Z",
|
||||
"references/core_flows/types/core_flows.ValidatePaymentsRefundStepInput/page.mdx": "2025-06-25T10:11:34.185Z",
|
||||
"references/core_flows/types/core_flows.ValidateRefundStepInput/page.mdx": "2025-09-12T14:10:36.022Z",
|
||||
"app/plugins/guides/wishlist/page.mdx": "2025-08-15T10:52:37.465Z",
|
||||
"app/plugins/guides/wishlist/page.mdx": "2025-09-29T15:35:46.093Z",
|
||||
"app/plugins/page.mdx": "2025-02-26T11:39:25.709Z",
|
||||
"app/admin-components/components/data-table/page.mdx": "2025-03-03T14:55:58.556Z",
|
||||
"references/order_models/variables/order_models.Order/page.mdx": "2025-09-18T17:05:02.047Z",
|
||||
@@ -5870,7 +5870,7 @@ export const generatedEditDates = {
|
||||
"references/types/interfaces/types.BaseProductTypeListParams/page.mdx": "2025-01-27T11:43:54.550Z",
|
||||
"references/core_flows/Order/Steps_Order/variables/core_flows.Order.Steps_Order.updateOrderChangesStepId/page.mdx": "2025-01-27T11:43:49.278Z",
|
||||
"app/commerce-modules/payment/account-holder/page.mdx": "2025-04-07T07:31:20.235Z",
|
||||
"app/troubleshooting/test-errors/page.mdx": "2025-01-31T13:08:42.639Z",
|
||||
"app/troubleshooting/test-errors/page.mdx": "2025-09-29T15:35:52.805Z",
|
||||
"app/commerce-modules/product/variant-inventory/page.mdx": "2025-04-25T13:25:02.408Z",
|
||||
"app/examples/guides/custom-item-price/page.mdx": "2025-06-26T11:53:06.748Z",
|
||||
"references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.validateShippingStep/page.mdx": "2025-04-11T09:04:35.729Z",
|
||||
@@ -6542,7 +6542,7 @@ export const generatedEditDates = {
|
||||
"references/utils/types/utils.NormalizedRow/page.mdx": "2025-06-05T19:05:53.365Z",
|
||||
"references/utils/utils.Payment/page.mdx": "2025-06-05T19:05:53.489Z",
|
||||
"app/integrations/guides/slack/page.mdx": "2025-06-26T12:57:20.880Z",
|
||||
"app/integrations/guides/sentry/page.mdx": "2025-06-16T10:11:29.955Z",
|
||||
"app/integrations/guides/sentry/page.mdx": "2025-10-02T10:30:51.194Z",
|
||||
"app/integrations/guides/mailchimp/page.mdx": "2025-06-26T11:59:15.303Z",
|
||||
"app/how-to-tutorials/tutorials/first-purchase-discounts/page.mdx": "2025-06-26T11:55:27.175Z",
|
||||
"references/types/CommonTypes/interfaces/types.CommonTypes.CookieOptions/page.mdx": "2025-06-25T10:11:37.088Z",
|
||||
@@ -6611,6 +6611,15 @@ export const generatedEditDates = {
|
||||
"app/integrations/guides/meilisearch/page.mdx": "2025-10-09T11:30:25.084Z",
|
||||
"app/nextjs-starter/guides/storefront-returns/page.mdx": "2025-09-22T06:02:00.580Z",
|
||||
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.views/page.mdx": "2025-09-18T17:04:59.240Z",
|
||||
"app/data-model-repository-reference/methods/create/page.mdx": "2025-10-09T11:42:23.826Z",
|
||||
"app/data-model-repository-reference/methods/delete/page.mdx": "2025-10-09T11:42:59.141Z",
|
||||
"app/data-model-repository-reference/methods/find/page.mdx": "2025-10-09T11:43:05.478Z",
|
||||
"app/data-model-repository-reference/methods/findAndCount/page.mdx": "2025-10-09T11:43:49.941Z",
|
||||
"app/data-model-repository-reference/methods/restore/page.mdx": "2025-10-09T11:44:02.906Z",
|
||||
"app/data-model-repository-reference/methods/softDelete/page.mdx": "2025-10-09T11:44:16.158Z",
|
||||
"app/data-model-repository-reference/methods/upsert/page.mdx": "2025-10-09T11:44:39.838Z",
|
||||
"app/data-model-repository-reference/methods/update/page.mdx": "2025-10-09T11:44:27.403Z",
|
||||
"app/data-model-repository-reference/methods/upsertWithReplace/page.mdx": "2025-10-09T11:44:53.535Z",
|
||||
"app/how-to-tutorials/tutorials/agentic-commerce/page.mdx": "2025-10-09T11:25:48.831Z",
|
||||
"app/storefront-development/production-optimizations/page.mdx": "2025-10-03T13:28:37.909Z",
|
||||
"app/troubleshooting/subscribers/not-working/page.mdx": "2025-10-16T09:25:57.376Z"
|
||||
|
||||
@@ -6419,6 +6419,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
|
||||
"path": "https://docs.medusajs.com/user-guide/orders/returns",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "ref",
|
||||
"title": "Manage Refund Reasons",
|
||||
"path": "https://docs.medusajs.com/user-guide/settings/refund-reasons",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
|
||||
Reference in New Issue
Block a user