docs: create docs workspace (#5174)

* docs: migrate ui docs to docs universe

* created yarn workspace

* added eslint and tsconfig configurations

* fix eslint configurations

* fixed eslint configurations

* shared tailwind configurations

* added shared ui package

* added more shared components

* migrating more components

* made details components shared

* move InlineCode component

* moved InputText

* moved Loading component

* Moved Modal component

* moved Select components

* Moved Tooltip component

* moved Search components

* moved ColorMode provider

* Moved Notification components and providers

* used icons package

* use UI colors in api-reference

* moved Navbar component

* used Navbar and Search in UI docs

* added Feedback to UI docs

* general enhancements

* fix color mode

* added copy colors file from ui-preset

* added features and enhancements to UI docs

* move Sidebar component and provider

* general fixes and preparations for deployment

* update docusaurus version

* adjusted versions

* fix output directory

* remove rootDirectory property

* fix yarn.lock

* moved code component

* added vale for all docs MD and MDX

* fix tests

* fix vale error

* fix deployment errors

* change ignore commands

* add output directory

* fix docs test

* general fixes

* content fixes

* fix announcement script

* added changeset

* fix vale checks

* added nofilter option

* fix vale error
This commit is contained in:
Shahed Nasser
2023-09-21 20:57:15 +03:00
committed by GitHub
parent 19c5d5ba36
commit fa7c94b4cc
3209 changed files with 32188 additions and 31018 deletions
@@ -0,0 +1,781 @@
---
description: 'Learn how to manage inventory items using the admin APIs. This includes how to manage inventory items and inventory levels.'
addHowToData: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# How to Manage Inventory Items
In this document, youll learn how to manage inventory items using the admin APIs.
## Overview
Using the inventory items admin REST APIs, you can manage inventory items and inventory levels in your store.
### Scenario
You want to add or use the following admin functionalities:
- Manage inventory items. This includes listing, creating, updating, and deleting items.
- Manage inventory levels. This includes creating, updating, and deleting inventory levels.
---
## Prerequisites
### Medusa Components
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow the [quickstart guide](../../../development/backend/install.mdx) to get started.
### Required Module
This guide assumes you have the Inventory module installed on your Medusa backend. If not, you can learn how to install it using [this guide](../install-modules.md#inventory-module).
Furthermore, inventory levels are tied to a location ID. So, its recommended to use the [Stock Location module](../install-modules.md#stock-location-module) if you dont have any location logic implemented in place.
### JS Client
This guide includes code snippets to send requests to your Medusa backend using Medusas JS Client, among other methods.
If you follow the JS Client code blocks, its assumed you already have [Medusas JS Client](../../../js-client/overview.md) installed and have [created an instance of the client](../../../js-client/overview.md#configuration).
### Medusa React
This guide also includes code snippets to send requests to your Medusa backend using Medusa React, among other methods.
If you follow the Medusa React code blocks, it's assumed you already have [Medusa React installed](../../../medusa-react/overview.mdx) and have [used MedusaProvider higher in your component tree](../../../medusa-react/overview.mdx#usage).
### Authenticated Admin User
You must be an authenticated admin user before following along with the steps in the tutorial.
You can learn more about [authenticating as an admin user in the API reference](https://docs.medusajs.com/api/admin#authentication).
---
## List Inventory Items
You can list inventory items by sending a request to the [List Inventory Items endpoint](https://docs.medusajs.com/api/admin#inventory-items_getinventoryitems):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.list()
.then(({ inventory_items }) => {
console.log(inventory_items.length)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminInventoryItems } from "medusa-react"
function InventoryItems() {
const {
inventory_items,
isLoading } = useAdminInventoryItems()
return (
<div>
{isLoading && <span>Loading...</span>}
{inventory_items && !inventory_items.length && (
<span>No Items</span>
)}
{inventory_items && inventory_items.length > 0 && (
<ul>
{inventory_items.map(
(item) => (
<li key={item.id}>{item.id}</li>
)
)}
</ul>
)}
</div>
)
}
export default InventoryItems
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/inventory-items`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ inventory_items }) => {
console.log(inventory_items.length)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/inventory-items' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint does not require any path or query parameters. You can, however, pass path parameters to search or filter inventory items. For example, you can get inventory items in a specific location by passing the `location_id` query parameter. You can learn more about available query parameters in the [API reference](https://docs.medusajs.com/api/admin#inventory-items_getinventoryitems).
This request returns an array of inventory item objects.
---
## Create an Inventory Item
:::tip
Inventory items are automatically created when a variant is created with `manage_inventory` set to `true` or updated to enable the `manage_inventory` attribute. So, in general cases, you dont need to create an inventory item manually.
:::
You can create an inventory item by sending a request to the [Create Inventory Item endpoint](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitems):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.create({
variant_id,
})
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminCreateInventoryItem } from "medusa-react"
const CreateInventoryItem = () => {
const createInventoryItem = useAdminCreateInventoryItem()
// ...
const handleCreate = () => {
createInventoryItem.mutate({
variant_id,
})
}
// ...
}
export default CreateInventoryItem
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/inventory-items`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
variant_id,
}),
})
.then((response) => response.json())
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/inventory-items' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"variant_id": "variant_123"
}'
```
</TabItem>
</Tabs>
This endpoint requires in the body parameter the `variant_id` parameter, which is the ID of the variant to create this inventory item for. You can also pass other inventory-related parameters, such as `sku`. You can learn more about other available parameters in the [API reference](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitems).
This request returns the created inventory item as an object.
---
## Retrieve Inventory Item
You can retrieve an inventory item by sending a request to the [Get Inventory Item endpoint](https://docs.medusajs.com/api/admin#inventory-items_getinventoryitemsinventoryitem):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.retrieve(inventoryItemId)
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminInventoryItem } from "medusa-react"
function InventoryItem() {
const {
inventory_item,
isLoading } = useAdminInventoryItem(inventoryItemId)
return (
<div>
{isLoading && <span>Loading...</span>}
{inventory_item && (
<span>{inventory_item.sku}</span>
)}
</div>
)
}
export default InventoryItem
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/inventory-items/${inventoryItemId}`,
{
credentials: "include",
}
)
.then((response) => response.json())
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/inventory-items/<ITEM_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint accepts the ID of the inventory item as a path parameter. You can also path query parameters such as [expand](https://docs.medusajs.com/api/admin#expanding-fields) and [fields](https://docs.medusajs.com/api/admin#selecting-fields).
The request returns the inventory item as an object.
---
## Update Inventory Item
You can update an inventory item by sending a request to the [Update Inventory Item endpoint](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsinventoryitem):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.update(inventoryItemId, {
origin_country: "US",
})
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminUpdateInventoryItem } from "medusa-react"
const UpdateInventoryItem = () => {
const updateInventoryItem = useAdminUpdateInventoryItem(
inventoryItemId
)
// ...
const handleUpdate = () => {
updateInventoryItem.mutate({
origin_country: "US",
})
}
// ...
}
export default UpdateInventoryItem
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/inventory-items/${inventoryItemId}`,
{
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
origin_country: "US",
}),
}
)
.then((response) => response.json())
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/inventory-items/<ITEM_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"origin_country": "US"
}'
```
</TabItem>
</Tabs>
This endpoint requires the inventory items ID as a path parameter. You can pass any of the inventory items attributes that you want to update in its body parameter. The example above updates the value of the `origin_country` attribute. You can learn more about available body parameters in the [API reference](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsinventoryitem).
The request returns the updated inventory item as an object.
---
## Manage Inventory levels
This section shows you the different ways you can manage inventory levels. Each location level is associated with an inventory item.
### List inventory levels
You can list inventory levels of an inventory item by sending a request to the [List inventory levels endpoint](https://docs.medusajs.com/api/admin#inventory-items_getinventoryitemsinventoryitemlocationlevels):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.listLocationLevels(inventoryItemId)
.then(({ inventory_item }) => {
console.log(inventory_item.location_levels)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import {
useAdminInventoryItemLocationLevels,
} from "medusa-react"
function InventoryItem() {
const {
inventory_item,
isLoading,
} = useAdminInventoryItemLocationLevels(inventoryItemId)
return (
<div>
{isLoading && <span>Loading...</span>}
{inventory_item && (
<ul>
{inventory_item.location_levels.map((level) => (
<span key={level.id}>{level.stocked_quantity}</span>
))}
</ul>
)}
</div>
)
}
export default InventoryItem
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/inventory-items/${inventoryItemId}/location-levels`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ inventory_item }) => {
console.log(inventory_item.location_levels)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/inventory-items/<ITEM_ID>/location-levels' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint requires the ID of the inventory item as a path parameter. You can also pass [expand](https://docs.medusajs.com/api/admin#expanding-fields) and [fields](https://docs.medusajs.com/api/admin#selecting-fields) query parameters.
The request returns the inventory item as an object. In that object, the list of inventory levels are available under the property `location_levels`.
### Create Inventory Level
You can create a location level by sending a request to the [Create Inventory Level endpoint](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsinventoryitemlocationlevels):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.createLocationLevel(
inventoryItemId,
{
location_id,
stocked_quantity: 10,
}
)
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminCreateLocationLevel } from "medusa-react"
const CreateLocationLevel = () => {
const createLocationLevel = useAdminCreateLocationLevel(
inventoryItemId
)
// ...
const handleCreate = () => {
createLocationLevel.mutate({
location_id,
stocked_quantity: 10,
})
}
// ...
}
export default CreateLocationLevel
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/inventory-items/${inventoryItemId}/location-levels`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
location_id,
stocked_quantity: 10,
}),
})
.then((response) => response.json())
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/inventory-items/<ITEM_ID>/location-levels' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"location_id": "<LOCATION_ID>",
"stocked_quantity": 10
}'
```
</TabItem>
</Tabs>
This endpoint requires the inventory item ID as a path parameter. In the request body, it requires the following parameters:
- `location_id`: The ID of the location associated with this location level. This ID is typically available through using the stock location module.
- `stocked_quantity`: A number indicating the items quantity in stock.
You can also pass other optional request body parameters, as explained in the [API reference](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsinventoryitemlocationlevels).
This request returns the inventory item associated with the created location level.
### Update Location Level
You can update a location level by sending a request to the [Update Location Level endpoint](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsinventoryitemlocationlevelslocationlevel):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.updateLocationLevel(
inventoryItemId,
locationId,
{
stocked_quantity: 15,
}
)
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminUpdateLocationLevel } from "medusa-react"
const UpdateLocationLevel = () => {
const updateLocationLevel = useAdminUpdateLocationLevel(
inventoryItemId
)
// ...
const handleUpdate = () => {
updateLocationLevel.mutate({
stockLocationId,
stocked_quantity: 10,
})
}
// ...
}
export default UpdateLocationLevel
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/inventory-items/${inventoryItemId}/location-levels/${locationId}`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
stocked_quantity: 10,
}),
})
.then((response) => response.json())
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/inventory-items/<ITEM_ID>/location-levels/<LOCATION_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"stocked_quantity": 10
}'
```
</TabItem>
</Tabs>
This endpoint requires two path parameters: the first one being the ID of the inventory item, and the second one being the ID of the location.
In the body, you can optionally pass any of the location levels attributes to update. In the example above, you update the `stocked_quantity` attribute of the location level.
The request returns the inventory item associated with the location level as an object.
### Delete Location Level
You can delete a location level of an inventory item by sending a request to the [Delete Location Level endpoint](https://docs.medusajs.com/api/admin#inventory-items_deleteinventoryitemsinventoryitelocationlevelslocation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.deleteLocationLevel(
inventoryItemId,
locationId
)
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminDeleteLocationLevel } from "medusa-react"
const DeleteLocationLevel = () => {
const deleteLocationLevel = useAdminDeleteLocationLevel(
inventoryItemId
)
// ...
const handleDelete = () => {
deleteLocationLevel.mutate(locationId)
}
// ...
}
export default DeleteLocationLevel
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/inventory-items/${inventoryItemId}/location-levels/${locationId}`, {
credentials: "include",
method: "DELETE",
})
.then((response) => response.json())
.then(({ inventory_item }) => {
console.log(inventory_item.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X DELETE '<BACKEND_URL>/admin/inventory-items/<ITEM_ID>/location-levels/<LOC_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint requires two path parameters: the first one being the inventory items ID and the second one being the location levels ID.
The request returns the inventory item as an object.
---
## Delete Inventory Item
You can delete an inventory item by sending a request to the [Delete Inventory Item endpoint](https://docs.medusajs.com/api/admin#inventory-items_deleteinventoryitemsinventoryitem):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.inventoryItems.delete(inventoryItemId)
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminDeleteInventoryItem } from "medusa-react"
const DeleteInventoryItem = () => {
const deleteInventoryItem = useAdminDeleteInventoryItem(
inventoryItemId
)
// ...
const handleDelete = () => {
deleteInventoryItem.mutate()
}
// ...
}
export default DeleteInventoryItem
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/inventory-items/${inventoryItemId}`,
{
credentials: "include",
method: "DELETE",
}
)
.then((response) => response.json())
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X DELETE '<BACKEND_URL>/admin/inventory-items/<ITEM_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint requires the inventory items ID be passed as a path parameter.
It returns the following fields in the response:
- `id`: The ID of the inventory item.
- `object`: The type of object that was deleted. In this case, the value will be `inventory_item`.
- `deleted`: A boolean value indicating whether the inventory item was deleted successfully.
---
## See Also
- [How to manage stock locations](./manage-stock-locations.mdx)
- [How to manage item allocations in orders](./manage-item-allocations-in-orders.mdx)
@@ -0,0 +1,689 @@
---
description: 'Learn how to manage item allocations in orders using the admin APIs. This includes how to manage reservations and specify a location for fulfillments and returns.'
addHowToData: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# How to Manage Item Allocations in Orders
In this document, youll learn how to manage item allocations in orders using the admin APIs.
:::note
The terms “item allocation” and “reservation” are synonymous and are used interchangeably throughout this guide.
:::
## Overview
When an order is placed, the ordered quantity is reserved from the stocked quantity of each product variant having `manage_inventory` enabled. The reserved quantity is allocated to a random location associated with the orders sales channel, but can also be changed afterward.
When a fulfillment is created, a location can be specified to fulfill the item from. This will deduct the stocked quantity and reset the reserved quantity in the chosen location based on the quantity chosen for fulfillment.
When a return is requested, a location can be specified to return the item to. The will increment the stocked quantity in the chosen location based on the returned quantity.
![Item Allocation Diagram](https://res.cloudinary.com/dza7lstvk/image/upload/v1680772185/Medusa%20Docs/Diagrams/item-allocation-diagram_bu6hqs.jpg)
### Scenario
You want to add or use the following admin functionalities:
- Manage an item allocation for any resource, and not just for an item in an order. This includes listing, updating, or deleting item allocation.
- Specify location when creating fulfillment.
- Specify location when creating a return. This also applies to returns that are part of a swap or a claim.
---
## Prerequisites
### Medusa Components
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow the [quickstart guide](../../../development/backend/install.mdx) to get started.
### Required Module
This guide assumes you have a stock location and inventory modules installed. You can use Medusas [Stock Location and Inventory modules](../install-modules.md) or create your own modules.
### JS Client
This guide includes code snippets to send requests to your Medusa backend using Medusas JS Client, among other methods.
If you follow the JS Client code blocks, its assumed you already have [Medusas JS Client](../../../js-client/overview.md) installed and have [created an instance of the client](../../../js-client/overview.md#configuration).
### Medusa React
This guide also includes code snippets to send requests to your Medusa backend using Medusa React, among other methods.
If you follow the Medusa React code blocks, it's assumed you already have [Medusa React installed](../../../medusa-react/overview.mdx) and have [used MedusaProvider higher in your component tree](../../../medusa-react/overview.mdx#usage).
### Authenticated Admin User
You must be an authenticated admin user before following along with the steps in the tutorial.
You can learn more about [authenticating as an admin user in the API reference](https://docs.medusajs.com/api/admin#authentication).
---
## Manage Item Allocations in an Order
Using the reservations admin REST APIs, you can create an item allocation or reservation for any resource, and not just items in an order. This gives you more options in how you handle item allocations.
In this guide, however, the focus will be on how to use these endpoints for order-related functionalities.
### Create Item Allocation
Item allocations are created automatically for items that are associated with product variants having the `manage_inventory` attribute enabled. You typically dont need to create an item allocation, unless you delete the previous item allocation.
You can create an item allocation by sending a request to the [Create a Reservation endpoint](https://docs.medusajs.com/api/admin#reservations_postreservations):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.create({
line_item_id,
location_id,
inventory_item_id,
quantity,
})
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminCreateReservation } from "medusa-react"
const CreateReservation = () => {
const createReservation = useAdminCreateReservation()
// ...
const handleCreate = () => {
createReservation.mutate({
line_item_id,
location_id,
inventory_item_id,
quantity,
})
}
// ...
}
export default CreateReservation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/reservations`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
line_item_id,
location_id,
inventory_item_id,
quantity,
}),
})
.then((response) => response.json())
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/reservations' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"line_item_id": "<LINE_ITEM_ID>",
"location_id": "<LOC_ID>",
"inventory_item_id": "<INV_ITEM_ID>",
"quantity": 1
}'
```
</TabItem>
</Tabs>
This endpoint requires the following body parameters:
- `line_item_id`: The ID of the orders line item the allocation is being created for.
- `location_id`: The ID of the location the item is being allocated from.
- `inventory_item_id`: The ID of the inventory item the line items variant is associated with.
- `quantity`: The quantity to allocate.
The request returns the created reservation as an object.
### List Item Allocations
When listing item allocations, by default, youll be retrieving all item allocations in your commerce system. You can, however, provide optional fields to filter the item allocations retrieved.
You can retrieve the item allocations of a line item in an order using the [List Reservations endpoint](https://docs.medusajs.com/api/admin#reservations_getreservations):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.list({
line_item_id,
})
.then(({ reservations, count, limit, offset }) => {
console.log(reservations.length)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminReservations } from "medusa-react"
function Reservations() {
const { reservations, isLoading } = useAdminReservations({
line_item_id,
})
return (
<div>
{isLoading && <span>Loading...</span>}
{reservations && !reservations.length && (
<span>No Reservations</span>
)}
{reservations && reservations.length > 0 && (
<ul>
{reservations.map((reservation) => (
<li key={reservation.id}>{reservation.quantity}</li>
))}
</ul>
)}
</div>
)
}
export default Reservations
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/reservations?line_item_id=${lineItemId}`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ reservations, count, limit, offset }) => {
console.log(reservations.length)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/reservations?line_item_id=<LINE_ITEM_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint does not require any path or query parameters. As mentioned earlier, you can pass query parameters to filter the reservations. In the code snippets above, you filter the reservations by a line item ID. You can, however, filter by other attributes, such as the ID of the location. You can refer to the [API reference](https://docs.medusajs.com/api/admin#reservations_getreservations) for a full list of query parameters.
The request returns the reservations along with [pagination fields](https://docs.medusajs.com/api/admin#pagination).
### Retrieve Item Allocation
You can retrieve a single item allocation by its ID using the [Get a Reservation endpoint](https://docs.medusajs.com/api/admin#reservations_getreservationsreservation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.retrieve(reservationId)
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminReservation } from "medusa-react"
function Reservation() {
const {
reservation,
isLoading } = useAdminReservation(reservationId)
return (
<div>
{isLoading && <span>Loading...</span>}
{reservation && (
<span>{reservation.quantity}</span>
)}
</div>
)
}
export default Reservation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/reservations/${reservationId}`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/reservations/<RESERVATION_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint requires the reservations ID as a path parameter.
The request returns the reservation as an object.
### Update Item Allocation
You can update an item allocation to change the location to allocate from or the quantity to allocate by sending a request to the [Update Reservation endpoint](https://docs.medusajs.com/api/admin#reservations_postreservationsreservation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.update(reservationId, {
quantity,
})
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminUpdateReservation } from "medusa-react"
const UpdateReservation = () => {
const updateReservation = useAdminUpdateReservation(
reservationId
)
// ...
const handleCreate = () => {
updateReservation.mutate({
quantity,
})
}
// ...
}
export default UpdateReservation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/reservations/${reservationId}`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
quantity,
}),
})
.then((response) => response.json())
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/reservations/<RESERVATION_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"quantity": 3
}'
```
</TabItem>
</Tabs>
This endpoint requires the ID of the reservation as a path parameter.
In the request body parameters, you can optionally pass any of the following parameters to make updates to the reservation:
- `quantity`: The quantity that should be reserved.
- `location_id`: The ID of the location that the item should be allocated from.
- `metadata`: set or change the reservations metadata.
The request returns the updated reservation as an object.
### Delete Item Allocation
Deleting an item allocation means that the quantity that was previously reserved is no longer reserved.
You can delete an item allocation by sending a request to the [Delete Reservation endpoint](https://docs.medusajs.com/api/admin#reservations_deletereservationsreservation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.delete(reservationId)
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminDeleteReservation } from "medusa-react"
const DeleteReservation = () => {
const deleteReservation = useAdminDeleteReservation(
reservationId
)
// ...
const handleDelete = () => {
deleteReservation.mutate()
}
// ...
}
export default DeleteReservation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/reservations/${reservationId}`, {
credentials: "include",
method: "DELETE",
})
.then((response) => response.json())
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X DELETE '<BACKEND_URL>/admin/reservations/<RESERVATION_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint requires the reservation ID to be passed as a path parameter.
The request returns the following fields:
- `id`: The ID of the reservation.
- `object`: The type of object that was removed. In this case, the value will be `reservation`.
- `deleted`: A boolean value indicating whether the reservation was successfully deleted.
---
## Specify Location when Creating Fulfillment
When you create a fulfillment of an order, you can specify the location to fulfill the item from by passing the `location_id` parameter:
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.orders.createFulfillment(orderId, {
items: [
{
item_id,
quantity,
},
],
// ...other parameters
location_id,
})
.then(({ order }) => {
console.log(order.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminCreateFulfillment } from "medusa-react"
const CreateFulfillment = () => {
const createFulfillment = useAdminCreateFulfillment(orderId)
// ...
const handleCreate = () => {
createFulfillment.mutate({
items: [
{
item_id,
quantity,
},
],
// ...other parameters
location_id,
})
}
// ...
}
export default CreateFulfillment
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/orders/${orderId}/fulfillment`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [
{
item_id,
quantity,
},
],
// ...other parameters
location_id,
}),
})
.then((response) => response.json())
.then(({ order }) => {
console.log(order.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/orders/<ORDER_ID>/fulfillment' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"items": [
{
"item_id": "<LINE_ITEM_ID>",
"quantity": 1
}
],
"location_id": "<LOC_ID>"
}'
```
</TabItem>
</Tabs>
The `location_id` is an optional parameter that allows you to specify where to fulfill the item from. This subsequently decrements the stock quantity of the product variant in that location.
You can learn more about this endpoints parameters and response in the [API reference](https://docs.medusajs.com/api/admin#orders_postordersorderfulfillments).
---
## Specify Location when Requesting Return
When requesting a return, you can specify the location to return the item to by passing the `location_id` parameter:
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.orders.requestReturn(orderId, {
items: [
{
item_id,
quantity: 1,
},
],
// other parameters...
location_id,
})
.then(({ order }) => {
console.log(order.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminRequestReturn } from "medusa-react"
const RequestReturn = () => {
const requestReturn = useAdminRequestReturn(orderId)
// ...
const handleRequest = () => {
requestReturn.mutate({
items: [
{
item_id,
quantity,
},
],
// ...other parameters
location_id,
})
}
// ...
}
export default RequestReturn
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/orders/${orderId}/return`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [
{
item_id,
quantity,
},
],
// ...other parameters
location_id,
}),
})
.then((response) => response.json())
.then(({ order }) => {
console.log(order.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/orders/<ORDER_ID>/return' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"items": [
{
"item_id": "<LINE_ITEM_ID>",
"quantity": 1
}
],
"location_id": "<LOC_ID>"
}'
```
</TabItem>
</Tabs>
The `location_id` is an optional parameter that allows you to specify where to return the item to. This subsequently increments the stock quantity of the product variant in that location.
You can learn more about this endpoints parameters and response in [the API reference](https://docs.medusajs.com/api/admin#orders_postordersorderreturns).
---
## See Also
- [How to manage inventory items](./manage-inventory-items.mdx)
- [How to manage stock locations](./manage-stock-locations.mdx)
@@ -0,0 +1,401 @@
---
description: 'Learn how to manage custom reservations of a product variant using the admin APIs. This includes how to list, create, update, and delete reservations.'
addHowToData: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# How to Manage Custom Reservations
In this document, youll learn how to manage custom reservations of a product variant using the admin APIs.
:::tip
Although this guide covers how to manage custom reservations of product variants, you can create custom reservations for any entity that is associated with an Inventory Item.
:::
## Overview
When an order is created, reservations are created for the items in that order automatically. However, Medusa also provides the capability to create custom reservations that arent related to any order. Custom reservations allow you to allocate quantities of a product variant manually. You can do that using the Reservations admin APIs.
The functionalities in this guide apply to all types of reservations, including those associated with an order and those that arent.
### Scenario
You want to add or use the following admin functionalities:
- List reservations, including custom reservations or those associated with orders.
- Manage a reservation, including creating, updating, and deleting a reservation.
:::note
You can check the [API reference](https://docs.medusajs.com/api/admin) for all available Reservations endpoints.
:::
---
## Prerequisites
### Medusa Components
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow the [quickstart guide](../../../development/backend/install.mdx) to get started.
### Required Module
This guide assumes you have a stock location and inventory modules installed. You can use Medusas [Stock Location and Inventory modules](../install-modules.md) or create your own modules.
### JS Client
This guide includes code snippets to send requests to your Medusa backend using Medusas JS Client, among other methods.
If you follow the JS Client code blocks, its assumed you already have [Medusas JS Client](../../../js-client/overview.md) installed and have [created an instance of the client](../../../js-client/overview.md#configuration).
### Medusa React
This guide also includes code snippets to send requests to your Medusa backend using Medusa React, among other methods.
If you follow the Medusa React code blocks, it's assumed you already have [Medusa React installed](../../../medusa-react/overview.mdx) and have [used MedusaProvider higher in your component tree](../../../medusa-react/overview.mdx#usage).
### Authenticated Admin User
You must be an authenticated admin user before following along with the steps in the tutorial.
You can learn more about [authenticating as an admin user in the API reference](https://docs.medusajs.com/api/admin#authentication).
---
## List Reservations
You can list all reservations in your store by sending a request to the [List Reservations endpoint](https://docs.medusajs.com/api/admin#reservations_getreservations):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.list()
.then(({ reservations, limit, count, offset }) => {
console.log(reservations.length)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminReservations } from "medusa-react"
const Reservations = () => {
const { reservations, isLoading } = useAdminReservations()
return (
<div>
{isLoading && <span>Loading...</span>}
{reservations && !reservations.length && (
<span>No Reservations</span>
)}
{reservations && reservations.length > 0 && (
<ul>
{reservations.map((reservation) => (
<li key={reservation.id}>{reservation.quantity}</li>
))}
</ul>
)}
</div>
)
}
export default Reservations
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/reservations`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ reservations, limit, count, offset }) => {
console.log(reservations.length)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/reservations' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint does not require any query or path parameters. You can pass it query parameters for filtering or pagination purposes. Check out the [API reference](https://docs.medusajs.com/api/admin#reservations_getreservations) for a list of accepted query parameters.
This endpoint returns an array of reservations along with [pagination parameters](https://docs.medusajs.com/api/admin#pagination).
---
## Create a Reservation
:::note
Before you create a reservation for a product variant, make sure youve created an inventory item for that variant. You can learn how to do that in [this guide](./manage-inventory-items.mdx#create-an-inventory-item).
:::
You can create a reservation by sending a request to the [Create Reservation endpoint](https://docs.medusajs.com/api/admin#reservations_postreservations):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.create({
location_id,
inventory_item_id,
quantity,
})
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminCreateReservation } from "medusa-react"
const CreateReservation = () => {
const createReservation = useAdminCreateReservation()
// ...
const handleCreate = () => {
createReservation.mutate({
location_id,
inventory_item_id,
quantity,
})
}
// ...
}
export default CreateReservation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/reservations`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
location_id,
inventory_item_id,
quantity,
}),
})
.then((response) => response.json())
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/reservations' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"location_id": "<LOC_ID>",
"inventory_item_id": "<INV_ITEM_ID>",
"quantity": 1
}'
```
</TabItem>
</Tabs>
This endpoint requires the following body parameters:
- `location_id`: The ID of the location the reservation is created in.
- `inventory_item_id`: The ID of the inventory item the product variant is associated with.
- `quantity`: The quantity to allocate.
The request returns the created reservation as an object.
---
## Update a Reservation
You can update a reservation by sending a request to the [Update Reservation endpoint](https://docs.medusajs.com/api/admin#reservations_postreservationsreservation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.update(reservationId, {
quantity,
})
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminUpdateReservation } from "medusa-react"
const UpdateReservation = () => {
const updateReservation = useAdminUpdateReservation(
reservationId
)
// ...
const handleCreate = () => {
updateReservation.mutate({
quantity,
})
}
// ...
}
export default UpdateReservation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/reservations/${reservationId}`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
quantity,
}),
})
.then((response) => response.json())
.then(({ reservation }) => {
console.log(reservation.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/reservations/<RESERVATION_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"quantity": 3
}'
```
</TabItem>
</Tabs>
This endpoint requires the ID of the reservation as a path parameter.
In the request body parameters, you can optionally pass any of the following parameters to make updates to the reservation:
- `quantity`: The quantity that should be reserved.
- `location_id`: The ID of the location that the product variant should be allocated from.
- `metadata`: set or change the reservations metadata.
The request returns the updated reservation as an object.
---
## Delete a Reservation
You can delete a reservation by sending a request to the [Delete Reservation endpoint](https://docs.medusajs.com/api/admin#reservations_deletereservationsreservation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.reservations.delete(reservationId)
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminDeleteReservation } from "medusa-react"
const DeleteReservation = () => {
const deleteReservation = useAdminDeleteReservation(
reservationId
)
// ...
const handleDelete = () => {
deleteReservation.mutate()
}
// ...
}
export default DeleteReservation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/reservations/${reservationId}`, {
credentials: "include",
method: "DELETE",
})
.then((response) => response.json())
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X DELETE '<BACKEND_URL>/admin/reservations/<RESERVATION_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint requires the reservation ID to be passed as a path parameter.
The request returns the following fields:
- `id`: The ID of the reservation.
- `object`: The type of object that was removed. In this case, the value will be `reservation`.
- `deleted`: A boolean value indicating whether the reservation was successfully deleted.
---
## See Also
- [How to manage inventory items](./manage-inventory-items.mdx)
- [How to manage stock locations](./manage-stock-locations.mdx)
@@ -0,0 +1,620 @@
---
description: 'Learn how to manage stock locations using the admin APIs. This includes how to list, create, update, and delete stock locations.'
addHowToData: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# How to Manage Stock Locations
In this document, youll learn how to manage stock locations using the admin APIs.
## Overview
Using the stock locations admin REST APIs, you can manage stock locations in your store, including creating, updating, and deleting locations.
### Scenario
You want to add or use the following admin functionalities:
- List stock locations.
- Create a stock location.
- Retrieve a stock location.
- Associate a stock location with a sales channel, and remove the association.
- Update a stock location.
- Delete a stock location.
---
## Prerequisites
### Medusa Components
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow the [quickstart guide](../../../development/backend/install.mdx) to get started.
### Required Module
This guide assumes you have a stock location module installed. You can use Medusas [Stock Location module](../install-modules.md#stock-location-module) or [create your own module](../backend/create-stock-location-service.md).
### JS Client
This guide includes code snippets to send requests to your Medusa backend using Medusas JS Client, among other methods.
If you follow the JS Client code blocks, its assumed you already have [Medusas JS Client](../../../js-client/overview.md) installed and have [created an instance of the client](../../../js-client/overview.md#configuration).
### Medusa React
This guide also includes code snippets to send requests to your Medusa backend using Medusa React, among other methods.
If you follow the Medusa React code blocks, it's assumed you already have [Medusa React installed](../../../medusa-react/overview.mdx) and have [used MedusaProvider higher in your component tree](../../../medusa-react/overview.mdx#usage).
### Authenticated Admin User
You must be an authenticated admin user before following along with the steps in the tutorial.
You can learn more about [authenticating as an admin user in the API reference](https://docs.medusajs.com/api/admin#authentication).
---
## List Stock Locations
You can list stock locations by using the [List Stock Locations endpoint](https://docs.medusajs.com/api/admin#stock-locations_getstocklocations):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.stockLocations.list()
.then(({ stock_locations, limit, offset, count }) => {
console.log(stock_locations.length)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminStockLocations } from "medusa-react"
function StockLocations() {
const {
stock_locations,
isLoading } = useAdminStockLocations()
return (
<div>
{isLoading && <span>Loading...</span>}
{stock_locations && !stock_locations.length && (
<span>No Locations</span>
)}
{stock_locations && stock_locations.length > 0 && (
<ul>
{stock_locations.map(
(location) => (
<li key={location.id}>{location.name}</li>
)
)}
</ul>
)}
</div>
)
}
export default StockLocations
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/stock-locations`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ stock_locations, limit, offset, count }) => {
console.log(stock_locations.length)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/stock-locations' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint does not require any path or query parameters. You can, however, pass it query parameters to search or filter the list of stock locations. For example, you can pass the `q` query parameter to search through the locations by name. You can learn about available query parameters in the [API reference](https://docs.medusajs.com/api/admin#stock-locations_getstocklocations).
The request returns an array of stock location objects along with [pagination parameters](https://docs.medusajs.com/api/admin#pagination).
---
## Create a Stock Location
You can create a stock location using the [Create a Stock Location endpoint](https://docs.medusajs.com/api/admin#stock-locations_poststocklocations):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.stockLocations.create({
name: "Main Warehouse",
})
.then(({ stock_location }) => {
console.log(stock_location.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminCreateStockLocation } from "medusa-react"
const CreateStockLocation = () => {
const createStockLocation = useAdminCreateStockLocation()
// ...
const handleCreate = () => {
createStockLocation.mutate({
name: "Main Warehouse",
})
}
// ...
}
export default CreateStockLocation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/stock-locations`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Main Warehouse",
}),
})
.then((response) => response.json())
.then(({ stock_location }) => {
console.log(stock_location.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/stock-locations' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"name": "Main Warehouse"
}'
```
</TabItem>
</Tabs>
This endpoint requires in its body parameters the `name` field, which is the name of the stock location. You can also pass in the request body parameters other fields related to the address or metadata. You can learn more in the [API reference](https://docs.medusajs.com/api/admin#stock-locations_poststocklocations).
This request returns the created stock location as an object.
---
## Retrieve a Stock Location
You can retrieve a stock location by sending a request to the [Get Stock Location endpoint](https://docs.medusajs.com/api/admin#stock-locations_getstocklocationsstocklocation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.stockLocations.retrieve(stockLocationId)
.then(({ stock_location }) => {
console.log(stock_location.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminStockLocation } from "medusa-react"
function StockLocation() {
const {
stock_location,
isLoading } = useAdminStockLocation(stockLocationId)
return (
<div>
{isLoading && <span>Loading...</span>}
{stock_location && (
<span>{stock_location.name}</span>
)}
</div>
)
}
export default StockLocation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/stock-locations/${stockLocationId}`,
{
credentials: "include",
}
)
.then((response) => response.json())
.then(({ stock_location }) => {
console.log(stock_location.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/stock-locations/<LOC_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint requires the stock location ID to be passed as a path parameter. It also accepts query parameters related to expanding and selecting fields. You can learn more in the [API reference](https://docs.medusajs.com/api/admin#stock-locations_getstocklocationsstocklocation).
It returns the stock location as an object.
---
## Associate a Stock Location with a Sales Channel
You can associate a stock location with a sales channel by sending a request to the [Associate Stock Channel endpoint](https://docs.medusajs.com/api/admin#sales-channels_postsaleschannelssaleschannelstocklocation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.salesChannels.addLocation(salesChannelId, {
location_id,
})
.then(({ sales_channel }) => {
console.log(sales_channel.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminAddLocationToSalesChannel } from "medusa-react"
function StockLocation() {
const addLocation = useAdminAddLocationToSalesChannel()
// ...
const handleAdd = () => {
addLocation.mutate({
sales_channel_id,
location_id,
})
}
}
export default StockLocation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/sales-channels/${salesChannelId}/stock-locations`, {
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
location_id,
}),
})
.then((response) => response.json())
.then(({ sales_channel }) => {
console.log(sales_channel.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/sales-channels/<CHANNEL_ID>/stock-locations' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"location_id": "<LOC_ID>"
}'
```
</TabItem>
</Tabs>
This endpoint requires the ID of the sales channel as a path parameter. In its body parameters, it requires the ID of the stock location youre associating the sales channel with.
This request returns the sales channel object.
:::note
You can associate a location with more than one sales channel, and you can associate a sales channel with more than one location.
:::
---
## Remove Association Between Stock Location and Sales Channel
You can remove the association between a stock location and a sales channel by sending a request to the [Remove Stock Location Association endpoint](https://docs.medusajs.com/api/admin#sales-channels_deletesaleschannelssaleschannelstocklocation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.salesChannels.removeLocation(salesChannelId, {
location_id,
})
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import {
useAdminRemoveLocationFromSalesChannel,
} from "medusa-react"
function StockLocation() {
const removeLocation =
useAdminRemoveLocationFromSalesChannel()
// ...
const handleRemove = () => {
removeLocation.mutate({
sales_channel_id,
location_id,
})
}
}
export default StockLocation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/sales-channels/${salesChannelId}/stock-locations`, {
credentials: "include",
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
location_id,
}),
})
.then((response) => response.json())
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X DELETE '<BACKEND_URL>/admin/sales-channels/<CHANNEL_ID>/stock-locations' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"location_id": "<LOC_ID>"
}'
```
</TabItem>
</Tabs>
This endpoint requires the ID of the sales channel as a path parameter. In its body parameters, it requires the ID of the stock location youre removing the association of the sales channel with.
The request returns the following fields:
- `id`: The ID of the location.
- `object`: The type of object that was removed. In this case, the value will be `stock-location`.
- `deleted`: A boolean value indicating whether the association with the stock location was removed.
:::note
This request does not delete the stock location. It only removes the association between it and the specified sales channel.
:::
---
## Update a Stock Location
You can update a stock location by sending a request to the [Update Stock Location endpoint](https://docs.medusajs.com/api/admin#stock-locations_poststocklocationsstocklocation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.stockLocations.update(stockLocationId, {
name: "Warehouse",
})
.then(({ stock_location }) => {
console.log(stock_location.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminUpdateStockLocation } from "medusa-react"
function StockLocation() {
const updateLocation = useAdminUpdateStockLocation(
stockLocationId
)
// ...
const handleRemove = () => {
updateLocation.mutate({
name: "Warehouse",
})
}
}
export default StockLocation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/stock-locations/${stockLocationId}`,
{
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Warehouse",
}),
}
)
.then((response) => response.json())
.then(({ stock_location }) => {
console.log(stock_location.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/stock-locations/<LOC_ID>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"name": "Main Warehouse"
}'
```
</TabItem>
</Tabs>
This endpoint requires the ID of a stock location as a path parameter. In its body parameters, you can pass any of the locations attributes to update, such as the name or address. You can learn more in the [API reference](https://docs.medusajs.com/api/admin#stock-locations_poststocklocationsstocklocation).
This request returns the updated stock location as an object.
---
## Delete a Stock Location
You can delete a stock location by sending a request to the [Delete Stock Location endpoint](https://docs.medusajs.com/api/admin#stock-locations_deletestocklocationsstocklocation):
<Tabs groupId="request-type" isCodeTabs={true}>
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.stockLocations.delete(stockLocationId)
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminDeleteStockLocation } from "medusa-react"
function StockLocation() {
const deleteLocation = useAdminDeleteStockLocation(
stockLocationId
)
// ...
const handleDelete = () => {
deleteLocation.mutate()
}
}
export default StockLocation
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/stock-locations/${stockLocationId}`,
{
credentials: "include",
method: "DELETE",
}
)
.then((response) => response.json())
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X DELETE '<BACKEND_URL>/admin/stock-locations/<LOC_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This endpoint requires the ID of the stock location as a path parameter.
It returns the following fields in the response:
- `id`: The ID of the location.
- `object`: The type of object that was deleted. In this case, the value will be `stock_location`.
- `deleted`: A boolean value indicating whether the stock location was deleted successfully.
---
## See Also
- [How to manage inventory items](./manage-inventory-items.mdx)
- [How to manage item allocations in orders](./manage-item-allocations-in-orders.mdx)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,384 @@
---
description: 'Learn how to create a stock location service, which you can use in a custom stock location module in the Medusa backend.'
addHowToData: true
---
# How to Create a Stock Location Service
In this document, youll learn how to create a stock location service, which you can use in a custom stock location module in the Medusa backend.
## Overview
A stock location module is used in a commerce application, such as the Medusa backend, to handle functionalities related to the different locations a stock-kept item can be located in.
While Medusa provides a [stock-location module](../stock-location-module.md) that you can use in your Medusa backend, you can also create a custom module to handle these functionalities.
The module is expected to, at the very least, export the stock-location service. If necessary, you can include entities, migrations, and other resources as part of the export.
This guide will only explain what is required to create in your custom stock location service, and not the entities or other resources, as those you have the freedom to choose how to implement. You can refer to the [Modules documentation](../../../development/modules/create.mdx) for other details on how to create and use the module.
:::note
It should be noted that the Medusa backend expects the stock location module to have entities for a location and a location address, as it uses the IDs of those entities when orchestrating between different modules and the in the endpoints it exposes. You can learn more about this in the [Stock Location Module Architecture documentation](../stock-location-module.md).
:::
---
## Prerequisites
The `IStockLocationService` interface that youll be implementing is available in the `@medusajs/types` package. So, make sure to install it in your Medusa backend or the module project (depending on where youre adding your implementation):
```bash npm2yarn
npm install @medusajs/types
```
Youll also be using decorators in your methods that are imported from the `@medusajs/utils` package, so make sure to install it as well:
```bash npm2yarn
npm install @medusajs/utils
```
---
## Step 1: Implement the Stock Location Service
Create a file in the `src/services` directory that will hold your custom stock location service. For example, `src/services/stock-location.ts`.
In that file, add the following content:
```ts title=src/services/stock-location.ts
import {
CreateStockLocationInput,
FilterableStockLocationProps,
FindConfig,
IStockLocationService,
SharedContext,
StockLocationDTO,
UpdateStockLocationInput,
} from "@medusajs/types"
import {
InjectEntityManager,
MedusaContext,
} from "@medusajs/utils"
class StockLocationService implements IStockLocationService {
async list(
selector: FilterableStockLocationProps,
config?: FindConfig<StockLocationDTO> | undefined,
context?: SharedContext | undefined
): Promise<StockLocationDTO[]> {
throw new Error("Method not implemented.")
}
async listAndCount(
selector: FilterableStockLocationProps,
config?: FindConfig<StockLocationDTO> | undefined,
context?: SharedContext | undefined
): Promise<[StockLocationDTO[], number]> {
throw new Error("Method not implemented.")
}
async retrieve(
id: string,
config?: FindConfig<StockLocationDTO> | undefined,
context?: SharedContext | undefined
): Promise<StockLocationDTO> {
throw new Error("Method not implemented.")
}
async create(
input: CreateStockLocationInput,
context?: SharedContext | undefined
): Promise<StockLocationDTO> {
throw new Error("Method not implemented.")
}
async update(
id: string,
input: UpdateStockLocationInput,
context?: SharedContext | undefined
): Promise<StockLocationDTO> {
throw new Error("Method not implemented.")
}
async delete(
id: string,
context?: SharedContext | undefined
): Promise<void> {
throw new Error("Method not implemented.")
}
}
export default StockLocationService
```
This defines the class `StockLocationService` that implements the `IStockLocationService` service imported from the `@medusajs/types` package.
The following sections explain the different methods you need to implement.
### Using Method Decorators
For each of the methods, youll be using the following decorators:
1. `@InjectEntityManager`: Ensures that a transaction entity manager is always passed to the method. The transaction manager is useful when performing database operations.
2. `@MedusaContext`: Used on a parameter passed to a method having the `@InjectEntityManager` decorator. It indicates which parameter should include the injected transaction manager. When used on a `context` parameter as shown below, the context is no longer optional and you can always expect the transaction manager to be passed as a parameter.
To use these decorators, its recommended to include the following configurations in your `tsconfig.json` file:
```json
{
//other configurations
"compilerOptions": {
// other configurations
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
}
}
```
### Implementing list Method
The `list` method is used to retrieve a list of stock locations. It accepts the following parameters:
1. `selector`: This is the first parameter passed to the method, and the only required parameter. It is an object that has the following properties:
1. `id`: an optional string or array of strings indicating the IDs of locations. It is used to filter the retrieved locations by ID.
2. `name`: an optional string, array of strings, or a `StringComparisonOperator` object that is used to search and filter locations by their name. The `StringComparisonOperator` can have the following properties:
1. `lt`: indicates a string that the name should be less than.
2. `gt`: indicates a string that the name should be greater than.
3. `gte`: indicates a string that the name should be greater than or equal to.
4. `lte`: indicates a string the name should be less than or equal to.
2. `config`: This is the second parameter and it is an optional parameter. Its an object that can have any of the following optional properties:
1. `select`: an array of strings indicating the attributes in your location entity to retrieve.
2. `skip`: a number indicating how many location records to skip before retrieving the list.
3. `take`: a number indicating how many location records to retrieve.
4. `order`: an object indicating the order to retrieve the locations by. The order is specified per attribute. So, the attribute in your entity is the property of this object, and the value of the property is either `ASC` or `DESC`.
3. `context`: This is the third parameter and its an optional parameter. This parameter should be used to inject the transaction manager, as explained in the [Method Decorators section](#using-method-decorators). Its an object that can have any of the following optional properties:
1. `transactionManager`: the transaction manager to use to perform database operations.
This method is expected to return an array of objects of the following type:
```ts
type StockLocationDTO = {
id: string;
name: string;
metadata: Record<string, unknown> | null;
address_id: string;
address?: StockLocationAddressDTO;
created_at: string | Date;
updated_at: string | Date;
deleted_at: string | Date | null;
}
```
Heres an example implementation of the method:
```ts title=src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async list(
selector: FilterableStockLocationProps,
config?: FindConfig<StockLocationDTO> | undefined,
@MedusaContext() context?: SharedContext | undefined
): Promise<StockLocationDTO[]> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)
// TODO retrieve and return locations
// for example
return await locationRepo.find(selector)
}
}
```
This example shows how you can use the context to retrieve the transaction manager, then retrieve your repository that you will use to retrieve and return locations. Make sure to replace `CustomStockLocation` with your stock location entity.
### Implementing listAndCount Method
This method is similar to the list method, but it returns both the list of locations and a count of the locations.
It accepts the exact same parameters as the [list method](#implementing-list-method), but expects to return an array of two items. The first one being the list of locations, and the second one being the count of locations.
Heres an example implementation of the method:
```ts title=src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async listAndCount(
selector: FilterableStockLocationProps,
config?: FindConfig<StockLocationDTO> | undefined,
@MedusaContext() context?: SharedContext | undefined
): Promise<[StockLocationDTO[], number]> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)
// TODO retrieve and return locations
// for example
return await locationRepo.findAndCount(selector)
}
}
```
Make sure to replace `CustomStockLocation` with your stock location entity.
### Implementing retrieve Method
This method is used to retrieve a single location. It accepts the following parameters:
1. `id`: this is the first parameter and is required. Its a string indicating the ID of the location to retrieve.
2. `config`: This is the second parameter and an optional parameter. Its an object having the same properties as the `config` parameter mentioned in the [list method](#implementing-list-method).
3. `context`: This is the third parameter and an optional parameter. Its of the same type as the `context` parameter mentioned in the [list method](#implementing-list-method).
This method returns the location as an object.
For example:
```ts title=src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async retrieve(
id: string,
config?: FindConfig<StockLocationDTO> | undefined,
@MedusaContext() context?: SharedContext | undefined
): Promise<StockLocationDTO> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)
// TODO retrieve the location using its ID
// for example
const [location] = await locationRepo.find({
id,
})
return location
}
}
```
Make sure to replace `CustomStockLocation` with your stock location entity.
### Implementing create Method
This method is used to create a new location. It accepts the following parameters:
1. `input`: This is the first parameter, and its required. It's an object holding the following properties:
1. `name`: this property is required, and its value is the name of the location.
2. `address_id`: this property is optional, and its the ID of the address to associate with this location.
3. `address`: this property is optional, and its an object holding address properties including `address_1`, `country_code`, `city`, and more.
4. `metadata`: this property is optional, and its an object that should be stored in the `metadata` attribute of the created location.
2. `context`: This is the second parameter and an optional parameter. Its of the same type as the `context` parameter mentioned in the [list method](#implementing-list-method).
The method is expected to return the created location as an object.
For example:
```ts title=src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async create(
input: CreateStockLocationInput,
@MedusaContext() context?: SharedContext | undefined
): Promise<StockLocationDTO> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)
// TODO create the location and return it
// for example
return await locationRepo.create(input)
}
}
```
Make sure to replace `CustomStockLocation` with your stock location entity.
### Implementing update Method
This method is used to update a location by its ID. It accepts the following parameters:
1. `id`: this is the first parameter and is required. Its a string indicating the ID of the location to update.
2. `input`: this is the second parameter and is required. Its an object having any of the following optional properties:
1. `name`: a string indicating the name of the location.
2. `address_id`: the ID of the address to associate with this location.
3. `address`: an object holding address properties including `address_1`, `country_code`, `city`, and more.
4. `metadata`: an object that should be stored in the `metadata` attribute of the created location.
3. `context`: This is the third parameter and an optional parameter. Its of the same type as the `context` parameter mentioned in the [list method](#implementing-list-method).
This method is expected to return the updated location object.
For example:
```ts title=src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async update(
id: string,
input: UpdateStockLocationInput,
@MedusaContext() context?: SharedContext | undefined
): Promise<StockLocationDTO> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)
// TODO update the location and return it
// for example
await locationRepo.update(id, input)
return await this.retrieve(id)
}
}
```
Make sure to replace `CustomStockLocation` with your stock location entity.
### Implementing delete Method
This method is used to delete a location by its ID. It accepts the following parameters:
1. `id`: this is the first parameter and is required. Its a string indicating the ID of the location to delete.
2. `context`: This is the second parameter and an optional parameter. Its of the same type as the `context` parameter mentioned in the [list method](#implementing-list-method).
This method is not expected to return anything.
For example:
```ts title=src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async delete(
id: string,
@MedusaContext() context?: SharedContext | undefined
): Promise<void> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)
// TODO delete the location
// for example
await locationRepo.delete(id)
}
}
```
Make sure to replace `CustomStockLocation` with your stock location entity.
---
## Step 2: Use the Stock Location Service
After implementing your custom service along with any other necessary resources, you can test it out or use it in your Medusa backend. You can learn more about how to do that in the [Create Module documentation](../../../development/modules/create.mdx).
---
## See Also
- [How to create an inventory service](./create-inventory-service.md)
- [Stock location module architecture](../stock-location-module.md)
@@ -0,0 +1,103 @@
---
description: "In this document, youll learn how to install multi-warehouse related modules using NPM in the Medusa backend."
---
# Install Multi-Warehouse Modules
In this document, youll learn how to install multi-warehouse related modules using NPM in the Medusa backend.
:::tip
You can also install these modules in any NPM project.
:::
## Inventory Module
### Step 1: Install Inventory Module
To install the Inventory Module, run the following command in the root directory of the Medusa backend:
```bash npm2yarn
npm install @medusajs/inventory
```
### Step 2: Add Inventory Module to Configurations
In `medusa-config.js`, add the inventory module to the exported object under the `modules` property:
```js
module.exports = {
// ...
modules: {
// ...
inventoryService: {
resolve: "@medusajs/inventory",
},
},
}
```
### Step 3: Run Migrations of Inventory Module
Run the following command to reflect schema changes into your database:
```bash
npx medusa migrations run
```
### Step 4: Run Migration Script
After installing the Stock Location module, make sure to [run the migration script](#run-migration-script)
---
## Stock Location Module
### Step 1: Install Stock Location Module
To install the Stock Location Module, run the following command in the root directory of the Medusa backend:
```bash npm2yarn
npm install @medusajs/stock-location
```
### Step 2: Add Stock Location Module to Configurations
In `medusa-config.js`, add the stock location module to the exported object under the `modules` property:
```js
module.exports = {
// ...
modules: {
// ...
stockLocationService: {
resolve: "@medusajs/stock-location",
},
},
}
```
### Step 3: Run Migrations of Stock Location Module
Run the following command to reflect schema changes into your database:
```bash
npx medusa migrations run
```
### Step 4: Run Migration Script
After installing both modules, make sure to [run the migration script](#run-migration-script)
---
## Run Migration Script
After installing both modules, run the following command to migrate current product variant information to fit the schema changes introduced by the modules:
```bash
node ./node_modules/@medusajs/medusa/dist/scripts/migrate-inventory-items.js
```
You can now start the Medusa backend and use the stock location module in your commerce application.
@@ -0,0 +1,102 @@
---
description: "In this document, youll learn about the inventory module, how it works, and its relation to other processes in your commerce application."
---
# Inventory Module
In this document, youll learn about the inventory module and how it works.
## Overview
The inventory module includes all functionalities related to product inventory. It implements inventory management for a product, confirming whether a product is available across inventory levels, and updating the inventory availability of a product variant at different points in the order lifecycle.
Medusa's Inventory module is a standalone module that can be used in any commerce application, not just in a Medusa backend. This document gives a general overview of how the inventory module is designed, then explains how the Medusa core orchestrates relations and processes around this module when it's used with the Medusa backend.
---
## Entities Overview
![Inventory Module Entities Diagram](https://res.cloudinary.com/dza7lstvk/image/upload/v1680184977/Medusa%20Docs/Diagrams/inventory-diagram_1_eaupf2.jpg)
### InventoryItem
The `InventoryItem` entity represents a stock-kept item. It holds inventory details, such as `origin_country` or `hs_code`. It's not associated with any entity that represents a stock-kept item, enabling you to choose how you integrate it with your existing commerce application.
The `InventoryItem` entity doesnt hold data regarding where the item is stored or its available quantity. This data is stored in the `InventoryLevel` entity.
### InventoryLevel
The `InventoryLevel` entity represents the quantity of an inventory item in a stock location. This ensures that an inventory item can be located in multiple stock locations.
Among the `InventoryLevel` entitys attributes, the following provide insights into the available quantity within that entity:
- `stocked_quantity`: a number indicating the available stock of that item in the associated location.
- `reserved_quantity`: a number indicating the quantity that should be reserved from the available `stocked_quantity`. It can be used to indicate a quantity that is still not removed from stock, but should be considered as unavailable which confirming that an item is in stock.
- `incoming_quantity`: a number indicating an incoming stock quantity of the item into the associated location. This attribute doesn't play into the `stocked_quantity` or when confirming whether a stock-kept item is in stock.
The `InventoryLevel` entity is associated with a location through the `location_id` attribute. The entity representing a location isn't implemented with this module, allowing for greater flexibility in how you choose to implement a location.
### ReservationItem
The `ReservationItem` entity represents a quantity of an inventory item that is reserved when an order is placed but not fulfilled yet. This indicates that the stock-kept item still hasn't been moved from stock, but should be considered as unavailable.
The `ReservationItem` entity has the following notable attributes, among others:
- `line_item_id`: The ID of the line item in the order that this reservation item refers to.
- `inventory_item_id`: The ID of the inventory item this reservation item refers to.
- `location_id`: The ID of the location that this item is reserved from.
- `quantity`: A number indicating the quantity to be reserved.
---
## How the Module Integrates into Medusa
This section explains how the Medusa backend uses the inventory module along with its entities and other modules, and in its processes.
### Entities Relation Overview
The core Medusa package contains an entity `ProductVariantInventoryItem` that is used to establish a relation between a product variant and an inventory item. This enables you to use inventory management features on the product variant level, while maintaining the modularity that allows you to use Medusa's inventory module or implement your custom inventory module.
When you use Medusa's Inventory Module, the Medusa backend uses the `ProductVariantInventoryItem` entity as a bridge between the `InventoryItem` entity and the `ProductVariant` entity.
![Inventory Item's Relation to Product Variants in the Medusa Backend](https://res.cloudinary.com/dza7lstvk/image/upload/v1680185070/Medusa%20Docs/Diagrams/inventory-item-medusa-diagram_i21ht8.jpg)
The Medusa backend also orchestrates between the installed inventory and stock location modules. The association between an Inventory Level and a location is handled by passing the ID of a location from the stock location module to the inventory module when an Inventory Level is being created. When using Medusa's [Stock Location module](./stock-location-module.md), the entity representing the location is `StockLocation`.
![Inventory Level's relation to Stock Location Module in the Medusa Backend](https://res.cloudinary.com/dza7lstvk/image/upload/v1680185151/Medusa%20Docs/Diagrams/inventory-medusa-diagram_ltojt9.jpg)
Similarly, the Medusa backend associates the `ReservationItem` entity with a line item and a location by passing the IDs of each to the inventory module when a reservation item is created.
### Product Variant Creation Process
In the Medusa backend, when a product variant that has an enabled `manage_inventory` attribute is created, the backend uses the inventory module to automatically create an inventory item along with the product variant. When the inventory item is created, the Medusa backend attaches it to the product variant using the `ProductVariantInventoryItem` entity as explained earlier.
The Medusa backend uses the inventory module to create Inventory Levels when the admin sets the available quantity of a product variant in a stock location.
### Cart and Checkout
During the cart and checkout workflow, for example when a product variant is added to the cart or during cart validation, the Medusa backend uses the inventory module to confirm that items in the cart have sufficient stock to be purchased in the desired quantity. If a product variant doesn't have an inventory item, which is the case when the `manage_inventory` attribute of the variant is disabled, the variant is assumed to be available in stock.
As an inventory item can exist in multiple locations, the inventory module checks across those locations. The Medusa backend retrieves the locations based on the sales channel of the cart, as each location is associated with a sales channel, and passes them along to the inventory module to perform the checking.
:::tip
You can learn more about the relation between Stock Locations and Sales Channels in the [Stock Location documentation](./stock-location-module.md).
:::
Then, the inventory module confirms that the product variant has sufficient quantity across these locations by summing all the `stocked_quantity` of the inventory levels associated with these locations. When retrieving the `stocked_quantity` of each of the inventory levels, the `reserved_quantity` is subtracted from it to ensure accurate availability.
### Order Placement
When an order is placed, the Medusa backend uses the inventory module to reserve the ordered quantity of line items that are associated with product variants having an enabled `manage_inventory` attribute. The reserved quantity is indicated by creating a reservation item for each line item, associating it with its inventory item and a stock location.
The Medusa backend chooses the stock location randomly from the available stock locations associated with the orders sales channel. The admin can later change which stock location the item will be fulfilled from.
### Order Fulfillment
When an item in the order is fulfilled, and the item is associated with a product variant that has an enabled `manage_inventory`, the Medusa backend uses the inventory module to subtract the inventory level's `reserved_quantity` from the `stocked_quantity`. The inventory module also resets the `reserved_quantity` to `0`.
### Order Return
When an item in the order is returned, and the item is associated with a product variant that has an enabled `manage_inventory`, the Medusa backend uses the inventory module to increment the inventory level's `stocked_quantity` with the returned amount.
@@ -0,0 +1,188 @@
---
description: "Multi-warehouse allows merchants to store a product in multiple locations with accurate and consistent inventory data within the commerce application."
---
import DocCardList from '@theme/DocCardList';
import DocCard from '@theme/DocCard';
import Icons from '@theme/Icon';
# Multi-Warehouse
Multi-warehouse allows merchants to store a product in multiple locations with accurate and consistent inventory data within the commerce application.
Multi-warehouse in Medusa is composed of two modules: an inventory module - which is the NPM package `@medusajs/inventory` - and a stock location module - which is the NPM package `@medusajs/stock-location`.
<DocCard item={{
type: 'link',
href: '/modules/multiwarehouse/install-modules',
label: 'Install Modules',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to install the modules related to multi-warehouse',
}
}} />
You can also create your own modules and use them with the Medusa backend.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/modules/multiwarehouse/backend/create-inventory-service',
label: 'Backend: Create Inventory Service',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create an inventory service to be used in a custom inventory module.'
}
},
{
type: 'link',
href: '/modules/multiwarehouse/backend/create-stock-location-service',
label: 'Backend: Create Stock Location Service',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create a stock location service to be used in a custom stock location module.'
}
},
]} />
---
## Features
### Multiple Stock Locations
Admins can manage the stock locations, which are the places they store their products. Stock locations are associated with different sales channels.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/user-guide/multiwarehouse/locations',
label: 'User Guide: Manage Stock Locations',
customProps: {
icon: Icons['users-solid'],
description: 'Learn how to manage stock locations in Medusa admin.'
}
},
{
type: 'link',
href: '/modules/multiwarehouse/admin/manage-stock-locations',
label: 'Admin: Manage Stock Locations',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to manage stock locations using admin APIs.',
}
},
]} />
### Inventory Management Across Locations
Admins can manage the inventory of product variants across the different stock locations.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/user-guide/multiwarehouse/inventory',
label: 'User Guide: Manage Inventory',
customProps: {
icon: Icons['users-solid'],
description: 'Learn how to manage inventory using the Medusa admin.'
}
},
{
type: 'link',
href: '/modules/multiwarehouse/admin/manage-inventory-items',
label: 'Admin: Manage Inventory',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to manage inventory using the admin APIs.',
}
},
]} />
### Manage Item Allocations in Orders
Admins can manage item allocations to choose which stock location to fulfill items from or return items to. Item quantities are reserved in a stock location until the item is fulfilled to ensure data consistency.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/user-guide/orders/manage#manage-item-allocation',
label: 'User Guide: Manage Allocations in Orders',
customProps: {
icon: Icons['users-solid'],
description: 'Learn how to manage allocations of items in an order using the Medusa admin.'
}
},
{
type: 'link',
href: '/modules/multiwarehouse/admin/manage-item-allocations-in-orders',
label: 'Admin: Manage Allocations in Orders',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to manage allocations of items in an order using the admin APIs.',
}
},
]} />
---
## Understanding the Architecture
The commerce modules automatically detect whether a product variant is in stock, decrement the variants stock on fulfillment, and increment the variants stock on returns.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/modules/multiwarehouse/inventory-module',
label: 'Architecture: Inventory Module',
customProps: {
icon: Icons['circle-stack-solid'],
description: 'Learn about the Inventory Module and how it works.'
}
},
{
type: 'link',
href: '/modules/multiwarehouse/stock-location-module',
label: 'Architecture: Stock Location Module',
customProps: {
icon: Icons['circle-stack-solid'],
description: 'Learn about the Stock Location Module and how it works.'
}
},
]} />
---
## Related Modules
Discover Multi-warehouses relation to other modules in Medusa.
<DocCardList colSize={4} items={[
{
type: 'link',
href: '/modules/products/overview',
label: 'Products',
customProps: {
icon: Icons['tag-solid'],
description: 'Manage the inventory of product variants across locations.',
}
},
{
type: 'link',
href: '/modules/sales-channels/overview',
label: 'Sales Channels',
customProps: {
icon: Icons['channels-solid'],
description: 'Stock locations are associated with sales channels.',
}
},
{
type: 'link',
href: '/modules/orders/overview',
label: 'Orders',
customProps: {
icon: Icons['check-circle-solid'],
description: 'Change available stock based on returns and fulfillment.',
}
},
]} />
@@ -0,0 +1,63 @@
---
description: "In this document, youll learn about the inventory module, how it works, and its relation to other processes in your commerce application."
---
# Stock Level Module
In this document, youll learn about the Stock Level module and how it works.
## Overview
A stock location indicates a physical address that stock-kept items can be stored in. The stock location module handles functionalities related to managing stock locations and their addresses.
Medusa's Stock Location module is a standalone module that can be used in any commerce application, not just in a Medusa backend. This document gives a general overview of how the stock location module is designed, and highlights how the Medusa core orchestrates relations around this module when it's used with the Medusa backend.
---
## StockLocation Entity
The `StockLocation` entity represents a stock location. It has minimal attributes including a `name` attribute. Its associated with the `StockLocationAddress` entity.
---
## StockLocationAddress Entity
The `StockLocationAddress` is an entity that contains address-related fields, such as `city` or `country_code`.
The `StockLocationAddress` entity belongs to the `StockLocation` entity. It is used to store the address details of a stock location.
---
## How the Module Integrates into Medusa
This section explains how the Medusa backend uses the stock location module along with its entities and other modules.
### Entities Relation Overview
The following entities in the Medusa backend each have an attribute that is used to associate them with a stock location:
- `Fulfillment`: The `location_id` attribute within the `Fulfillment` entity is used to indicate from which stock location an order item is fulfilled.
- `Return`: The `location_id` attribute within the `Return` entity is used to indicate to which stock location an order item is returned.
- `Store`: The `default_location_id` attribute within the `Store` entity is used to indicate the default stock location to use in the ecommerce store.
- `SalesChannelLocation`: This entity is used to attach a stock location to a `SalesChannel`. The relation between these two entities is explained further in the [Relation to Sales Channel section](#relation-to-saleschannel).
When the Medusa's Stock Location module is used with the Medusa backend, the ID that is associated with the attributes mentioned above is from the `StockLocation` module.
The Medusa backend also orchestrates between different modules. The [Inventory Module](./inventory-module.md)'s entities contain the following attributes to handle associations between them and a stock location:
- `InventoryLevel`: This entity is used to indicate the stocked quantity of an inventory item in a stock location. As explained in the [Inventory Module documentation](./inventory-module.md#inventorylevel), the `InventoryLevel` entity has an attribute `location_id`.
- `ReservationItem`: This entity is used to indicate the reserved quantity of an inventory item in a stock location. As explained in the [Inventory Module documentation](./inventory-module.md#reservationitem), the `ReservationItem` entity has an attribute `location_id`.
When both modules are used within the Medusa backend, the Medusa backend bridges between these modules by passing the ID of a `StockLocation` from the stock location module to the inventory module, and the inventory module uses the ID in its entities.
### Relation to SalesChannel
A stock location can be associated with more than one sales channel. For example, a physical store and an online store can use the same stock location.
As the `StockLocation` and `SalesChannel` entities are available in separate modules, the Medusa backend handles attaching the stock location with the sales channel within the `SalesChannelLocation` entity.
This relation is used across the Medusa backend and within checkout and order workflows:
- When checking the availability of an inventory item during checkout, the Medusa backend retrieves the location IDs that are associated with the carts sales channel using the `SalesChannelLocation`, then passes it along to the inventory module to perform the quantity check.
- When an order is placed, the Medusa backend retrieves the location IDs that are associated with the carts sales channel using the `SalesChannelLocation` entity, and passes it to the inventory module that reserves the ordered quantity of the inventory item from that location. The admin can later change the stock location if necessary.
- When an item in an order is fulfilled, the admin chooses a stock location to fulfill the item from. Similarly, when an item in an order is returned, the admin can choose a stock location to return the item to. The Medusa backend then passes the ID of the location from the stock module to the inventory module to perform inventory management functionalities.