feat(medusa): Stock location module (#2907)
* feat: stock location module
This commit is contained in:
@@ -44,6 +44,7 @@ export * from "./routes/admin/returns"
|
||||
export * from "./routes/admin/sales-channels"
|
||||
export * from "./routes/admin/shipping-options"
|
||||
export * from "./routes/admin/shipping-profiles"
|
||||
export * from "./routes/admin/stock-locations"
|
||||
export * from "./routes/admin/store"
|
||||
export * from "./routes/admin/swaps"
|
||||
export * from "./routes/admin/tax-rates"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
|
||||
export function checkRegisteredModules(services: {
|
||||
[serviceName: string]: string
|
||||
}): (req: Request, res: Response, next: NextFunction) => Promise<void> {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
for (const service of Object.keys(services)) {
|
||||
if (!req.scope.resolve(service, { allowUnregistered: true })) {
|
||||
return next(new Error(services[service]))
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import returnRoutes from "./returns"
|
||||
import salesChannelRoutes from "./sales-channels"
|
||||
import shippingOptionRoutes from "./shipping-options"
|
||||
import shippingProfileRoutes from "./shipping-profiles"
|
||||
import stockLocationRoutes from "./stock-locations"
|
||||
import storeRoutes from "./store"
|
||||
import swapRoutes from "./swaps"
|
||||
import taxRateRoutes from "./tax-rates"
|
||||
@@ -98,6 +99,7 @@ export default (app, container, config) => {
|
||||
salesChannelRoutes(route)
|
||||
shippingOptionRoutes(route, featureFlagRouter)
|
||||
shippingProfileRoutes(route)
|
||||
stockLocationRoutes(route)
|
||||
storeRoutes(route)
|
||||
swapRoutes(route)
|
||||
taxRateRoutes(route)
|
||||
|
||||
@@ -75,7 +75,7 @@ export default async (req, res) => {
|
||||
result = await claimService.retrieve(result.claim_order_id)
|
||||
}
|
||||
|
||||
const order = await orderService.retrieve(result.order_id, {
|
||||
const order = await orderService.retrieve(result.order_id!, {
|
||||
select: defaultAdminOrdersFields,
|
||||
relations: defaultAdminOrdersRelations,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { IsString } from "class-validator"
|
||||
import { Request, Response } from "express"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
import {
|
||||
SalesChannelService,
|
||||
SalesChannelLocationService,
|
||||
} from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [post] /sales-channels/{id}/stock-locations
|
||||
* operationId: "PostSalesChannelsSalesChannelStockLocation"
|
||||
* summary: "Associate a stock location to a Sales Channel"
|
||||
* description: "Associates a stock location to a Sales Channel."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The ID of the Sales Channel.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/AdminPostSalesChannelsChannelStockLocationsReq"
|
||||
* 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.salesChannels.addLocation(sales_channel_id, {
|
||||
* location_id: 'App'
|
||||
* })
|
||||
* .then(({ sales_channel }) => {
|
||||
* console.log(sales_channel.id);
|
||||
* });
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request POST 'https://medusa-url.com/admin/sales-channels/{id}/stock-locations' \
|
||||
* --header 'Authorization: Bearer {api_token}' \
|
||||
* --header 'Content-Type: application/json' \
|
||||
* --data-raw '{
|
||||
* "locaton_id": "stock_location_id"
|
||||
* }'
|
||||
* security:
|
||||
* - api_token: []
|
||||
* - cookie_auth: []
|
||||
* tags:
|
||||
* - Sales Channel
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* sales_channel:
|
||||
* $ref: "#/components/schemas/SalesChannel"
|
||||
* "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: Request, res: Response) => {
|
||||
const { id } = req.params
|
||||
const { validatedBody } = req as {
|
||||
validatedBody: AdminPostSalesChannelsChannelStockLocationsReq
|
||||
}
|
||||
|
||||
const salesChannelService: SalesChannelService = req.scope.resolve(
|
||||
"salesChannelService"
|
||||
)
|
||||
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
return await channelLocationService
|
||||
.withTransaction(transactionManager)
|
||||
.associateLocation(id, validatedBody.location_id)
|
||||
})
|
||||
|
||||
const channel = await salesChannelService.retrieve(id)
|
||||
|
||||
res.status(200).json({ sales_channel: channel })
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema AdminPostSalesChannelsChannelStockLocationsReq
|
||||
* type: object
|
||||
* required:
|
||||
* - location_id
|
||||
* properties:
|
||||
* location_id:
|
||||
* description: The ID of the stock location
|
||||
* type: string
|
||||
*/
|
||||
|
||||
export class AdminPostSalesChannelsChannelStockLocationsReq {
|
||||
@IsString()
|
||||
location_id: string
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import { AdminPostSalesChannelsReq } from "./create-sales-channel"
|
||||
import { AdminDeleteSalesChannelsChannelProductsBatchReq } from "./delete-products-batch"
|
||||
import { AdminGetSalesChannelsParams } from "./list-sales-channels"
|
||||
import { AdminPostSalesChannelsSalesChannelReq } from "./update-sales-channel"
|
||||
import { AdminPostSalesChannelsChannelStockLocationsReq } from "./associate-stock-location"
|
||||
import { AdminDeleteSalesChannelsChannelStockLocationsReq } from "./remove-stock-location"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -43,6 +45,16 @@ export default (app) => {
|
||||
transformBody(AdminPostSalesChannelsSalesChannelReq),
|
||||
middlewares.wrap(require("./update-sales-channel").default)
|
||||
)
|
||||
salesChannelRouter.post(
|
||||
"/stock-locations",
|
||||
transformBody(AdminPostSalesChannelsChannelStockLocationsReq),
|
||||
middlewares.wrap(require("./associate-stock-location").default)
|
||||
)
|
||||
salesChannelRouter.delete(
|
||||
"/stock-locations",
|
||||
transformBody(AdminDeleteSalesChannelsChannelStockLocationsReq),
|
||||
middlewares.wrap(require("./remove-stock-location").default)
|
||||
)
|
||||
salesChannelRouter.delete(
|
||||
"/products/batch",
|
||||
transformBody(AdminDeleteSalesChannelsChannelProductsBatchReq),
|
||||
@@ -81,3 +93,5 @@ export * from "./delete-sales-channel"
|
||||
export * from "./get-sales-channel"
|
||||
export * from "./list-sales-channels"
|
||||
export * from "./update-sales-channel"
|
||||
export * from "./associate-stock-location"
|
||||
export * from "./remove-stock-location"
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import { IsString } from "class-validator"
|
||||
import { Request, Response } from "express"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
import { SalesChannelLocationService } from "../../../../services"
|
||||
|
||||
/**
|
||||
* @oas [delete] /sales-channels/{id}/stock-locations
|
||||
* operationId: "DeleteSalesChannelsSalesChannelStockLocation"
|
||||
* summary: "Remove a stock location from a Sales Channel"
|
||||
* description: "Removes a stock location from a Sales Channel."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The ID of the Sales Channel.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/AdminDeleteSalesChannelsChannelStockLocationsReq"
|
||||
* 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.salesChannels.removeLocation(sales_channel_id, {
|
||||
* location_id: 'App'
|
||||
* })
|
||||
* .then(({ sales_channel }) => {
|
||||
* console.log(sales_channel.id);
|
||||
* });
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request DELETE 'https://medusa-url.com/admin/sales-channels/{id}/stock-locations' \
|
||||
* --header 'Authorization: Bearer {api_token}' \
|
||||
* --header 'Content-Type: application/json' \
|
||||
* --data-raw '{
|
||||
* "locaton_id": "stock_location_id"
|
||||
* }'
|
||||
* security:
|
||||
* - api_token: []
|
||||
* - cookie_auth: []
|
||||
* tags:
|
||||
* - Sales Channel
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* id:
|
||||
* type: string
|
||||
* description: The ID of the removed stock location from a sales channel
|
||||
* object:
|
||||
* type: string
|
||||
* description: The type of the object that was removed.
|
||||
* default: stock-location
|
||||
* deleted:
|
||||
* type: boolean
|
||||
* description: Whether or not the items were deleted.
|
||||
* default: true
|
||||
* "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: Request, res: Response) => {
|
||||
const { id } = req.params
|
||||
const { validatedBody } = req as {
|
||||
validatedBody: AdminDeleteSalesChannelsChannelStockLocationsReq
|
||||
}
|
||||
|
||||
const channelLocationService: SalesChannelLocationService = req.scope.resolve(
|
||||
"salesChannelLocationService"
|
||||
)
|
||||
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
await manager.transaction(async (transactionManager) => {
|
||||
await channelLocationService
|
||||
.withTransaction(transactionManager)
|
||||
.removeLocation(id, validatedBody.location_id)
|
||||
})
|
||||
|
||||
res.json({
|
||||
id,
|
||||
object: "stock-location",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema AdminDeleteSalesChannelsChannelStockLocationsReq
|
||||
* type: object
|
||||
* required:
|
||||
* - location_id
|
||||
* properties:
|
||||
* location_id:
|
||||
* description: The ID of the stock location
|
||||
* type: string
|
||||
*/
|
||||
export class AdminDeleteSalesChannelsChannelStockLocationsReq {
|
||||
@IsString()
|
||||
location_id: string
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import { Request, Response } from "express"
|
||||
import { Type } from "class-transformer"
|
||||
import { ValidateNested, IsOptional, IsString, IsObject } from "class-validator"
|
||||
|
||||
import { IStockLocationService } from "../../../../interfaces"
|
||||
import { FindParams } from "../../../../types/common"
|
||||
|
||||
/**
|
||||
* @oas [post] /stock-locations
|
||||
* operationId: "PostStockLocations"
|
||||
* summary: "Create a Stock Location"
|
||||
* description: "Creates a Stock Location."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (query) expand {string} Comma separated list of relations to include in the results.
|
||||
* - (query) fields {string} Comma separated list of fields to include in the results.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/AdminPostStockLocationsReq"
|
||||
* 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.stockLocations.create({
|
||||
* name: 'Main Warehouse',
|
||||
* location_id: 'sloc'
|
||||
* })
|
||||
* .then(({ stock_location }) => {
|
||||
* console.log(stock_location.id);
|
||||
* });
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request POST 'https://medusa-url.com/admin/stock-locations' \
|
||||
* --header 'Authorization: Bearer {api_token}' \
|
||||
* --header 'Content-Type: application/json' \
|
||||
* --data-raw '{
|
||||
* "name": "App"
|
||||
* }'
|
||||
* security:
|
||||
* - api_token: []
|
||||
* - cookie_auth: []
|
||||
* tags:
|
||||
* - Stock Location
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* stock_location:
|
||||
* $ref: "#/components/schemas/StockLocationDTO"
|
||||
* "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: Request, res: Response) => {
|
||||
const locationService: IStockLocationService = req.scope.resolve(
|
||||
"stockLocationService"
|
||||
)
|
||||
|
||||
const createdStockLocation = await locationService.create(
|
||||
req.validatedBody as AdminPostStockLocationsReq
|
||||
)
|
||||
|
||||
const stockLocation = await locationService.retrieve(
|
||||
createdStockLocation.id,
|
||||
req.retrieveConfig
|
||||
)
|
||||
|
||||
res.status(200).json({ stock_location: stockLocation })
|
||||
}
|
||||
|
||||
class StockLocationAddress {
|
||||
@IsString()
|
||||
address_1: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address_2?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
city?: string
|
||||
|
||||
@IsString()
|
||||
country_code: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
postal_code?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
province?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema AdminPostStockLocationsReq
|
||||
* type: object
|
||||
* required:
|
||||
* - name
|
||||
* properties:
|
||||
* name:
|
||||
* description: the name of the stock location
|
||||
* type: string
|
||||
* address_id:
|
||||
* description: the stock location address ID
|
||||
* type: string
|
||||
* metadata:
|
||||
* type: object
|
||||
* description: An optional key-value map with additional details
|
||||
* example: {car: "white"}
|
||||
* address:
|
||||
* $ref: "#/components/schemas/StockLocationAddressInput"
|
||||
*/
|
||||
export class AdminPostStockLocationsReq {
|
||||
@IsString()
|
||||
name: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => StockLocationAddress)
|
||||
address?: StockLocationAddress
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address_id?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export class AdminPostStockLocationsParams extends FindParams {}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { IStockLocationService } from "../../../../interfaces"
|
||||
import { Request, Response } from "express"
|
||||
import { FindParams } from "../../../../types/common"
|
||||
|
||||
/**
|
||||
* @oas [get] /stock-locations/{id}
|
||||
* operationId: "GetStockLocationsStockLocation"
|
||||
* summary: "Get a Stock Location"
|
||||
* description: "Retrieves the Stock Location."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The ID of the Stock Location.
|
||||
* - (query) expand {string} Comma separated list of relations to include in the results.
|
||||
* - (query) fields {string} Comma separated list of fields to include in the results.
|
||||
* 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.stockLocations.retrieve(stock_location_id)
|
||||
* .then(({ stock_location }) => {
|
||||
* console.log(stock_location.id);
|
||||
* });
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request GET 'https://medusa-url.com/admin/stock-locations/{id}' \
|
||||
* --header 'Authorization: Bearer {api_token}' \
|
||||
* security:
|
||||
* - api_token: []
|
||||
* - cookie_auth: []
|
||||
* tags:
|
||||
* - Stock Location
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* stock_location:
|
||||
* $ref: "#/components/schemas/StockLocationDTO"
|
||||
*/
|
||||
export default async (req: Request, res: Response) => {
|
||||
const { id } = req.params
|
||||
|
||||
const locationService: IStockLocationService = req.scope.resolve(
|
||||
"stockLocationService"
|
||||
)
|
||||
const stockLocation = await locationService.retrieve(id, req.retrieveConfig)
|
||||
|
||||
res.status(200).json({ stock_location: stockLocation })
|
||||
}
|
||||
|
||||
export class AdminGetStockLocationsLocationParams extends FindParams {}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { Router } from "express"
|
||||
import "reflect-metadata"
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import { StockLocationDTO } from "../../../../types/stock-location"
|
||||
import middlewares, {
|
||||
transformBody,
|
||||
transformQuery,
|
||||
} from "../../../middlewares"
|
||||
import { AdminGetStockLocationsParams } from "./list-stock-locations"
|
||||
import { AdminGetStockLocationsLocationParams } from "./get-stock-location"
|
||||
import {
|
||||
AdminPostStockLocationsLocationParams,
|
||||
AdminPostStockLocationsLocationReq,
|
||||
} from "./update-stock-location"
|
||||
import {
|
||||
AdminPostStockLocationsParams,
|
||||
AdminPostStockLocationsReq,
|
||||
} from "./create-stock-location"
|
||||
import { checkRegisteredModules } from "../../../middlewares/check-registered-modules"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use(
|
||||
"/stock-locations",
|
||||
checkRegisteredModules({
|
||||
stockLocationService:
|
||||
"Stock Locations are not enabled. Please add a Stock Location module to enable this functionality.",
|
||||
}),
|
||||
route
|
||||
)
|
||||
|
||||
route.get(
|
||||
"/",
|
||||
transformQuery(AdminGetStockLocationsParams, {
|
||||
defaultFields: defaultAdminStockLocationFields,
|
||||
defaultRelations: defaultAdminStockLocationRelations,
|
||||
isList: true,
|
||||
}),
|
||||
middlewares.wrap(require("./list-stock-locations").default)
|
||||
)
|
||||
route.post(
|
||||
"/",
|
||||
transformQuery(AdminPostStockLocationsParams, {
|
||||
defaultFields: defaultAdminStockLocationFields,
|
||||
defaultRelations: defaultAdminStockLocationRelations,
|
||||
isList: false,
|
||||
}),
|
||||
transformBody(AdminPostStockLocationsReq),
|
||||
middlewares.wrap(require("./create-stock-location").default)
|
||||
)
|
||||
|
||||
route.get(
|
||||
"/:id",
|
||||
transformQuery(AdminGetStockLocationsLocationParams, {
|
||||
defaultFields: defaultAdminStockLocationFields,
|
||||
defaultRelations: defaultAdminStockLocationRelations,
|
||||
isList: false,
|
||||
}),
|
||||
middlewares.wrap(require("./get-stock-location").default)
|
||||
)
|
||||
|
||||
route.post(
|
||||
"/:id",
|
||||
transformQuery(AdminPostStockLocationsLocationParams, {
|
||||
defaultFields: defaultAdminStockLocationFields,
|
||||
defaultRelations: defaultAdminStockLocationRelations,
|
||||
isList: false,
|
||||
}),
|
||||
transformBody(AdminPostStockLocationsLocationReq),
|
||||
middlewares.wrap(require("./update-stock-location").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultAdminStockLocationFields: (keyof StockLocationDTO)[] = [
|
||||
"id",
|
||||
"name",
|
||||
"address_id",
|
||||
"metadata",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
export const defaultAdminStockLocationRelations = []
|
||||
|
||||
export type AdminStockLocationsRes = {
|
||||
stock_location: StockLocationDTO
|
||||
}
|
||||
|
||||
export type AdminStockLocationsDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminStockLocationsListRes = PaginatedResponse & {
|
||||
stock_locations: StockLocationDTO[]
|
||||
}
|
||||
|
||||
export * from "./list-stock-locations"
|
||||
export * from "./get-stock-location"
|
||||
export * from "./create-stock-location"
|
||||
export * from "./update-stock-location"
|
||||
@@ -0,0 +1,179 @@
|
||||
import { IsOptional } from "class-validator"
|
||||
import { IsType } from "../../../../utils/validators/is-type"
|
||||
|
||||
import { IStockLocationService } from "../../../../interfaces"
|
||||
import { extendedFindParamsMixin } from "../../../../types/common"
|
||||
import { Request, Response } from "express"
|
||||
|
||||
/**
|
||||
* @oas [get] /stock-locations
|
||||
* operationId: "GetStockLocations"
|
||||
* summary: "List Stock Locations"
|
||||
* description: "Retrieves a list of stock locations"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (query) id {string} ID of the stock location
|
||||
* - (query) name {string} Name of the stock location
|
||||
* - (query) order {string} The field to order the results by.
|
||||
* - in: query
|
||||
* name: created_at
|
||||
* description: Date comparison for when resulting collections were created.
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* lt:
|
||||
* type: string
|
||||
* description: filter by dates less than this date
|
||||
* format: date
|
||||
* gt:
|
||||
* type: string
|
||||
* description: filter by dates greater than this date
|
||||
* format: date
|
||||
* lte:
|
||||
* type: string
|
||||
* description: filter by dates less than or equal to this date
|
||||
* format: date
|
||||
* gte:
|
||||
* type: string
|
||||
* description: filter by dates greater than or equal to this date
|
||||
* format: date
|
||||
* - in: query
|
||||
* name: updated_at
|
||||
* description: Date comparison for when resulting collections were updated.
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* lt:
|
||||
* type: string
|
||||
* description: filter by dates less than this date
|
||||
* format: date
|
||||
* gt:
|
||||
* type: string
|
||||
* description: filter by dates greater than this date
|
||||
* format: date
|
||||
* lte:
|
||||
* type: string
|
||||
* description: filter by dates less than or equal to this date
|
||||
* format: date
|
||||
* gte:
|
||||
* type: string
|
||||
* description: filter by dates greater than or equal to this date
|
||||
* format: date
|
||||
* - in: query
|
||||
* name: deleted_at
|
||||
* description: Date comparison for when resulting collections were deleted.
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* lt:
|
||||
* type: string
|
||||
* description: filter by dates less than this date
|
||||
* format: date
|
||||
* gt:
|
||||
* type: string
|
||||
* description: filter by dates greater than this date
|
||||
* format: date
|
||||
* lte:
|
||||
* type: string
|
||||
* description: filter by dates less than or equal to this date
|
||||
* format: date
|
||||
* gte:
|
||||
* type: string
|
||||
* description: filter by dates greater than or equal to this date
|
||||
* format: date
|
||||
* - (query) offset=0 {integer} How many stock locations to skip in the result.
|
||||
* - (query) limit=20 {integer} Limit the number of stock locations returned.
|
||||
* - (query) expand {string} (Comma separated) Which fields should be expanded in each stock location of the result.
|
||||
* - (query) fields {string} (Comma separated) Which fields should be included in each stock location of the result.
|
||||
* 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.stockLocations.list()
|
||||
* .then(({ stock_locations, limit, offset, count }) => {
|
||||
* console.log(stock_locations.length);
|
||||
* });
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request GET 'https://medusa-url.com/admin/stock-locations' \
|
||||
* --header 'Authorization: Bearer {api_token}'
|
||||
* security:
|
||||
* - api_token: []
|
||||
* - cookie_auth: []
|
||||
* tags:
|
||||
* - Sales Channel
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* stock_locations:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/StockLocationDTO"
|
||||
* count:
|
||||
* type: integer
|
||||
* description: The total number of items available
|
||||
* offset:
|
||||
* type: integer
|
||||
* description: The number of items skipped before these items
|
||||
* limit:
|
||||
* type: integer
|
||||
* description: The number of items per page
|
||||
* "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: Request, res: Response) => {
|
||||
const stockLocationService: IStockLocationService = req.scope.resolve(
|
||||
"stockLocationService"
|
||||
)
|
||||
|
||||
const { filterableFields, listConfig } = req
|
||||
const { skip, take } = listConfig
|
||||
|
||||
const [locations, count] = await stockLocationService.listAndCount(
|
||||
filterableFields,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({
|
||||
stock_locations: locations,
|
||||
count,
|
||||
offset: skip,
|
||||
limit: take,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetStockLocationsParams extends extendedFindParamsMixin({
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
}) {
|
||||
@IsOptional()
|
||||
@IsType([String, [String]])
|
||||
id?: string | string[]
|
||||
|
||||
@IsOptional()
|
||||
@IsType([String, [String]])
|
||||
name?: string | string[]
|
||||
|
||||
@IsOptional()
|
||||
@IsType([String, [String]])
|
||||
address_id?: string | string[]
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import { Request, Response } from "express"
|
||||
import { Type } from "class-transformer"
|
||||
import { ValidateNested, IsOptional, IsString, IsObject } from "class-validator"
|
||||
|
||||
import { IStockLocationService } from "../../../../interfaces"
|
||||
import { FindParams } from "../../../../types/common"
|
||||
|
||||
/**
|
||||
* @oas [post] /stock-locations/{id}
|
||||
* operationId: "PostStockLocationsStockLocation"
|
||||
* summary: "Update a Stock Location"
|
||||
* description: "Updates a Stock Location."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The ID of the Stock Location.
|
||||
* - (query) expand {string} Comma separated list of relations to include in the results.
|
||||
* - (query) fields {string} Comma separated list of fields to include in the results.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: "#/components/schemas/AdminPostStockLocationsLocationReq"
|
||||
* 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.stockLocations.update(stock_location_id, {
|
||||
* name: 'App'
|
||||
* })
|
||||
* .then(({ stock_location }) => {
|
||||
* console.log(stock_location.id);
|
||||
* });
|
||||
* - lang: Shell
|
||||
* label: cURL
|
||||
* source: |
|
||||
* curl --location --request POST 'https://medusa-url.com/admin/stock-locations/{id}' \
|
||||
* --header 'Authorization: Bearer {api_token}' \
|
||||
* --header 'Content-Type: application/json' \
|
||||
* --data-raw '{
|
||||
* "name": "App"
|
||||
* }'
|
||||
* security:
|
||||
* - api_token: []
|
||||
* - cookie_auth: []
|
||||
* tags:
|
||||
* - Stock Location
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* stock_location:
|
||||
* $ref: "#/components/schemas/StockLocationDTO"
|
||||
* "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: Request, res: Response) => {
|
||||
const { id } = req.params
|
||||
|
||||
const locationService: IStockLocationService = req.scope.resolve(
|
||||
"stockLocationService"
|
||||
)
|
||||
|
||||
await locationService.update(
|
||||
id,
|
||||
req.validatedBody as AdminPostStockLocationsLocationReq
|
||||
)
|
||||
|
||||
const stockLocation = await locationService.retrieve(id, req.retrieveConfig)
|
||||
|
||||
res.status(200).json({ stock_location: stockLocation })
|
||||
}
|
||||
|
||||
class StockLocationAddress {
|
||||
@IsString()
|
||||
address_1: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address_2?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
city?: string
|
||||
|
||||
@IsString()
|
||||
country_code: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
phone?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
postal_code?: string
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
province?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @schema AdminPostStockLocationsLocationReq
|
||||
* type: object
|
||||
* properties:
|
||||
* name:
|
||||
* description: the name of the stock location
|
||||
* type: string
|
||||
* address_id:
|
||||
* description: the stock location address ID
|
||||
* type: string
|
||||
* metadata:
|
||||
* type: object
|
||||
* description: An optional key-value map with additional details
|
||||
* example: {car: "white"}
|
||||
* address:
|
||||
* $ref: "#/components/schemas/StockLocationAddressInput"
|
||||
*/
|
||||
export class AdminPostStockLocationsLocationReq {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => StockLocationAddress)
|
||||
address?: StockLocationAddress
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
address_id?: string
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export class AdminPostStockLocationsLocationParams extends FindParams {}
|
||||
Reference in New Issue
Block a user