feat(medusa): Stock location module (#2907)

* feat: stock location module
This commit is contained in:
Carlos R. L. Rodrigues
2023-01-04 13:11:59 -03:00
committed by GitHub
parent cc10c20f35
commit c07ffb6165
50 changed files with 2040 additions and 198 deletions
+1
View File
@@ -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 {}