feat(medusa-js, medusa-react, medusa): Prepare API for admin implementations (#3110)
********What********
Add `joinSalesChannels util to stock locations
Add the following endpoints to medusa-react
- inventory items
- mutations
- update
- delete
- update location level
- delete location level
- create location level
- queries
- list inventory items
- get inventory item
- list location levels
- Stock locations
- mutations
- create stock location
- update stock location
- delete stock location
- queries
- list stock locations
- get stock locatoin
- Variants
- queries
- get inventory
- Reservations
- mutations
- create reservation
- update reservation
- delete reservation
- queries
- list reservations
- get reservation
- sales channels
- mutations
- associate location with sc
- remove location association
**Why**
- Update clients to reflect new api endpoints in the core with inventory modules
Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
1 parent
121b42acfe
commit
12d304307a
48 files changed
+1753
-54
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/medusa-js": patch
|
||||||
|
"medusa-react": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa,medusa-js,medusa-react): Add inventory module endpoints
|
||||||
@@ -68,7 +68,7 @@ describe("Sales channels", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await salesChannelLocationService.listLocations(sc.id)
|
await salesChannelLocationService.listLocationIds(sc.id)
|
||||||
).toHaveLength(2)
|
).toHaveLength(2)
|
||||||
|
|
||||||
await api.delete(`/admin/sales-channels/${sc.id}`, {
|
await api.delete(`/admin/sales-channels/${sc.id}`, {
|
||||||
@@ -78,7 +78,7 @@ describe("Sales channels", () => {
|
|||||||
await expect(salesChannelService.retrieve(sc.id)).rejects.toThrowError()
|
await expect(salesChannelService.retrieve(sc.id)).rejects.toThrowError()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
salesChannelLocationService.listLocations(sc.id)
|
salesChannelLocationService.listLocationIds(sc.id)
|
||||||
).rejects.toThrowError()
|
).rejects.toThrowError()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ describe("Sales channels", () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await salesChannelLocationService.listLocations(saleChannel.id)
|
await salesChannelLocationService.listLocationIds(saleChannel.id)
|
||||||
).toHaveLength(1)
|
).toHaveLength(1)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await salesChannelLocationService.listLocations(otherChannel.id)
|
await salesChannelLocationService.listLocationIds(otherChannel.id)
|
||||||
).toHaveLength(1)
|
).toHaveLength(1)
|
||||||
|
|
||||||
await api.delete(`/admin/stock-locations/${loc.id}`, {
|
await api.delete(`/admin/stock-locations/${loc.id}`, {
|
||||||
@@ -82,11 +82,11 @@ describe("Sales channels", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await salesChannelLocationService.listLocations(saleChannel.id)
|
await salesChannelLocationService.listLocationIds(saleChannel.id)
|
||||||
).toHaveLength(0)
|
).toHaveLength(0)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await salesChannelLocationService.listLocations(otherChannel.id)
|
await salesChannelLocationService.listLocationIds(otherChannel.id)
|
||||||
).toHaveLength(0)
|
).toHaveLength(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ describe("Sales channels", () => {
|
|||||||
await salesChannelLocationService.associateLocation(sc.id, loc2.id)
|
await salesChannelLocationService.associateLocation(sc.id, loc2.id)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await salesChannelLocationService.listLocations(sc.id)
|
await salesChannelLocationService.listLocationIds(sc.id)
|
||||||
).toHaveLength(2)
|
).toHaveLength(2)
|
||||||
|
|
||||||
const [channels] = await salesChannelService.listAndCount(
|
const [channels] = await salesChannelService.listAndCount(
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import AdminUsersResource from "./users"
|
|||||||
import AdminVariantsResource from "./variants"
|
import AdminVariantsResource from "./variants"
|
||||||
import AdminPaymentCollectionsResource from "./payment-collections"
|
import AdminPaymentCollectionsResource from "./payment-collections"
|
||||||
import AdminPaymentsResource from "./payments"
|
import AdminPaymentsResource from "./payments"
|
||||||
|
import AdminInventoryItemsResource from "./inventory-item"
|
||||||
|
import AdminReservationsResource from "./reservations"
|
||||||
import AdminProductCategoriesResource from "./product-categories"
|
import AdminProductCategoriesResource from "./product-categories"
|
||||||
|
|
||||||
class Admin extends BaseResource {
|
class Admin extends BaseResource {
|
||||||
@@ -46,6 +48,7 @@ class Admin extends BaseResource {
|
|||||||
public draftOrders = new AdminDraftOrdersResource(this.client)
|
public draftOrders = new AdminDraftOrdersResource(this.client)
|
||||||
public giftCards = new AdminGiftCardsResource(this.client)
|
public giftCards = new AdminGiftCardsResource(this.client)
|
||||||
public invites = new AdminInvitesResource(this.client)
|
public invites = new AdminInvitesResource(this.client)
|
||||||
|
public inventoryItems = new AdminInventoryItemsResource(this.client)
|
||||||
public notes = new AdminNotesResource(this.client)
|
public notes = new AdminNotesResource(this.client)
|
||||||
public priceLists = new AdminPriceListResource(this.client)
|
public priceLists = new AdminPriceListResource(this.client)
|
||||||
public products = new AdminProductsResource(this.client)
|
public products = new AdminProductsResource(this.client)
|
||||||
@@ -65,6 +68,7 @@ class Admin extends BaseResource {
|
|||||||
public store = new AdminStoresResource(this.client)
|
public store = new AdminStoresResource(this.client)
|
||||||
public shippingOptions = new AdminShippingOptionsResource(this.client)
|
public shippingOptions = new AdminShippingOptionsResource(this.client)
|
||||||
public regions = new AdminRegionsResource(this.client)
|
public regions = new AdminRegionsResource(this.client)
|
||||||
|
public reservations = new AdminReservationsResource(this.client)
|
||||||
public notifications = new AdminNotificationsResource(this.client)
|
public notifications = new AdminNotificationsResource(this.client)
|
||||||
public taxRates = new AdminTaxRatesResource(this.client)
|
public taxRates = new AdminTaxRatesResource(this.client)
|
||||||
public uploads = new AdminUploadsResource(this.client)
|
public uploads = new AdminUploadsResource(this.client)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
AdminGetInventoryItemsItemParams,
|
AdminGetInventoryItemsItemParams,
|
||||||
AdminInventoryItemsListWithVariantsAndLocationLevelsRes,
|
AdminInventoryItemsListWithVariantsAndLocationLevelsRes,
|
||||||
AdminInventoryItemsLocationLevelsRes,
|
AdminInventoryItemsLocationLevelsRes,
|
||||||
|
AdminPostInventoryItemsItemLocationLevelsReq,
|
||||||
} from "@medusajs/medusa"
|
} from "@medusajs/medusa"
|
||||||
import { ResponsePromise } from "../../typings"
|
import { ResponsePromise } from "../../typings"
|
||||||
import BaseResource from "../base"
|
import BaseResource from "../base"
|
||||||
@@ -119,6 +120,29 @@ class AdminInventoryItemsResource extends BaseResource {
|
|||||||
return this.client.request("POST", path, payload, {}, customHeaders)
|
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create stock for an Inventory Item at a Stock Location
|
||||||
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
* To use this feature please install @medusajs/inventory
|
||||||
|
* @description creates stock levle for an Inventory Item
|
||||||
|
* @returns the Inventory Item
|
||||||
|
*/
|
||||||
|
createLocationLevel(
|
||||||
|
inventoryItemId: string,
|
||||||
|
payload: AdminPostInventoryItemsItemLocationLevelsReq,
|
||||||
|
query?: AdminGetInventoryItemsParams,
|
||||||
|
customHeaders: Record<string, any> = {}
|
||||||
|
): ResponsePromise<AdminInventoryItemsRes> {
|
||||||
|
let path = `/admin/inventory-items/${inventoryItemId}/location-levels`
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
const queryString = qs.stringify(query)
|
||||||
|
path += `?${queryString}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an Inventory Item from a Stock Location. This erases trace of any quantity currently at the location.
|
* Removes an Inventory Item from a Stock Location. This erases trace of any quantity currently at the location.
|
||||||
* @experimental This feature is under development and may change in the future.
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import {
|
||||||
|
AdminPostReservationsReq,
|
||||||
|
AdminPostReservationsReservationReq,
|
||||||
|
AdminReservationsDeleteRes,
|
||||||
|
AdminReservationsRes,
|
||||||
|
AdminGetReservationsParams,
|
||||||
|
AdminReservationsListRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import qs from "qs"
|
||||||
|
import { ResponsePromise } from "../../typings"
|
||||||
|
import BaseResource from "../base"
|
||||||
|
|
||||||
|
class AdminReservationsResource extends BaseResource {
|
||||||
|
/**
|
||||||
|
* Get a reservation
|
||||||
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
* To use this feature please install @medusajs/inventory
|
||||||
|
* @description gets a reservation
|
||||||
|
* @returns The reservation with the provided id
|
||||||
|
*/
|
||||||
|
retrieve(
|
||||||
|
id: string,
|
||||||
|
customHeaders: Record<string, unknown> = {}
|
||||||
|
): ResponsePromise<AdminReservationsRes> {
|
||||||
|
const path = `/admin/reservations/${id}`
|
||||||
|
return this.client.request("GET", path, undefined, {}, customHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List reservations
|
||||||
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
* To use this feature please install @medusajs/inventory
|
||||||
|
* @description Lists reservations
|
||||||
|
* @returns A list of reservations matching the provided query
|
||||||
|
*/
|
||||||
|
list(
|
||||||
|
query?: AdminGetReservationsParams,
|
||||||
|
customHeaders: Record<string, unknown> = {}
|
||||||
|
): ResponsePromise<AdminReservationsListRes> {
|
||||||
|
let path = `/admin/reservations`
|
||||||
|
|
||||||
|
if (query) {
|
||||||
|
const queryString = qs.stringify(query)
|
||||||
|
path += `?${queryString}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.client.request("GET", path, undefined, {}, customHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a reservation
|
||||||
|
* @description create a reservation
|
||||||
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
* To use this feature please install @medusajs/inventory
|
||||||
|
* @returns the created reservation
|
||||||
|
*/
|
||||||
|
create(
|
||||||
|
payload: AdminPostReservationsReq,
|
||||||
|
customHeaders: Record<string, unknown> = {}
|
||||||
|
): ResponsePromise<AdminReservationsRes> {
|
||||||
|
const path = `/admin/reservations`
|
||||||
|
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update a reservation
|
||||||
|
* @description update a reservation
|
||||||
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
* To use this feature please install @medusajs/inventory
|
||||||
|
* @returns The updated reservation
|
||||||
|
*/
|
||||||
|
update(
|
||||||
|
id: string,
|
||||||
|
payload: AdminPostReservationsReservationReq,
|
||||||
|
customHeaders: Record<string, unknown> = {}
|
||||||
|
): ResponsePromise<AdminReservationsRes> {
|
||||||
|
const path = `/admin/reservations/${id}`
|
||||||
|
return this.client.request("POST", path, payload, {}, customHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove a reservation
|
||||||
|
* @description remove a reservation
|
||||||
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
* To use this feature please install @medusajs/inventory
|
||||||
|
* @returns reservation removal confirmation
|
||||||
|
*/
|
||||||
|
delete(
|
||||||
|
id: string,
|
||||||
|
customHeaders: Record<string, unknown> = {}
|
||||||
|
): ResponsePromise<AdminReservationsDeleteRes> {
|
||||||
|
const path = `/admin/reservations/${id}`
|
||||||
|
return this.client.request("DELETE", path, undefined, {}, customHeaders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AdminReservationsResource
|
||||||
@@ -1140,6 +1140,46 @@
|
|||||||
"updated_at": "2021-03-17 12:09:59.156058+01",
|
"updated_at": "2021-03-17 12:09:59.156058+01",
|
||||||
"metadata": null
|
"metadata": null
|
||||||
},
|
},
|
||||||
|
"inventory_item": {
|
||||||
|
"id": "iitem_320ZXDBAB4WJVW6115VEM7",
|
||||||
|
"sku": "sku123",
|
||||||
|
"origin_country": "dk",
|
||||||
|
"hs_code": null,
|
||||||
|
"mid_code": null,
|
||||||
|
"material": null,
|
||||||
|
"weight": 10,
|
||||||
|
"lenght": 4,
|
||||||
|
"height": 5,
|
||||||
|
"width": 2,
|
||||||
|
"requires_shipping": true,
|
||||||
|
"created_at": "2021-03-17 11:58:56.975971+01",
|
||||||
|
"updated_at": "2021-03-17 12:09:59.156058+01",
|
||||||
|
"metadata": null,
|
||||||
|
"location_levels": [
|
||||||
|
{
|
||||||
|
"id": "ilev_320ZXDBAB4WJVW6115VEM7",
|
||||||
|
"inventory_item_id": "iitem_320ZXDBAB4WJVW6115VEM7",
|
||||||
|
"location_id": "loc_01F0ZXDBAB9KVZWJVW6115VEM7",
|
||||||
|
"stocked_quantity": 1,
|
||||||
|
"reserved_quantity": 0,
|
||||||
|
"incoming_quantity": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stock_location": {
|
||||||
|
"id": "loc_01F0ZXDBAB9KVZWJVW6115VEM7",
|
||||||
|
"name": "Stock location 1",
|
||||||
|
"address_id": "addr_01F0ZXDBAB9KVZWJVW6115VEM7",
|
||||||
|
"created_at": "2021-03-17 11:58:56.975971+01",
|
||||||
|
"updated_at": "2021-03-17 12:09:59.156058+01"
|
||||||
|
},
|
||||||
|
"reservation": {
|
||||||
|
"id": "res_01F0ZXDBAB9KVZWJVW6115VEM7",
|
||||||
|
"line_item_id": "li_01F0ZXDBAB9KVZWJVW6115VEM7",
|
||||||
|
"inventory_item_id": "iitem_320ZXDBAB4WJVW6115VEM7",
|
||||||
|
"location_id": "loc_01F0ZXDBAB9KVZWJVW6115VEM7",
|
||||||
|
"quantity": 1
|
||||||
|
},
|
||||||
"invite": {
|
"invite": {
|
||||||
"id": "invite_320ZXDBAB4WJVW6115VEM7",
|
"id": "invite_320ZXDBAB4WJVW6115VEM7",
|
||||||
"user_email": "lebron@james.com",
|
"user_email": "lebron@james.com",
|
||||||
|
|||||||
@@ -353,6 +353,61 @@ export const adminHandlers = [
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
|
rest.get("/admin/reservations/", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
reservations: fixtures.list("reservation"),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
rest.post("/admin/reservations/", (req, res, ctx) => {
|
||||||
|
const body = req.body as Record<string, any>
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
reservation: {
|
||||||
|
...fixtures.get("reservation"),
|
||||||
|
...body,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
rest.get("/admin/reservations/:id", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
reservation: { ...fixtures.get("reservation"), id: req.params.id },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.post("/admin/reservations/:id", (req, res, ctx) => {
|
||||||
|
const body = req.body as Record<string, any>
|
||||||
|
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
reservation: {
|
||||||
|
...fixtures.get("reservation"),
|
||||||
|
...body,
|
||||||
|
id: req.params.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.delete("/admin/reservations/:id", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
id: req.params.id,
|
||||||
|
object: "reservation",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
rest.post("/admin/return-reasons/", (req, res, ctx) => {
|
rest.post("/admin/return-reasons/", (req, res, ctx) => {
|
||||||
const body = req.body as Record<string, any>
|
const body = req.body as Record<string, any>
|
||||||
return res(
|
return res(
|
||||||
@@ -465,6 +520,61 @@ export const adminHandlers = [
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
rest.get("/admin/stock-locations", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
stock_locations: fixtures.list("stock_location"),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
rest.post("/admin/stock-locations", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
stock_location: {
|
||||||
|
...fixtures.get("stock_location"),
|
||||||
|
...(req.body as any),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
rest.get("/admin/stock-locations/:id", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
stock_location: {
|
||||||
|
...fixtures.get("stock_location"),
|
||||||
|
id: req.params.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.post("/admin/stock-locations/:id", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
stock_location: {
|
||||||
|
...fixtures.get("stock_location"),
|
||||||
|
...(req.body as any),
|
||||||
|
id: req.params.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.delete("/admin/stock-locations/:id", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
id: req.params.id,
|
||||||
|
object: "stock_location",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
rest.get("/admin/notifications/", (req, res, ctx) => {
|
rest.get("/admin/notifications/", (req, res, ctx) => {
|
||||||
return res(
|
return res(
|
||||||
ctx.status(200),
|
ctx.status(200),
|
||||||
@@ -487,6 +597,112 @@ export const adminHandlers = [
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// inventory
|
||||||
|
rest.get("/admin/inventory-items", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
inventory_items: fixtures.list("inventory_item"),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.get("/admin/inventory-items/:id", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
inventory_item: fixtures.get("inventory_item"),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.post("/admin/inventory-items/:id", (req, res, ctx) => {
|
||||||
|
const body = req.body as Record<string, any>
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
inventory_item: {
|
||||||
|
...fixtures.get("inventory_item"),
|
||||||
|
...body,
|
||||||
|
id: req.params.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.delete("/admin/inventory-items/:id", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
id: req.params.id,
|
||||||
|
object: "inventory_item",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.get("/admin/inventory-items/:id/location-levels", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
inventory_item: {
|
||||||
|
...fixtures.get("inventory_item"),
|
||||||
|
id: req.params.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.post("/admin/inventory-items/:id/location-levels", (req, res, ctx) => {
|
||||||
|
const body = req.body as Record<string, any>
|
||||||
|
const { location_levels } = fixtures.get("inventory_item")
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
inventory_item: {
|
||||||
|
...fixtures.get("inventory_item"),
|
||||||
|
id: req.params.id,
|
||||||
|
location_levels: [...location_levels, { ...body, id: "2" }],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
rest.post(
|
||||||
|
"/admin/inventory-items/:id/location-levels/:location_id",
|
||||||
|
(req, res, ctx) => {
|
||||||
|
const body = req.body as Record<string, any>
|
||||||
|
const inventoryItem = fixtures.get("inventory_item")
|
||||||
|
const locationlevel = { ...inventoryItem.location_levels[0], ...body }
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
inventory_item: {
|
||||||
|
...fixtures.get("inventory_item"),
|
||||||
|
id: req.params.id,
|
||||||
|
location_levels: [locationlevel],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
rest.delete(
|
||||||
|
"/admin/inventory-items/:id/location-levels/:location_id",
|
||||||
|
(req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
inventory_item: {
|
||||||
|
...fixtures.get("inventory_item"),
|
||||||
|
id: req.params.id,
|
||||||
|
location_levels: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
rest.get("/admin/invites", (req, res, ctx) => {
|
rest.get("/admin/invites", (req, res, ctx) => {
|
||||||
return res(
|
return res(
|
||||||
ctx.status(200),
|
ctx.status(200),
|
||||||
@@ -1189,6 +1405,24 @@ export const adminHandlers = [
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
rest.get("/admin/variants/:id/inventory", (req, res, ctx) => {
|
||||||
|
return res(
|
||||||
|
ctx.status(200),
|
||||||
|
ctx.json({
|
||||||
|
variant: {
|
||||||
|
...fixtures.get("product_variant"),
|
||||||
|
sales_channel_availability: [
|
||||||
|
{
|
||||||
|
channel_name: "default channel",
|
||||||
|
channel_id: "1",
|
||||||
|
available_quantity: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
rest.get("/admin/users/:id", (req, res, ctx) => {
|
rest.get("/admin/users/:id", (req, res, ctx) => {
|
||||||
return res(
|
return res(
|
||||||
ctx.status(200),
|
ctx.status(200),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Medusa from "@medusajs/medusa-js"
|
import Medusa from "@medusajs/medusa-js"
|
||||||
import {
|
import {
|
||||||
QueryClientProvider,
|
QueryClientProvider,
|
||||||
QueryClientProviderProps
|
QueryClientProviderProps,
|
||||||
} from "@tanstack/react-query"
|
} from "@tanstack/react-query"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export * from "./customers"
|
|||||||
export * from "./discounts"
|
export * from "./discounts"
|
||||||
export * from "./draft-orders"
|
export * from "./draft-orders"
|
||||||
export * from "./gift-cards"
|
export * from "./gift-cards"
|
||||||
|
export * from "./inventory-item"
|
||||||
export * from "./invites"
|
export * from "./invites"
|
||||||
export * from "./notes"
|
export * from "./notes"
|
||||||
export * from "./notifications"
|
export * from "./notifications"
|
||||||
@@ -21,6 +22,7 @@ export * from "./publishable-api-keys"
|
|||||||
export * from "./regions"
|
export * from "./regions"
|
||||||
export * from "./return-reasons"
|
export * from "./return-reasons"
|
||||||
export * from "./returns"
|
export * from "./returns"
|
||||||
|
export * from "./reservations"
|
||||||
export * from "./sales-channels"
|
export * from "./sales-channels"
|
||||||
export * from "./shipping-options"
|
export * from "./shipping-options"
|
||||||
export * from "./shipping-profiles"
|
export * from "./shipping-profiles"
|
||||||
@@ -32,3 +34,4 @@ export * from "./users"
|
|||||||
export * from "./variants"
|
export * from "./variants"
|
||||||
export * from "./payment-collections"
|
export * from "./payment-collections"
|
||||||
export * from "./payments"
|
export * from "./payments"
|
||||||
|
export * from "./stock-locations"
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./queries"
|
||||||
|
export * from "./mutations"
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import {
|
||||||
|
AdminInventoryItemsDeleteRes,
|
||||||
|
AdminInventoryItemsRes,
|
||||||
|
AdminPostInventoryItemsInventoryItemReq,
|
||||||
|
AdminPostInventoryItemsItemLocationLevelsLevelReq,
|
||||||
|
AdminPostInventoryItemsItemLocationLevelsReq,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import { Response } from "@medusajs/medusa-js"
|
||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
UseMutationOptions,
|
||||||
|
useQueryClient,
|
||||||
|
} from "@tanstack/react-query"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { buildOptions } from "../../utils/buildOptions"
|
||||||
|
import { adminInventoryItemsKeys } from "./queries"
|
||||||
|
|
||||||
|
// inventory item
|
||||||
|
|
||||||
|
// update inventory item
|
||||||
|
export const useAdminUpdateInventoryItem = (
|
||||||
|
inventoryItemId: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminInventoryItemsRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostInventoryItemsInventoryItemReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminPostInventoryItemsInventoryItemReq) =>
|
||||||
|
client.admin.inventoryItems.update(inventoryItemId, payload),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[adminInventoryItemsKeys.detail(inventoryItemId)],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete inventory item
|
||||||
|
export const useAdminDeleteInventoryItem = (
|
||||||
|
inventoryItemId: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminInventoryItemsDeleteRes>,
|
||||||
|
Error,
|
||||||
|
void
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
() => client.admin.inventoryItems.delete(inventoryItemId),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[adminInventoryItemsKeys.detail(inventoryItemId)],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// location level
|
||||||
|
export const useAdminUpdateLocationLevel = (
|
||||||
|
inventoryItemId: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminInventoryItemsRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostInventoryItemsItemLocationLevelsLevelReq & {
|
||||||
|
stockLocationId: string
|
||||||
|
}
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(
|
||||||
|
payload: AdminPostInventoryItemsItemLocationLevelsLevelReq & {
|
||||||
|
stockLocationId: string
|
||||||
|
}
|
||||||
|
) =>
|
||||||
|
client.admin.inventoryItems.updateLocationLevel(
|
||||||
|
inventoryItemId,
|
||||||
|
payload.stockLocationId,
|
||||||
|
{
|
||||||
|
incoming_quantity: payload.incoming_quantity,
|
||||||
|
stocked_quantity: payload.stocked_quantity,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[
|
||||||
|
adminInventoryItemsKeys.detail(inventoryItemId),
|
||||||
|
adminInventoryItemsKeys.lists(),
|
||||||
|
],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminDeleteLocationLevel = (
|
||||||
|
inventoryItemId: string,
|
||||||
|
options?: UseMutationOptions<Response<AdminInventoryItemsRes>, Error, string>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(stockLocationId: string) =>
|
||||||
|
client.admin.inventoryItems.deleteLocationLevel(
|
||||||
|
inventoryItemId,
|
||||||
|
stockLocationId
|
||||||
|
),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[
|
||||||
|
adminInventoryItemsKeys.detail(inventoryItemId),
|
||||||
|
adminInventoryItemsKeys.lists(),
|
||||||
|
],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminCreateLocationLevel = (
|
||||||
|
inventoryItemId: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminInventoryItemsRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostInventoryItemsItemLocationLevelsReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminPostInventoryItemsItemLocationLevelsReq) =>
|
||||||
|
client.admin.inventoryItems.createLocationLevel(inventoryItemId, payload),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[
|
||||||
|
adminInventoryItemsKeys.detail(inventoryItemId),
|
||||||
|
adminInventoryItemsKeys.lists(),
|
||||||
|
],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import {
|
||||||
|
AdminGetStockLocationsParams,
|
||||||
|
AdminInventoryItemsListWithVariantsAndLocationLevelsRes,
|
||||||
|
AdminInventoryItemsLocationLevelsRes,
|
||||||
|
AdminInventoryItemsRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import { Response } from "@medusajs/medusa-js"
|
||||||
|
import { useQuery } from "@tanstack/react-query"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { UseQueryOptionsWrapper } from "../../../types"
|
||||||
|
import { queryKeysFactory } from "../../utils"
|
||||||
|
|
||||||
|
const ADMIN_INVENTORY_ITEMS_QUERY_KEY = `admin_inventory_items` as const
|
||||||
|
|
||||||
|
export const adminInventoryItemsKeys = queryKeysFactory(
|
||||||
|
ADMIN_INVENTORY_ITEMS_QUERY_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryItemsQueryKeys = typeof adminInventoryItemsKeys
|
||||||
|
|
||||||
|
export const useAdminInventoryItems = (
|
||||||
|
query?: AdminGetStockLocationsParams,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminInventoryItemsListWithVariantsAndLocationLevelsRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<InventoryItemsQueryKeys["list"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminInventoryItemsKeys.list(query),
|
||||||
|
() => client.admin.inventoryItems.list(query),
|
||||||
|
{ ...options }
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminInventoryItem = (
|
||||||
|
inventoryItemId: string,
|
||||||
|
query?: AdminGetStockLocationsParams,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminInventoryItemsRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<InventoryItemsQueryKeys["detail"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminInventoryItemsKeys.detail(inventoryItemId),
|
||||||
|
() => client.admin.inventoryItems.retrieve(inventoryItemId, query),
|
||||||
|
{ ...options }
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminInventoryItemLocationLevels = (
|
||||||
|
inventoryItemId: string,
|
||||||
|
query?: AdminGetStockLocationsParams,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminInventoryItemsLocationLevelsRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<InventoryItemsQueryKeys["detail"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminInventoryItemsKeys.detail(inventoryItemId),
|
||||||
|
() =>
|
||||||
|
client.admin.inventoryItems.listLocationLevels(inventoryItemId, query),
|
||||||
|
{ ...options }
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./mutations"
|
||||||
|
export * from "./queries"
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import {
|
||||||
|
AdminPostReservationsReq,
|
||||||
|
AdminPostReservationsReservationReq,
|
||||||
|
AdminReservationsDeleteRes,
|
||||||
|
AdminReservationsRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import { Response } from "@medusajs/medusa-js/src"
|
||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
UseMutationOptions,
|
||||||
|
useQueryClient,
|
||||||
|
} from "@tanstack/react-query"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { buildOptions } from "../../utils/buildOptions"
|
||||||
|
import { adminReservationsKeys } from "./queries"
|
||||||
|
|
||||||
|
export const useAdminCreateReservation = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminReservationsRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostReservationsReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminPostReservationsReq) =>
|
||||||
|
client.admin.reservations.create(payload),
|
||||||
|
buildOptions(queryClient, [adminReservationsKeys.list()], options)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminUpdateReservation = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminReservationsRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostReservationsReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminPostReservationsReservationReq) =>
|
||||||
|
client.admin.reservations.update(id, payload),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[adminReservationsKeys.lists(), adminReservationsKeys.detail(id)],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminDeleteReservation = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminReservationsDeleteRes>,
|
||||||
|
Error,
|
||||||
|
void
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
() => client.admin.reservations.delete(id),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[adminReservationsKeys.lists(), adminReservationsKeys.detail(id)],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
AdminGetReservationsParams,
|
||||||
|
AdminReservationsListRes,
|
||||||
|
AdminReservationsRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import { Response } from "@medusajs/medusa-js"
|
||||||
|
import { useQuery } from "@tanstack/react-query"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { UseQueryOptionsWrapper } from "../../../types"
|
||||||
|
import { queryKeysFactory } from "../../utils"
|
||||||
|
|
||||||
|
const ADMIN_RESERVATIONS_QUERY_KEY = `admin_stock_locations` as const
|
||||||
|
|
||||||
|
export const adminReservationsKeys = queryKeysFactory(
|
||||||
|
ADMIN_RESERVATIONS_QUERY_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReservationsQueryKeys = typeof adminReservationsKeys
|
||||||
|
|
||||||
|
export const useAdminReservations = (
|
||||||
|
query?: AdminGetReservationsParams,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminReservationsListRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<ReservationsQueryKeys["list"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminReservationsKeys.list(query),
|
||||||
|
() => client.admin.reservations.list(query),
|
||||||
|
{ ...options }
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminReservation = (
|
||||||
|
id: string,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminReservationsRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<ReservationsQueryKeys["detail"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminReservationsKeys.detail(id),
|
||||||
|
() => client.admin.reservations.retrieve(id),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
import { useMedusa } from "../../../contexts"
|
import { useMedusa } from "../../../contexts"
|
||||||
import { buildOptions } from "../../utils/buildOptions"
|
import { buildOptions } from "../../utils/buildOptions"
|
||||||
import { adminProductKeys } from "../products"
|
import { adminProductKeys } from "../products"
|
||||||
|
import { adminStockLocationsKeys } from "../stock-locations"
|
||||||
import { adminSalesChannelsKeys } from "./queries"
|
import { adminSalesChannelsKeys } from "./queries"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,3 +163,73 @@ export const useAdminAddProductsToSalesChannel = (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a location to a sales channel
|
||||||
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
* To use this feature please install the stock location in your medusa backend project.
|
||||||
|
* @description Add a location to a sales channel
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export const useAdminAddLocationToSalesChannel = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminSalesChannelsRes>,
|
||||||
|
Error,
|
||||||
|
{
|
||||||
|
sales_channel_id: string
|
||||||
|
location_id: string
|
||||||
|
}
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
return useMutation(({ sales_channel_id, location_id }) => {
|
||||||
|
return client.admin.salesChannels.addLocation(sales_channel_id, {
|
||||||
|
location_id,
|
||||||
|
})
|
||||||
|
}, buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[
|
||||||
|
adminSalesChannelsKeys.lists(),
|
||||||
|
adminSalesChannelsKeys.details(),
|
||||||
|
adminStockLocationsKeys.all
|
||||||
|
],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a location from a sales channel
|
||||||
|
* @experimental This feature is under development and may change in the future.
|
||||||
|
* To use this feature please install the stock location in your medusa backend project.
|
||||||
|
* @description Remove a location from a sales channel
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
export const useAdminRemoveLocationFromSalesChannel = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminSalesChannelsRes>,
|
||||||
|
Error,
|
||||||
|
{
|
||||||
|
sales_channel_id: string
|
||||||
|
location_id: string
|
||||||
|
}
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
return useMutation(({ sales_channel_id, location_id }) => {
|
||||||
|
return client.admin.salesChannels.removeLocation(sales_channel_id, {
|
||||||
|
location_id,
|
||||||
|
})
|
||||||
|
}, buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[
|
||||||
|
adminSalesChannelsKeys.lists(),
|
||||||
|
adminSalesChannelsKeys.details(),
|
||||||
|
adminStockLocationsKeys.all
|
||||||
|
],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./queries"
|
||||||
|
export * from "./mutations"
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import {
|
||||||
|
AdminPostStockLocationsReq,
|
||||||
|
AdminStockLocationsDeleteRes,
|
||||||
|
AdminStockLocationsRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import { Response } from "@medusajs/medusa-js"
|
||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
UseMutationOptions,
|
||||||
|
useQueryClient,
|
||||||
|
} from "@tanstack/react-query"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { buildOptions } from "../../utils/buildOptions"
|
||||||
|
import { adminStockLocationsKeys } from "./queries"
|
||||||
|
|
||||||
|
export const useAdminCreateStockLocation = (
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminStockLocationsRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostStockLocationsReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminPostStockLocationsReq) =>
|
||||||
|
client.admin.stockLocations.create(payload),
|
||||||
|
buildOptions(queryClient, [adminStockLocationsKeys.lists()], options)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminUpdateStockLocation = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminStockLocationsRes>,
|
||||||
|
Error,
|
||||||
|
AdminPostStockLocationsReq
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
(payload: AdminPostStockLocationsReq) =>
|
||||||
|
client.admin.stockLocations.update(id, payload),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[adminStockLocationsKeys.lists(), adminStockLocationsKeys.detail(id)],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminDeleteStockLocation = (
|
||||||
|
id: string,
|
||||||
|
options?: UseMutationOptions<
|
||||||
|
Response<AdminStockLocationsDeleteRes>,
|
||||||
|
Error,
|
||||||
|
void
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
|
return useMutation(
|
||||||
|
() => client.admin.stockLocations.delete(id),
|
||||||
|
buildOptions(
|
||||||
|
queryClient,
|
||||||
|
[adminStockLocationsKeys.lists(), adminStockLocationsKeys.detail(id)],
|
||||||
|
options
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
AdminGetStockLocationsParams,
|
||||||
|
AdminStockLocationsListRes,
|
||||||
|
AdminStockLocationsRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
|
import { Response } from "@medusajs/medusa-js"
|
||||||
|
import { useQuery } from "@tanstack/react-query"
|
||||||
|
import { useMedusa } from "../../../contexts"
|
||||||
|
import { UseQueryOptionsWrapper } from "../../../types"
|
||||||
|
import { queryKeysFactory } from "../../utils"
|
||||||
|
|
||||||
|
const ADMIN_STOCK_LOCATIONS_QUERY_KEY = `admin_stock_locations` as const
|
||||||
|
|
||||||
|
export const adminStockLocationsKeys = queryKeysFactory(
|
||||||
|
ADMIN_STOCK_LOCATIONS_QUERY_KEY
|
||||||
|
)
|
||||||
|
|
||||||
|
type StockLocationsQueryKeys = typeof adminStockLocationsKeys
|
||||||
|
|
||||||
|
export const useAdminStockLocations = (
|
||||||
|
query?: AdminGetStockLocationsParams,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminStockLocationsListRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<StockLocationsQueryKeys["list"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminStockLocationsKeys.list(query),
|
||||||
|
() => client.admin.stockLocations.list(query),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAdminStockLocation = (
|
||||||
|
id: string,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminStockLocationsRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<StockLocationsQueryKeys["detail"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminStockLocationsKeys.detail(id),
|
||||||
|
() => client.admin.stockLocations.retrieve(id),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
import { AdminGetVariantsParams, AdminVariantsListRes } from "@medusajs/medusa"
|
import {
|
||||||
|
AdminGetVariantsParams,
|
||||||
|
AdminGetVariantsVariantInventoryRes,
|
||||||
|
AdminVariantsListRes,
|
||||||
|
} from "@medusajs/medusa"
|
||||||
import { Response } from "@medusajs/medusa-js"
|
import { Response } from "@medusajs/medusa-js"
|
||||||
import { useQuery } from "@tanstack/react-query"
|
import { useQuery } from "@tanstack/react-query"
|
||||||
import { useMedusa } from "../../../contexts"
|
import { useMedusa } from "../../../contexts"
|
||||||
@@ -27,3 +31,20 @@ export const useAdminVariants = (
|
|||||||
)
|
)
|
||||||
return { ...data, ...rest } as const
|
return { ...data, ...rest } as const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useAdminVariantsInventory = (
|
||||||
|
id: string,
|
||||||
|
options?: UseQueryOptionsWrapper<
|
||||||
|
Response<AdminGetVariantsVariantInventoryRes>,
|
||||||
|
Error,
|
||||||
|
ReturnType<VariantQueryKeys["detail"]>
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { client } = useMedusa()
|
||||||
|
const { data, ...rest } = useQuery(
|
||||||
|
adminVariantKeys.detail(id),
|
||||||
|
() => client.admin.variants.getInventory(id),
|
||||||
|
options
|
||||||
|
)
|
||||||
|
return { ...data, ...rest } as const
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import {
|
||||||
|
useAdminUpdateInventoryItem,
|
||||||
|
useAdminDeleteInventoryItem,
|
||||||
|
useAdminUpdateLocationLevel,
|
||||||
|
useAdminDeleteLocationLevel,
|
||||||
|
useAdminCreateLocationLevel,
|
||||||
|
} from "../../../../src/"
|
||||||
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useAdminUpdateInventoryItem hook", () => {
|
||||||
|
test("updates an inventory item", async () => {
|
||||||
|
const payload = {
|
||||||
|
sku: "test-sku",
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminUpdateInventoryItem("inventory-item-id"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate(payload)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.inventory_item).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "inventory-item-id",
|
||||||
|
sku: "test-sku",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminDeleteInventoryItem hook", () => {
|
||||||
|
test("Deletes an inventory item", async () => {
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminDeleteInventoryItem("inventory-item-id"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate()
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "inventory-item-id",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminUpdateLocationLevel hook", () => {
|
||||||
|
test("Updates a location level", async () => {
|
||||||
|
const payload = {
|
||||||
|
incoming_quantity: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminUpdateLocationLevel("inventory-item-id"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate({ ...payload, stockLocationId: "location_id"})
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.inventory_item).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "inventory-item-id",
|
||||||
|
location_levels: [
|
||||||
|
expect.objectContaining({
|
||||||
|
incoming_quantity: 10,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminDeleteLocationLevel hook", () => {
|
||||||
|
test("removes a location level", async () => {
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminDeleteLocationLevel("inventory-item-id"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate("location_id")
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.inventory_item).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "inventory-item-id",
|
||||||
|
location_levels: [],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminCreateLocationLevel hook", () => {
|
||||||
|
test("creates a location level", async () => {
|
||||||
|
const payload = {
|
||||||
|
location_id: "loc_1",
|
||||||
|
incoming_quantity: 10,
|
||||||
|
stocked_quantity: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminCreateLocationLevel("inventory-item-id"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate(payload)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.inventory_item).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "inventory-item-id",
|
||||||
|
location_levels: expect.arrayContaining([
|
||||||
|
expect.objectContaining({ ...payload }),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import {
|
||||||
|
useAdminInventoryItem,
|
||||||
|
useAdminInventoryItemLocationLevels,
|
||||||
|
useAdminInventoryItems,
|
||||||
|
useAdminPriceList,
|
||||||
|
useAdminPriceLists,
|
||||||
|
} from "../../../../src"
|
||||||
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
import { fixtures } from "../../../../mocks/data"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useAdminInventoryItems hook", () => {
|
||||||
|
test("returns a list of inventory items", async () => {
|
||||||
|
const inventoryItems = fixtures.list("inventory_item")
|
||||||
|
const { result, waitFor } = renderHook(() => useAdminInventoryItems(), {
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
})
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.inventory_items).toEqual(inventoryItems)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminInventoryItem hook", () => {
|
||||||
|
test("returns a single inventory item", async () => {
|
||||||
|
const inventoryItem = fixtures.get("inventory_item")
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminInventoryItem(inventoryItem.id),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.inventory_item).toEqual(inventoryItem)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminInventoryItem hook", () => {
|
||||||
|
test("returns a location levels for an inventory item", async () => {
|
||||||
|
const inventoryItem = fixtures.get("inventory_item")
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminInventoryItemLocationLevels(inventoryItem.id),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.inventory_item).toEqual(inventoryItem)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminPriceList hook", () => {
|
||||||
|
test("returns a price list", async () => {
|
||||||
|
const priceList = fixtures.get("price_list")
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminPriceList(priceList.id),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.price_list).toEqual(priceList)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import {
|
||||||
|
useAdminCreateShippingProfile,
|
||||||
|
useAdminUpdateShippingProfile,
|
||||||
|
useAdminDeleteShippingProfile,
|
||||||
|
useAdminCreateReservation,
|
||||||
|
useAdminUpdateReservation,
|
||||||
|
useAdminDeleteReservation,
|
||||||
|
} from "../../../../src/"
|
||||||
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
import { fixtures } from "../../../../mocks/data"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useAdminCreateShippingProfile hook", () => {
|
||||||
|
test("creates a shipping profile and returns it", async () => {
|
||||||
|
const reservationPayload = {
|
||||||
|
location_id: "loc_1",
|
||||||
|
inventory_item_id: "inv_1",
|
||||||
|
quantity: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(() => useAdminCreateReservation(), {
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
})
|
||||||
|
|
||||||
|
result.current.mutate(reservationPayload)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.reservation).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
...fixtures.get("reservation"),
|
||||||
|
...reservationPayload,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminUpdateShippingProfile hook", () => {
|
||||||
|
test("updates a shipping profile and returns it", async () => {
|
||||||
|
const reservationPayload = {
|
||||||
|
quantity: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminUpdateReservation(fixtures.get("reservation").id),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate(reservationPayload)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.reservation).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
...fixtures.get("reservation"),
|
||||||
|
quantity: 3,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminDeleteShippingProfile hook", () => {
|
||||||
|
test("deletes a shipping profile", async () => {
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminDeleteReservation(fixtures.get("reservation").id),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate()
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: fixtures.get("reservation").id,
|
||||||
|
object: "reservation",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { useAdminReservation, useAdminReservations } from "../../../../src"
|
||||||
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
import { fixtures } from "../../../../mocks/data"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useAdminShippingProfiles hook", () => {
|
||||||
|
test("returns a list of shipping profiles", async () => {
|
||||||
|
const reservations = fixtures.list("reservation")
|
||||||
|
const { result, waitFor } = renderHook(() => useAdminReservations(), {
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
})
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.reservations).toEqual(reservations)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminShippingProfile hook", () => {
|
||||||
|
test("returns a shipping profile", async () => {
|
||||||
|
const reservation = fixtures.get("reservation")
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminReservation(reservation.id),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.reservation).toEqual(reservation)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -97,15 +97,15 @@ describe("useAdminDeleteProductsFromSalesChannel hook", () => {
|
|||||||
{ wrapper: createWrapper() }
|
{ wrapper: createWrapper() }
|
||||||
)
|
)
|
||||||
|
|
||||||
result.current.mutate({ product_ids: [
|
result.current.mutate({ product_ids: [{ id: productId }] })
|
||||||
{ id: productId }
|
|
||||||
]})
|
|
||||||
|
|
||||||
await waitFor(() => result.current.isSuccess)
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
expect(result.current.data).toEqual(expect.objectContaining({
|
expect(result.current.data).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
sales_channel: fixtures.get("sales_channel"),
|
sales_channel: fixtures.get("sales_channel"),
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -119,14 +119,14 @@ describe("useAdminAddProductsToSalesChannel hook", () => {
|
|||||||
{ wrapper: createWrapper() }
|
{ wrapper: createWrapper() }
|
||||||
)
|
)
|
||||||
|
|
||||||
result.current.mutate({ product_ids: [
|
result.current.mutate({ product_ids: [{ id: productId }] })
|
||||||
{ id: productId }
|
|
||||||
]})
|
|
||||||
|
|
||||||
await waitFor(() => result.current.isSuccess)
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
expect(result.current.data).toEqual(expect.objectContaining({
|
expect(result.current.data).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
sales_channel: fixtures.get("sales_channel"),
|
sales_channel: fixtures.get("sales_channel"),
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
import { fixtures } from "../../../../mocks/data"
|
||||||
|
import {
|
||||||
|
useAdminCreateStockLocation,
|
||||||
|
useAdminDeleteStockLocation,
|
||||||
|
useAdminUpdateStockLocation,
|
||||||
|
} from "../../../../src"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useAdminUpdateStockLocation hook", () => {
|
||||||
|
test("updates a stock location", async () => {
|
||||||
|
const payload = {
|
||||||
|
name: "updated name",
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminUpdateStockLocation("stock-location-id"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate(payload)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.stock_location).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "stock-location-id",
|
||||||
|
name: "updated name",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminCreateStockLocation hook", () => {
|
||||||
|
test("creates a stock location", async () => {
|
||||||
|
const locationFixture = fixtures.get("stock_location")
|
||||||
|
const payload = {
|
||||||
|
name: "updated name",
|
||||||
|
}
|
||||||
|
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminCreateStockLocation(),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate(payload)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data.stock_location).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
...locationFixture,
|
||||||
|
...payload,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminDeleteStockLocation hook", () => {
|
||||||
|
test("deletes a stock location", async () => {
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminDeleteStockLocation("stock-location-id"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result.current.mutate()
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.data.response.status).toEqual(200)
|
||||||
|
expect(result.current.data).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "stock-location-id",
|
||||||
|
object: "stock_location",
|
||||||
|
deleted: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
|
import { fixtures } from "../../../../mocks/data"
|
||||||
|
import { useAdminStockLocation, useAdminStockLocations } from "../../../../src"
|
||||||
|
import { createWrapper } from "../../../utils"
|
||||||
|
|
||||||
|
describe("useAdminUpdateStockLocation hook", () => {
|
||||||
|
test("gets a stock location", async () => {
|
||||||
|
const stockLocation = fixtures.get("stock_location")
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminStockLocation("stock-location-id"),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.stock_location).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
...stockLocation,
|
||||||
|
id: "stock-location-id",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("useAdminUpdateStockLocations hook", () => {
|
||||||
|
test("lists stock locations", async () => {
|
||||||
|
const stockLocation = fixtures.list("stock_location")
|
||||||
|
const { result, waitFor } = renderHook(() => useAdminStockLocations(), {
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
})
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.stock_locations).toEqual(stockLocation)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useAdminVariants } from "../../../../src"
|
import { useAdminVariants, useAdminVariantsInventory } from "../../../../src"
|
||||||
import { renderHook } from "@testing-library/react-hooks"
|
import { renderHook } from "@testing-library/react-hooks"
|
||||||
import { fixtures } from "../../../../mocks/data"
|
import { fixtures } from "../../../../mocks/data"
|
||||||
import { createWrapper } from "../../../utils"
|
import { createWrapper } from "../../../utils"
|
||||||
@@ -16,3 +16,29 @@ describe("useAdminVariants hook", () => {
|
|||||||
expect(result.current.variants).toEqual(variants)
|
expect(result.current.variants).toEqual(variants)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("useAdminVariants hook", () => {
|
||||||
|
test("returns a variant with saleschannel locations", async () => {
|
||||||
|
const variant = fixtures.get("product_variant")
|
||||||
|
const { result, waitFor } = renderHook(
|
||||||
|
() => useAdminVariantsInventory(variant.id),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await waitFor(() => result.current.isSuccess)
|
||||||
|
|
||||||
|
expect(result.current.response.status).toEqual(200)
|
||||||
|
expect(result.current.variant).toEqual({
|
||||||
|
...variant,
|
||||||
|
sales_channel_availability: [
|
||||||
|
{
|
||||||
|
channel_name: "default channel",
|
||||||
|
channel_id: "1",
|
||||||
|
available_quantity: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -43,6 +43,7 @@ export * from "./routes/admin/publishable-api-keys"
|
|||||||
export * from "./routes/admin/regions"
|
export * from "./routes/admin/regions"
|
||||||
export * from "./routes/admin/return-reasons"
|
export * from "./routes/admin/return-reasons"
|
||||||
export * from "./routes/admin/returns"
|
export * from "./routes/admin/returns"
|
||||||
|
export * from "./routes/admin/reservations"
|
||||||
export * from "./routes/admin/sales-channels"
|
export * from "./routes/admin/sales-channels"
|
||||||
export * from "./routes/admin/shipping-options"
|
export * from "./routes/admin/shipping-options"
|
||||||
export * from "./routes/admin/shipping-profiles"
|
export * from "./routes/admin/shipping-profiles"
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ export default async (req, res) => {
|
|||||||
* @schema AdminPostReservationsReq
|
* @schema AdminPostReservationsReq
|
||||||
* type: object
|
* type: object
|
||||||
* required:
|
* required:
|
||||||
* - line_item_id
|
|
||||||
* - location_id
|
* - location_id
|
||||||
* - inventory_item_id
|
* - inventory_item_id
|
||||||
* - quantity
|
* - quantity
|
||||||
|
|||||||
@@ -109,3 +109,4 @@ export * from "./create-reservation"
|
|||||||
export * from "./delete-reservation"
|
export * from "./delete-reservation"
|
||||||
export * from "./get-reservation"
|
export * from "./get-reservation"
|
||||||
export * from "./update-reservation"
|
export * from "./update-reservation"
|
||||||
|
export * from "./list-reservations"
|
||||||
@@ -15,6 +15,7 @@ import { AdminGetSalesChannelsParams } from "./list-sales-channels"
|
|||||||
import { AdminPostSalesChannelsSalesChannelReq } from "./update-sales-channel"
|
import { AdminPostSalesChannelsSalesChannelReq } from "./update-sales-channel"
|
||||||
import { AdminPostSalesChannelsChannelStockLocationsReq } from "./associate-stock-location"
|
import { AdminPostSalesChannelsChannelStockLocationsReq } from "./associate-stock-location"
|
||||||
import { AdminDeleteSalesChannelsChannelStockLocationsReq } from "./remove-stock-location"
|
import { AdminDeleteSalesChannelsChannelStockLocationsReq } from "./remove-stock-location"
|
||||||
|
import { checkRegisteredModules } from "../../../middlewares/check-registered-modules"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
@@ -47,11 +48,19 @@ export default (app) => {
|
|||||||
)
|
)
|
||||||
salesChannelRouter.post(
|
salesChannelRouter.post(
|
||||||
"/stock-locations",
|
"/stock-locations",
|
||||||
|
checkRegisteredModules({
|
||||||
|
stockLocationService:
|
||||||
|
"Stock Locations are not enabled. Please add a Stock Location module to enable this functionality.",
|
||||||
|
}),
|
||||||
transformBody(AdminPostSalesChannelsChannelStockLocationsReq),
|
transformBody(AdminPostSalesChannelsChannelStockLocationsReq),
|
||||||
middlewares.wrap(require("./associate-stock-location").default)
|
middlewares.wrap(require("./associate-stock-location").default)
|
||||||
)
|
)
|
||||||
salesChannelRouter.delete(
|
salesChannelRouter.delete(
|
||||||
"/stock-locations",
|
"/stock-locations",
|
||||||
|
checkRegisteredModules({
|
||||||
|
stockLocationService:
|
||||||
|
"Stock Locations are not enabled. Please add a Stock Location module to enable this functionality.",
|
||||||
|
}),
|
||||||
transformBody(AdminDeleteSalesChannelsChannelStockLocationsReq),
|
transformBody(AdminDeleteSalesChannelsChannelStockLocationsReq),
|
||||||
middlewares.wrap(require("./remove-stock-location").default)
|
middlewares.wrap(require("./remove-stock-location").default)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { IStockLocationService } from "../../../../interfaces"
|
import { IStockLocationService } from "../../../../interfaces"
|
||||||
import { Request, Response } from "express"
|
import { Request, Response } from "express"
|
||||||
import { FindParams } from "../../../../types/common"
|
import { FindParams } from "../../../../types/common"
|
||||||
|
import { joinSalesChannels } from "./utils/join-sales-channels"
|
||||||
|
import {
|
||||||
|
SalesChannelLocationService,
|
||||||
|
SalesChannelService,
|
||||||
|
} from "../../../../services"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /stock-locations/{id}
|
* @oas [get] /stock-locations/{id}
|
||||||
@@ -50,7 +55,34 @@ export default async (req: Request, res: Response) => {
|
|||||||
const locationService: IStockLocationService = req.scope.resolve(
|
const locationService: IStockLocationService = req.scope.resolve(
|
||||||
"stockLocationService"
|
"stockLocationService"
|
||||||
)
|
)
|
||||||
const stockLocation = await locationService.retrieve(id, req.retrieveConfig)
|
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
|
||||||
|
"salesChannelLocationService"
|
||||||
|
)
|
||||||
|
const salesChannelService: SalesChannelService = req.scope.resolve(
|
||||||
|
"salesChannelService"
|
||||||
|
)
|
||||||
|
|
||||||
|
const { retrieveConfig } = req
|
||||||
|
|
||||||
|
const includeSalesChannels =
|
||||||
|
!!retrieveConfig.relations?.includes("sales_channels")
|
||||||
|
|
||||||
|
if (includeSalesChannels) {
|
||||||
|
retrieveConfig.relations = retrieveConfig.relations?.filter(
|
||||||
|
(r) => r !== "sales_channels"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let stockLocation = await locationService.retrieve(id, retrieveConfig)
|
||||||
|
|
||||||
|
if (includeSalesChannels) {
|
||||||
|
const [location] = await joinSalesChannels(
|
||||||
|
[stockLocation],
|
||||||
|
channelLocationService,
|
||||||
|
salesChannelService
|
||||||
|
)
|
||||||
|
stockLocation = location
|
||||||
|
}
|
||||||
|
|
||||||
res.status(200).json({ stock_location: stockLocation })
|
res.status(200).json({ stock_location: stockLocation })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Router } from "express"
|
import { Router } from "express"
|
||||||
import "reflect-metadata"
|
import "reflect-metadata"
|
||||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||||
import { StockLocationDTO } from "../../../../types/stock-location"
|
import {
|
||||||
|
StockLocationDTO,
|
||||||
|
StockLocationExpandedDTO,
|
||||||
|
} from "../../../../types/stock-location"
|
||||||
import middlewares, {
|
import middlewares, {
|
||||||
transformBody,
|
transformBody,
|
||||||
transformQuery,
|
transformQuery,
|
||||||
@@ -113,10 +116,10 @@ export type AdminStockLocationsDeleteRes = DeleteResponse
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
* stock_location:
|
* stock_location:
|
||||||
* $ref: "#/components/schemas/StockLocationDTO"
|
* $ref: "#/components/schemas/StockLocationExpandedDTO"
|
||||||
*/
|
*/
|
||||||
export type AdminStockLocationsRes = {
|
export type AdminStockLocationsRes = {
|
||||||
stock_location: StockLocationDTO
|
stock_location: StockLocationExpandedDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,7 +129,7 @@ export type AdminStockLocationsRes = {
|
|||||||
* stock_locations:
|
* stock_locations:
|
||||||
* type: array
|
* type: array
|
||||||
* items:
|
* items:
|
||||||
* $ref: "#/components/schemas/StockLocationDTO"
|
* $ref: "#/components/schemas/StockLocationExpandedDTO"
|
||||||
* count:
|
* count:
|
||||||
* type: integer
|
* type: integer
|
||||||
* description: The total number of items available
|
* description: The total number of items available
|
||||||
@@ -138,7 +141,7 @@ export type AdminStockLocationsRes = {
|
|||||||
* description: The number of items per page
|
* description: The number of items per page
|
||||||
*/
|
*/
|
||||||
export type AdminStockLocationsListRes = PaginatedResponse & {
|
export type AdminStockLocationsListRes = PaginatedResponse & {
|
||||||
stock_locations: StockLocationDTO[]
|
stock_locations: StockLocationExpandedDTO[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from "./list-stock-locations"
|
export * from "./list-stock-locations"
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import { IsType } from "../../../../utils/validators/is-type"
|
|||||||
import { IStockLocationService } from "../../../../interfaces"
|
import { IStockLocationService } from "../../../../interfaces"
|
||||||
import { extendedFindParamsMixin } from "../../../../types/common"
|
import { extendedFindParamsMixin } from "../../../../types/common"
|
||||||
import { Request, Response } from "express"
|
import { Request, Response } from "express"
|
||||||
|
import {
|
||||||
|
SalesChannelLocationService,
|
||||||
|
SalesChannelService,
|
||||||
|
} from "../../../../services"
|
||||||
|
import { joinSalesChannels } from "./utils/join-sales-channels"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /stock-locations
|
* @oas [get] /stock-locations
|
||||||
@@ -133,15 +138,38 @@ export default async (req: Request, res: Response) => {
|
|||||||
const stockLocationService: IStockLocationService = req.scope.resolve(
|
const stockLocationService: IStockLocationService = req.scope.resolve(
|
||||||
"stockLocationService"
|
"stockLocationService"
|
||||||
)
|
)
|
||||||
|
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
|
||||||
|
"salesChannelLocationService"
|
||||||
|
)
|
||||||
|
const salesChannelService: SalesChannelService = req.scope.resolve(
|
||||||
|
"salesChannelService"
|
||||||
|
)
|
||||||
|
|
||||||
const { filterableFields, listConfig } = req
|
const { filterableFields, listConfig } = req
|
||||||
const { skip, take } = listConfig
|
const { skip, take } = listConfig
|
||||||
|
|
||||||
const [locations, count] = await stockLocationService.listAndCount(
|
const includeSalesChannels =
|
||||||
|
!!listConfig.relations?.includes("sales_channels")
|
||||||
|
|
||||||
|
if (includeSalesChannels) {
|
||||||
|
listConfig.relations = listConfig.relations?.filter(
|
||||||
|
(r) => r !== "sales_channels"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let [locations, count] = await stockLocationService.listAndCount(
|
||||||
filterableFields,
|
filterableFields,
|
||||||
listConfig
|
listConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (includeSalesChannels) {
|
||||||
|
locations = await joinSalesChannels(
|
||||||
|
locations,
|
||||||
|
channelLocationService,
|
||||||
|
salesChannelService
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
stock_locations: locations,
|
stock_locations: locations,
|
||||||
count,
|
count,
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import {
|
||||||
|
SalesChannelLocationService,
|
||||||
|
SalesChannelService,
|
||||||
|
} from "../../../../../services"
|
||||||
|
import {
|
||||||
|
StockLocationDTO,
|
||||||
|
StockLocationExpandedDTO,
|
||||||
|
} from "../../../../../types/stock-location"
|
||||||
|
|
||||||
|
const joinSalesChannels = async (
|
||||||
|
locations: StockLocationDTO[],
|
||||||
|
channelLocationService: SalesChannelLocationService,
|
||||||
|
salesChannelService: SalesChannelService
|
||||||
|
): Promise<StockLocationExpandedDTO[]> => {
|
||||||
|
return await Promise.all(
|
||||||
|
locations.map(async (location: StockLocationExpandedDTO) => {
|
||||||
|
const salesChannelIds = await channelLocationService.listSalesChannelIds(
|
||||||
|
location.id
|
||||||
|
)
|
||||||
|
const [salesChannels] = await salesChannelService.listAndCount({
|
||||||
|
id: salesChannelIds,
|
||||||
|
})
|
||||||
|
|
||||||
|
location.sales_channels = salesChannels
|
||||||
|
|
||||||
|
return location
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { joinSalesChannels }
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import { FulfillmentProvider, PaymentProvider, Store } from "../../../../models"
|
|
||||||
import {
|
import {
|
||||||
FulfillmentProviderService,
|
FulfillmentProviderService,
|
||||||
PaymentProviderService,
|
PaymentProviderService,
|
||||||
StoreService,
|
StoreService,
|
||||||
} from "../../../../services"
|
} from "../../../../services"
|
||||||
import { FeatureFlagsResponse } from "../../../../types/feature-flags"
|
import { ExtendedStoreDTO } from "../../../../types/store"
|
||||||
import { ModulesResponse } from "../../../../types/modules"
|
|
||||||
import { FlagRouter } from "../../../../utils/flag-router"
|
import { FlagRouter } from "../../../../utils/flag-router"
|
||||||
import { ModulesHelper } from "../../../../utils/module-helper"
|
import { ModulesHelper } from "../../../../utils/module-helper"
|
||||||
|
|
||||||
@@ -77,12 +75,7 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const data = (await storeService.retrieve({
|
const data = (await storeService.retrieve({
|
||||||
relations,
|
relations,
|
||||||
})) as Store & {
|
})) as ExtendedStoreDTO
|
||||||
payment_providers: PaymentProvider[]
|
|
||||||
fulfillment_providers: FulfillmentProvider[]
|
|
||||||
feature_flags: FeatureFlagsResponse
|
|
||||||
modules: ModulesResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
data.feature_flags = featureFlagRouter.listFlags()
|
data.feature_flags = featureFlagRouter.listFlags()
|
||||||
data.modules = modulesHelper.modules
|
data.modules = modulesHelper.modules
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Router } from "express"
|
import { Router } from "express"
|
||||||
import { PaymentProvider, Store, TaxProvider } from "./../../../../"
|
import { PaymentProvider, TaxProvider } from "./../../../../"
|
||||||
import middlewares from "../../../middlewares"
|
import middlewares from "../../../middlewares"
|
||||||
|
import { ExtendedStoreDTO } from "../../../../types/store"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
@@ -34,10 +35,10 @@ export default (app) => {
|
|||||||
* type: object
|
* type: object
|
||||||
* properties:
|
* properties:
|
||||||
* store:
|
* store:
|
||||||
* $ref: "#/components/schemas/Store"
|
* $ref: "#/components/schemas/ExtendedStoreDTO"
|
||||||
*/
|
*/
|
||||||
export type AdminStoresRes = {
|
export type AdminStoresRes = {
|
||||||
store: Store
|
store: ExtendedStoreDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -72,7 +72,9 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const inventoryService: IInventoryService =
|
const inventoryService: IInventoryService =
|
||||||
req.scope.resolve("inventoryService")
|
req.scope.resolve("inventoryService")
|
||||||
|
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
|
||||||
|
"salesChannelLocationService"
|
||||||
|
)
|
||||||
const channelService: SalesChannelService = req.scope.resolve(
|
const channelService: SalesChannelService = req.scope.resolve(
|
||||||
"salesChannelService"
|
"salesChannelService"
|
||||||
)
|
)
|
||||||
@@ -91,11 +93,17 @@ export default async (req, res) => {
|
|||||||
sales_channel_availability: [],
|
sales_channel_availability: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
const [channels] = await channelService.listAndCount(
|
const [rawChannels] = await channelService.listAndCount({})
|
||||||
{},
|
const channels: SalesChannelDTO[] = await Promise.all(
|
||||||
{
|
rawChannels.map(async (channel) => {
|
||||||
relations: ["locations"],
|
const locationIds = await channelLocationService.listLocationIds(
|
||||||
|
channel.id
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
...channel,
|
||||||
|
locations: locationIds,
|
||||||
}
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const inventory =
|
const inventory =
|
||||||
@@ -116,7 +124,7 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
const quantity = await inventoryService.retrieveAvailableQuantity(
|
const quantity = await inventoryService.retrieveAvailableQuantity(
|
||||||
inventory[0].id,
|
inventory[0].id,
|
||||||
channel.locations.map((loc) => loc.id)
|
channel.locations
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -133,6 +141,10 @@ export default async (req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SalesChannelDTO = Omit<SalesChannel, "beforeInsert" | "locations"> & {
|
||||||
|
locations: string[]
|
||||||
|
}
|
||||||
|
|
||||||
type ResponseInventoryItem = Partial<InventoryItemDTO> & {
|
type ResponseInventoryItem = Partial<InventoryItemDTO> & {
|
||||||
location_levels?: InventoryLevelDTO[]
|
location_levels?: InventoryLevelDTO[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,9 +105,9 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
let locations: string[] = []
|
let locationIds: string[] = []
|
||||||
if (context.salesChannelId) {
|
if (context.salesChannelId) {
|
||||||
locations = await this.salesChannelLocationService_.listLocations(
|
locationIds = await this.salesChannelLocationService_.listLocationIds(
|
||||||
context.salesChannelId
|
context.salesChannelId
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -115,7 +115,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
{},
|
{},
|
||||||
{ select: ["id"] }
|
{ select: ["id"] }
|
||||||
)
|
)
|
||||||
locations = stockLocations.map((l) => l.id)
|
locationIds = stockLocations.map((l) => l.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasInventory = await Promise.all(
|
const hasInventory = await Promise.all(
|
||||||
@@ -125,7 +125,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
.withTransaction(manager)
|
.withTransaction(manager)
|
||||||
.confirmInventory(
|
.confirmInventory(
|
||||||
inventoryPart.inventory_item_id,
|
inventoryPart.inventory_item_id,
|
||||||
locations,
|
locationIds,
|
||||||
itemQuantity
|
itemQuantity
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -382,7 +382,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
|
|||||||
if (!isDefined(locationId) && context.salesChannelId) {
|
if (!isDefined(locationId) && context.salesChannelId) {
|
||||||
const locations = await this.salesChannelLocationService_
|
const locations = await this.salesChannelLocationService_
|
||||||
.withTransaction(manager)
|
.withTransaction(manager)
|
||||||
.listLocations(context.salesChannelId)
|
.listLocationIds(context.salesChannelId)
|
||||||
|
|
||||||
if (!locations.length) {
|
if (!locations.length) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
|
|||||||
@@ -40,13 +40,13 @@ class SalesChannelInventoryService {
|
|||||||
salesChannelId: string,
|
salesChannelId: string,
|
||||||
inventoryItemId: string
|
inventoryItemId: string
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
const locations = await this.salesChannelLocationService_.listLocations(
|
const locationIds = await this.salesChannelLocationService_.listLocationIds(
|
||||||
salesChannelId
|
salesChannelId
|
||||||
)
|
)
|
||||||
|
|
||||||
return await this.inventoryService_.retrieveAvailableQuantity(
|
return await this.inventoryService_.retrieveAvailableQuantity(
|
||||||
inventoryItemId,
|
inventoryItemId,
|
||||||
locations
|
locationIds
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class SalesChannelLocationService extends TransactionBaseService {
|
|||||||
* @param salesChannelId - The ID of the sales channel.
|
* @param salesChannelId - The ID of the sales channel.
|
||||||
* @returns A promise that resolves with an array of location IDs.
|
* @returns A promise that resolves with an array of location IDs.
|
||||||
*/
|
*/
|
||||||
async listLocations(salesChannelId: string): Promise<string[]> {
|
async listLocationIds(salesChannelId: string): Promise<string[]> {
|
||||||
const manager = this.transactionManager_ || this.manager_
|
const manager = this.transactionManager_ || this.manager_
|
||||||
const salesChannel = await this.salesChannelService_
|
const salesChannel = await this.salesChannelService_
|
||||||
.withTransaction(manager)
|
.withTransaction(manager)
|
||||||
@@ -109,10 +109,28 @@ class SalesChannelLocationService extends TransactionBaseService {
|
|||||||
|
|
||||||
const locations = await manager.find(SalesChannelLocation, {
|
const locations = await manager.find(SalesChannelLocation, {
|
||||||
where: { sales_channel_id: salesChannel.id },
|
where: { sales_channel_id: salesChannel.id },
|
||||||
|
select: ["location_id"],
|
||||||
})
|
})
|
||||||
|
|
||||||
return locations.map((l) => l.location_id)
|
return locations.map((l) => l.location_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the sales channels associated with a stock location.
|
||||||
|
* @param {string} salesChannelId - The ID of the stock location.
|
||||||
|
* @returns {Promise<string[]>} A promise that resolves with an array of sales channel IDs.
|
||||||
|
*/
|
||||||
|
async listSalesChannelIds(locationId: string): Promise<string[]> {
|
||||||
|
const manager = this.transactionManager_ || this.manager_
|
||||||
|
const location = await this.stockLocationService.retrieve(locationId)
|
||||||
|
|
||||||
|
const salesChannelLocations = await manager.find(SalesChannelLocation, {
|
||||||
|
where: { location_id: location.id },
|
||||||
|
select: ["sales_channel_id"],
|
||||||
|
})
|
||||||
|
|
||||||
|
return salesChannelLocations.map((l) => l.sales_channel_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SalesChannelLocationService
|
export default SalesChannelLocationService
|
||||||
@@ -3,6 +3,22 @@ export interface IFlagRouter {
|
|||||||
listFlags: () => FeatureFlagsResponse
|
listFlags: () => FeatureFlagsResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @schema FeatureFlagsResponse
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* type: object
|
||||||
|
* required:
|
||||||
|
* - key
|
||||||
|
* - value
|
||||||
|
* properties:
|
||||||
|
* key:
|
||||||
|
* description: The key of the feature flag.
|
||||||
|
* type: string
|
||||||
|
* value:
|
||||||
|
* description: The value of the feature flag.
|
||||||
|
* type: boolean
|
||||||
|
*/
|
||||||
export type FeatureFlagsResponse = {
|
export type FeatureFlagsResponse = {
|
||||||
key: string
|
key: string
|
||||||
value: boolean
|
value: boolean
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @schema ModulesResponse
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* type: object
|
||||||
|
* required:
|
||||||
|
* - module
|
||||||
|
* - resolution
|
||||||
|
* properties:
|
||||||
|
* module:
|
||||||
|
* description: The key of the module.
|
||||||
|
* type: string
|
||||||
|
* resolution:
|
||||||
|
* description: The resolution path of the module or false if module is not installed.
|
||||||
|
* type: string
|
||||||
|
*/
|
||||||
export type ModulesResponse = {
|
export type ModulesResponse = {
|
||||||
module: string
|
module: string
|
||||||
resolution: string | false
|
resolution: string | false
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { SalesChannel } from "../models"
|
||||||
import { StringComparisonOperator } from "./common"
|
import { StringComparisonOperator } from "./common"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,6 +138,19 @@ export type StockLocationDTO = {
|
|||||||
deleted_at: string | Date | null
|
deleted_at: string | Date | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @schema StockLocationExpandedDTO
|
||||||
|
* allOf:
|
||||||
|
* - $ref: "#/components/schemas/StockLocationDTO"
|
||||||
|
* - type: object
|
||||||
|
* properties:
|
||||||
|
* sales_channels:
|
||||||
|
* $ref: "#/components/schemas/SalesChannel"
|
||||||
|
*/
|
||||||
|
export type StockLocationExpandedDTO = StockLocationDTO & {
|
||||||
|
sales_channels?: SalesChannel[]
|
||||||
|
}
|
||||||
|
|
||||||
export type FilterableStockLocationProps = {
|
export type FilterableStockLocationProps = {
|
||||||
id?: string | string[]
|
id?: string | string[]
|
||||||
name?: string | string[] | StringComparisonOperator
|
name?: string | string[] | StringComparisonOperator
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import { Store, PaymentProvider, FulfillmentProvider } from "../models"
|
||||||
|
import { FeatureFlagsResponse } from "./feature-flags"
|
||||||
|
import { ModulesResponse } from "./modules"
|
||||||
|
|
||||||
export type UpdateStoreInput = {
|
export type UpdateStoreInput = {
|
||||||
name?: string
|
name?: string
|
||||||
swap_link_template?: string
|
swap_link_template?: string
|
||||||
@@ -8,3 +12,31 @@ export type UpdateStoreInput = {
|
|||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
default_sales_channel_id?: string
|
default_sales_channel_id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @schema ExtendedStoreDTO
|
||||||
|
* allOf:
|
||||||
|
* - $ref: "#/components/schemas/Store"
|
||||||
|
* - type: object
|
||||||
|
* required:
|
||||||
|
* - payment_providers
|
||||||
|
* - fulfillment_providers
|
||||||
|
* - feature_flags
|
||||||
|
* - modules
|
||||||
|
* properties:
|
||||||
|
* payment_providers:
|
||||||
|
* $ref: "#/components/schemas/PaymentProvider"
|
||||||
|
* fulfillment_providers:
|
||||||
|
* $ref: "#/components/schemas/FulfillmentProvider"
|
||||||
|
* feature_flags:
|
||||||
|
* $ref: "#/components/schemas/FeatureFlagsResponse"
|
||||||
|
* modules:
|
||||||
|
* $ref: "#/components/schemas/ModulesResponse"
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ExtendedStoreDTO = Store & {
|
||||||
|
payment_providers: PaymentProvider[]
|
||||||
|
fulfillment_providers: FulfillmentProvider[]
|
||||||
|
feature_flags: FeatureFlagsResponse
|
||||||
|
modules: ModulesResponse
|
||||||
|
}
|
||||||
Reference in new issue
Block a user