feat(admin-ui, medusa-js, medusa-react, medusa): Multiwarehousing UI (#3403)
* add "get-variant" endpoint * import from a different place * fix unit test * add changeset * inventory management for orders * add changeset * initial create-fulfillment * add changeset * type oas and admin * Move inv. creation and listing from admin repo * Fix location editing bug (CORE-1216) * Fix default warehouse on inventory table view * remove actions from each table line * Use feature flag hook instead of context directly * remove manage inventory action if inventory management is not enabled * Address review comments * fix queries made when inventorymodules are disabled * variant form changes for feature enabled * move exclamation icon into warning icon * ensure queries are not run unless feature is enabled for create-fulfillment --------- Co-authored-by: Philip Korsholm <philip.korsholm@hotmail.com> Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
co-authored by
Philip Korsholm
Philip Korsholm
parent
53eda215e0
commit
57d7728dd9
@@ -3,10 +3,7 @@ import {
|
||||
InventoryItemDTO,
|
||||
InventoryLevelDTO,
|
||||
} from "../../../../../types/inventory"
|
||||
|
||||
type LevelWithAvailability = InventoryLevelDTO & {
|
||||
available_quantity: number
|
||||
}
|
||||
import { LevelWithAvailability, ResponseInventoryItem } from "../../variants"
|
||||
|
||||
export const buildLevelsByInventoryItemId = (
|
||||
inventoryLevels: InventoryLevelDTO[],
|
||||
@@ -27,7 +24,7 @@ export const getLevelsByInventoryItemId = async (
|
||||
items: InventoryItemDTO[],
|
||||
locationIds: string[],
|
||||
inventoryService: IInventoryService
|
||||
) => {
|
||||
): Promise<Record<string, LevelWithAvailability[]>> => {
|
||||
const [levels] = await inventoryService.listInventoryLevels({
|
||||
inventory_item_id: items.map((inventoryItem) => inventoryItem.id),
|
||||
})
|
||||
@@ -52,7 +49,7 @@ export const joinLevels = async (
|
||||
inventoryItems: InventoryItemDTO[],
|
||||
locationIds: string[],
|
||||
inventoryService: IInventoryService
|
||||
) => {
|
||||
): Promise<ResponseInventoryItem[]> => {
|
||||
const levelsByItemId = await getLevelsByInventoryItemId(
|
||||
inventoryItems,
|
||||
locationIds,
|
||||
|
||||
@@ -151,8 +151,23 @@ type SalesChannelDTO = Omit<SalesChannel, "beforeInsert" | "locations"> & {
|
||||
locations: string[]
|
||||
}
|
||||
|
||||
type ResponseInventoryItem = Partial<InventoryItemDTO> & {
|
||||
location_levels?: InventoryLevelDTO[]
|
||||
export type LevelWithAvailability = InventoryLevelDTO & {
|
||||
available_quantity: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema ResponseInventoryItem
|
||||
* allOf:
|
||||
* - $ref: "#/components/schemas/InventoryItemDTO"
|
||||
* - type: object
|
||||
* required:
|
||||
* - available_quantity
|
||||
* properties:
|
||||
* available_quantity:
|
||||
* type: number
|
||||
*/
|
||||
export type ResponseInventoryItem = Partial<InventoryItemDTO> & {
|
||||
location_levels?: LevelWithAvailability[]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,7 +179,7 @@ type ResponseInventoryItem = Partial<InventoryItemDTO> & {
|
||||
* type: string
|
||||
* inventory:
|
||||
* description: the stock location address ID
|
||||
* type: string
|
||||
* $ref: "#/components/schemas/ResponseInventoryItem"
|
||||
* sales_channel_availability:
|
||||
* type: object
|
||||
* description: An optional key-value map with additional details
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import { PricingService, ProductVariantService } from "../../../../services"
|
||||
import { FindParams } from "../../../../types/common"
|
||||
|
||||
/**
|
||||
* @oas [get] /admin/variants/{id}
|
||||
* operationId: "GetVariantsVariant"
|
||||
* summary: "Get a Product variant"
|
||||
* description: "Retrieves a Product variant."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The ID of the variant.
|
||||
* x-codegen:
|
||||
* method: retrieve
|
||||
* x-codeSamples:
|
||||
* - lang: JavaScript
|
||||
* label: JS Client
|
||||
* source: |
|
||||
* import Medusa from "@medusajs/medusa-js"
|
||||
* const medusa = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 })
|
||||
* // must be previously logged in or use api token
|
||||
* medusa.admin.variants.retrieve(product_id)
|
||||
* .then(({ product }) => {
|
||||
* console.log(product.id);
|
||||
* });
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request GET 'https://medusa-url.com/admin/variants/{id}' \
|
||||
* --header 'Authorization: Bearer {api_token}'
|
||||
* security:
|
||||
* - api_token: []
|
||||
* - cookie_auth: []
|
||||
* tags:
|
||||
* - Products
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/AdminVariantRes"
|
||||
* "400":
|
||||
* $ref: "#/components/responses/400_error"
|
||||
* "401":
|
||||
* $ref: "#/components/responses/unauthorized"
|
||||
* "404":
|
||||
* $ref: "#/components/responses/not_found_error"
|
||||
* "409":
|
||||
* $ref: "#/components/responses/invalid_state_error"
|
||||
* "422":
|
||||
* $ref: "#/components/responses/invalid_request_error"
|
||||
* "500":
|
||||
* $ref: "#/components/responses/500_error"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const productVariantService: ProductVariantService = req.scope.resolve(
|
||||
"productVariantService"
|
||||
)
|
||||
const pricingService: PricingService = req.scope.resolve("pricingService")
|
||||
|
||||
const rawVariant = await productVariantService.retrieve(
|
||||
id,
|
||||
req.retrieveConfig
|
||||
)
|
||||
|
||||
const [variant] = await pricingService.setVariantPrices([rawVariant])
|
||||
|
||||
res.status(200).json({ variant })
|
||||
}
|
||||
|
||||
export class AdminGetVariantParams extends FindParams {}
|
||||
@@ -5,6 +5,7 @@ import { PaginatedResponse } from "../../../../types/common"
|
||||
import { PricedVariant } from "../../../../types/pricing"
|
||||
import middlewares, { transformQuery } from "../../../middlewares"
|
||||
import { checkRegisteredModules } from "../../../middlewares/check-registered-modules"
|
||||
import { AdminGetVariantParams } from "./get-variant"
|
||||
import { AdminGetVariantsParams } from "./list-variants"
|
||||
|
||||
const route = Router()
|
||||
@@ -22,6 +23,16 @@ export default (app) => {
|
||||
middlewares.wrap(require("./list-variants").default)
|
||||
)
|
||||
|
||||
route.get(
|
||||
"/:id",
|
||||
transformQuery(AdminGetVariantParams, {
|
||||
defaultRelations: defaultAdminVariantRelations,
|
||||
defaultFields: defaultAdminVariantFields,
|
||||
isList: false,
|
||||
}),
|
||||
middlewares.wrap(require("./get-variant").default)
|
||||
)
|
||||
|
||||
route.get(
|
||||
"/:id/inventory",
|
||||
checkRegisteredModules({
|
||||
@@ -88,5 +99,19 @@ export type AdminVariantsListRes = PaginatedResponse & {
|
||||
variants: PricedVariant[]
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema AdminVariantRes
|
||||
* type: object
|
||||
* required:
|
||||
* - variant
|
||||
* properties:
|
||||
* variant:
|
||||
* $ref: "#/components/schemas/PricedVariant"
|
||||
*/
|
||||
export type AdminVariantsRes = {
|
||||
variant: PricedVariant
|
||||
}
|
||||
|
||||
export * from "./list-variants"
|
||||
export * from "./get-variant"
|
||||
export * from "./get-inventory"
|
||||
|
||||
Reference in New Issue
Block a user