feat(medusa): Support deleting prices from a price list by product or variant (#1555)
This commit is contained in:
committed by
GitHub
parent
ad9cfedf04
commit
fa031fd28b
@@ -94,6 +94,24 @@ class AdminPriceListResource extends BaseResource {
|
||||
const path = `/admin/price-lists/${id}/prices/batch`
|
||||
return this.client.request("DELETE", path, payload, {}, customHeaders)
|
||||
}
|
||||
|
||||
deleteProductPrices(
|
||||
priceListId: string,
|
||||
productId: string,
|
||||
customHeaders: Record<string, any> = {}
|
||||
): ResponsePromise<AdminPriceListDeleteBatchRes> {
|
||||
const path = `/admin/price-lists/${priceListId}/products/${productId}/prices`
|
||||
return this.client.request("DELETE", path, {}, {}, customHeaders)
|
||||
}
|
||||
|
||||
deleteVariantPrices(
|
||||
priceListId: string,
|
||||
variantId: string,
|
||||
customHeaders: Record<string, any> = {}
|
||||
): ResponsePromise<AdminPriceListDeleteBatchRes> {
|
||||
const path = `/admin/price-lists/${priceListId}/variants/${variantId}/prices`
|
||||
return this.client.request("DELETE", path, {}, {}, customHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
export default AdminPriceListResource
|
||||
|
||||
@@ -253,6 +253,28 @@ export const adminHandlers = [
|
||||
)
|
||||
}),
|
||||
|
||||
rest.delete("/admin/price-lists/:id/products/:product_id/prices", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
ids: [],
|
||||
object: "money-amount",
|
||||
deleted: true,
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.delete("/admin/price-lists/:id/variants/:variant_id/prices", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
ids: [],
|
||||
object: "money-amount",
|
||||
deleted: true,
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
rest.post("/admin/return-reasons/", (req, res, ctx) => {
|
||||
const body = req.body as Record<string, any>
|
||||
return res(
|
||||
|
||||
@@ -6,12 +6,16 @@ import {
|
||||
AdminDeletePriceListPricesPricesReq,
|
||||
AdminPriceListDeleteRes,
|
||||
AdminPriceListDeleteBatchRes,
|
||||
AdminPriceListDeleteProductPricesRes,
|
||||
AdminPriceListDeleteVariantPricesRes,
|
||||
} from "@medusajs/medusa"
|
||||
import { Response } from "@medusajs/medusa-js"
|
||||
import { useMutation, UseMutationOptions, useQueryClient } from "react-query"
|
||||
import { useMedusa } from "../../../contexts/medusa"
|
||||
import { buildOptions } from "../../utils/buildOptions"
|
||||
import { adminPriceListKeys } from "./queries"
|
||||
import { adminProductKeys } from "../products"
|
||||
import { adminVariantKeys } from "../variants"
|
||||
|
||||
export const useAdminCreatePriceList = (
|
||||
options?: UseMutationOptions<
|
||||
@@ -118,3 +122,53 @@ export const useAdminDeletePriceListPrices = (
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export const useAdminDeletePriceListProductPrices = (
|
||||
id: string,
|
||||
productId: string,
|
||||
options?: UseMutationOptions<
|
||||
Response<AdminPriceListDeleteProductPricesRes>,
|
||||
Error
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation(
|
||||
() => client.admin.priceLists.deleteProductPrices(id, productId),
|
||||
buildOptions(
|
||||
queryClient,
|
||||
[
|
||||
adminPriceListKeys.detail(id),
|
||||
adminPriceListKeys.lists(),
|
||||
adminProductKeys.detail(productId)
|
||||
],
|
||||
options
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export const useAdminDeletePriceListVariantPrices = (
|
||||
id: string,
|
||||
variantId: string,
|
||||
options?: UseMutationOptions<
|
||||
Response<AdminPriceListDeleteVariantPricesRes>,
|
||||
Error
|
||||
>
|
||||
) => {
|
||||
const { client } = useMedusa()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation(
|
||||
() => client.admin.priceLists.deleteVariantPrices(id, variantId),
|
||||
buildOptions(
|
||||
queryClient,
|
||||
[
|
||||
adminPriceListKeys.detail(id),
|
||||
adminPriceListKeys.lists(),
|
||||
adminVariantKeys.detail(variantId)
|
||||
],
|
||||
options
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export const adminPriceListKeys = {
|
||||
"products" as const,
|
||||
{ ...(query || {}) },
|
||||
] as const
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type PriceListQueryKeys = typeof adminPriceListKeys
|
||||
|
||||
@@ -3,6 +3,8 @@ import {
|
||||
useAdminCreatePriceListPrices,
|
||||
useAdminDeletePriceList,
|
||||
useAdminDeletePriceListPrices,
|
||||
useAdminDeletePriceListProductPrices,
|
||||
useAdminDeletePriceListVariantPrices,
|
||||
useAdminUpdatePriceList,
|
||||
} from "../../../../src"
|
||||
import { renderHook } from "@testing-library/react-hooks"
|
||||
@@ -137,3 +139,53 @@ describe("useAdminDeletePriceList hook", () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("useAdminDeletePriceListProductPrices hook", () => {
|
||||
test("should delete prices from a price list for all the variants related to the specified product", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useAdminDeletePriceListProductPrices(
|
||||
fixtures.get("price_list").id,
|
||||
fixtures.get("product").id
|
||||
),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate()
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data).toEqual(expect.objectContaining({
|
||||
ids: [],
|
||||
object: "money-amount",
|
||||
deleted: true
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
describe("useAdminDeletePriceListVariantPrices hook", () => {
|
||||
test("should delete prices from a price list for the specified variant", async () => {
|
||||
const { result, waitFor } = renderHook(
|
||||
() => useAdminDeletePriceListVariantPrices(
|
||||
fixtures.get("price_list").id,
|
||||
fixtures.get("product_variant").id
|
||||
),
|
||||
{
|
||||
wrapper: createWrapper(),
|
||||
}
|
||||
)
|
||||
|
||||
result.current.mutate()
|
||||
|
||||
await waitFor(() => result.current.isSuccess)
|
||||
|
||||
expect(result.current.data.response.status).toEqual(200)
|
||||
expect(result.current.data).toEqual(expect.objectContaining({
|
||||
ids: [],
|
||||
object: "money-amount",
|
||||
deleted: true
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
|
||||
/**
|
||||
* @oas [delete] /price-lists/{id}/products/{product_id}/prices
|
||||
* operationId: "DeletePriceListsPriceListProductsProductPrices"
|
||||
* summary: "Delete all the prices related to a specific product in a price list"
|
||||
* description: "Delete all the prices related to a specific product in 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.
|
||||
* - (path) product_id=* {string} The id of the product from which the money amount will be deleted.
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* type: number
|
||||
* description: The price ids that have been deleted.
|
||||
* count:
|
||||
* type: number
|
||||
* description: The number of prices that have been deleted.
|
||||
* object:
|
||||
* type: string
|
||||
* description: The type of the object that was deleted.
|
||||
* deleted:
|
||||
* type: boolean
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, product_id } = req.params
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
const [deletedPriceIds] = await priceListService.deleteProductPrices(id, [
|
||||
product_id,
|
||||
])
|
||||
|
||||
return res.json({
|
||||
ids: deletedPriceIds,
|
||||
object: "money-amount",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
|
||||
/**
|
||||
* @oas [delete] /price-lists/{id}/variants/{variant_id}/prices
|
||||
* operationId: "DeletePriceListsPriceListVariantsVariantPrices"
|
||||
* summary: "Delete all the prices related to a specific variant in a price list"
|
||||
* description: "Delete all the prices related to a specific variant in 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.
|
||||
* - (path) variant_id=* {string} The id of the variant from which the money amount will be deleted.
|
||||
* tags:
|
||||
* - Price List
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* ids:
|
||||
* type: number
|
||||
* description: The price ids that have been deleted.
|
||||
* count:
|
||||
* type: number
|
||||
* description: The number of prices that have been deleted.
|
||||
* object:
|
||||
* type: string
|
||||
* description: The type of the object that was deleted.
|
||||
* deleted:
|
||||
* type: boolean
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id, variant_id } = req.params
|
||||
|
||||
const priceListService: PriceListService =
|
||||
req.scope.resolve("priceListService")
|
||||
|
||||
const [deletedPriceIds] = await priceListService.deleteVariantPrices(id, [
|
||||
variant_id,
|
||||
])
|
||||
|
||||
return res.json({
|
||||
ids: deletedPriceIds,
|
||||
object: "money-amount",
|
||||
deleted: true,
|
||||
})
|
||||
}
|
||||
@@ -22,6 +22,15 @@ export default (app) => {
|
||||
middlewares.wrap(require("./list-price-list-products").default)
|
||||
)
|
||||
|
||||
route.delete(
|
||||
"/:id/products/:product_id/prices",
|
||||
middlewares.wrap(require("./delete-product-prices").default)
|
||||
)
|
||||
route.delete(
|
||||
"/:id/variants/:variant_id/prices",
|
||||
middlewares.wrap(require("./delete-variant-prices").default)
|
||||
)
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-price-list").default))
|
||||
|
||||
route.post("/:id", middlewares.wrap(require("./update-price-list").default))
|
||||
@@ -68,6 +77,9 @@ export type AdminPriceListDeleteBatchRes = {
|
||||
object: string
|
||||
}
|
||||
|
||||
export type AdminPriceListDeleteProductPricesRes = AdminPriceListDeleteBatchRes
|
||||
export type AdminPriceListDeleteVariantPricesRes = AdminPriceListDeleteBatchRes
|
||||
|
||||
export type AdminPriceListDeleteRes = DeleteResponse
|
||||
|
||||
export type AdminPriceListsListRes = PaginatedResponse & {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
IsString,
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import { Product } from "../../../../models/product"
|
||||
import { Product } from "../../../../models"
|
||||
import { DateComparisonOperator } from "../../../../types/common"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { FilterableProductProps } from "../../../../types/product"
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
defaultAdminProductFields,
|
||||
defaultAdminProductRelations,
|
||||
} from "../products"
|
||||
import listAndCount from "../../../../controllers/products/admin-list-products"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { getListConfig } from "../../../../utils/get-query-config"
|
||||
import PriceListService from "../../../../services/price-list"
|
||||
|
||||
@@ -49,7 +49,7 @@ export default async (req, res) => {
|
||||
expandFields = validated.expand.split(",")
|
||||
}
|
||||
|
||||
const listConfig: FindConfig<PriceList> = {
|
||||
const listConfig: FindConfig<FilterablePriceListProps> = {
|
||||
relations: expandFields,
|
||||
skip: validated.offset,
|
||||
take: validated.limit,
|
||||
@@ -65,7 +65,7 @@ export default async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
const filterableFields = omit(validated, [
|
||||
const filterableFields: FilterablePriceListProps = omit(validated, [
|
||||
"limit",
|
||||
"offset",
|
||||
"expand",
|
||||
|
||||
@@ -119,18 +119,24 @@ export class MoneyAmountRepository extends Repository<MoneyAmount> {
|
||||
|
||||
public async findManyForVariantInPriceList(
|
||||
variant_id: string,
|
||||
price_list_id: string
|
||||
price_list_id: string,
|
||||
requiresPriceList = false
|
||||
): Promise<[MoneyAmount[], number]> {
|
||||
const qb = this.createQueryBuilder("ma")
|
||||
.leftJoinAndSelect("ma.price_list", "price_list")
|
||||
.where("ma.variant_id = :variant_id", { variant_id })
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.where("ma.price_list_id = :price_list_id", {
|
||||
price_list_id,
|
||||
}).orWhere("ma.price_list_id IS NULL")
|
||||
})
|
||||
)
|
||||
|
||||
const getAndWhere = (subQb) => {
|
||||
const andWhere = subQb.where("ma.price_list_id = :price_list_id", {
|
||||
price_list_id,
|
||||
})
|
||||
if (!requiresPriceList) {
|
||||
andWhere.orWhere("ma.price_list_id IS NULL")
|
||||
}
|
||||
return andWhere
|
||||
}
|
||||
|
||||
qb.andWhere(new Brackets(getAndWhere))
|
||||
|
||||
return await qb.getManyAndCount()
|
||||
}
|
||||
|
||||
@@ -8,16 +8,18 @@ import {
|
||||
} from "typeorm"
|
||||
import { PriceList } from "../models/price-list"
|
||||
import { CustomFindOptions, ExtendedFindConfig } from "../types/common"
|
||||
import { CustomerGroup } from "../models"
|
||||
import { FilterablePriceListProps } from "../types/price-list"
|
||||
|
||||
type PriceListFindOptions = CustomFindOptions<PriceList, "status" | "type">
|
||||
export type PriceListFindOptions = CustomFindOptions<PriceList, "status" | "type">
|
||||
|
||||
@EntityRepository(PriceList)
|
||||
export class PriceListRepository extends Repository<PriceList> {
|
||||
public async getFreeTextSearchResultsAndCount(
|
||||
q: string,
|
||||
options: PriceListFindOptions = { where: {} },
|
||||
groups?: FindOperator<PriceList>,
|
||||
relations: (keyof PriceList)[] = []
|
||||
groups: FindOperator<string[]>,
|
||||
relations: string[] = []
|
||||
): Promise<[PriceList[], number]> {
|
||||
options.where = options.where ?? {}
|
||||
|
||||
@@ -50,7 +52,7 @@ export class PriceListRepository extends Repository<PriceList> {
|
||||
}
|
||||
|
||||
public async findWithRelations(
|
||||
relations: (keyof PriceList)[] = [],
|
||||
relations: string[] = [],
|
||||
idsOrOptionsWithoutRelations:
|
||||
| Omit<FindManyOptions<PriceList>, "relations">
|
||||
| string[] = {}
|
||||
@@ -97,8 +99,8 @@ export class PriceListRepository extends Repository<PriceList> {
|
||||
}
|
||||
|
||||
async listAndCount(
|
||||
query: ExtendedFindConfig<PriceList>,
|
||||
groups?: FindOperator<PriceList>
|
||||
query: ExtendedFindConfig<FilterablePriceListProps>,
|
||||
groups: FindOperator<string[]>
|
||||
): Promise<[PriceList[], number]> {
|
||||
const qb = this.createQueryBuilder("price_list")
|
||||
.where(query.where)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { EntityManager, FindOperator } from "typeorm"
|
||||
import { CustomerGroupService } from "."
|
||||
import { Product } from "../models"
|
||||
import { CustomerGroup } from "../models/customer-group"
|
||||
import { PriceList } from "../models/price-list"
|
||||
import { CustomerGroup, PriceList, Product, ProductVariant } from "../models"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
import { PriceListRepository } from "../repositories/price-list"
|
||||
import { FindConfig } from "../types/common"
|
||||
import {
|
||||
PriceListFindOptions,
|
||||
PriceListRepository,
|
||||
} from "../repositories/price-list"
|
||||
import { FindConfig, Selector } from "../types/common"
|
||||
import {
|
||||
CreatePriceListInput,
|
||||
FilterablePriceListProps,
|
||||
@@ -18,12 +18,18 @@ import {
|
||||
import { formatException } from "../utils/exception-formatter"
|
||||
import ProductService from "./product"
|
||||
import RegionService from "./region"
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import { buildQuery } from "../utils"
|
||||
import { FilterableProductProps } from "../types/product"
|
||||
import ProductVariantService from "./product-variant"
|
||||
import { FilterableProductVariantProps } from "../types/product-variant"
|
||||
|
||||
type PriceListConstructorProps = {
|
||||
manager: EntityManager
|
||||
customerGroupService: CustomerGroupService
|
||||
regionService: RegionService
|
||||
productService: ProductService
|
||||
productVariantService: ProductVariantService
|
||||
priceListRepository: typeof PriceListRepository
|
||||
moneyAmountRepository: typeof MoneyAmountRepository
|
||||
}
|
||||
@@ -32,50 +38,38 @@ type PriceListConstructorProps = {
|
||||
* Provides layer to manipulate product tags.
|
||||
* @extends BaseService
|
||||
*/
|
||||
class PriceListService extends BaseService {
|
||||
private manager_: EntityManager
|
||||
private customerGroupService_: CustomerGroupService
|
||||
private regionService_: RegionService
|
||||
private productService_: ProductService
|
||||
private priceListRepo_: typeof PriceListRepository
|
||||
private moneyAmountRepo_: typeof MoneyAmountRepository
|
||||
class PriceListService extends TransactionBaseService<PriceListService> {
|
||||
protected manager_: EntityManager
|
||||
protected transactionManager_: EntityManager | undefined
|
||||
|
||||
protected readonly customerGroupService_: CustomerGroupService
|
||||
protected readonly regionService_: RegionService
|
||||
protected readonly productService_: ProductService
|
||||
protected readonly variantService_: ProductVariantService
|
||||
protected readonly priceListRepo_: typeof PriceListRepository
|
||||
protected readonly moneyAmountRepo_: typeof MoneyAmountRepository
|
||||
|
||||
constructor({
|
||||
manager,
|
||||
customerGroupService,
|
||||
regionService,
|
||||
productService,
|
||||
productVariantService,
|
||||
priceListRepository,
|
||||
moneyAmountRepository,
|
||||
}: PriceListConstructorProps) {
|
||||
super()
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
super(arguments[0])
|
||||
|
||||
this.manager_ = manager
|
||||
this.customerGroupService_ = customerGroupService
|
||||
this.productService_ = productService
|
||||
this.variantService_ = productVariantService
|
||||
this.regionService_ = regionService
|
||||
this.priceListRepo_ = priceListRepository
|
||||
this.moneyAmountRepo_ = moneyAmountRepository
|
||||
}
|
||||
|
||||
withTransaction(transactionManager: EntityManager): PriceListService {
|
||||
if (!transactionManager) {
|
||||
return this
|
||||
}
|
||||
|
||||
const cloned = new PriceListService({
|
||||
manager: transactionManager,
|
||||
customerGroupService: this.customerGroupService_,
|
||||
productService: this.productService_,
|
||||
regionService: this.regionService_,
|
||||
priceListRepository: this.priceListRepo_,
|
||||
moneyAmountRepository: this.moneyAmountRepo_,
|
||||
})
|
||||
|
||||
cloned.transactionManager_ = transactionManager
|
||||
|
||||
return cloned
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a product tag by id.
|
||||
* @param {string} priceListId - the id of the product tag to retrieve
|
||||
@@ -88,7 +82,7 @@ class PriceListService extends BaseService {
|
||||
): Promise<PriceList> {
|
||||
const priceListRepo = this.manager_.getCustomRepository(this.priceListRepo_)
|
||||
|
||||
const query = this.buildQuery_({ id: priceListId }, config)
|
||||
const query = buildQuery({ id: priceListId }, config)
|
||||
const priceList = await priceListRepo.findOne(query)
|
||||
|
||||
if (!priceList) {
|
||||
@@ -167,11 +161,9 @@ class PriceListService extends BaseService {
|
||||
await this.upsertCustomerGroups_(id, customer_groups)
|
||||
}
|
||||
|
||||
const result = await this.retrieve(id, {
|
||||
return await this.retrieve(id, {
|
||||
relations: ["prices", "customer_groups"],
|
||||
})
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
@@ -195,11 +187,9 @@ class PriceListService extends BaseService {
|
||||
const prices_ = await this.addCurrencyFromRegion(prices)
|
||||
await moneyAmountRepo.addPriceListPrices(priceList.id, prices_, replace)
|
||||
|
||||
const result = await this.retrieve(priceList.id, {
|
||||
return await this.retrieve(priceList.id, {
|
||||
relations: ["prices"],
|
||||
})
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
@@ -216,8 +206,6 @@ class PriceListService extends BaseService {
|
||||
const priceList = await this.retrieve(id, { select: ["id"] })
|
||||
|
||||
await moneyAmountRepo.deletePriceListPrices(priceList.id, priceIds)
|
||||
|
||||
return Promise.resolve()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -249,14 +237,18 @@ class PriceListService extends BaseService {
|
||||
*/
|
||||
async list(
|
||||
selector: FilterablePriceListProps = {},
|
||||
config: FindConfig<PriceList> = { skip: 0, take: 20 }
|
||||
config: FindConfig<FilterablePriceListProps> = { skip: 0, take: 20 }
|
||||
): Promise<PriceList[]> {
|
||||
return await this.atomicPhase_(async (manager: EntityManager) => {
|
||||
const priceListRepo = manager.getCustomRepository(this.priceListRepo_)
|
||||
|
||||
const query = this.buildQuery_(selector, config)
|
||||
const { q, ...priceListSelector } = selector
|
||||
const query = buildQuery<FilterablePriceListProps>(
|
||||
priceListSelector,
|
||||
config
|
||||
)
|
||||
|
||||
const groups = query.where.customer_groups
|
||||
const groups = query.where.customer_groups as FindOperator<string[]>
|
||||
query.where.customer_groups = undefined
|
||||
|
||||
const [priceLists] = await priceListRepo.listAndCount(query, groups)
|
||||
@@ -273,21 +265,26 @@ class PriceListService extends BaseService {
|
||||
*/
|
||||
async listAndCount(
|
||||
selector: FilterablePriceListProps = {},
|
||||
config: FindConfig<PriceList> = { skip: 0, take: 20 }
|
||||
config: FindConfig<FilterablePriceListProps> = {
|
||||
skip: 0,
|
||||
take: 20,
|
||||
}
|
||||
): Promise<[PriceList[], number]> {
|
||||
return await this.atomicPhase_(async (manager: EntityManager) => {
|
||||
const priceListRepo = manager.getCustomRepository(this.priceListRepo_)
|
||||
const q = selector.q
|
||||
const { relations, ...query } = this.buildQuery_(selector, config)
|
||||
const { q, ...priceListSelector } = selector
|
||||
const { relations, ...query } = buildQuery<FilterablePriceListProps>(
|
||||
priceListSelector,
|
||||
config
|
||||
)
|
||||
|
||||
const groups = query.where.customer_groups
|
||||
const groups = query.where.customer_groups as FindOperator<string[]>
|
||||
delete query.where.customer_groups
|
||||
|
||||
if (q) {
|
||||
delete query.where.q
|
||||
return await priceListRepo.getFreeTextSearchResultsAndCount(
|
||||
q,
|
||||
query,
|
||||
query as PriceListFindOptions,
|
||||
groups,
|
||||
relations
|
||||
)
|
||||
@@ -296,7 +293,7 @@ class PriceListService extends BaseService {
|
||||
})
|
||||
}
|
||||
|
||||
async upsertCustomerGroups_(
|
||||
protected async upsertCustomerGroups_(
|
||||
priceListId: string,
|
||||
customerGroups: { id: string }[]
|
||||
): Promise<void> {
|
||||
@@ -317,12 +314,13 @@ class PriceListService extends BaseService {
|
||||
|
||||
async listProducts(
|
||||
priceListId: string,
|
||||
selector = {},
|
||||
selector: FilterableProductProps | Selector<Product> = {},
|
||||
config: FindConfig<Product> = {
|
||||
relations: [],
|
||||
skip: 0,
|
||||
take: 20,
|
||||
}
|
||||
},
|
||||
requiresPriceList = false
|
||||
): Promise<[Product[], number]> {
|
||||
return await this.atomicPhase_(async (manager: EntityManager) => {
|
||||
const [products, count] = await this.productService_.listAndCount(
|
||||
@@ -340,7 +338,8 @@ class PriceListService extends BaseService {
|
||||
const [prices] =
|
||||
await moneyAmountRepo.findManyForVariantInPriceList(
|
||||
v.id,
|
||||
priceListId
|
||||
priceListId,
|
||||
requiresPriceList
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -359,6 +358,109 @@ class PriceListService extends BaseService {
|
||||
})
|
||||
}
|
||||
|
||||
async listVariants(
|
||||
priceListId: string,
|
||||
selector: FilterableProductVariantProps = {},
|
||||
config: FindConfig<ProductVariant> = {
|
||||
relations: [],
|
||||
skip: 0,
|
||||
take: 20,
|
||||
},
|
||||
requiresPriceList = false
|
||||
): Promise<[ProductVariant[], number]> {
|
||||
return await this.atomicPhase_(async (manager: EntityManager) => {
|
||||
const [variants, count] = await this.variantService_.listAndCount(
|
||||
selector,
|
||||
config
|
||||
)
|
||||
|
||||
const moneyAmountRepo = manager.getCustomRepository(this.moneyAmountRepo_)
|
||||
|
||||
const variantsWithPrices = await Promise.all(
|
||||
variants.map(async (variant) => {
|
||||
const [prices] = await moneyAmountRepo.findManyForVariantInPriceList(
|
||||
variant.id,
|
||||
priceListId,
|
||||
requiresPriceList
|
||||
)
|
||||
|
||||
variant.prices = prices
|
||||
return variant
|
||||
})
|
||||
)
|
||||
|
||||
return [variantsWithPrices, count]
|
||||
})
|
||||
}
|
||||
|
||||
public async deleteProductPrices(
|
||||
priceListId: string,
|
||||
productIds: string[]
|
||||
): Promise<[string[], number]> {
|
||||
return await this.atomicPhase_(async () => {
|
||||
const [products, count] = await this.listProducts(
|
||||
priceListId,
|
||||
{
|
||||
id: productIds,
|
||||
},
|
||||
{
|
||||
relations: ["variants"],
|
||||
},
|
||||
true
|
||||
)
|
||||
|
||||
if (count === 0) {
|
||||
return [[], count]
|
||||
}
|
||||
|
||||
const priceIds = products
|
||||
.map(({ variants }) =>
|
||||
variants
|
||||
.map((variant) => variant.prices.map((price) => price.id))
|
||||
.flat()
|
||||
)
|
||||
.flat()
|
||||
|
||||
if (!priceIds.length) {
|
||||
return [[], 0]
|
||||
}
|
||||
|
||||
await this.deletePrices(priceListId, priceIds)
|
||||
return [priceIds, priceIds.length]
|
||||
})
|
||||
}
|
||||
|
||||
public async deleteVariantPrices(
|
||||
priceListId: string,
|
||||
variantIds: string[]
|
||||
): Promise<[string[], number]> {
|
||||
return await this.atomicPhase_(async () => {
|
||||
const [variants, count] = await this.listVariants(
|
||||
priceListId,
|
||||
{
|
||||
id: variantIds,
|
||||
},
|
||||
{},
|
||||
true
|
||||
)
|
||||
|
||||
if (count === 0) {
|
||||
return [[], count]
|
||||
}
|
||||
|
||||
const priceIds = variants
|
||||
.map((variant) => variant.prices.map((price) => price.id))
|
||||
.flat()
|
||||
|
||||
if (!priceIds.length) {
|
||||
return [[], 0]
|
||||
}
|
||||
|
||||
await this.deletePrices(priceListId, priceIds)
|
||||
return [priceIds, priceIds.length]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add `currency_code` to an MA record if `region_id`is passed.
|
||||
* @param prices - a list of PriceListPrice(Create/Update)Input records
|
||||
|
||||
@@ -7,18 +7,24 @@ import {
|
||||
IsString,
|
||||
} from "class-validator"
|
||||
import "reflect-metadata"
|
||||
import { FindManyOptions, OrderByCondition } from "typeorm"
|
||||
import { FindManyOptions, FindOperator, OrderByCondition } from "typeorm"
|
||||
import { transformDate } from "../utils/validators/date-transform"
|
||||
|
||||
export type PartialPick<T, K extends keyof T> = {
|
||||
[P in K]?: T[P]
|
||||
}
|
||||
|
||||
export type Writable<T> = { -readonly [key in keyof T]: T[key] }
|
||||
export type Writable<T> = {
|
||||
-readonly [key in keyof T]:
|
||||
| T[key]
|
||||
| FindOperator<T[key][]>
|
||||
| FindOperator<string[]>
|
||||
}
|
||||
|
||||
export type ExtendedFindConfig<TEntity> = FindConfig<TEntity> & {
|
||||
where: Partial<Writable<TEntity>>
|
||||
withDeleted?: boolean
|
||||
relations?: string[]
|
||||
}
|
||||
|
||||
export type Selector<TEntity> = {
|
||||
@@ -28,6 +34,7 @@ export type Selector<TEntity> = {
|
||||
| DateComparisonOperator
|
||||
| StringComparisonOperator
|
||||
| NumericalComparisonOperator
|
||||
| FindOperator<TEntity[key][] | string[]>
|
||||
}
|
||||
|
||||
export type TotalField =
|
||||
|
||||
Reference in New Issue
Block a user