feat: Implement PriceList and extend MoneyAmount (#1152)
* init * added buld id validation to repo * admin done * updated price reqs * intial implementation of PriceList * integration tests for price lists * updated admin/product integration tests * update updateVariantPrices method * remove comment from error handler * add integration test for batch deleting prices associated with price list * make update to prices through variant service limited to default prices * update store/products.js snapshot * add api unit tests and update product integration tests to validate that prices from Price List are ignored * fix product test * requested changes * cascade * ensure delete variant cascades to MoneyAmount * addresses PR feedback * removed unused endpoint * update mock * fix failing store integration tests * remove medusajs ressource * re add env.template * Update integration-tests/api/__tests__/admin/price-list.js Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> * Update integration-tests/api/__tests__/admin/price-list.js Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> * fix: update snapshots Co-authored-by: Sebastian Rindom <skrindom@gmail.com> Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
co-authored by
Philip Korsholm
Sebastian Rindom
parent
23f8399c16
commit
5300926db8
@@ -1,33 +1,33 @@
|
||||
import { Router } from "express"
|
||||
import cors from "cors"
|
||||
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../middlewares"
|
||||
import appRoutes from "./apps"
|
||||
import authRoutes from "./auth"
|
||||
import productRoutes from "./products"
|
||||
import userRoutes, { unauthenticatedUserRoutes } from "./users"
|
||||
import collectionRoutes from "./collections"
|
||||
import customerGroupRoutes from "./customer-groups"
|
||||
import customerRoutes from "./customers"
|
||||
import discountRoutes from "./discounts"
|
||||
import draftOrderRoutes from "./draft-orders"
|
||||
import giftCardRoutes from "./gift-cards"
|
||||
import inviteRoutes, { unauthenticatedInviteRoutes } from "./invites"
|
||||
import noteRoutes from "./notes"
|
||||
import notificationRoutes from "./notifications"
|
||||
import orderRoutes from "./orders"
|
||||
import priceListRoutes from "./price-lists"
|
||||
import productTagRoutes from "./product-tags"
|
||||
import productTypesRoutes from "./product-types"
|
||||
import productRoutes from "./products"
|
||||
import regionRoutes from "./regions"
|
||||
import returnReasonRoutes from "./return-reasons"
|
||||
import returnRoutes from "./returns"
|
||||
import shippingOptionRoutes from "./shipping-options"
|
||||
import shippingProfileRoutes from "./shipping-profiles"
|
||||
import discountRoutes from "./discounts"
|
||||
import giftCardRoutes from "./gift-cards"
|
||||
import orderRoutes from "./orders"
|
||||
import returnReasonRoutes from "./return-reasons"
|
||||
import storeRoutes from "./store"
|
||||
import uploadRoutes from "./uploads"
|
||||
import customerRoutes from "./customers"
|
||||
import appRoutes from "./apps"
|
||||
import swapRoutes from "./swaps"
|
||||
import returnRoutes from "./returns"
|
||||
import variantRoutes from "./variants"
|
||||
import draftOrderRoutes from "./draft-orders"
|
||||
import collectionRoutes from "./collections"
|
||||
import productTagRoutes from "./product-tags"
|
||||
import notificationRoutes from "./notifications"
|
||||
import noteRoutes from "./notes"
|
||||
import taxRateRoutes from "./tax-rates"
|
||||
import productTypesRoutes from "./product-types"
|
||||
import customerGroupRoutes from "./customer-groups"
|
||||
import uploadRoutes from "./uploads"
|
||||
import userRoutes, { unauthenticatedUserRoutes } from "./users"
|
||||
import variantRoutes from "./variants"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -86,6 +86,7 @@ export default (app, container, config) => {
|
||||
inviteRoutes(route)
|
||||
taxRateRoutes(route)
|
||||
customerGroupRoutes(route)
|
||||
priceListRoutes(route)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { PriceListServiceMock } from "../../../../../services/__mocks__/price-list"
|
||||
|
||||
describe("POST /price-lists/:id/prices/batch", () => {
|
||||
describe("successfully adds several new prices to a price list", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/price-lists/pl_1234/prices/batch`,
|
||||
{
|
||||
payload: {
|
||||
prices: [
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 500,
|
||||
min_quantity: 10,
|
||||
max_quantity: 20,
|
||||
variant_id: "variant_12",
|
||||
},
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 430,
|
||||
min_quantity: 21,
|
||||
max_quantity: 40,
|
||||
variant_id: "variant_12",
|
||||
},
|
||||
],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls PriceListService addPrices", () => {
|
||||
expect(PriceListServiceMock.addPrices).toHaveBeenCalledTimes(1)
|
||||
expect(PriceListServiceMock.addPrices).toHaveBeenCalledWith(
|
||||
"pl_1234",
|
||||
[
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 500,
|
||||
min_quantity: 10,
|
||||
max_quantity: 20,
|
||||
variant_id: "variant_12",
|
||||
},
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 430,
|
||||
min_quantity: 21,
|
||||
max_quantity: 40,
|
||||
variant_id: "variant_12",
|
||||
},
|
||||
],
|
||||
undefined
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("fails if no prices were provided", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/price-lists/pl_1234/prices/batch`,
|
||||
{
|
||||
payload: {},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 400", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
})
|
||||
|
||||
it("returns descriptive error that name is missing", () => {
|
||||
expect(subject.body.type).toEqual("invalid_data")
|
||||
expect(subject.body.message).toEqual("prices must be an array")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,119 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { PriceListServiceMock } from "../../../../../services/__mocks__/price-list"
|
||||
|
||||
describe("POST /price-lists", () => {
|
||||
describe("successfully creates a price list", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", `/admin/price-lists`, {
|
||||
payload: {
|
||||
name: "My Price List",
|
||||
description: "testing",
|
||||
ends_at: "2022-03-14T08:28:38.551Z",
|
||||
starts_at: "2022-03-14T08:28:38.551Z",
|
||||
customer_groups: [
|
||||
{
|
||||
id: "gc_123",
|
||||
},
|
||||
],
|
||||
type: "sale",
|
||||
prices: [
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 500,
|
||||
min_quantity: 10,
|
||||
max_quantity: 20,
|
||||
variant_id: "variant_12",
|
||||
},
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 430,
|
||||
min_quantity: 21,
|
||||
max_quantity: 40,
|
||||
variant_id: "variant_12",
|
||||
},
|
||||
],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls PriceListService addPrices", () => {
|
||||
expect(PriceListServiceMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(PriceListServiceMock.create).toHaveBeenCalledWith({
|
||||
name: "My Price List",
|
||||
description: "testing",
|
||||
ends_at: "2022-03-14T08:28:38.551Z",
|
||||
starts_at: "2022-03-14T08:28:38.551Z",
|
||||
customer_groups: [
|
||||
{
|
||||
id: "gc_123",
|
||||
},
|
||||
],
|
||||
type: "sale",
|
||||
prices: [
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 500,
|
||||
min_quantity: 10,
|
||||
max_quantity: 20,
|
||||
variant_id: "variant_12",
|
||||
},
|
||||
{
|
||||
currency_code: "usd",
|
||||
amount: 430,
|
||||
min_quantity: 21,
|
||||
max_quantity: 40,
|
||||
variant_id: "variant_12",
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("fails if required fields are missing", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", `/admin/price-lists`, {
|
||||
payload: {
|
||||
description: "bad payload",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 400", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
})
|
||||
|
||||
it("returns descriptive error that several fields are missing", () => {
|
||||
expect(subject.body.type).toEqual("invalid_data")
|
||||
expect(subject.body.message).toEqual(
|
||||
"name must be a string, type must be a valid enum value, prices must be an array"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { PriceListServiceMock } from "../../../../../services/__mocks__/price-list"
|
||||
|
||||
describe("POST /price-lists/:id/prices/batch", () => {
|
||||
describe("successfully adds several new prices to a price list", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("DELETE", `/admin/price-lists/pl_1234`, {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls PriceListService addPrices", () => {
|
||||
expect(PriceListServiceMock.delete).toHaveBeenCalledTimes(1)
|
||||
expect(PriceListServiceMock.delete).toHaveBeenCalledWith("pl_1234")
|
||||
|
||||
expect(subject.body).toEqual({
|
||||
id: "pl_1234",
|
||||
object: "price-list",
|
||||
deleted: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { PriceListServiceMock } from "../../../../../services/__mocks__/price-list"
|
||||
|
||||
describe("DELETE /price-lists/:id/prices/batch", () => {
|
||||
describe("successfully adds several new prices to a price list", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/price-lists/pl_1234/prices/batch`,
|
||||
{
|
||||
payload: {
|
||||
price_ids: ["price_1234", "price_1235", "price_1236", "price_1237"],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls PriceListService addPrices", () => {
|
||||
expect(PriceListServiceMock.deletePrices).toHaveBeenCalledTimes(1)
|
||||
expect(PriceListServiceMock.deletePrices).toHaveBeenCalledWith(
|
||||
"pl_1234",
|
||||
["price_1234", "price_1235", "price_1236", "price_1237"]
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("fails if no prices were provided", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/price-lists/pl_1234/prices/batch`,
|
||||
{
|
||||
payload: {},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 400", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
})
|
||||
|
||||
it("returns descriptive error that price_ids is missing", () => {
|
||||
expect(subject.body.type).toEqual("invalid_data")
|
||||
expect(subject.body.message).toEqual(
|
||||
"each value in price_ids must be a string, price_ids should not be empty"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,36 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from ".."
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { PriceListServiceMock } from "../../../../../services/__mocks__/price-list"
|
||||
|
||||
describe("GET /price-lists/:id", () => {
|
||||
describe("", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("GET", `/admin/price-lists/pl_1234`, {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls PriceListService retrieve", () => {
|
||||
expect(PriceListServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(PriceListServiceMock.retrieve).toHaveBeenCalledWith("pl_1234", {
|
||||
relations: defaultAdminPriceListRelations,
|
||||
select: defaultAdminPriceListFields,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { PriceListServiceMock } from "../../../../../services/__mocks__/price-list"
|
||||
|
||||
describe("GET /price-lists", () => {
|
||||
describe("successfully lists price lists", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("GET", `/admin/price-lists`, {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls PriceListService listAndCount", () => {
|
||||
expect(PriceListServiceMock.listAndCount).toHaveBeenCalledTimes(1)
|
||||
expect(PriceListServiceMock.listAndCount).toHaveBeenCalledWith(
|
||||
{
|
||||
created_at: undefined,
|
||||
deleted_at: undefined,
|
||||
description: undefined,
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
q: undefined,
|
||||
status: undefined,
|
||||
type: undefined,
|
||||
updated_at: undefined,
|
||||
},
|
||||
{ order: { created_at: "DESC" }, relations: [], skip: 0, take: 10 }
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,37 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { PriceListServiceMock } from "../../../../../services/__mocks__/price-list"
|
||||
|
||||
describe("POST /price-lists/:id", () => {
|
||||
describe("successfully updates a price list", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", `/admin/price-lists/pl_1234`, {
|
||||
payload: {
|
||||
description: "new description",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls PriceListService update", () => {
|
||||
expect(PriceListServiceMock.update).toHaveBeenCalledTimes(1)
|
||||
expect(PriceListServiceMock.update).toHaveBeenCalledWith("pl_1234", {
|
||||
description: "new description",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,99 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsArray, IsBoolean, IsOptional, ValidateNested } from "class-validator"
|
||||
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
||||
import { PriceList } from "../../../.."
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /price-lists/{id}/prices/batch
|
||||
* operationId: "PostPriceListsPriceListPricesBatch"
|
||||
* summary: "Batch update prices for a Price List"
|
||||
* description: "Batch update prices for a Price List"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Price List to update prices for.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* prices:
|
||||
* description: The prices to update or add.
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* id:
|
||||
* description: The id of the price.
|
||||
* type: string
|
||||
* status:
|
||||
* description: The status of the Price List.
|
||||
* type: string
|
||||
* enum:
|
||||
* - active
|
||||
* - draft
|
||||
* region_id:
|
||||
* description: The id of the Region for which the price is used.
|
||||
* type: string
|
||||
* currency_code:
|
||||
* description: The 3 character ISO currency code for which the price will be used.
|
||||
* type: string
|
||||
* amount:
|
||||
* description: The amount of the price.
|
||||
* type: number
|
||||
* min_quantity:
|
||||
* description: The minimum quantity for which the price will be used.
|
||||
* type: number
|
||||
* max_quantity:
|
||||
* description: The maximum quantity for which the price will be used.
|
||||
* type: number
|
||||
* override:
|
||||
* description: "If true the prices will replace all existing prices associated with the Price List."
|
||||
* type: boolean
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* type: string
|
||||
* description: The id of the deleted Price List.
|
||||
* object:
|
||||
* type: string
|
||||
* description: The type of the object that was deleted.
|
||||
* deleted:
|
||||
* type: boolean
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(AdminPostPriceListPricesPricesReq, req.body)
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
await priceListService.addPrices(id, validated.prices, validated.override)
|
||||
|
||||
const priceList = await priceListService.retrieve(id, {
|
||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||
relations: defaultAdminPriceListRelations,
|
||||
})
|
||||
|
||||
res.json({ price_list: priceList })
|
||||
}
|
||||
|
||||
export class AdminPostPriceListPricesPricesReq {
|
||||
@IsArray()
|
||||
@Type(() => AdminPriceListPricesUpdateReq)
|
||||
@ValidateNested({ each: true })
|
||||
prices: AdminPriceListPricesUpdateReq[]
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
override?: boolean
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsEnum,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import {
|
||||
AdminPriceListPricesCreateReq,
|
||||
PriceListStatus,
|
||||
PriceListType,
|
||||
} from "../../../../types/price-list"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /price_lists
|
||||
* operationId: "PostPriceListsPriceList"
|
||||
* summary: "Creates a Price List"
|
||||
* description: "Creates a Price List"
|
||||
* x-authenticated: true
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* name:
|
||||
* description: "The name of the Price List"
|
||||
* type: string
|
||||
* description:
|
||||
* description: "A description of the Price List."
|
||||
* type: string
|
||||
* type:
|
||||
* description: The type of the Price List.
|
||||
* type: string
|
||||
* enum:
|
||||
* - sale
|
||||
* - override
|
||||
* status:
|
||||
* description: The status of the Price List.
|
||||
* type: string
|
||||
* enum:
|
||||
* - active
|
||||
* - draft
|
||||
* prices:
|
||||
* description: The prices of the Price List.
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* region_id:
|
||||
* description: The id of the Region for which the price is used.
|
||||
* type: string
|
||||
* currency_code:
|
||||
* description: The 3 character ISO currency code for which the price will be used.
|
||||
* type: string
|
||||
* amount:
|
||||
* description: The amount to charge for the Product Variant.
|
||||
* type: integer
|
||||
* min_quantity:
|
||||
* description: The minimum quantity for which the price will be used.
|
||||
* type: integer
|
||||
* max_quantity:
|
||||
* description: The maximum quantity for which the price will be used.
|
||||
* type: integer
|
||||
* customer_groups:
|
||||
* type: array
|
||||
* description: A list of customer groups that the Price List applies to.
|
||||
* items:
|
||||
* required:
|
||||
* - id
|
||||
* properties:
|
||||
* id:
|
||||
* description: The id of a customer group
|
||||
* type: string
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* product:
|
||||
* $ref: "#/components/schemas/price_list"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(AdminPostPriceListsPriceListReq, req.body)
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
const priceList = await priceListService.create(validated)
|
||||
|
||||
res.json({ price_list: priceList })
|
||||
}
|
||||
|
||||
class CustomerGroup {
|
||||
@IsString()
|
||||
id: string
|
||||
}
|
||||
|
||||
export class AdminPostPriceListsPriceListReq {
|
||||
@IsString()
|
||||
name: string
|
||||
|
||||
@IsString()
|
||||
description: string
|
||||
|
||||
@IsOptional()
|
||||
starts_at?: Date
|
||||
|
||||
@IsOptional()
|
||||
ends_at?: Date
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(PriceListStatus)
|
||||
status?: PriceListStatus
|
||||
|
||||
@IsEnum(PriceListType)
|
||||
type: PriceListType
|
||||
|
||||
@IsArray()
|
||||
@Type(() => AdminPriceListPricesCreateReq)
|
||||
@ValidateNested({ each: true })
|
||||
prices: AdminPriceListPricesCreateReq[]
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@Type(() => CustomerGroup)
|
||||
@ValidateNested({ each: true })
|
||||
customer_groups?: CustomerGroup[]
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
|
||||
/**
|
||||
* @oas [delete] /price-lists/{id}
|
||||
* operationId: "DeletePriceListsPriceList"
|
||||
* summary: "Delete a Price List"
|
||||
* description: "Deletes a Price List"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Price List to delete.
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* id:
|
||||
* type: string
|
||||
* description: The id of the deleted Price List.
|
||||
* object:
|
||||
* type: string
|
||||
* description: The type of the object that was deleted.
|
||||
* deleted:
|
||||
* type: boolean
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
await priceListService.delete(id)
|
||||
|
||||
res.json({
|
||||
id,
|
||||
object: "price-list",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { ArrayNotEmpty, IsString } from "class-validator"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [delete] /price-lists/{id}/prices/batch
|
||||
* operationId: "DeletePriceListsPriceListPricesBatch"
|
||||
* summary: "Batch delete prices that belongs to a Price List"
|
||||
* description: "Batch delete prices that belongs to a Price List"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Price List that the Money Amounts that will be deleted belongs to.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* price_ids:
|
||||
* description: The price id's of the Money Amounts to delete.
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* type: array
|
||||
* items:
|
||||
* type: string
|
||||
* description: The id of the deleted Money Amount.
|
||||
* object:
|
||||
* type: string
|
||||
* description: The type of the object that was deleted.
|
||||
* deleted:
|
||||
* type: boolean
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(
|
||||
AdminDeletePriceListPricesPricesReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
await priceListService.deletePrices(id, validated.price_ids)
|
||||
|
||||
res.json({ ids: validated.price_ids, object: "money-amount", deleted: true })
|
||||
}
|
||||
|
||||
export class AdminDeletePriceListPricesPricesReq {
|
||||
@ArrayNotEmpty()
|
||||
@IsString({ each: true })
|
||||
price_ids: string[]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
||||
import { PriceList } from "../../../.."
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
|
||||
/**
|
||||
* @oas [get] /price-lists/{id}
|
||||
* operationId: "GetPriceListsPriceList"
|
||||
* summary: "Retrieve a Price List"
|
||||
* description: "Retrieves a Price List."
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Price List.
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* price_list:
|
||||
* $ref: "#/components/schemas/price_list"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
const priceList = await priceListService.retrieve(id, {
|
||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||
relations: defaultAdminPriceListRelations,
|
||||
})
|
||||
|
||||
res.status(200).json({ price_list: priceList })
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Router } from "express"
|
||||
import "reflect-metadata"
|
||||
import { PriceList } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use("/price-lists", route)
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-price-list").default))
|
||||
|
||||
route.get("/", middlewares.wrap(require("./list-price-lists").default))
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-price-list").default))
|
||||
|
||||
route.post("/:id", middlewares.wrap(require("./update-price-list").default))
|
||||
|
||||
route.delete("/:id", middlewares.wrap(require("./delete-price-list").default))
|
||||
|
||||
route.delete(
|
||||
"/:id/prices/batch",
|
||||
middlewares.wrap(require("./delete-prices-batch").default)
|
||||
)
|
||||
|
||||
route.post(
|
||||
"/:id/prices/batch",
|
||||
middlewares.wrap(require("./add-prices-batch").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultAdminPriceListFields = [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"type",
|
||||
"status",
|
||||
"starts_at",
|
||||
"ends_at",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"deleted_at",
|
||||
]
|
||||
|
||||
export const defaultAdminPriceListRelations = ["prices", "customer_groups"]
|
||||
|
||||
export const allowedAdminPriceListFields = ["prices", "customer_groups"]
|
||||
|
||||
export type AdminPriceListRes = {
|
||||
price_list: PriceList
|
||||
}
|
||||
|
||||
export type AdminPriceListDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminPriceListsListRes = PaginatedResponse & {
|
||||
price_lists: PriceList[]
|
||||
}
|
||||
|
||||
export * from "./add-prices-batch"
|
||||
export * from "./create-price-list"
|
||||
export * from "./delete-price-list"
|
||||
export * from "./get-price-list"
|
||||
export * from "./list-price-lists"
|
||||
export * from "./update-price-list"
|
||||
@@ -0,0 +1,106 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { IsNumber, IsOptional, IsString } from "class-validator"
|
||||
import omit from "lodash/omit"
|
||||
import { PriceList } from "../../../.."
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import { FindConfig } from "../../../../types/common"
|
||||
import { FilterablePriceListProps } from "../../../../types/price-list"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [get] /price-lists
|
||||
* operationId: "GetPriceLists"
|
||||
* summary: "List Price Lists"
|
||||
* description: "Retrieves a list of Price Lists."
|
||||
* x-authenticated: true
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* price_lists:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: "#/components/schemas/price_list"
|
||||
* count:
|
||||
* description: The number of Price Lists.
|
||||
* type: integer
|
||||
* offset:
|
||||
* description: The offset of the Price List query.
|
||||
* type: integer
|
||||
* limit:
|
||||
* description: The limit of the Price List query.
|
||||
* type: integer
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const validated = await validator(
|
||||
AdminGetPriceListPaginationParams,
|
||||
req.query
|
||||
)
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
let expandFields: string[] = []
|
||||
if (validated.expand) {
|
||||
expandFields = validated.expand.split(",")
|
||||
}
|
||||
|
||||
const listConfig: FindConfig<PriceList> = {
|
||||
relations: expandFields,
|
||||
skip: validated.offset,
|
||||
take: validated.limit,
|
||||
order: { created_at: "DESC" } as { [k: string]: "DESC" },
|
||||
}
|
||||
|
||||
if (typeof validated.order !== "undefined") {
|
||||
if (validated.order.startsWith("-")) {
|
||||
const [, field] = validated.order.split("-")
|
||||
listConfig.order = { [field]: "DESC" }
|
||||
} else {
|
||||
listConfig.order = { [validated.order]: "ASC" }
|
||||
}
|
||||
}
|
||||
|
||||
const filterableFields = omit(validated, [
|
||||
"limit",
|
||||
"offset",
|
||||
"expand",
|
||||
"order",
|
||||
])
|
||||
|
||||
const [price_lists, count] = await priceListService.listAndCount(
|
||||
filterableFields,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.json({
|
||||
price_lists,
|
||||
count,
|
||||
offset: validated.offset,
|
||||
limit: validated.limit,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminGetPriceListPaginationParams extends FilterablePriceListProps {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
offset?: number = 0
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
limit?: number = 10
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
expand?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
order?: string
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsEnum,
|
||||
IsOptional,
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "."
|
||||
import { PriceList } from "../../../.."
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
import {
|
||||
AdminPriceListPricesUpdateReq,
|
||||
PriceListStatus,
|
||||
PriceListType,
|
||||
} from "../../../../types/price-list"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
* @oas [post] /price_lists/{id}
|
||||
* operationId: "PostPriceListsPriceListPriceList"
|
||||
* summary: "Update a Price List"
|
||||
* description: "Updates a Price List"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Price List.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* name:
|
||||
* description: "The name of the Price List"
|
||||
* type: string
|
||||
* description:
|
||||
* description: "A description of the Price List."
|
||||
* type: string
|
||||
* type:
|
||||
* description: The type of the Price List.
|
||||
* type: string
|
||||
* enum:
|
||||
* - sale
|
||||
* - override
|
||||
* status:
|
||||
* description: The status of the Price List.
|
||||
* type: string
|
||||
* enum:
|
||||
* - active
|
||||
* - draft
|
||||
* prices:
|
||||
* description: The prices of the Price List.
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* id:
|
||||
* description: The id of the price.
|
||||
* type: string
|
||||
* region_id:
|
||||
* description: The id of the Region for which the price is used.
|
||||
* type: string
|
||||
* currency_code:
|
||||
* description: The 3 character ISO currency code for which the price will be used.
|
||||
* type: string
|
||||
* amount:
|
||||
* description: The amount to charge for the Product Variant.
|
||||
* type: integer
|
||||
* min_quantity:
|
||||
* description: The minimum quantity for which the price will be used.
|
||||
* type: integer
|
||||
* max_quantity:
|
||||
* description: The maximum quantity for which the price will be used.
|
||||
* type: integer
|
||||
* customer_groups:
|
||||
* type: array
|
||||
* description: A list of customer groups that the Price List applies to.
|
||||
* items:
|
||||
* required:
|
||||
* - id
|
||||
* properties:
|
||||
* id:
|
||||
* description: The id of a customer group
|
||||
* type: string
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* product:
|
||||
* $ref: "#/components/schemas/price_list"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(
|
||||
AdminPostPriceListsPriceListPriceListReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
await priceListService.update(id, validated)
|
||||
|
||||
const priceList = await priceListService.retrieve(id, {
|
||||
select: defaultAdminPriceListFields as (keyof PriceList)[],
|
||||
relations: defaultAdminPriceListRelations,
|
||||
})
|
||||
|
||||
res.json({ price_list: priceList })
|
||||
}
|
||||
|
||||
class CustomerGroup {
|
||||
@IsString()
|
||||
id: string
|
||||
}
|
||||
|
||||
export class AdminPostPriceListsPriceListPriceListReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
name?: string
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string
|
||||
|
||||
@IsOptional()
|
||||
starts_at?: Date
|
||||
|
||||
@IsOptional()
|
||||
ends_at?: Date
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(PriceListStatus)
|
||||
status?: PriceListStatus
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(PriceListType)
|
||||
type?: PriceListType
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@Type(() => AdminPriceListPricesUpdateReq)
|
||||
@ValidateNested({ each: true })
|
||||
prices: AdminPriceListPricesUpdateReq[]
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@Type(() => CustomerGroup)
|
||||
@ValidateNested({ each: true })
|
||||
customer_groups: CustomerGroup[]
|
||||
}
|
||||
@@ -3,12 +3,10 @@ import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsInt,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Validate,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
@@ -19,7 +17,7 @@ import {
|
||||
ShippingProfileService,
|
||||
} from "../../../../services"
|
||||
import { ProductStatus } from "../../../../types/product"
|
||||
import { XorConstraint } from "../../../../types/validators/xor"
|
||||
import { ProductVariantPricesCreateReq } from "../../../../types/product-variant"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
@@ -308,21 +306,6 @@ class ProductOptionReq {
|
||||
title: string
|
||||
}
|
||||
|
||||
class ProductVariantPricesReq {
|
||||
@Validate(XorConstraint, ["currency_code"])
|
||||
region_id?: string
|
||||
|
||||
@Validate(XorConstraint, ["region_id"])
|
||||
currency_code?: string
|
||||
|
||||
@IsInt()
|
||||
amount: number
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sale_amount?: number
|
||||
}
|
||||
|
||||
class ProductVariantReq {
|
||||
@IsString()
|
||||
title: string
|
||||
@@ -393,8 +376,8 @@ class ProductVariantReq {
|
||||
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ProductVariantPricesReq)
|
||||
prices: ProductVariantPricesReq[]
|
||||
@Type(() => ProductVariantPricesCreateReq)
|
||||
prices: ProductVariantPricesCreateReq[]
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => ProductVariantOptionReq)
|
||||
|
||||
@@ -2,17 +2,15 @@ import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Validate,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||
import { ProductService, ProductVariantService } from "../../../../services"
|
||||
import { XorConstraint } from "../../../../types/validators/xor"
|
||||
import { ProductVariantPricesCreateReq } from "../../../../types/product-variant"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
@@ -96,8 +94,11 @@ import { validator } from "../../../../utils/validator"
|
||||
* amount:
|
||||
* description: The amount to charge for the Product Variant.
|
||||
* type: integer
|
||||
* sale_amount:
|
||||
* description: The sale amount to charge for the Product Variant.
|
||||
* min_quantity:
|
||||
* description: The minimum quantity for which the price will be used.
|
||||
* type: integer
|
||||
* max_quantity:
|
||||
* description: The maximum quantity for which the price will be used.
|
||||
* type: integer
|
||||
* options:
|
||||
* type: array
|
||||
@@ -152,21 +153,6 @@ class ProductVariantOptionReq {
|
||||
option_id: string
|
||||
}
|
||||
|
||||
class ProductVariantPricesReq {
|
||||
@Validate(XorConstraint, ["currency_code"])
|
||||
region_id?: string
|
||||
|
||||
@Validate(XorConstraint, ["region_id"])
|
||||
currency_code?: string
|
||||
|
||||
@IsInt()
|
||||
amount: number
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sale_amount?: number
|
||||
}
|
||||
|
||||
export class AdminPostProductsProductVariantsReq {
|
||||
@IsString()
|
||||
title: string
|
||||
@@ -237,8 +223,8 @@ export class AdminPostProductsProductVariantsReq {
|
||||
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ProductVariantPricesReq)
|
||||
prices: ProductVariantPricesReq[]
|
||||
@Type(() => ProductVariantPricesCreateReq)
|
||||
prices: ProductVariantPricesCreateReq[]
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => ProductVariantOptionReq)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Router } from "express"
|
||||
import { Product, ProductTag, ProductType } from "../../../.."
|
||||
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
import "reflect-metadata"
|
||||
import { Product, ProductTag, ProductType } from "../../../.."
|
||||
import { PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
IsOptional,
|
||||
IsString,
|
||||
NotEquals,
|
||||
Validate,
|
||||
ValidateIf,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
@@ -19,7 +18,7 @@ import {
|
||||
ProductStatus,
|
||||
} from "."
|
||||
import { ProductService } from "../../../../services"
|
||||
import { XorConstraint } from "../../../../types/validators/xor"
|
||||
import { ProductVariantPricesUpdateReq } from "../../../../types/product-variant"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
@@ -248,21 +247,6 @@ class ProductVariantOptionReq {
|
||||
option_id: string
|
||||
}
|
||||
|
||||
class ProductVariantPricesReq {
|
||||
@Validate(XorConstraint, ["currency_code"])
|
||||
region_id?: string
|
||||
|
||||
@Validate(XorConstraint, ["region_id"])
|
||||
currency_code?: string
|
||||
|
||||
@IsInt()
|
||||
amount: number
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sale_amount?: number
|
||||
}
|
||||
|
||||
class ProductVariantReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@@ -339,8 +323,8 @@ class ProductVariantReq {
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ProductVariantPricesReq)
|
||||
prices: ProductVariantPricesReq[]
|
||||
@Type(() => ProductVariantPricesUpdateReq)
|
||||
prices: ProductVariantPricesUpdateReq[]
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => ProductVariantOptionReq)
|
||||
|
||||
@@ -2,17 +2,15 @@ import { Type } from "class-transformer"
|
||||
import {
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
IsNumber,
|
||||
IsObject,
|
||||
IsOptional,
|
||||
IsString,
|
||||
Validate,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { defaultAdminProductFields, defaultAdminProductRelations } from "."
|
||||
import { ProductService, ProductVariantService } from "../../../../services"
|
||||
import { XorConstraint } from "../../../../types/validators/xor"
|
||||
import { ProductVariantPricesUpdateReq } from "../../../../types/product-variant"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
|
||||
/**
|
||||
@@ -84,6 +82,9 @@ import { validator } from "../../../../utils/validator"
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* id:
|
||||
* description: The id of the price.
|
||||
* type: string
|
||||
* region_id:
|
||||
* description: The id of the Region for which the price is used.
|
||||
* type: string
|
||||
@@ -93,8 +94,11 @@ import { validator } from "../../../../utils/validator"
|
||||
* amount:
|
||||
* description: The amount to charge for the Product Variant.
|
||||
* type: integer
|
||||
* sale_amount:
|
||||
* description: The sale amount to charge for the Product Variant.
|
||||
* min_quantity:
|
||||
* description: The minimum quantity for which the price will be used.
|
||||
* type: integer
|
||||
* max_quantity:
|
||||
* description: The maximum quantity for which the price will be used.
|
||||
* type: integer
|
||||
* options:
|
||||
* type: array
|
||||
@@ -152,21 +156,6 @@ class ProductVariantOptionReq {
|
||||
option_id: string
|
||||
}
|
||||
|
||||
class ProductVariantPricesReq {
|
||||
@Validate(XorConstraint, ["currency_code"])
|
||||
region_id?: string
|
||||
|
||||
@Validate(XorConstraint, ["region_id"])
|
||||
currency_code?: string
|
||||
|
||||
@IsInt()
|
||||
amount: number
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
sale_amount?: number
|
||||
}
|
||||
|
||||
export class AdminPostProductsProductVariantsVariantReq {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@@ -238,8 +227,8 @@ export class AdminPostProductsProductVariantsVariantReq {
|
||||
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => ProductVariantPricesReq)
|
||||
prices: ProductVariantPricesReq[]
|
||||
@Type(() => ProductVariantPricesUpdateReq)
|
||||
prices: ProductVariantPricesUpdateReq[]
|
||||
|
||||
@Type(() => ProductVariantOptionReq)
|
||||
@ValidateNested({ each: true })
|
||||
|
||||
Reference in New Issue
Block a user