docs: improved commerce module docs [2/n] (#9501)

Improve + add docs for commerce modules from currency to inventory

[2/n]
This commit is contained in:
Shahed Nasser
2024-10-09 12:53:17 +03:00
committed by GitHub
parent 853187410e
commit 3298cd3fd2
25 changed files with 993 additions and 373 deletions

View File

@@ -13,16 +13,15 @@ In this guide, youll find common examples of how you can use the Currency Mod
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ICurrencyModuleService } 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(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const currencyModuleService: ICurrencyModuleService = req.scope.resolve(
const currencyModuleService = req.scope.resolve(
Modules.CURRENCY
)
@@ -59,16 +58,15 @@ export async function GET(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ICurrencyModuleService } 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(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const currencyModuleService: ICurrencyModuleService = req.scope.resolve(
const currencyModuleService = req.scope.resolve(
Modules.CURRENCY
)

View File

@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
The Currency Module is the `@medusajs/medusa/currency` NPM package that provides currency-related features in your Medusa and Node.js applications.
The Currency Module provides currency-related features in your Medusa and Node.js applications.
## How to Use Currency Module's Service
@@ -15,18 +15,33 @@ You can use the Currency Module's main service by resolving from the Medusa cont
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 currencyModuleService = container.resolve(
Modules.CURRENCY
)
const currencies = await currencyModuleService.listCurrencies()
})
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ICurrencyModuleService } 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(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const currencyModuleService: ICurrencyModuleService = req.scope.resolve(
const currencyModuleService = req.scope.resolve(
Modules.CURRENCY
)
@@ -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 { ICurrencyModuleService } 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 currencyModuleService: ICurrencyModuleService = container.resolve(
const currencyModuleService = container.resolve(
Modules.CURRENCY
)
const currencies = await currencyModuleService.listCurrencies()
}
```
</CodeTab>
<CodeTab label="Workflow Step" value="workflow-step">
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { ICurrencyModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const currencyModuleService: ICurrencyModuleService = container.resolve(
Modules.CURRENCY
)
const currencies = await currencyModuleService.listCurrencies()
})
```
</CodeTab>
@@ -87,12 +84,12 @@ const currency = await currencyModuleService.retrieveCurrency("usd")
### Support Currencies in Modules
Other commerce modules use currency codes in their data models or operations. You can use the Currency Module to retrieve a currency code and its details.
Other commerce modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details.
An example with the Region Module:
```ts
const region = await regionModuleService.retrieveCurrency("reg_123")
const region = await regionModuleService.retrieveRegion("reg_123")
const currency = await currencyModuleService.retrieveCurrency(
region.currency_code
)

View File

@@ -1,13 +0,0 @@
export const metadata = {
title: `Relations between Currency Module and Other Modules`,
}
# {metadata.title}
This document showcases the link modules defined between the Currency Module and other commerce modules.
## Store Module
A store has a default currency. Medusa defines a link module that builds a relationship between the `Store` and `Currency` data models.
![A diagram showcasing an example of how resources from the Stock Location and Inventory modules are linked.](https://res.cloudinary.com/dza7lstvk/image/upload/v1716798146/Medusa%20Resources/store-currency_wzftwh.jpg)

View File

@@ -18,6 +18,6 @@ When this or another guest customer registers an account with the same email, a
## Email Uniqueness
The above behavior means that two `Customer` records may exist of the same email. However, the main difference is the `has_account` property's value.
The above behavior means that two `Customer` records may exist with the same email. However, the main difference is the `has_account` property's value.
So, there can only be one guest customer (having `has_account=false`) and one registered customer (having `has_account=true`) with the same email.

View File

@@ -13,13 +13,12 @@ In this guide, youll find common examples of how you can use the Customer Mod
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ICustomerModuleService } 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) {
const customerModuleService: ICustomerModuleService = request.scope.resolve(
const customerModuleService = request.scope.resolve(
Modules.CUSTOMER
)
@@ -68,13 +67,12 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ICustomerModuleService } 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) {
const customerModuleService: ICustomerModuleService = request.scope.resolve(
const customerModuleService = request.scope.resolve(
Modules.CUSTOMER
)
@@ -119,13 +117,12 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ICustomerModuleService } 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) {
const customerModuleService: ICustomerModuleService = request.scope.resolve(
const customerModuleService = request.scope.resolve(
Modules.CUSTOMER
)
@@ -168,13 +165,12 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ICustomerModuleService } 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) {
const customerModuleService: ICustomerModuleService = request.scope.resolve(
const customerModuleService = request.scope.resolve(
Modules.CUSTOMER
)

View File

@@ -0,0 +1,684 @@
import { Prerequisites } from "docs-ui"
export const metadata = {
title: `Extend Customer Data Model`,
}
# {metadata.title}
In this documentation, you'll learn how to extend a data model of the Customer Module to add a custom property.
You'll create a `Custom` data model in a module. This data model will have a `custom_name` property, which is the property you want to add to the [Customer data model](/references/customer/models/Customer) defined in the Customer Module.
You'll then learn how to:
- Link the `Custom` data model to the `Customer` data model.
- Set the `custom_name` property when a customer is created or updated using Medusa's API routes.
- Retrieve the `custom_name` property with the customer's details, in custom or existing API routes.
<Note title="Tip">
Similar steps can bee applied to the `CustomerAddress` data model.
</Note>
## Step 1: Define Custom Data Model
Consider you have a Hello Module defined in the `/src/modules/hello` directory.
<Note title="Tip">
If you don't have a module, follow [this guide](!docs!/basics/modules) to create one.
</Note>
To add the `custom_name` property to the `Customer` data model, you'll create in the Hello Module a data model that has the `custom_name` property.
Create the file `src/modules/hello/models/custom.ts` with the following content:
```ts title="src/modules/hello/models/custom.ts"
import { model } from "@medusajs/framework/utils"
export const Custom = model.define("custom", {
id: model.id().primaryKey(),
custom_name: model.text(),
})
```
This creates a `Custom` data model that has the `id` and `custom_name` properties.
<Note title="Tip">
Learn more about data models in [this guide](!docs!/data-models).
</Note>
---
## Step 2: Define Link to Customer Data Model
Next, you'll define a module link between the `Custom` and `Customer` data model. A module link allows you to form a relation between two data models of separate modules while maintaining module isolation.
<Note title="Tip">
Learn more about module links in [this guide](!docs!/module-links).
</Note>
Create the file `src/links/customer-custom.ts` with the following content:
```ts title="src/links/customer-custom.ts"
import { defineLink } from "@medusajs/framework/utils"
import HelloModule from "../modules/hello"
import CustomerModule from "@medusajs/medusa/customer"
export default defineLink(
CustomerModule.linkable.customer,
HelloModule.linkable.custom
)
```
This defines a link between the `Customer` and `Custom` data models. Using this link, you'll later query data across the modules, and link records of each data model.
---
## Step 3: Generate and Run Migrations
<Prerequisites
items={[
{
text: "Module must be registered in medusa-config.js",
link: "!docs!/basics/modules#4-add-module-to-configurations"
}
]}
/>
To reflect the `Custom` data model in the database, generate a migration that defines the table to be created for it.
Run the following command in your Medusa project's root:
```bash
npx medusa db:generate helloModuleService
```
Where `helloModuleService` is your module's name.
Then, run the `db:migrate` command to run the migrations and create a table in the database for the link between the `Customer` and `Custom` data models:
```bash
npx medusa db:migrate
```
A table for the link is now created in the database. You can now retrieve and manage the link between records of the data models.
---
## Step 4: Consume customersCreated Workflow Hook
When a customer is created, you also want to create a `Custom` record and set the `custom_name` property, then create a link between the `Customer` and `Custom` records.
To do that, you'll consume the [customersCreated](/resources/references/medusa-workflows/createCustomersWorkflow#customerscreated) hook of the [createCustomersWorkflow](/references/medusa-workflows/createCustomersWorkflow). This workflow is executed in the [Create Customer Admin API route](!api!/admin#customers_postcustomers)
<Note title="Tip">
Learn more about workflow hooks in [this guide](!docs!/advanced-development/workflows/workflow-hooks).
</Note>
The API route accepts in its request body an `additional_data` parameter. You can pass in it custom data, which is passed to the workflow hook handler.
### Add custom_name to Additional Data Validation
To pass the `custom_name` in the `additional_data` parameter, you must add a validation rule that tells the Medusa application about this custom property.
Create the file `src/api/middlewares.ts` with the following content:
```ts title="src/api/middlewares.ts"
import { defineMiddlewares } from "@medusajs/medusa"
import { z } from "zod"
export default defineMiddlewares({
routes: [
{
method: "POST",
matcher: "/admin/customers",
additionalDataValidator: {
custom_name: z.string().optional(),
},
},
],
})
```
The `additional_data` parameter validation is customized using the `defineMiddlewares` utility function. In the routes middleware configuration object, the `additionalDataValidator` property accepts [Zod](https://zod.dev/) validaiton rules.
In the snippet above, you add a validation rule indicating that `custom_name` is a string that can be passed in the `additional_data` object.
<Note title="Tip">
Learn more about additional data validation in [this guide](!docs!/advanced-development/api-routes/additional-data).
</Note>
### Create Workflow to Create Custom Record
You'll now create a workflow that will be used in the hook handler.
This workflow will create a `Custom` record, then link it to the customer.
Start by creating the step that creates the `Custom` record. Create the file `src/workflows/create-custom-from-customer/steps/create-custom.ts` with the following content:
```ts title="src/workflows/create-custom-from-customer/steps/create-custom.ts"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import HelloModuleService from "../../../modules/hello/service"
import { HELLO_MODULE } from "../../../modules/hello"
type CreateCustomStepInput = {
custom_name?: string
}
export const createCustomStep = createStep(
"create-custom",
async (data: CreateCustomStepInput, { container }) => {
if (!data.custom_name) {
return
}
const helloModuleService: HelloModuleService = container.resolve(
HELLO_MODULE
)
const custom = await helloModuleService.createCustoms(data)
return new StepResponse(custom, custom)
},
async (custom, { container }) => {
const helloModuleService: HelloModuleService = container.resolve(
HELLO_MODULE
)
await helloModuleService.deleteCustoms(custom.id)
}
)
```
In the step, you resolve the Hello Module's main service and create a `Custom` record.
In the compensation function that undoes the step's actions in case of an error, you delete the created record.
<Note title="Tip">
Learn more about compensation functions in [this guide](!docs!/advanced-development/workflows/compensation-function).
</Note>
Then, create the workflow at `src/workflows/create-custom-from-customer/index.ts` with the following content:
```ts title="src/workflows/create-custom-from-customer/index.ts" collapsibleLines="1-7" expandButtonLabel="Show Imports"
import { createWorkflow, transform, when, WorkflowResponse } from "@medusajs/framework/workflows-sdk"
import { CustomerDTO } from "@medusajs/framework/types"
import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
import { Modules } from "@medusajs/framework/utils"
import { HELLO_MODULE } from "../../modules/hello"
import { createCustomStep } from "./steps/create-custom"
export type CreateCustomFromCustomerWorkflowInput = {
customer: CustomerDTO
additional_data?: {
custom_name?: string
}
}
export const createCustomFromCustomerWorkflow = createWorkflow(
"create-custom-from-customer",
(input: CreateCustomFromCustomerWorkflowInput) => {
const customName = transform(
{
input,
},
(data) => data.input.additional_data.custom_name || ""
)
const custom = createCustomStep({
custom_name: customName,
})
when(({ custom }), ({ custom }) => custom !== undefined)
.then(() => {
createRemoteLinkStep([{
[Modules.CUSTOMER]: {
customer_id: input.customer.id,
},
[HELLO_MODULE]: {
custom_id: custom.id,
},
}])
})
return new WorkflowResponse({
custom,
})
}
)
```
The workflow accepts as an input the created customer and the `additional_data` parameter passed in the request. This is the same input that the `customersCreated` hook accepts.
In the workflow, you:
1. Use the `transform` utility to get the value of `custom_name` based on whether it's set in `additional_data`. Learn more about why you can't use conditional operators in a workflow without using `transform` in [this guide](!docs!/advanced-development/workflows/conditions#why-if-conditions-arent-allowed-in-workflows).
2. Create the `Custom` record using the `createCustomStep`.
3. Use the `when-then` utility to link the customer to the `Custom` record if it was created. Learn more about why you can't use if-then conditions in a workflow without using `when-then` in [this guide](!docs!/advanced-development/workflows/conditions#why-if-conditions-arent-allowed-in-workflows).
You'll next call the workflow in the hook handler.
### Consume Workflow Hook
You can now consume the `customersCreated` hook, which is executed in the `createCustomersWorkflow` after the customer is created.
To consume the hook, create the file `src/workflow/hooks/customer-created.ts` with the following content:
```ts title="src/workflow/hooks/customer-created.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
import { createCustomersWorkflow } from "@medusajs/medusa/core-flows"
import {
createCustomFromCustomerWorkflow,
CreateCustomFromCustomerWorkflowInput,
} from "../create-custom-from-customer"
createCustomersWorkflow.hooks.customersCreated(
async ({ customers, additional_data }, { container }) => {
const workflow = createCustomFromCustomerWorkflow(container)
for (const customer of customers) {
await workflow.run({
input: {
customer,
additional_data,
} as CreateCustomFromCustomerWorkflowInput,
})
}
}
)
```
The hook handler executes the `createCustomFromCustomerWorkflow`, passing it its input.
### Test it Out
To test it out, send a `POST` request to `/admin/customers` to create a customer, passing `custom_name` in `additional_data`:
```bash
curl --location 'localhost:9000/admin/customers' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data-raw '{
"email": "customer@gmail.com",
"additional_data": {
"custom_name": "test"
}
}'
```
Make sure to replace `{token}` with an admin user's JWT token. Learn how to retrieve it in the [API reference](!api!/admin#1-bearer-authorization-with-jwt-tokens).
The request will return the customer's details. You'll learn how to retrive the `custom_name` property with the customer's details in the next section.
---
## Step 5: Retrieve custom_name with Customer Details
When you extend an existing data model through links, you also want to retrieve the custom properties with the data model.
### Retrieve in API Routes
To retrieve the `custom_name` property when you're retrieving the customer through API routes, such as the [Get Customer API Route](!api!/admin#customers_getcustomersid), pass in the `fields` query parameter `+custom.*`, which retrieves the linked `Custom` record's details.
<Note title="Tip">
The `+` prefix in `+custom.*` indicates that the relation should be retrieved with the default customer fields. Learn more about selecting fields and relations in the [API reference](!api!/admin#select-fields-and-relations).
</Note>
For example:
```bash
curl -X POST 'localhost:9000/admin/customers/{customer_id}?fields=+custom.*' \
-H 'Authorization: Bearer {token}'
```
Make sure to replace `{customer_id}` with the customer's ID, and `{token}` with an admin user's JWT token.
Among the returned `customer` object, you'll find a `custom` property which holds the details of the linked `Custom` record:
```json
{
"customer": {
// ...
"custom": {
"id": "01J9NP7ANXDZ0EAYF0956ZE1ZA",
"custom_name": "test",
"created_at": "2024-10-08T09:09:06.877Z",
"updated_at": "2024-10-08T09:09:06.877Z",
"deleted_at": null
}
}
}
```
### Retrieve using Query
You can also retrieve the `Custom` record linked to a customer in your code using [Query](!docs!/advanced-development/module-links/query).
For example:
```ts
const { data: [customer] } = await query.graph({
entity: "customer",
fields: ["*", "custom.*"],
filters: {
id: customer_id,
},
})
```
Learn more about how to use Query in [this guide](!docs!/advanced-development/module-links/query).
---
## Step 6: Consume customersUpdated Workflow Hook
Similar to the `customersCreated` hook, you'll consume the [customersUpdated](/references/medusa-workflows/updateCustomersWorkflow#customersUpdated) hook of the [updateCustomersWorkflow](/references/medusa-workflows/updateCustomersWorkflow) to update `custom_name` when the customer is updated.
The `updateCustomersWorkflow` is executed by the [Update Customer API route](!api!/admin#customers_postcustomersid), which accepts the `additional_data` parameter to pass custom data to the hook.
### Add custom_name to Additional Data Validation
To allow passing `custom_name` in the `additional_data` parameter of the update customer route, add in `src/api/middlewares.ts` a new route middleware configuration object:
```ts title="src/api/middlewares.ts"
import { defineMiddlewares } from "@medusajs/medusa"
import { z } from "zod"
export default defineMiddlewares({
routes: [
// ...
{
method: "POST",
matcher: "/admin/customers/:id",
additionalDataValidator: {
custom_name: z.string().nullish(),
},
},
],
})
```
The validation schema is the similar to that of the Create Customer API route, except you can pass a `null` value for `custom_name` to remove or unset the `custom_name`'s value.
### Create Workflow to Update Custom Record
Next, you'll create a workflow that creates, updates, or deletes `Custom` records based on the provided `additional_data` parameter:
1. If `additional_data.custom_name` is set and it's `null`, the `Custom` record linked to the customer is deleted.
2. If `additional_data.custom_name` is set and the customer doesn't have a linked `Custom` record, a new record is created and linked to the customer.
3. If `additional_data.custom_name` is set and the customer has a linked `Custom` record, the `custom_name` property of the `Custom` record is updated.
Start by creating the step that updates a `Custom` record. Create the file `src/workflows/update-custom-from-customer/steps/update-custom.ts` with the following content:
```ts title="src/workflows/update-custom-from-customer/steps/update-custom.ts"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import { HELLO_MODULE } from "../../../modules/hello"
import HelloModuleService from "../../../modules/hello/service"
type UpdateCustomStepInput = {
id: string
custom_name: string
}
export const updateCustomStep = createStep(
"update-custom",
async ({ id, custom_name }: UpdateCustomStepInput, { container }) => {
const helloModuleService: HelloModuleService = container.resolve(
HELLO_MODULE
)
const prevData = await helloModuleService.retrieveCustom(id)
const custom = await helloModuleService.updateCustoms({
id,
custom_name,
})
return new StepResponse(custom, prevData)
},
async (prevData, { container }) => {
const helloModuleService: HelloModuleService = container.resolve(
HELLO_MODULE
)
await helloModuleService.updateCustoms(prevData)
}
)
```
In this step, you update a `Custom` record. In the compensation function, you revert the update.
Next, you'll create the step that deletes a `Custom` record. Create the file `src/workflows/update-custom-from-customer/steps/delete-custom.ts` with the following content:
```ts title="src/workflows/update-custom-from-customer/steps/delete-custom.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import { Custom } from "../../../modules/hello/models/custom"
import { InferTypeOf } from "@medusajs/framework/types"
import HelloModuleService from "../../../modules/hello/service"
import { HELLO_MODULE } from "../../../modules/hello"
type DeleteCustomStepInput = {
custom: InferTypeOf<typeof Custom>
}
export const deleteCustomStep = createStep(
"delete-custom",
async ({ custom }: DeleteCustomStepInput, { container }) => {
const helloModuleService: HelloModuleService = container.resolve(
HELLO_MODULE
)
await helloModuleService.deleteCustoms(custom.id)
return new StepResponse(custom, custom)
},
async (custom, { container }) => {
const helloModuleService: HelloModuleService = container.resolve(
HELLO_MODULE
)
await helloModuleService.createCustoms(custom)
}
)
```
In this step, you delete a `Custom` record. In the compensation function, you create it again.
Finally, you'll create the workflow. Create the file `src/workflows/update-custom-from-customer/index.ts` with the following content:
```ts title="src/workflows/update-custom-from-customer/index.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports"
import { CustomerDTO } from "@medusajs/framework/types"
import { createWorkflow, when, WorkflowResponse } from "@medusajs/framework/workflows-sdk"
import { createRemoteLinkStep, dismissRemoteLinkStep, useRemoteQueryStep } from "@medusajs/medusa/core-flows"
import { createCustomStep } from "../create-custom-from-customer/steps/create-custom"
import { Modules } from "@medusajs/framework/utils"
import { HELLO_MODULE } from "../../modules/hello"
import { deleteCustomStep } from "./steps/delete-custom"
import { updateCustomStep } from "./steps/update-custom"
export type UpdateCustomFromCustomerStepInput = {
customer: CustomerDTO
additional_data?: {
custom_name?: string | null
}
}
export const updateCustomFromCustomerWorkflow = createWorkflow(
"update-custom-from-customer",
(input: UpdateCustomFromCustomerStepInput) => {
const customerData = useRemoteQueryStep({
entry_point: "customer",
fields: ["custom.*"],
variables: {
filters: {
id: input.customer.id,
},
},
list: false,
})
// TODO create, update, or delete Custom record
}
)
```
The workflow accepts the same input as the `customersUpdated` workflow hook handler would.
In the workflow, you retrieve the customer's linked `Custom` record using Query.
Next, replace the `TODO` with the following:
```ts title="src/workflows/update-custom-from-customer/index.ts"
const created = when({
input,
customerData,
}, (data) =>
!data.customerData.custom &&
data.input.additional_data?.custom_name?.length > 0
)
.then(() => {
const custom = createCustomStep({
custom_name: input.additional_data.custom_name,
})
createRemoteLinkStep([{
[Modules.CUSTOMER]: {
customer_id: input.customer.id,
},
[HELLO_MODULE]: {
custom_id: custom.id,
},
}])
return custom
})
// TODO update, or delete Custom record
```
Using the `when-then` utility, you check if the customer doesn't have a linked `Custom` record and the `custom_name` property is set. If so, you create a `Custom` record and link it to the customer.
To create the `Custom` record, you use the `createCustomStep` you created in an earlier section.
Next, replace the new `TODO` with the following:
```ts title="src/workflows/update-custom-from-customer/index.ts"
const deleted = when({
input,
customerData,
}, (data) =>
data.customerData.custom && (
data.input.additional_data?.custom_name === null ||
data.input.additional_data?.custom_name.length === 0
)
)
.then(() => {
deleteCustomStep({
custom: customerData.custom,
})
dismissRemoteLinkStep({
[HELLO_MODULE]: {
custom_id: customerData.custom.id,
},
})
return customerData.custom.id
})
// TODO delete Custom record
```
Using the `when-then` utility, you check if the customer has a linked `Custom` record and `custom_name` is `null` or an empty string. If so, you delete the linked `Custom` record and dismiss its links.
Finally, replace the new `TODO` with the following:
```ts title="src/workflows/update-custom-from-customer/index.ts"
const updated = when({
input,
customerData,
}, (data) => data.customerData.custom && data.input.additional_data?.custom_name?.length > 0)
.then(() => {
const custom = updateCustomStep({
id: customerData.custom.id,
custom_name: input.additional_data.custom_name,
})
return custom
})
return new WorkflowResponse({
created,
updated,
deleted,
})
```
Using the `when-then` utility, you check if the customer has a linked `Custom` record and `custom_name` is passed in the `additional_data`. If so, you update the linked `Custom` recod.
You return in the workflow response the created, updated, and deleted `Custom` record.
### Consume customersUpdated Workflow Hook
You can now consume the `customersUpdated` and execute the workflow you created.
Create the file `src/workflows/hooks/customer-updated.ts` with the following content:
```ts title="src/workflows/hooks/customer-updated.ts"
import { updateCustomersWorkflow } from "@medusajs/medusa/core-flows"
import {
UpdateCustomFromCustomerStepInput,
updateCustomFromCustomerWorkflow,
} from "../update-custom-from-customer"
updateCustomersWorkflow.hooks.customersUpdated(
async ({ customers, additional_data }, { container }) => {
const workflow = updateCustomFromCustomerWorkflow(container)
for (const customer of customers) {
await workflow.run({
input: {
customer,
additional_data,
} as UpdateCustomFromCustomerStepInput,
})
}
}
)
```
In the workflow hook handler, you execute the workflow, passing it the hook's input.
### Test it Out
To test it out, send a `POST` request to `/admin/customers/:id` to update a customer, passing `custom_name` in `additional_data`:
```bash
curl -X POST 'localhost:9000/admin/customers/{customer_id}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data '{
"additional_data": {
"custom_name": "test3"
}
}'
```
Make sure to replace `{customer_id}` with the customer's ID, and `{token}` with the JWT token of an admin user.
The request will return the customer's details with the updated `custom` linked record.

View File

@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
The Customer Module is the `@medusajs/medusa/customer` NPM package that provides customer-related features in your Medusa and Node.js applications.
The Customer Module provides customer-related features in your Medusa and Node.js applications.
## How to Use Customer Module's Service
@@ -15,15 +15,30 @@ You can use the Customer Module's main service by resolving from the Medusa cont
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 customerModuleService = container.resolve(
Modules.CUSTOMER
)
const customers = await customerModuleService.listCustomers()
})
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ICustomerModuleService } 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) {
const customerModuleService: ICustomerModuleService = request.scope.resolve(
const customerModuleService = request.scope.resolve(
Modules.CUSTOMER
)
@@ -36,35 +51,17 @@ export async function GET(request: MedusaRequest, res: MedusaResponse) {
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { ICustomerModuleService } 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 customerModuleService: ICustomerModuleService = container.resolve(
const customerModuleService = container.resolve(
Modules.CUSTOMER
)
const customers = await customerModuleService.listCustomers()
}
```
</CodeTab>
<CodeTab label="Workflow Step" value="workflow-step">
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { ICustomerModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const customerModuleService: ICustomerModuleService = container.resolve(
Modules.CUSTOMER
)
const customers = await customerModuleService.listCustomers()
})
```
</CodeTab>
@@ -76,7 +73,7 @@ const step1 = createStep("step-1", async (_, { container }) => {
### Customer Management
With the Customer Module, store and manage customers in your store.
With the Customer Module, store and manage customers.
```ts
const customer = await customerModuleService.createCustomers({
@@ -88,7 +85,9 @@ const customer = await customerModuleService.createCustomers({
### Customer Organization
You can organize customers into groups. This has a lot of benefits and supports more use cases, such as provide discounts for specific customer groups using the Promotion Module.
Organize customers into groups.
This has a lot of benefits and supports many use cases, such as provide discounts for specific customer groups using the Promotion Module.
```ts
const customerGroup = await customerModuleService.createCustomerGroups({

View File

@@ -1,22 +0,0 @@
export const metadata = {
title: `Relations between Customer Module and Other Modules`,
}
# {metadata.title}
This document showcases the link modules defined between the Customer Module and other commerce modules.
## Cart Module
A customer has a cart to purchase items from the store. Medusa defines a link module that builds a relationship between the `Customer` and the `Cart` data models.
![A diagram showcasing an example of how data models from the Customer and Cart modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711537276/Medusa%20Resources/cart-customer_pnjvuw.jpg)
---
## Order Module
A customer is associated with the orders they place. Medusa defines a link module that builds a relationship between the `Customer` and the `Order` data models.
![A diagram showcasing an example of how data models from the Customer and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716545470/Medusa%20Resources/customer-order_pkla6f.jpg)

View File

@@ -10,7 +10,7 @@ In this document, youll learn what a fulfillment module provider is.
A fulfillment module provider handles fulfilling items, typically using a third-party integration.
Fulfillment module providers are stored and represented by the [FulfillmentProvider data model](/references/fulfillment/models/FulfillmentProvider).
Fulfillment module providers registered in the Fulfillment Module's [options](../module-options/page.mdx) are stored and represented by the [FulfillmentProvider data model](/references/fulfillment/models/FulfillmentProvider).
---

View File

@@ -12,7 +12,7 @@ A fulfillment is the shipping and delivery of one or more items to the customer.
---
## Fulfillment Processing
## Fulfillment Processing by a Fulfillment Provider
A fulfillment is associated with a fulfillment provider that handles all its processing, such as creating a shipment for the fulfillments items.

View File

@@ -0,0 +1,43 @@
export const metadata = {
title: `Links between Fulfillment Module and Other Modules`,
}
# {metadata.title}
This document showcases the module links defined between the Fulfillment Module and other commerce modules.
## Order Module
The Order Module provides order-management functionalities.
Medusa defines a link between the `Fulfillment` and `Order` data models. A fulfillment is created for an orders' items.
![A diagram showcasing an example of how data models from the Fulfillment and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716549903/Medusa%20Resources/order-fulfillment_h0vlps.jpg)
A fulfillment is also created for a return's items. So, Medusa defines a link between the `Fulfillment` and `Return` data models.
![A diagram showcasing an example of how data models from the Fulfillment and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728399052/Medusa%20Resources/Social_Media_Graphics_2024_Order_Return_vetimk.jpg)
---
## Pricing Module
The Pricing Module provides features to store, manage, and retrieve the best prices in a specified context.
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)
---
## Stock Location Module
The Stock Location Module provides features to manage stock locations in a store.
Medusa defines a link between the `FulfillmentSet` and `StockLocation` data models. A fulfillment set can be conditioned to a specific stock location.
![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1712567101/Medusa%20Resources/fulfillment-stock-location_nlkf7e.jpg)
Medusa also defines a link between the `FulfillmentProvider` and `StockLocation` data models to indicate the providers that can be used in a location.
![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728399492/Medusa%20Resources/fulfillment-provider-stock-location_b0mulo.jpg)

View File

@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
The Fulfillment Module is the `@medusajs/medusa/fulfillment` NPM package that provides fulfillment-related features in your Medusa and Node.js applications.
The Fulfillment Module provides fulfillment-related features in your Medusa and Node.js applications.
## How to Use Fulfillment Module's Service
@@ -15,18 +15,33 @@ You can use the Fulfillment Module's main service by resolving from the Medusa c
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 fulfillmentModuleService = container.resolve(
Modules.FULFILLMENT
)
const fulfillments = await fulfillmentModuleService.listFulfillments()
})
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IFulfillmentModuleService } 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(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const fulfillmentModuleService: IFulfillmentModuleService = req.scope.resolve(
const fulfillmentModuleService = req.scope.resolve(
Modules.FULFILLMENT
)
@@ -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 { IFulfillmentModuleService } 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 fulfillmentModuleService: IFulfillmentModuleService = container.resolve(
const fulfillmentModuleService = container.resolve(
Modules.FULFILLMENT
)
const fulfillments = await fulfillmentModuleService.listFulfillments()
}
```
</CodeTab>
<CodeTab label="Workflow Step" value="workflow-step">
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { IFulfillmentModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const fulfillmentModuleService: IFulfillmentModuleService = container.resolve(
Modules.FULFILLMENT
)
const fulfillments = await fulfillmentModuleService.listFulfillments()
})
```
</CodeTab>

View File

@@ -1,29 +0,0 @@
export const metadata = {
title: `Relations between Fulfillment Module and Other Modules`,
}
# {metadata.title}
This document showcases the link modules defined between the Fulfillment Module and other commerce modules.
## Order Module
A fulfillment is created for an orders' items. Medusa defines a link module that builds a relationship between the `Fulfillment` and `Order` data models.
![A diagram showcasing an example of how data models from the Fulfillment and Order modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716549903/Medusa%20Resources/order-fulfillment_h0vlps.jpg)
---
## Pricing 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)
---
## Stock Location Module
A fulfillment set can be conditioned to a specific stock location. Medusa defines a link module that builds a relationship between the `FulfillmentSet` and `StockLocation` data models.
![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1712567101/Medusa%20Resources/fulfillment-stock-location_nlkf7e.jpg)

View File

@@ -45,7 +45,7 @@ These rules are represented by the [ShippingOptionRule data model](/references/f
![A diagram showcasing the relation between shipping option and shipping option rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712331340/Medusa%20Resources/shipping-option-rule_oosopf.jpg)
A shipping option can have multiple rules. For example, a shipping option is available if the customer belongs to the VIP group and the total weight is less than 2000g.
A shipping option can have multiple rules. For example, you can add rules to a shipping option so that it's available if the customer belongs to the VIP group and the total weight is less than 2000g.
![A diagram showcasing how a shipping option can have multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712331462/Medusa%20Resources/shipping-option-rule-2_ylaqdb.jpg)

View File

@@ -34,6 +34,6 @@ The inventory level's location is determined by the `location_id` property. Medu
## ReservationItem
A reservation item, represented by the `ReservationItem` data model, represents unavailable quantity of an inventory item in a location. It's used when an order is placed but not fulfilled yet.
A reservation item, represented by the [ReservationItem](/references/inventory-next/models/ReservationItem) data model, represents unavailable quantity of an inventory item in a location. It's used when an order is placed but not fulfilled yet.
The reserved quantity is associated with a location, so it has a similar relation to that of the `InventoryLevel` with the Stock Location Module.

View File

@@ -13,16 +13,15 @@ In this document, youll find common examples of how you can use the Inventory
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -68,16 +67,15 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -114,16 +112,15 @@ export async function GET(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -170,16 +167,15 @@ export async function GET(request: Request, { params }: ContextType) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -225,16 +221,15 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -280,16 +275,15 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -335,16 +329,15 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -392,16 +385,15 @@ export async function POST(request: Request) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -479,16 +471,15 @@ export async function POST(request: Request, { params }: ContextType) {
<CodeTabs groupId="app-type">
<CodeTab label="Medusa API Router" value="medusa">
```ts
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 DELETE(
request: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)

View File

@@ -47,3 +47,7 @@ When an item in an order is fulfilled and the associated variant has its `manage
When an item in an order is returned and the associated variant has its `manage_inventory` property set to `true`, the Medusa application increments the `stocked_quantity` of the inventory item's level with the returned quantity.
![A diagram showcasing how the Inventory Module is used in the order return flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1709712457/Medusa%20Resources/inventory-order-return_ihftyk.jpg)
### Dismissed Returned Items
If a returned item is considered damanged or its dismissed, its quantity doesn't increment the `stocked_quantity` of the inventory item's level.

View File

@@ -0,0 +1,15 @@
export const metadata = {
title: `Links between Inventory Module and Other Modules`,
}
# {metadata.title}
This document showcases the module links defined between the Inventory Module and other commerce modules.
## Product Module
Each product variant has different inventory details. Medusa defines a link between the `ProductVariant` and `InventoryItem` data models.
![A diagram showcasing an example of how data models from the Inventory and Product Module are linked.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709658720/Medusa%20Resources/inventory-product_ejnray.jpg)
A product variant whose `manage_inventory` property is enabled has an associated inventory item. Through that inventory's items relations in the Inventory Module, you can manage and check the variant's inventory quantity.

View File

@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
The Inventory Module is the `@medusajs/medusa/inventory-next` NPM package that provides inventory-related features in your Medusa and Node.js applications.
The Inventory Module provides inventory-related features in your Medusa and Node.js applications.
## How to Use Inventory Module's Service
@@ -15,18 +15,33 @@ You can use the Inventory Module's main service by resolving from the Medusa con
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 inventoryModuleService = container.resolve(
Modules.INVENTORY
)
const inventoryItems = await inventoryModuleService.listInventoryItems({})
})
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { IInventoryService } 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 inventoryModuleService: IInventoryService = request.scope.resolve(
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
@@ -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 { IInventoryService } 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 inventoryModuleService: IInventoryService = container.resolve(
const inventoryModuleService = container.resolve(
Modules.INVENTORY
)
const inventoryItems = await inventoryModuleService.listInventoryItems({})
}
```
</CodeTab>
<CodeTab label="Workflow Step" value="workflow-step">
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { IInventoryService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const inventoryModuleService: IInventoryService = container.resolve(
Modules.INVENTORY
)
const inventoryItems = await inventoryModuleService.listInventoryItems({})
})
```
</CodeTab>
@@ -93,7 +90,9 @@ const inventoryItem = await inventoryModuleService.createInventoryItems({
### Inventory Across Locations
Inventory items' quantities are set per locations through inventory levels. This gives you more flexibility in managing the quantity of a stock-kept item across different locations, such as different warehouses.
Inventory items' quantities are set per locations through inventory levels.
This gives you more flexibility in managing the quantity of a stock-kept item across different locations, such as different warehouses.
```ts
const inventoryLevel = await inventoryModuleService.createInventoryLevels([
@@ -107,7 +106,9 @@ const inventoryLevel = await inventoryModuleService.createInventoryLevels([
### Reservation Management
Reserve quantities of inventory items at specific locations for orders or other purposes. The reserved quantity isn't considered for purchase, but can be deleted to revert the reservation.
Reserve quantities of inventory items at specific locations for orders or other purposes.
The reserved quantity isn't considered for purchase, but can be deleted to revert the reservation.
```ts
const reservationItem = await inventoryModuleService.createReservationItems([
@@ -121,7 +122,9 @@ const reservationItem = await inventoryModuleService.createReservationItems([
### Check Inventory Availability
Check whether an inventory item has the necessary quantity for purchase. Any reserved quantity is considered unavailable.
Check whether an inventory item has the necessary quantity for purchase.
Any reserved quantity is considered unavailable.
```ts
const isAvailable = await inventoryModuleService.confirmInventory(

View File

@@ -1,25 +0,0 @@
export const metadata = {
title: `Relations between Inventory Module and Other Modules`,
}
# {metadata.title}
This document showcases the link modules defined between the Inventory Module and other commerce modules.
## Product Module
Each product variant has different inventory details. Medusa defines a link module that builds a relationship between the `ProductVariant` and `InventoryItem` data models.
![A diagram showcasing an example of how data models from the Inventory and Product Module are linked.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709658720/Medusa%20Resources/inventory-product_ejnray.jpg)
A product variant, whose `manage_inventory` property is enabled, has an associated inventory item. Through that inventory's items relations in the Inventory Module, you can manage and check the variant's inventory quantity.
---
## Stock Location Module
Reservation items and inventory items are associated with a location. The Medusa application forms a relation between the `ReservationItem` and `StockLocation` data models, and the `InventoryLevel` and `StockLocation` data models.
![A diagram showcasing an example of how data models from the Inventory and Stock Location modules are linked.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709660383/Medusa%20Resources/inventory-stock-location_yp26k3.jpg)
The stock location provides address details of the location.