diff --git a/docs/.eslintrc.js b/docs/.eslintrc.js index fb64f612c1..ad6ac30037 100644 --- a/docs/.eslintrc.js +++ b/docs/.eslintrc.js @@ -18,8 +18,8 @@ module.exports = { ], settings: { react: { - version: "detect" - } + version: "detect", + }, }, rules: { "no-undef": "off", @@ -30,8 +30,6 @@ module.exports = { curly: ["error", "all"], "new-cap": "off", "require-jsdoc": "off", - "no-unused-expressions": "off", - "no-unused-vars": "off", camelcase: "off", "no-invalid-this": "off", "max-len": [ @@ -78,15 +76,15 @@ module.exports = { "space-infix-ops": "off", "eol-last": ["error", "always"], "react/prop-types": "off", - "react/jsx-no-undef": "off" + "react/jsx-no-undef": "off", }, env: { es6: true, node: true, }, ignorePatterns: [ - 'docs/content/references/**', - 'docs/content/**/events-list.md' + "docs/content/references/**", + "docs/content/**/events-list.md", ], overrides: [ { @@ -115,6 +113,7 @@ module.exports = { "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", }, }, ], diff --git a/docs/content/modules/multiwarehouse/admin/manage-inventory-items.mdx b/docs/content/modules/multiwarehouse/admin/manage-inventory-items.mdx new file mode 100644 index 0000000000..2dd4691b7a --- /dev/null +++ b/docs/content/modules/multiwarehouse/admin/manage-inventory-items.mdx @@ -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, you’ll 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, it’s recommended to use the [Stock Location module](../install-modules.md#stock-location-module) if you don’t have any location logic implemented in place. + +### JS Client + +This guide includes code snippets to send requests to your Medusa backend using Medusa’s JS Client, among other methods. + +If you follow the JS Client code blocks, it’s assumed you already have [Medusa’s 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.md) and have [used MedusaProvider higher in your component tree](../../../medusa-react/overview.md#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](/api/admin/#section/Authentication). + +--- + +## List Inventory Items + +You can list inventory items by sending a request to the [List Inventory Items endpoint](/api/admin#tag/Inventory-Items/operation/GetInventoryItems): + + + + +```ts +medusa.admin.inventoryItems.list() +.then(({ inventory_items }) => { + console.log(inventory_items.length) +}) +``` + + + + +```tsx +import { useAdminInventoryItems } from "medusa-react" + +function InventoryItems() { + const { + inventory_items, + isLoading } = useAdminInventoryItems() + + return ( +
+ {isLoading && Loading...} + {inventory_items && !inventory_items.length && ( + No Items + )} + {inventory_items && inventory_items.length > 0 && ( +
    + {inventory_items.map( + (item) => ( +
  • {item.id}
  • + ) + )} +
+ )} +
+ ) +} + +export default InventoryItems +``` + +
+ + +```ts +fetch(`/admin/inventory-items`, { + credentials: "include", +}) +.then((response) => response.json()) +.then(({ inventory_items }) => { + console.log(inventory_items.length) +}) +``` + + + + +```bash +curl -L -X GET '/admin/inventory-items' \ + -H 'Authorization: Bearer ' +``` + + +
+ +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](/api/admin#tag/Inventory-Items/operation/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 don’t need to create an inventory item manually. + +::: + +You can create an inventory item by sending a request to the [Create Inventory Item endpoint](/api/admin#tag/Inventory-Items/operation/PostInventoryItems): + + + + +```ts +medusa.admin.inventoryItems.create({ + variant_id, +}) +.then(({ inventory_item }) => { + console.log(inventory_item.id) +}) +``` + + + + +```tsx +import { useAdminCreateInventoryItem } from "medusa-react" + +const CreateInventoryItem = () => { + const createInventoryItem = useAdminCreateInventoryItem() + // ... + + const handleCreate = () => { + createInventoryItem.mutate({ + variant_id, + }) + } + + // ... +} + +export default CreateInventoryItem +``` + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/inventory-items' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "variant_id": "variant_123" + }' +``` + + + + +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](/api/admin#tag/Inventory-Items/operation/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](/api/admin#tag/Inventory-Items/operation/GetInventoryItemsInventoryItem): + + + + +```ts +medusa.admin.inventoryItems.retrieve(inventoryItemId) +.then(({ inventory_item }) => { + console.log(inventory_item.id) +}) +``` + + + + +```tsx +import { useAdminInventoryItem } from "medusa-react" + +function InventoryItem() { + const { + inventory_item, + isLoading } = useAdminInventoryItem(inventoryItemId) + + return ( +
+ {isLoading && Loading...} + {inventory_item && ( + {inventory_item.sku} + )} +
+ ) +} + +export default InventoryItem +``` + +
+ + +```ts +fetch(`/admin/inventory-items/${inventoryItemId}`, + { + credentials: "include", + } +) +.then((response) => response.json()) +.then(({ inventory_item }) => { + console.log(inventory_item.id) +}) +``` + + + + +```bash +curl -L -X GET '/admin/inventory-items/' \ + -H 'Authorization: Bearer ' +``` + + +
+ +This endpoint accepts the ID of the inventory item as a path parameter. You can also path query parameters such as [expand](/api/admin#section/Expanding-Fields) and [fields](/api/admin#section/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](/api/admin#tag/Inventory-Items/operation/PostInventoryItemsInventoryItem): + + + + +```ts +medusa.admin.inventoryItems.update(inventoryItemId, { + origin_country: "US", +}) +.then(({ inventory_item }) => { + console.log(inventory_item.id) +}) +``` + + + + +```tsx +import { useAdminUpdateInventoryItem } from "medusa-react" + +const UpdateInventoryItem = () => { + const updateInventoryItem = useAdminUpdateInventoryItem( + inventoryItemId + ) + // ... + + const handleUpdate = () => { + updateInventoryItem.mutate({ + origin_country: "US", + }) + } + + // ... +} + +export default UpdateInventoryItem +``` + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/inventory-items/' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "origin_country": "US" + }' +``` + + + + +This endpoint requires the inventory item’s ID as a path parameter. You can pass any of the inventory item’s 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](/api/admin#tag/Inventory-Items/operation/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](/api/admin#tag/Inventory-Items/operation/GetInventoryItemsInventoryItemLocationLevels): + + + + +```ts +medusa.admin.inventoryItems.listLocationLevels(inventoryItemId) +.then(({ inventory_item }) => { + console.log(inventory_item.location_levels) +}) +``` + + + + +```tsx +import { + useAdminInventoryItemLocationLevels, +} from "medusa-react" + +function InventoryItem() { + const { + inventory_item, + isLoading, + } = useAdminInventoryItemLocationLevels(inventoryItemId) + + return ( +
+ {isLoading && Loading...} + {inventory_item && ( +
    + {inventory_item.location_levels.map((level) => ( + {level.stocked_quantity} + ))} +
+ )} +
+ ) +} + +export default InventoryItem +``` + +
+ + + + +```ts +fetch(`/admin/inventory-items/${inventoryItemId}/location-levels`, { + credentials: "include", +}) +.then((response) => response.json()) +.then(({ inventory_item }) => { + console.log(inventory_item.location_levels) +}) +``` + + + + +```bash +curl -L -X GET '/admin/inventory-items//location-levels' \ + -H 'Authorization: Bearer ' +``` + + +
+ +This endpoint requires the ID of the inventory item as a path parameter. You can also pass [expand](/api/admin#section/Expanding-Fields) and [fields](/api/admin#section/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](/api/admin#tag/Inventory-Items/operation/PostInventoryItemsInventoryItemLocationLevels): + + + + +```ts +medusa.admin.inventoryItems.createLocationLevel( + inventoryItemId, + { + location_id, + stocked_quantity: 10, + } +) +.then(({ inventory_item }) => { + console.log(inventory_item.id) +}) +``` + + + + +```tsx +import { useAdminCreateLocationLevel } from "medusa-react" + +const CreateLocationLevel = () => { + const createLocationLevel = useAdminCreateLocationLevel( + inventoryItemId + ) + // ... + + const handleCreate = () => { + createLocationLevel.mutate({ + location_id, + stocked_quantity: 10, + }) + } + + // ... +} + +export default CreateLocationLevel +``` + + + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/inventory-items//location-levels' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "location_id": "", + "stocked_quantity": 10 + }' +``` + + + + +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 item’s quantity in stock. + +You can also pass other optional request body parameters, as explained in the [API reference](/api/admin#tag/Inventory-Items/operation/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](/api/admin#tag/Inventory-Items/operation/PostInventoryItemsInventoryItemLocationLevelsLocationLevel): + + + + +```ts +medusa.admin.inventoryItems.updateLocationLevel( + inventoryItemId, + locationId, + { + stocked_quantity: 15, + } +) +.then(({ inventory_item }) => { + console.log(inventory_item.id) +}) +``` + + + + +```tsx +import { useAdminUpdateLocationLevel } from "medusa-react" + +const UpdateLocationLevel = () => { + const updateLocationLevel = useAdminUpdateLocationLevel( + inventoryItemId + ) + // ... + + const handleUpdate = () => { + updateLocationLevel.mutate({ + stockLocationId, + stocked_quantity: 10, + }) + } + + // ... +} + +export default UpdateLocationLevel +``` + + + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/inventory-items//location-levels/' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "stocked_quantity": 10 + }' +``` + + + + +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 level’s 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](/api/admin#tag/Inventory-Items/operation/DeleteInventoryItemsInventoryIteLocationLevelsLocation): + + + + +```ts +medusa.admin.inventoryItems.deleteLocationLevel( + inventoryItemId, + locationId +) +.then(({ inventory_item }) => { + console.log(inventory_item.id) +}) +``` + + + + +```tsx +import { useAdminDeleteLocationLevel } from "medusa-react" + +const DeleteLocationLevel = () => { + const deleteLocationLevel = useAdminDeleteLocationLevel( + inventoryItemId + ) + // ... + + const handleDelete = () => { + deleteLocationLevel.mutate(locationId) + } + + // ... +} + +export default DeleteLocationLevel +``` + + + + + + +```ts +fetch(`/admin/inventory-items/${inventoryItemId}/location-levels/${locationId}`, { + credentials: "include", + method: "DELETE", +}) +.then((response) => response.json()) +.then(({ inventory_item }) => { + console.log(inventory_item.id) +}) +``` + + + + +```bash +curl -L -X DELETE '/admin/inventory-items//location-levels/' \ + -H 'Authorization: Bearer ' +``` + + + + +This endpoint requires two path parameters: the first one being the inventory item’s ID and the second one being the location level’s 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](/api/admin#tag/Inventory-Items/operation/DeleteInventoryItemsInventoryItem): + + + + +```ts +medusa.admin.inventoryItems.delete(inventoryItemId) +.then(({ id, object, deleted }) => { + console.log(id) +}) +``` + + + + +```tsx +import { useAdminDeleteInventoryItem } from "medusa-react" + +const DeleteInventoryItem = () => { + const deleteInventoryItem = useAdminDeleteInventoryItem( + inventoryItemId + ) + // ... + + const handleDelete = () => { + deleteInventoryItem.mutate() + } + + // ... +} + +export default DeleteInventoryItem +``` + + + + +```ts +fetch(`/admin/inventory-items/${inventoryItemId}`, + { + credentials: "include", + method: "DELETE", + } +) +.then((response) => response.json()) +.then(({ id, object, deleted }) => { + console.log(id) +}) +``` + + + + +```bash +curl -L -X DELETE '/admin/inventory-items/' \ + -H 'Authorization: Bearer ' +``` + + + + +This endpoint requires the inventory item’s 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) diff --git a/docs/content/modules/multiwarehouse/admin/manage-item-allocations-in-orders.mdx b/docs/content/modules/multiwarehouse/admin/manage-item-allocations-in-orders.mdx new file mode 100644 index 0000000000..c35eff66ed --- /dev/null +++ b/docs/content/modules/multiwarehouse/admin/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, you’ll 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 order’s 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 Medusa’s [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 Medusa’s JS Client, among other methods. + +If you follow the JS Client code blocks, it’s assumed you already have [Medusa’s 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.md) and have [used MedusaProvider higher in your component tree](../../../medusa-react/overview.md#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](/api/admin/#section/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 don’t 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](/api/admin#tag/Reservations/operation/PostReservations): + + + + +```ts +medusa.admin.reservations.create({ + line_item_id, + location_id, + inventory_item_id, + quantity, +}) +.then(({ reservation }) => { + console.log(reservation.id) +}) +``` + + + + +```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 +``` + + + + +```ts +fetch(`/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) + }) +``` + + + + +```bash +curl -L -X POST '/admin/reservations' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "line_item_id": "", + "location_id": "", + "inventory_item_id": "", + "quantity": 1 + }' +``` + + + + +This endpoint requires the following body parameters: + +- `line_item_id`: The ID of the order’s 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 item’s 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, you’ll 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](/api/admin#tag/Reservations/operation/GetReservations): + + + + +```ts +medusa.admin.reservations.list({ + line_item_id, +}) +.then(({ reservations, count, limit, offset }) => { + console.log(reservations.length) +}) +``` + + + + +```tsx +import { useAdminReservations } from "medusa-react" + +function Reservations() { + const { reservations, isLoading } = useAdminReservations({ + line_item_id, + }) + + return ( +
+ {isLoading && Loading...} + {reservations && !reservations.length && ( + No Reservations + )} + {reservations && reservations.length > 0 && ( +
    + {reservations.map((reservation) => ( +
  • {reservation.quantity}
  • + ))} +
+ )} +
+ ) +} + +export default Reservations +``` + +
+ + + + +```ts +fetch(`/admin/reservations?line_item_id=${lineItemId}`, { + credentials: "include", +}) +.then((response) => response.json()) +.then(({ reservations, count, limit, offset }) => { + console.log(reservations.length) +}) +``` + + + + +```bash +curl -L -X GET '/admin/reservations?line_item_id=' \ + -H 'Authorization: Bearer ' +``` + + +
+ +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](/api/admin#tag/Reservations/operation/GetReservations) for a full list of query parameters. + +The request returns the reservations along with [pagination fields](/api/admin#section/Pagination). + +### Retrieve Item Allocation + +You can retrieve a single item allocation by its ID using the [Get a Reservation endpoint](/api/admin#tag/Reservations/operation/GetReservationsReservation): + + + + +```ts +medusa.admin.reservations.retrieve(reservationId) +.then(({ reservation }) => { + console.log(reservation.id) +}) +``` + + + + +```tsx +import { useAdminReservation } from "medusa-react" + +function Reservation() { + const { + reservation, + isLoading } = useAdminReservation(reservationId) + + return ( +
+ {isLoading && Loading...} + {reservation && ( + {reservation.quantity} + )} +
+ ) +} + +export default Reservation +``` + +
+ + +```ts +fetch(`/admin/reservations/${reservationId}`, { + credentials: "include", +}) +.then((response) => response.json()) +.then(({ reservation }) => { + console.log(reservation.id) +}) +``` + + + + +```bash +curl -L -X GET '/admin/reservations/' \ + -H 'Authorization: Bearer ' +``` + + +
+ +This endpoint requires the reservation’s 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](/api/admin#tag/Reservations/operation/PostReservationsReservation): + + + + +```ts +medusa.admin.reservations.update(reservationId, { + quantity, +}) +.then(({ reservation }) => { + console.log(reservation.id) +}) +``` + + + + +```tsx +import { useAdminUpdateReservation } from "medusa-react" + +const UpdateReservation = () => { + const updateReservation = useAdminUpdateReservation( + reservationId + ) + // ... + + const handleCreate = () => { + updateReservation.mutate({ + quantity, + }) + } + + // ... +} + +export default UpdateReservation +``` + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/reservations/' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "quantity": 3 + }' +``` + + + + +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 reservation’s 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](/api/admin#tag/Reservations/operation/DeleteReservationsReservation): + + + + +```ts +medusa.admin.reservations.delete(reservationId) +.then(({ id, object, deleted }) => { + console.log(id) +}) +``` + + + + +```tsx +import { useAdminDeleteReservation } from "medusa-react" + +const DeleteReservation = () => { + const deleteReservation = useAdminDeleteReservation( + reservationId + ) + // ... + + const handleDelete = () => { + deleteReservation.mutate() + } + + // ... +} + +export default DeleteReservation +``` + + + + +```ts +fetch(`/admin/reservations/${reservationId}`, { + credentials: "include", + method: "DELETE", +}) +.then((response) => response.json()) +.then(({ id, object, deleted }) => { + console.log(id) +}) +``` + + + + +```bash +curl -L -X DELETE '/admin/reservations/' \ + -H 'Authorization: Bearer ' +``` + + + + +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: + + + + +```ts +medusa.admin.orders.createFulfillment(orderId, { + items: [ + { + item_id, + quantity, + }, + ], + // ...other parameters + location_id, +}) +.then(({ order }) => { + console.log(order.id) +}) +``` + + + + +```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 +``` + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/orders//fulfillment' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "items": [ + { + "item_id": "", + "quantity": 1 + } + ], + "location_id": "" + }' +``` + + + + +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 endpoint’s parameters and response in the [API reference](/api/admin#tag/Orders/operation/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: + + + + +```ts +medusa.admin.orders.requestReturn(orderId, { + items: [ + { + item_id, + quantity: 1, + }, + ], + // other parameters... + location_id, +}) +.then(({ order }) => { + console.log(order.id) +}) +``` + + + + +```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 +``` + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/orders//return' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "items": [ + { + "item_id": "", + "quantity": 1 + } + ], + "location_id": "" + }' +``` + + + + +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 endpoint’s parameters and response in [the API reference](/api/admin#tag/Orders/operation/PostOrdersOrderReturns). + +--- + +## See Also + +- [How to manage inventory items](./manage-inventory-items.mdx) +- [How to manage stock locations](./manage-stock-locations.mdx) diff --git a/docs/content/modules/multiwarehouse/admin/manage-stock-locations.mdx b/docs/content/modules/multiwarehouse/admin/manage-stock-locations.mdx new file mode 100644 index 0000000000..f1e8f0bfc2 --- /dev/null +++ b/docs/content/modules/multiwarehouse/admin/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, you’ll 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 Medusa’s [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 Medusa’s JS Client, among other methods. + +If you follow the JS Client code blocks, it’s assumed you already have [Medusa’s 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.md) and have [used MedusaProvider higher in your component tree](../../../medusa-react/overview.md#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](/api/admin/#section/Authentication). + +--- + +## List Stock Locations + +You can list stock locations by using the [List Stock Locations endpoint](/api/admin#tag/Stock-Locations/operation/GetStockLocations): + + + + +```ts +medusa.admin.stockLocations.list() +.then(({ stock_locations, limit, offset, count }) => { + console.log(stock_locations.length) +}) +``` + + + + +```tsx +import { useAdminStockLocations } from "medusa-react" + +function StockLocations() { + const { + stock_locations, + isLoading } = useAdminStockLocations() + + return ( +
+ {isLoading && Loading...} + {stock_locations && !stock_locations.length && ( + No Locations + )} + {stock_locations && stock_locations.length > 0 && ( +
    + {stock_locations.map( + (location) => ( +
  • {location.name}
  • + ) + )} +
+ )} +
+ ) +} + +export default StockLocations +``` + +
+ + +```ts +fetch(`/admin/stock-locations`, { + credentials: "include", +}) +.then((response) => response.json()) +.then(({ stock_locations, limit, offset, count }) => { + console.log(stock_locations.length) +}) +``` + + + + +```bash +curl -L -X GET '/admin/stock-locations' \ + -H 'Authorization: Bearer ' +``` + + +
+ +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](/api/admin#tag/Stock-Locations/operation/GetStockLocations). + +The request returns an array of stock location objects along with [pagination parameters](/api/admin#section/Pagination). + +--- + +## Create a Stock Location + +You can create a stock location using the [Create a Stock Location endpoint](/api/admin#tag/Stock-Locations/operation/PostStockLocations): + + + + +```ts +medusa.admin.stockLocations.create({ + name: "Main Warehouse", +}) +.then(({ stock_location }) => { + console.log(stock_location.id) +}) +``` + + + + +```tsx +import { useAdminCreateStockLocation } from "medusa-react" + +const CreateStockLocation = () => { + const createStockLocation = useAdminCreateStockLocation() + // ... + + const handleCreate = () => { + createStockLocation.mutate({ + name: "Main Warehouse", + }) + } + + // ... +} + +export default CreateStockLocation +``` + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/stock-locations' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "name": "Main Warehouse" + }' +``` + + + + +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](/api/admin#tag/Stock-Locations/operation/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](/api/admin#tag/Stock-Locations/operation/GetStockLocationsStockLocation): + + + + +```ts +medusa.admin.stockLocations.retrieve(stockLocationId) +.then(({ stock_location }) => { + console.log(stock_location.id) +}) +``` + + + + +```tsx +import { useAdminStockLocation } from "medusa-react" + +function StockLocation() { + const { + stock_location, + isLoading } = useAdminStockLocation(stockLocationId) + + return ( +
+ {isLoading && Loading...} + {stock_location && ( + {stock_location.name} + )} +
+ ) +} + +export default StockLocation +``` + +
+ + +```ts +fetch(`/admin/stock-locations/${stockLocationId}`, + { + credentials: "include", + } +) +.then((response) => response.json()) +.then(({ stock_location }) => { + console.log(stock_location.id) +}) +``` + + + + +```bash +curl -L -X GET '/admin/stock-locations/' \ + -H 'Authorization: Bearer ' +``` + + +
+ +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](/api/admin#tag/Stock-Locations/operation/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](/api/admin#tag/Sales-Channels/operation/PostSalesChannelsSalesChannelStockLocation): + + + + +```ts +medusa.admin.salesChannels.addLocation(salesChannelId, { + location_id, +}) +.then(({ sales_channel }) => { + console.log(sales_channel.id) +}) +``` + + + + +```tsx +import { useAdminAddLocationToSalesChannel } from "medusa-react" + +function StockLocation() { + const addLocation = useAdminAddLocationToSalesChannel() + // ... + + const handleAdd = () => { + addLocation.mutate({ + sales_channel_id, + location_id, + }) + } +} + +export default StockLocation +``` + + + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/sales-channels//stock-locations' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "location_id": "" + }' +``` + + + + +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 you’re 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](/api/admin#tag/Sales-Channels/operation/DeleteSalesChannelsSalesChannelStockLocation): + + + + +```ts +medusa.admin.salesChannels.removeLocation(salesChannelId, { + location_id, +}) +.then(({ id, object, deleted }) => { + console.log(id) +}) +``` + + + + +```tsx +import { + useAdminRemoveLocationFromSalesChannel, +} from "medusa-react" + +function StockLocation() { + const removeLocation = + useAdminRemoveLocationFromSalesChannel() + // ... + + const handleRemove = () => { + removeLocation.mutate({ + sales_channel_id, + location_id, + }) + } +} + +export default StockLocation +``` + + + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X DELETE '/admin/sales-channels//stock-locations' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "location_id": "" + }' +``` + + + + +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 you’re 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](/api/admin#tag/Stock-Locations/operation/PostStockLocationsStockLocation): + + + + +```ts +medusa.admin.stockLocations.update(stockLocationId, { + name: "Warehouse", +}) +.then(({ stock_location }) => { + console.log(stock_location.id) +}) +``` + + + + +```tsx +import { useAdminUpdateStockLocation } from "medusa-react" + +function StockLocation() { + const updateLocation = useAdminUpdateStockLocation( + stockLocationId + ) + // ... + + const handleRemove = () => { + updateLocation.mutate({ + name: "Warehouse", + }) + } +} + +export default StockLocation +``` + + + + +```ts +fetch(`/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) +}) +``` + + + + +```bash +curl -L -X POST '/admin/stock-locations/' \ + -H 'Authorization: Bearer ' \ + -H 'Content-Type: application/json' \ + --data-raw '{ + "name": "Main Warehouse" + }' +``` + + + + +This endpoint requires the ID of a stock location as a path parameter. In its body parameters, you can pass any of the location’s attributes to update, such as the name or address. You can learn more in the [API reference](/api/admin#tag/Stock-Locations/operation/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](/api/admin#tag/Stock-Locations/operation/DeleteStockLocationsStockLocation): + + + + +```ts +medusa.admin.stockLocations.delete(stockLocationId) +.then(({ id, object, deleted }) => { + console.log(id) +}) +``` + + + + +```tsx +import { useAdminDeleteStockLocation } from "medusa-react" + +function StockLocation() { + const deleteLocation = useAdminDeleteStockLocation( + stockLocationId + ) + // ... + + const handleDelete = () => { + deleteLocation.mutate() + } +} + +export default StockLocation +``` + + + + +```ts +fetch(`/admin/stock-locations/${stockLocationId}`, + { + credentials: "include", + method: "DELETE", + } +) +.then((response) => response.json()) +.then(({ id, object, deleted }) => { + console.log(id) +}) +``` + + + + +```bash +curl -L -X DELETE '/admin/stock-locations/' \ + -H 'Authorization: Bearer ' +``` + + + + +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) diff --git a/docs/content/modules/multiwarehouse/backend/create-inventory-service.md b/docs/content/modules/multiwarehouse/backend/create-inventory-service.md new file mode 100644 index 0000000000..ce976b155d --- /dev/null +++ b/docs/content/modules/multiwarehouse/backend/create-inventory-service.md @@ -0,0 +1,1211 @@ +--- +description: 'Learn how to create an inventory service, which you can use in a custom inventory module in the Medusa backend.' +addHowToData: true +--- + +# How to Create an Inventory Service + +In this document, you’ll learn how to create an inventory service, which you can use in a custom inventory module in the Medusa backend. + +## Overview + +An inventory module is used in a commerce application, such as the Medusa backend, to handle functionalities related to stock-kept items. This includes managing those items, their locations, and their allocations in orders. + +While Medusa provides [an inventory module](../inventory-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 inventory 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 inventory 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 inventory module to have entities for an inventory item, an inventory level, and a reservation item, 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 [Inventory Module Architecture documentation](../inventory-module.md). + +::: + +--- + +## Prerequisites + +The `IStockLocationService` interface that you’ll 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 you’re adding your implementation): + +```bash npm2yarn +npm install @medusajs/types +``` + +You’ll 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 2: Implement the Inventory Service + +Create a file in the `src/services` directory that will hold your custom inventory service. For example, `src/services/inventory.ts`. + +In that file, add the following content: + +```ts title=src/services/inventory.ts +import { + CreateInventoryItemInput, + CreateInventoryLevelInput, + CreateReservationItemInput, + FilterableInventoryItemProps, + FilterableInventoryLevelProps, + FilterableReservationItemProps, + FindConfig, + IInventoryService, + InventoryItemDTO, + InventoryLevelDTO, + ReservationItemDTO, + SharedContext, + UpdateInventoryLevelInput, + UpdateReservationItemInput, +} from "@medusajs/types" +import { + InjectEntityManager, + MedusaContext, +} from "@medusajs/utils" + +class InventoryService implements IInventoryService { + async listInventoryItems( + selector: FilterableInventoryItemProps, + config?: FindConfig, + context?: SharedContext + ): Promise<[InventoryItemDTO[], number]> { + throw new Error("Method not implemented.") + } + async listReservationItems( + selector: FilterableReservationItemProps, + config?: FindConfig, + context?: SharedContext + ): Promise<[ReservationItemDTO[], number]> { + throw new Error("Method not implemented.") + } + async listInventoryLevels( + selector: FilterableInventoryLevelProps, + config?: FindConfig, + context?: SharedContext + ): Promise<[InventoryLevelDTO[], number]> { + throw new Error("Method not implemented.") + } + async retrieveInventoryItem( + inventoryItemId: string, + config?: FindConfig, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async retrieveInventoryLevel( + inventoryItemId: string, + locationId: string, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async retrieveReservationItem( + reservationId: string, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async createReservationItem( + input: CreateReservationItemInput, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async createInventoryItem( + input: CreateInventoryItemInput, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async createInventoryLevel( + data: CreateInventoryLevelInput, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async updateInventoryLevel( + inventoryItemId: string, + locationId: string, + update: UpdateInventoryLevelInput, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async updateInventoryItem( + inventoryItemId: string, + input: CreateInventoryItemInput, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async updateReservationItem( + reservationItemId: string, + input: UpdateReservationItemInput, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async deleteReservationItemsByLineItem( + lineItemId: string, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async deleteReservationItem( + reservationItemId: string | string[], + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async deleteInventoryItem( + inventoryItemId: string, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async deleteInventoryItemLevelByLocationId( + locationId: string, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async deleteReservationItemByLocationId( + locationId: string, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async deleteInventoryLevel( + inventoryItemId: string, + locationId: string, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async adjustInventory( + inventoryItemId: string, + locationId: string, + adjustment: number, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async confirmInventory( + inventoryItemId: string, + locationIds: string[], + quantity: number, + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async retrieveAvailableQuantity( + inventoryItemId: string, + locationIds: string[], + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async retrieveStockedQuantity( + inventoryItemId: string, + locationIds: string[], + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } + async retrieveReservedQuantity( + inventoryItemId: string, + locationIds: string[], + context?: SharedContext + ): Promise { + throw new Error("Method not implemented.") + } +} + +export default InventoryService +``` + +This defines the class `StockLocationService` that implements the `IInventoryService` 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, you’ll 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, it’s recommended to include the following configurations in your `tsconfig.json` file: + +```json +{ + //other configurations + "compilerOptions": { + // other configurations + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + } +} +``` + +### Implementing listInventoryItems Method + +This method is used to retrieve a list of inventory items. 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 inventory items. It is used to filter the retrieved inventory items by ID. + 2. `location_id`: an optional string or array of strings indicating the IDs of locations. It is used to filter the retrieved inventory items by the ID of the associated stock location. The association is managed through the inventory level. + 3. `q`: an optional string used to search through inventory items by sku. + 4. `sku`: an optional string, array of strings, or a `StringComparisonOperator` object that is used to search and filter inventory items by their sku. The `StringComparisonOperator` can have the following properties: + 1. `lt`: indicates a string that the sku should be less than. + 2. `gt`: indicates a string that the sku should be greater than. + 3. `gte`: indicates a string that the sku should be greater than or equal to. + 4. `lte`: indicates a string the sku should be less than or equal to. + 5. `origin_country`: an optional string or array of strings indicating the origin country of inventory items. It is used to filter the retrieved inventory items by origin country. + 6. `hs_code`: an optional string or array of strings indicating the HS code of inventory items. It is used to filter the retrieved inventory items by HS code. + 7. `requires_shipping`: an optional boolean that searches through inventory items based on their `requires_shipping` attribute value. +2. `config`: This is the second parameter and it is an optional parameter. It’s an object that can have any of the following optional properties: + 1. `select`: an array of strings indicating the attributes in your inventory item 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 it’s an optional parameter. This parameter should be used to inject the transaction manager, as explained in the [Method Decorators section](#using-method-decorators). It’s 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, with the first item being the array of inventory items, and the second item being the count. Each inventory item is an object of the following type: + +```ts +type InventoryItemDTO = { + id: string; + sku?: string | null; + origin_country?: string | null; + hs_code?: string | null; + requires_shipping: boolean; + mid_code?: string | null; + material?: string | null; + weight?: number | null; + length?: number | null; + height?: number | null; + width?: number | null; + metadata?: Record | null; + created_at: string | Date; + updated_at: string | Date; + deleted_at: string | Date | null; +}; +``` + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async listInventoryItems( + selector: FilterableInventoryItemProps, + config?: FindConfig, + @MedusaContext() context?: SharedContext + ): Promise<[InventoryItemDTO[], number]> { + const manager = context.transactionManager! + const inventoryItemRepo = manager.getRepository( + + CustomInventoryItem + + ) + + // TODO retrieve and return inventory items and their count + // for example + return await inventoryItemRepo.findAndCount(selector) + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +### Implementing listReservationItems Method + +This method is used to retrieve a list of reservation items. It accepts the same parameters as the [[listInventoryItems method](#implementing-listinventoryitems-method)](#implementing-listinventoryitems-method), but the `selector` parameter has the following properties: + +1. `id`: an optional string or array of strings indicating the IDs of reservation items. It is used to filter the retrieved reservation items by ID. +2. `type`: an optional string or array of strings indicating the type of reservation items. It is used to filter the retrieved reservation items by type. +3. `line_item_id`: an optional string or array of strings indicating the line item ID. It is used to filter the retrieved reservation items based on the line item they’re associated with. +4. `inventory_item_id`: an optional string or array of strings indicating the inventory item ID. It is used to filter the retrieved reservation items based on the inventory item they’re associated with. +5. `location_id`: an optional string or array of strings indicating the stock location ID. It is used to filter the retrieved reservation items based on the location they’re associated with. The association is managed through the inventory level. +6. `quantity`: an optional number or `NumericalComparisonOperator` object that is used to filter reservation items by their quantity. The `NumericalComparisonOperator` object can have any of the following properties: + 1. `lt`: indicates a number that the quantity should be less than. + 2. `gt`: indicates a number that the quantity should be greater than. + 3. `gte`: indicates a number that the quantity should be greater than or equal to. + 4. `lte`: indicates a number the quantity should be less than or equal to. + +This method is expected to return an array, with the first item being an array of reservation items, and the second item being their count. Each reservation item is an object of the following type: + +```ts +type ReservationItemDTO = { + id: string; + location_id: string; + inventory_item_id: string; + quantity: number; + line_item_id?: string | null; + metadata: Record | null; + created_at: string | Date; + updated_at: string | Date; + deleted_at: string | Date | null; +}; +``` + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async listReservationItems( + selector: FilterableReservationItemProps, + config?: FindConfig, + @MedusaContext() context?: SharedContext + ): Promise<[ReservationItemDTO[], number]> { + const manager = context.transactionManager! + const reservationItemRepo = manager.getRepository( + CustomReservationItem + ) + + // TODO retrieve and return reservation items + // and their count + // for example + return await reservationItemRepo.findAndCount(selector) + } +} +``` + +Make sure to replace `CustomReservationItem` with your reservation item entity. + +### Implementing listInventoryLevels Method + +This method is used to retrieve a list of inventory levels. It accepts the same parameters as the [listInventoryItems method](#implementing-listinventoryitems-method), but the `selector` parameter has the following properties: + +1. `inventory_item_id`: an optional string or array of strings indicating the IDs of inventory items. It is used to filter the retrieved location levels by their associated inventory items. +2. `location_id`: an optional string or array of strings indicating the stock location ID. It is used to filter the retrieved inventory level based on the stock location they’re associated with. +3. `stocked_quantity`: an optional number or `NumericalComparisonOperator` object that is used to filter reservation items by their stocked quantity. The `NumericalComparisonOperator` object can have any of the following properties: + 1. `lt`: indicates a number that the quantity should be less than. + 2. `gt`: indicates a number that the quantity should be greater than. + 3. `gte`: indicates a number that the quantity should be greater than or equal to. + 4. `lte`: indicates a number the quantity should be less than or equal to. +4. `reserved_quantity`: an optional number or `NumericalComparisonOperator` object that is used to filter reservation items by their reserved quantity. +5. `incoming_quantity`: an optional number or `NumericalComparisonOperator` object that is used to filter reservation items by their incoming quantity. + +This method is expected to return an array, with the first item being an array of reservation items, and the second item being their count. Each reservation item is an object of the following type: + +```ts +type InventoryLevelDTO = { + id: string; + inventory_item_id: string; + location_id: string; + stocked_quantity: number; + reserved_quantity: number; + incoming_quantity: number; + metadata: Record | null; + created_at: string | Date; + updated_at: string | Date; + deleted_at: string | Date | null; +} +``` + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async listInventoryLevels( + selector: FilterableInventoryLevelProps, + config?: FindConfig, + @MedusaContext() context?: SharedContext + ): Promise<[InventoryLevelDTO[], number]> { + const manager = context.transactionManager! + const inventoryLevelRepo = manager.getRepository( + CustomInventoryLevel + ) + + // TODO retrieve and return inventory levels + // for example + return await inventoryLevelRepo.findAndCount(selector) + } +} +``` + +Make sure to replace `CustomInventoryLevel` with your inventory level entity. + +### Implementing retrieveInventoryItem Method + +This method is used to retrieve a single inventory item. It accepts the following parameters: + +1. `inventoryItemId`: The first parameter and it’s required. It’s the ID of the inventory item to retrieve. +2. `config`: The second parameter and it’s optional. It’s the same `config` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). +3. `context`: The third parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the inventory item as an object. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async retrieveInventoryItem( + inventoryItemId: string, + config?: FindConfig, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const inventoryItemRepo = manager.getRepository( + CustomInventoryItem + ) + + // TODO retrieve and return inventory item + // for example + const [item] = await inventoryItemRepo.find({ + id: inventoryItemId, + }) + + return item + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +### Implementing retrieveInventoryLevel Method + +This method is used to retrieve a single inventory level. It accepts the following parameters: + +1. `inventoryItemId`: The first parameter and it’s required. It’s the ID of the inventory item that the inventory level is associated with. +2. `locationId`: The second parameter and it’s required. It’s the ID of the stock location that the inventory level is associated with. +3. `context`: The third parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the inventory level as an object. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async retrieveInventoryLevel( + inventoryItemId: string, + locationId: string, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const inventoryLevelRepo = manager.getRepository( + CustomInventoryLevel + ) + + // TODO retrieve and return inventory level + // for example + const [level] = await inventoryLevelRepo.find({ + inventory_item_id: inventoryItemId, + location_id: locationId, + }) + + return level + } +} +``` + +Make sure to replace `CustomInventoryLevel` with your inventory level entity. + +### Implementing retrieveReservationItem Method + +This method is used to retrieve a single reservation item. It accepts the following parameters: + +1. `reservationId`: The first parameter and it’s required. It’s the ID of the inventory item that the inventory level is associated with. +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the inventory level as an object. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async retrieveReservationItem( + reservationId: string, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const reservationItemRepo = manager.getRepository( + CustomReservationItem + ) + + // TODO retrieve and return reservation item + // for example + const [item] = await reservationItemRepo.find({ + id: reservationId, + }) + + return item + } +} +``` + +Make sure to replace `CustomReservationItem` with your reservation item entity. + +### Implementing createReservationItem Method + +This method is used to create a reservation item. It accepts the following parameters: + +1. `input`: It’s the first parameter and it’s required. It’s an object having the following properties: + 1. `inventory_item_id`: (required) the ID of the inventory item this reservation item is associated with. + 2. `location_id`: (required) the ID of the stock location this reservation item is associated with. + 3. `line_item_id`: the ID of the line item this reservation item is associated with. + 4. `quantity`: (required) the quantity to be reserved. + 5. `metadata`: an object used to set the `metadata` attribute of the reservation item. +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the newly created reservation item. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async createReservationItem( + input: CreateReservationItemInput, + @MedusaContext()context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const reservationItemRepo = manager.getRepository( + CustomReservationItem + ) + + // TODO create reservation item + // for example + return await reservationItemRepo.create(input) + } +} +``` + +Make sure to replace `CustomReservationItem` with your reservation item entity. + +### Implementing createInventoryItem Method + +This method is used to create an inventory item. It accepts the following parameters: + +1. `input`: It’s the first parameter and it’s required. It’s an object having the necessary properties for the inventory item entity such as `sku`, `origin_country`, etc… +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the newly created inventory item. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async createInventoryItem( + input: CreateInventoryItemInput, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryItem = manager.getRepository( + CustomInventoryItem + ) + + // TODO create inventory item + // for example + return await customInventoryItem.create(input) + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +### Implementing createInventoryLevel Method + +This method is used to create an inventory level. It accepts the following parameters: + +1. `data`: It’s the first parameter and it’s required. It’s an object having the following properties: + 1. `inventory_item_id`: (required) the ID of the inventory item this inventory level is associated with. + 2. `location_id`: (required) the ID of the stock location this inventory level is associated with. + 3. `stocked_quantity`: (required) the item’s quantity in stock in this location. + 4. `reserved_quantity`: the item’s reserved quantity in this location. + 5. `incoming_quantity`: the item’s incoming quantity in this location. +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the newly created inventory level. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async createInventoryLevel( + data: CreateInventoryLevelInput, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryLevel = manager.getRepository( + CustomInventoryLevel + ) + + // TODO create inventory level + // for example + return await customInventoryLevel.create(data) + } +} +``` + +Make sure to replace `CustomInventoryLevel` with your inventory level entity. + +### Implementing updateReservationItem Method + +This method is used to update a reservation item. It accepts the following parameters: + +1. `reservationItemId`: it’s the first parameter and it’s required. It’s the ID of the reservation item to update. +2. `input`: It’s the second parameter and it’s required. It’s an object having any of the reservation item’s attributes to update as a property, with their new value as the property’s value. For example, `quantity`. +3. `context`: The third parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the updated reservation item. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async updateReservationItem( + reservationItemId: string, + input: UpdateReservationItemInput, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customReservationItem = manager.getRepository( + CustomReservationItem + ) + + // TODO update reservation item + // for example + await customReservationItem.update({ + id: reservationItemId, + }, input) + + return await this.retrieveReservationItem( + reservationItemId, + context + ) + } +} +``` + +Make sure to replace `CustomReservationItem` with your reservation item entity. + +### Implementing updateInventoryItem Method + +This method is used to update an inventory item. It accepts the following parameters: + +1. `inventoryItemId`: it’s the first parameter and it’s required. It’s the ID of the inventory item to update. +2. `input`: It’s the second parameter and it’s required. It’s an object having any of the inventory item’s attributes to update as a property, with their new value as the property’s value. For example, `sku`. +3. `context`: The third parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the updated inventory item. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async updateInventoryItem( + inventoryItemId: string, + input: CreateInventoryItemInput, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryItem = manager.getRepository( + CustomInventoryItem + ) + + // TODO update inventory item + // for example + await customInventoryItem.update( + { + id: inventoryItemId, + }, + input + ) + + return await this.retrieveInventoryItem( + inventoryItemId, + {}, + context + ) + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +### Implementing updateInventoryLevel Method + +This method is used to update a inventory level. It accepts the following parameters: + +1. `inventoryItemId`: it’s the first parameter and it’s required. It’s the ID of the inventory item associated with the inventory level to update. +2. `locationId`: it’s the second parameter and it’s required. It’s the ID of the stock location associated with the inventory level to update. +3. `update`: It’s the third parameter and it’s required. It’s an object having any of the inventory level’s attributes to update as a property, with their new value as the property’s value. For example, `stocked_quantity`. +4. `context`: The fourth parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the updated inventory level. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async updateInventoryLevel( + inventoryItemId: string, + locationId: string, + update: UpdateInventoryLevelInput, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryLevel = manager.getRepository( + CustomInventoryLevel + ) + + // TODO create inventory level + // for example + await customInventoryLevel.update({ + inventory_item_id: inventoryItemId, + location_id: locationId, + }, update) + + return await this.retrieveInventoryLevel( + inventoryItemId, + locationId, + context + ) + } +} +``` + +Make sure to replace `CustomInventoryLevel` with your inventory level entity. + +### Implementing deleteReservationItem Method + +This method is used to delete a reservation item. It accepts the following parameters: + +1. `reservationItemId`: it’s the first parameter and it’s required. It’s the ID of the reservation item to delete. It can be a string, indicating that one reservation item should be deleted, or an array of strings, indicating that more than one item should be deleted. +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is not expected to return anything. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async deleteReservationItem( + reservationItemId: string | string[], + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customReservationItem = manager.getRepository( + CustomReservationItem + ) + + // TODO delete reservation item + // for example + await customReservationItem.delete(reservationItemId) + } +} +``` + +Make sure to replace `CustomReservationItem` with your reservation item entity. + +### Implementing deleteReservationItemsByLineItem Method + +This method is used to delete reservation items associated with a line item. It accepts the following parameters: + +1. `lineItemId`: it’s the first parameter and it’s required. It’s the ID of the line item that the reservation items should be associated with to delete. +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is not expected to return anything. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async deleteReservationItemsByLineItem( + lineItemId: string, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customReservationItem = manager.getRepository( + CustomReservationItem + ) + + // TODO delete reservation item + // for example + await customReservationItem.delete({ + line_item_id: lineItemId, + }) + } +} +``` + +Make sure to replace `CustomReservationItem` with your reservation item entity. + +### Implementing deleteReservationItemByLocationId Method + +This method is used to delete reservation items associated with a stock location. It accepts the following parameters: + +1. `locationId`: it’s the first parameter and it’s required. It’s the ID of the stock location that the reservation items should be associated with to be deleted. +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is not expected to return anything. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async deleteReservationItemByLocationId( + locationId: string, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customReservationItem = manager.getRepository( + CustomReservationItem + ) + + // TODO delete reservation item + // for example + await customReservationItem.delete({ + location_id: locationId, + }) + } +} +``` + +Make sure to replace `CustomReservationItem` with your reservation item entity. + +### Implementing deleteInventoryItem Method + +This method is used to delete an inventory item. It accepts the following parameters: + +1. `inventoryItemId`: it’s the first parameter and it’s required. It’s the ID of the inventory item to delete. +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is not expected to return anything. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async deleteInventoryItem( + inventoryItemId: string, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryItem = manager.getRepository( + CustomInventoryItem + ) + + // TODO delete inventory item + // for example + await customInventoryItem.delete(inventoryItemId) + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +### Implementing deleteInventoryLevel Method + +This method is used to delete an inventory level. It accepts the following parameters: + +1. `inventoryItemId`: it’s the first parameter and it’s required. It’s the ID of the inventory item that the inventory level should be associated with to be deleted. +2. `locationId`: it’s the second parameter and it’s required. It’s the ID of the stock location that the inventory level should be associated with to be deleted. +3. `context`: The third parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is not expected to return anything. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async deleteInventoryLevel( + inventoryItemId: string, + locationId: string, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryLevel = manager.getRepository( + CustomInventoryLevel + ) + + // TODO delete inventory level + // for example + await customInventoryLevel.delete({ + inventory_item_id: inventoryItemId, + location_id: locationId, + }) + } +} +``` + +Make sure to replace `CustomInventoryLevel` with your inventory level entity. + +### Implementing deleteInventoryItemLevelByLocationId Method + +This method is used to delete inventory levels associated with a location. It accepts the following parameters: + +1. `locationId`: it’s the first parameter and it’s required. It’s the ID of the stock location that the inventory levels should be associated with to be deleted. +2. `context`: The second parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is not expected to return anything. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async deleteInventoryItemLevelByLocationId( + locationId: string, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryLevel = manager.getRepository( + CustomInventoryLevel + ) + + // TODO delete inventory levels + // for example + await customInventoryLevel.delete({ + location_id: locationId, + }) + } +} +``` + +Make sure to replace `CustomInventoryLevel` with your inventory level entity. + +### Implementing adjustInventory Method + +This method is used to adjust the quantity of an inventory item in a location. It accepts the following parameters: + +1. `inventoryItemId`: It’s the first parameter and it’s required. It’s the ID of the inventory item that its quantity should be adjusted. +2. `locationId`: It’s the second parameter and it’s required. It’s the ID of the location that the inventory item’s quantity should be adjusted in. The relation with the location is done through the inventory level. +3. `adjustment`: It’s the third parameter and it’s required. It’s a number indicating how much should the quantity be increased or decreased. If the number is a positive number, then the quantity should be incremented by that number. If it’s a negative number, then the quantity should be decremented by that number. +4. `context`: The fourth parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return the updated location level. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async adjustInventory( + inventoryItemId: string, + locationId: string, + adjustment: number, + @MedusaContext()context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryLevel = manager.getRepository( + CustomInventoryLevel + ) + + // TODO adjust inventory level + // for example + const level = await this.retrieveInventoryLevel( + inventoryItemId, + locationId, + context + ) + customInventoryLevel.update({ + inventory_item_id: inventoryItemId, + location_id: locationId, + }, { + stocked_quantity: level.stocked_quantity + adjustment, + }) + + return await this.retrieveInventoryLevel( + inventoryItemId, + locationId, + context + ) + } +} +``` + +Make sure to replace `CustomInventoryLevel` with your inventory level entity. + +### Implementing confirmInventory Method + +This method is used to confirm whether an inventory item is available in stock. It accepts the following parameters: + +1. `inventoryItemId`: It’s the first parameter and it’s required. It’s the ID of the inventory item to confirm its availability. +2. `locationIds`: It’s the second parameter and it’s required. It’s an array of location IDs used to check the inventory item’s availability in them. +3. `quantity`: It’ the third parameter and it’s required. It’s the required quantity of the inventory item. +4. `context`: The fourth parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return a boolean value indicating whether the inventory item is in stock. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async confirmInventory( + inventoryItemId: string, + locationIds: string[], + quantity: number, + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryItem = manager.getRepository( + CustomInventoryItem + ) + + // TODO confirm inventory item's availability + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +### Implementing retrieveAvailableQuantity Method + +This method is used to retrieve the available quantity of an inventory item. You’re expected to subtract the reserved quantity from the stocked quantity in this method. + +This method accepts the following parameters: + +1. `inventoryItemId`: It’s the first parameter and it’s required. It’s the ID of the inventory item to retrieve its available quantity. +2. `locationIds`: It’s the second parameter and it’s required. It’s an array of location IDs used to retrieve the inventory item’s available quantity in them. +3. `context`: The third parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return a number being the available quantity of the item. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async retrieveAvailableQuantity( + inventoryItemId: string, + locationIds: string[], + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryItem = manager.getRepository( + CustomInventoryItem + ) + + // TODO retrieve inventory item's available quantity + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +### Implementing retrieveStockedQuantity Method + +This method is used to retrieve the stocked quantity of an inventory item. It accepts the following parameters: + +1. `inventoryItemId`: It’s the first parameter and it’s required. It’s the ID of the inventory item to retrieve its stocked quantity. +2. `locationIds`: It’s the second parameter and it’s required. It’s an array of location IDs used to retrieve the inventory item’s stocked quantity in them. +3. `context`: The third parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return a number being the stocked quantity of the item. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async retrieveStockedQuantity( + inventoryItemId: string, + locationIds: string[], + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryItem = manager.getRepository( + CustomInventoryItem + ) + + // TODO retrieve inventory item's stocked quantity + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +### Implementing retrieveReservedQuantity Method + +This method is used to retrieve the reserved quantity of an inventory item. It accepts the following parameters: + +1. `inventoryItemId`: It’s the first parameter and it’s required. It’s the ID of the inventory item to retrieve its reserved quantity. +2. `locationIds`: It’s the second parameter and it’s required. It’s an array of location IDs used to retrieve the inventory item’s reserved quantity in them. +3. `context`: The third parameter and it’s optional. It’s the same `context` parameter passed to the [listInventoryItems method](#implementing-listinventoryitems-method). + +This method is expected to return a number being the reserved quantity of the item. + +For example: + +```ts title=src/services/inventory.ts +class InventoryService implements IInventoryService { + // ... + @InjectEntityManager() + async retrieveReservedQuantity( + inventoryItemId: string, + locationIds: string[], + @MedusaContext() context?: SharedContext + ): Promise { + const manager = context.transactionManager! + const customInventoryItem = manager.getRepository( + CustomInventoryItem + ) + + // TODO retrieve inventory item's stocked quantity + } +} +``` + +Make sure to replace `CustomInventoryItem` with your inventory item entity. + +--- + +## Step 2: Use the Inventory 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 + +- [Inventory module architecture](../inventory-module.md) +- [How to create a stock location service](./create-stock-location-service.md) diff --git a/docs/content/modules/multiwarehouse/backend/create-stock-location-service.md b/docs/content/modules/multiwarehouse/backend/create-stock-location-service.md new file mode 100644 index 0000000000..de7981c7f5 --- /dev/null +++ b/docs/content/modules/multiwarehouse/backend/create-stock-location-service.md @@ -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, you’ll 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 you’ll 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 you’re adding your implementation): + +```bash npm2yarn +npm install @medusajs/types +``` + +You’ll 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 | undefined, + context?: SharedContext | undefined + ): Promise { + throw new Error("Method not implemented.") + } + async listAndCount( + selector: FilterableStockLocationProps, + config?: FindConfig | undefined, + context?: SharedContext | undefined + ): Promise<[StockLocationDTO[], number]> { + throw new Error("Method not implemented.") + } + async retrieve( + id: string, + config?: FindConfig | undefined, + context?: SharedContext | undefined + ): Promise { + throw new Error("Method not implemented.") + } + async create( + input: CreateStockLocationInput, + context?: SharedContext | undefined + ): Promise { + throw new Error("Method not implemented.") + } + async update( + id: string, + input: UpdateStockLocationInput, + context?: SharedContext | undefined + ): Promise { + throw new Error("Method not implemented.") + } + async delete( + id: string, + context?: SharedContext | undefined + ): Promise { + 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, you’ll 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, it’s 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. It’s 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 it’s an optional parameter. This parameter should be used to inject the transaction manager, as explained in the [Method Decorators section](#using-method-decorators). It’s 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 | null; + address_id: string; + address?: StockLocationAddressDTO; + created_at: string | Date; + updated_at: string | Date; + deleted_at: string | Date | null; +} +``` + +Here’s an example implementation of the method: + +```ts title=src/services/stock-location.ts +class StockLocationService implements IStockLocationService { + // ... + @InjectEntityManager() + async list( + selector: FilterableStockLocationProps, + config?: FindConfig | undefined, + @MedusaContext() context?: SharedContext | undefined + ): Promise { + 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. + +Here’s an example implementation of the method: + +```ts title=src/services/stock-location.ts +class StockLocationService implements IStockLocationService { + // ... + @InjectEntityManager() + async listAndCount( + selector: FilterableStockLocationProps, + config?: FindConfig | 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. It’s a string indicating the ID of the location to retrieve. +2. `config`: This is the second parameter and an optional parameter. It’s 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. It’s 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 | undefined, + @MedusaContext() context?: SharedContext | undefined + ): Promise { + 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 it’s 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 it’s the ID of the address to associate with this location. + 3. `address`: this property is optional, and it’s an object holding address properties including `address_1`, `country_code`, `city`, and more. + 4. `metadata`: this property is optional, and it’s 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. It’s 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 { + 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. It’s a string indicating the ID of the location to update. +2. `input`: this is the second parameter and is required. It’s 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. It’s 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 { + 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. It’s a string indicating the ID of the location to delete. +2. `context`: This is the second parameter and an optional parameter. It’s 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 { + 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) diff --git a/docs/content/modules/multiwarehouse/overview.mdx b/docs/content/modules/multiwarehouse/overview.mdx index f15a96bbd6..2ff20acf24 100644 --- a/docs/content/modules/multiwarehouse/overview.mdx +++ b/docs/content/modules/multiwarehouse/overview.mdx @@ -22,6 +22,29 @@ Multi-warehouse in Medusa is composed of two modules: an inventory module - whic } }} /> +You can also create your own modules and use them with the Medusa backend. + + + --- ## Features @@ -42,12 +65,11 @@ Admins can manage the stock locations, which are the places they store their pro }, { type: 'link', - href: '#', + 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.', - isSoon: true, } }, ]} /> @@ -68,12 +90,11 @@ Admins can manage the inventory of product variants across the different stock l }, { type: 'link', - href: '#', + 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.', - isSoon: true, } }, ]} /> @@ -94,12 +115,11 @@ Admins can manage item allocations to choose which stock location to fulfill ite }, { type: 'link', - href: '#', + 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.', - isSoon: true, } }, ]} /> diff --git a/www/docs/sidebars.js b/www/docs/sidebars.js index b547b94cd9..b7a7739490 100644 --- a/www/docs/sidebars.js +++ b/www/docs/sidebars.js @@ -932,28 +932,29 @@ module.exports = { }, }, { - type: "link", - href: "#", + type: "doc", + id: "modules/multiwarehouse/backend/create-inventory-service", + label: "Backend: Create Inventory Service", + }, + { + type: "doc", + id: "modules/multiwarehouse/backend/create-stock-location-service", + label: "Backend: Create Stock Location Service", + }, + { + type: "doc", + id: "modules/multiwarehouse/admin/manage-stock-locations", label: "Admin: Manage Stock Locations", - customProps: { - sidebar_is_soon: true, - }, }, { - type: "link", - href: "#", - label: "Admin: Manage Inventory", - customProps: { - sidebar_is_soon: true, - }, + type: "doc", + id: "modules/multiwarehouse/admin/manage-inventory-items", + label: "Admin: Manage Inventory Items", }, { - type: "link", - href: "#", + type: "doc", + id: "modules/multiwarehouse/admin/manage-item-allocations-in-orders", label: "Admin: Manage Allocations in Orders", - customProps: { - sidebar_is_soon: true, - }, }, ], }, @@ -2258,16 +2259,16 @@ module.exports = { }, }, { - type: 'doc', - id: 'js-client/overview', - label: 'Medusa JS Client', + type: "doc", + id: "js-client/overview", + label: "Medusa JS Client", customProps: { sidebar_is_title: true, - sidebar_icon: 'javascript', - } + sidebar_icon: "javascript", + }, }, { - type: 'category', + type: "category", collapsed: false, label: "Resources", customProps: { @@ -2275,177 +2276,177 @@ module.exports = { }, items: [ { - type: 'category', - label: 'admin', + type: "category", + label: "admin", collapsed: true, link: { - type: 'doc', - id: 'references/js-client/classes/Admin', + type: "doc", + id: "references/js-client/classes/Admin", }, items: [ { - type: 'doc', - id: 'references/js-client/classes/AdminAuthResource', - label: 'auth', + type: "doc", + id: "references/js-client/classes/AdminAuthResource", + label: "auth", }, { - type: 'doc', - id: 'references/js-client/classes/AdminBatchJobsResource', - label: 'batchJobs', + type: "doc", + id: "references/js-client/classes/AdminBatchJobsResource", + label: "batchJobs", }, { - type: 'doc', - id: 'references/js-client/classes/AdminCollectionsResource', - label: 'collections', + type: "doc", + id: "references/js-client/classes/AdminCollectionsResource", + label: "collections", }, { - type: 'doc', - id: 'references/js-client/classes/AdminCurrenciesResource', - label: 'currencies', + type: "doc", + id: "references/js-client/classes/AdminCurrenciesResource", + label: "currencies", }, { - type: 'doc', - id: 'references/js-client/classes/AdminCustomerGroupsResource', - label: 'customerGroups', + type: "doc", + id: "references/js-client/classes/AdminCustomerGroupsResource", + label: "customerGroups", }, { - type: 'doc', - id: 'references/js-client/classes/AdminCustomersResource', - label: 'customers', + type: "doc", + id: "references/js-client/classes/AdminCustomersResource", + label: "customers", }, { - type: 'doc', - id: 'references/js-client/classes/AdminDiscountsResource', - label: 'discounts', + type: "doc", + id: "references/js-client/classes/AdminDiscountsResource", + label: "discounts", }, { - type: 'doc', - id: 'references/js-client/classes/AdminDraftOrdersResource', - label: 'draftOrders', + type: "doc", + id: "references/js-client/classes/AdminDraftOrdersResource", + label: "draftOrders", }, { - type: 'doc', - id: 'references/js-client/classes/AdminGiftCardsResource', - label: 'giftCards', + type: "doc", + id: "references/js-client/classes/AdminGiftCardsResource", + label: "giftCards", }, { - type: 'doc', - id: 'references/js-client/classes/AdminInvitesResource', - label: 'invites', + type: "doc", + id: "references/js-client/classes/AdminInvitesResource", + label: "invites", }, { - type: 'doc', - id: 'references/js-client/classes/AdminNotesResource', - label: 'notes', + type: "doc", + id: "references/js-client/classes/AdminNotesResource", + label: "notes", }, { - type: 'doc', - id: 'references/js-client/classes/AdminNotificationsResource', - label: 'notifications', + type: "doc", + id: "references/js-client/classes/AdminNotificationsResource", + label: "notifications", }, { - type: 'doc', - id: 'references/js-client/classes/AdminOrdersResource', - label: 'orders', + type: "doc", + id: "references/js-client/classes/AdminOrdersResource", + label: "orders", }, { - type: 'doc', - id: 'references/js-client/classes/AdminOrderEditsResource', - label: 'orderEdits', + type: "doc", + id: "references/js-client/classes/AdminOrderEditsResource", + label: "orderEdits", }, { - type: 'doc', - id: 'references/js-client/classes/AdminPriceListResource', - label: 'priceLists', + type: "doc", + id: "references/js-client/classes/AdminPriceListResource", + label: "priceLists", }, { - type: 'doc', - id: 'references/js-client/classes/AdminProductsResource', - label: 'products', + type: "doc", + id: "references/js-client/classes/AdminProductsResource", + label: "products", }, { - type: 'doc', - id: 'references/js-client/classes/AdminProductTagsResource', - label: 'productTags', + type: "doc", + id: "references/js-client/classes/AdminProductTagsResource", + label: "productTags", }, { - type: 'doc', - id: 'references/js-client/classes/AdminProductTypesResource', - label: 'productTypes', + type: "doc", + id: "references/js-client/classes/AdminProductTypesResource", + label: "productTypes", }, { - type: 'doc', - id: 'references/js-client/classes/AdminRegionsResource', - label: 'regions', + type: "doc", + id: "references/js-client/classes/AdminRegionsResource", + label: "regions", }, { - type: 'doc', - id: 'references/js-client/classes/AdminReturnReasonsResource', - label: 'returnReasons', + type: "doc", + id: "references/js-client/classes/AdminReturnReasonsResource", + label: "returnReasons", }, { - type: 'doc', - id: 'references/js-client/classes/AdminReturnsResource', - label: 'returns', + type: "doc", + id: "references/js-client/classes/AdminReturnsResource", + label: "returns", }, { - type: 'doc', - id: 'references/js-client/classes/AdminSalesChannelsResource', - label: 'salesChannels', + type: "doc", + id: "references/js-client/classes/AdminSalesChannelsResource", + label: "salesChannels", }, { - type: 'doc', - id: 'references/js-client/classes/AdminShippingOptionsResource', - label: 'shippingOptions', + type: "doc", + id: "references/js-client/classes/AdminShippingOptionsResource", + label: "shippingOptions", }, { - type: 'doc', - id: 'references/js-client/classes/AdminShippingProfilesResource', - label: 'shippingProfiles', + type: "doc", + id: "references/js-client/classes/AdminShippingProfilesResource", + label: "shippingProfiles", }, { - type: 'doc', - id: 'references/js-client/classes/AdminStoresResource', - label: 'store', + type: "doc", + id: "references/js-client/classes/AdminStoresResource", + label: "store", }, { - type: 'doc', - id: 'references/js-client/classes/AdminSwapsResource', - label: 'swaps', + type: "doc", + id: "references/js-client/classes/AdminSwapsResource", + label: "swaps", }, { - type: 'doc', - id: 'references/js-client/classes/AdminTaxRatesResource', - label: 'taxRates', + type: "doc", + id: "references/js-client/classes/AdminTaxRatesResource", + label: "taxRates", }, { - type: 'doc', - id: 'references/js-client/classes/AdminUploadsResource', - label: 'uploads', + type: "doc", + id: "references/js-client/classes/AdminUploadsResource", + label: "uploads", }, { - type: 'doc', - id: 'references/js-client/classes/AdminUsersResource', - label: 'users', + type: "doc", + id: "references/js-client/classes/AdminUsersResource", + label: "users", }, { - type: 'doc', - id: 'references/js-client/classes/AdminVariantsResource', - label: 'variants', + type: "doc", + id: "references/js-client/classes/AdminVariantsResource", + label: "variants", }, ], }, { - type: 'doc', - id: 'references/js-client/classes/AuthResource', - label: 'auth', + type: "doc", + id: "references/js-client/classes/AuthResource", + label: "auth", }, { - type: 'category', - label: 'carts', + type: "category", + label: "carts", link: { - type: 'doc', - id: 'references/js-client/classes/CartsResource', + type: "doc", + id: "references/js-client/classes/CartsResource", }, collapsed: true, items: [ @@ -2457,17 +2458,17 @@ module.exports = { ], }, { - type: 'doc', - id: 'references/js-client/classes/CollectionsResource', - label: 'collections', + type: "doc", + id: "references/js-client/classes/CollectionsResource", + label: "collections", }, { - type: 'category', - label: 'customers', + type: "category", + label: "customers", collapsed: true, link: { - type: 'doc', - id: 'references/js-client/classes/CustomerResource', + type: "doc", + id: "references/js-client/classes/CustomerResource", }, items: [ { @@ -2483,31 +2484,31 @@ module.exports = { ], }, { - type: 'doc', - id: 'references/js-client/classes/GiftCardsResource', - label: 'giftCards', + type: "doc", + id: "references/js-client/classes/GiftCardsResource", + label: "giftCards", }, { - type: 'doc', - id: 'references/js-client/classes/OrdersResource', - label: 'orders', + type: "doc", + id: "references/js-client/classes/OrdersResource", + label: "orders", }, { - type: 'doc', - id: 'references/js-client/classes/OrderEditsResource', - label: 'orderEdits', + type: "doc", + id: "references/js-client/classes/OrderEditsResource", + label: "orderEdits", }, { - type: 'doc', - id: 'references/js-client/classes/PaymentMethodsResource', - label: 'paymentMethods', + type: "doc", + id: "references/js-client/classes/PaymentMethodsResource", + label: "paymentMethods", }, { - type: 'category', - label: 'products', + type: "category", + label: "products", link: { - type: 'doc', - id: 'references/js-client/classes/ProductsResource', + type: "doc", + id: "references/js-client/classes/ProductsResource", }, collapsed: true, items: [ @@ -2519,29 +2520,29 @@ module.exports = { ], }, { - type: 'doc', - id: 'references/js-client/classes/RegionsResource', - label: 'regions', + type: "doc", + id: "references/js-client/classes/RegionsResource", + label: "regions", }, { - type: 'doc', - id: 'references/js-client/classes/ReturnReasonsResource', - label: 'returnReasons', + type: "doc", + id: "references/js-client/classes/ReturnReasonsResource", + label: "returnReasons", }, { - type: 'doc', - id: 'references/js-client/classes/ReturnsResource', - label: 'returns', + type: "doc", + id: "references/js-client/classes/ReturnsResource", + label: "returns", }, { - type: 'doc', - id: 'references/js-client/classes/ShippingOptionsResource', - label: 'shippingOptions', + type: "doc", + id: "references/js-client/classes/ShippingOptionsResource", + label: "shippingOptions", }, { - type: 'doc', - id: 'references/js-client/classes/SwapsResource', - label: 'swaps', + type: "doc", + id: "references/js-client/classes/SwapsResource", + label: "swaps", }, ], },