feat(medusa): Filter product list by discount condition id (#2464)
This commit is contained in:
committed by
GitHub
parent
9deec0fc3c
commit
8be67c734c
5
.changeset/tidy-tigers-invite.md
Normal file
5
.changeset/tidy-tigers-invite.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
feat(medusa): Filter product list by discount condition id
|
||||
@@ -10,9 +10,16 @@ const {
|
||||
ProductVariant,
|
||||
ProductOptionValue,
|
||||
MoneyAmount,
|
||||
DiscountConditionType,
|
||||
DiscountConditionOperator,
|
||||
} = require("@medusajs/medusa")
|
||||
const priceListSeeder = require("../../helpers/price-list-seeder")
|
||||
const { simpleProductFactory } = require("../../factories")
|
||||
const {
|
||||
simpleProductFactory,
|
||||
simpleDiscountFactory,
|
||||
} = require("../../factories")
|
||||
const { DiscountRuleType, AllocationType } = require("@medusajs/medusa/dist")
|
||||
const { IdMap } = require("medusa-test-utils")
|
||||
|
||||
jest.setTimeout(50000)
|
||||
|
||||
@@ -172,11 +179,7 @@ describe("/admin/products", () => {
|
||||
const api = useApi()
|
||||
|
||||
const response = await api
|
||||
.get("/admin/products?type_id[]=test-type", {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.get("/admin/products?type_id[]=test-type", adminHeaders)
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
@@ -192,6 +195,79 @@ describe("/admin/products", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("returns a list of products filtered by discount condition id", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const resProd = await api.get("/admin/products", adminHeaders)
|
||||
|
||||
const prod1 = resProd.data.products[0]
|
||||
const prod2 = resProd.data.products[2]
|
||||
|
||||
const buildDiscountData = (code, conditionId, products) => {
|
||||
return {
|
||||
code,
|
||||
rule: {
|
||||
type: DiscountRuleType.PERCENTAGE,
|
||||
value: 10,
|
||||
allocation: AllocationType.TOTAL,
|
||||
conditions: [
|
||||
{
|
||||
id: conditionId,
|
||||
type: DiscountConditionType.PRODUCTS,
|
||||
operator: DiscountConditionOperator.IN,
|
||||
product_tags: products,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const discountConditionId = IdMap.getId("discount-condition-prod-1")
|
||||
await simpleDiscountFactory(
|
||||
dbConnection,
|
||||
buildDiscountData("code-1", discountConditionId, [prod1.id])
|
||||
)
|
||||
|
||||
const discountConditionId2 = IdMap.getId("discount-condition-prod-2")
|
||||
await simpleDiscountFactory(
|
||||
dbConnection,
|
||||
buildDiscountData("code-2", discountConditionId2, [prod2.id])
|
||||
)
|
||||
|
||||
let res = await api.get(
|
||||
`/admin/products?discount_condition_id=${discountConditionId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.products).toHaveLength(1)
|
||||
expect(res.data.products).toEqual(
|
||||
expect.arrayContaining([expect.objectContaining({ id: prod1.id })])
|
||||
)
|
||||
|
||||
res = await api.get(
|
||||
`/admin/products?discount_condition_id=${discountConditionId2}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.products).toHaveLength(1)
|
||||
expect(res.data.products).toEqual(
|
||||
expect.arrayContaining([expect.objectContaining({ id: prod2.id })])
|
||||
)
|
||||
|
||||
res = await api.get(`/admin/products`, adminHeaders)
|
||||
|
||||
expect(res.status).toEqual(200)
|
||||
expect(res.data.products).toHaveLength(5)
|
||||
expect(res.data.products).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ id: prod1.id }),
|
||||
expect.objectContaining({ id: prod2.id }),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("doesn't expand collection and types", async () => {
|
||||
const api = useApi()
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { FilterableProductProps } from "../../../../types/product"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (query) q {string} Query used for searching product title and description, variant title and sku, and collection title.
|
||||
* - (query) discount_condition_id {string} The discount condition id on which to filter the product.
|
||||
* - in: query
|
||||
* name: id
|
||||
* style: form
|
||||
|
||||
@@ -26,6 +26,7 @@ export type FindWithoutRelationsOptions = DefaultWithoutRelations & {
|
||||
where: DefaultWithoutRelations["where"] & {
|
||||
price_list_id?: FindOperator<PriceList>
|
||||
sales_channel_id?: FindOperator<SalesChannel>
|
||||
discount_condition_id?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +54,10 @@ export class ProductRepository extends Repository<Product> {
|
||||
const sales_channels = optionsWithoutRelations?.where?.sales_channel_id
|
||||
delete optionsWithoutRelations?.where?.sales_channel_id
|
||||
|
||||
const discount_condition_id =
|
||||
optionsWithoutRelations?.where?.discount_condition_id
|
||||
delete optionsWithoutRelations?.where?.discount_condition_id
|
||||
|
||||
const qb = this.createQueryBuilder("product")
|
||||
.select(["product.id"])
|
||||
.skip(optionsWithoutRelations.skip)
|
||||
@@ -100,6 +105,15 @@ export class ProductRepository extends Repository<Product> {
|
||||
)
|
||||
}
|
||||
|
||||
if (discount_condition_id) {
|
||||
qb.innerJoin(
|
||||
"discount_condition_product",
|
||||
"dc_product",
|
||||
`dc_product.product_id = product.id AND dc_product.condition_id = :dcId`,
|
||||
{ dcId: discount_condition_id }
|
||||
)
|
||||
}
|
||||
|
||||
if (optionsWithoutRelations.withDeleted) {
|
||||
qb.withDeleted()
|
||||
}
|
||||
@@ -358,6 +372,16 @@ export class ProductRepository extends Repository<Product> {
|
||||
.skip(cleanedOptions.skip)
|
||||
.take(cleanedOptions.take)
|
||||
|
||||
const discountConditionId = options.where.discount_condition_id
|
||||
if (discountConditionId) {
|
||||
qb.innerJoin(
|
||||
"discount_condition_product",
|
||||
"dc_product",
|
||||
`dc_product.product_id = product.id AND dc_product.condition_id = :dcId`,
|
||||
{ dcId: discountConditionId }
|
||||
)
|
||||
}
|
||||
|
||||
if (cleanedOptions.withDeleted) {
|
||||
qb = qb.withDeleted()
|
||||
}
|
||||
@@ -388,6 +412,10 @@ export class ProductRepository extends Repository<Product> {
|
||||
delete where?.price_list_id
|
||||
}
|
||||
|
||||
if ("discount_condition_id" in where) {
|
||||
delete where?.discount_condition_id
|
||||
}
|
||||
|
||||
return {
|
||||
...options,
|
||||
where,
|
||||
|
||||
@@ -1062,7 +1062,7 @@ class CartService extends TransactionBaseService {
|
||||
const productsToKeep = await this.productService_
|
||||
.withTransaction(this.manager_)
|
||||
.filterProductsBySalesChannel(productIds, newSalesChannelId, {
|
||||
select: ["id", "sales_channels"],
|
||||
select: ["id"],
|
||||
take: productIds.length,
|
||||
})
|
||||
const productIdsToKeep = new Set<string>(
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
FilterableProductProps,
|
||||
FindProductConfig,
|
||||
ProductOptionInput,
|
||||
ProductSelector,
|
||||
UpdateProductInput,
|
||||
} from "../types/product"
|
||||
import { buildQuery, isDefined, setMetadata } from "../utils"
|
||||
@@ -108,7 +109,7 @@ class ProductService extends TransactionBaseService {
|
||||
* @return the result of the find operation
|
||||
*/
|
||||
async list(
|
||||
selector: FilterableProductProps | Selector<Product> = {},
|
||||
selector: ProductSelector,
|
||||
config: FindProductConfig = {
|
||||
relations: [],
|
||||
skip: 0,
|
||||
@@ -116,20 +117,8 @@ class ProductService extends TransactionBaseService {
|
||||
include_discount_prices: false,
|
||||
}
|
||||
): Promise<Product[]> {
|
||||
const manager = this.manager_
|
||||
const productRepo = manager.getCustomRepository(this.productRepository_)
|
||||
|
||||
const { q, query, relations } = this.prepareListQuery_(selector, config)
|
||||
if (q) {
|
||||
const [products] = await productRepo.getFreeTextSearchResultsAndCount(
|
||||
q,
|
||||
query,
|
||||
relations
|
||||
)
|
||||
return products
|
||||
}
|
||||
|
||||
return await productRepo.findWithRelations(relations, query)
|
||||
const [products] = await this.listAndCount(selector, config)
|
||||
return products
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +133,7 @@ class ProductService extends TransactionBaseService {
|
||||
* as the second element.
|
||||
*/
|
||||
async listAndCount(
|
||||
selector: FilterableProductProps | Selector<Product>,
|
||||
selector: ProductSelector,
|
||||
config: FindProductConfig = {
|
||||
relations: [],
|
||||
skip: 0,
|
||||
|
||||
@@ -8,12 +8,19 @@ import {
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
import SalesChannelFeatureFlag from "../loaders/feature-flags/sales-channels"
|
||||
import { Product, ProductOptionValue, ProductStatus } from "../models"
|
||||
import {
|
||||
PriceList,
|
||||
Product,
|
||||
ProductOptionValue,
|
||||
ProductStatus,
|
||||
SalesChannel,
|
||||
} from "../models"
|
||||
import { FeatureFlagDecorators } from "../utils/feature-flag-decorators"
|
||||
import { optionalBooleanMapper } from "../utils/validators/is-boolean"
|
||||
import { IsType } from "../utils/validators/is-type"
|
||||
import { DateComparisonOperator, FindConfig } from "./common"
|
||||
import { DateComparisonOperator, FindConfig, Selector } from "./common"
|
||||
import { PriceListLoadConfig } from "./price-list"
|
||||
import { FindOperator } from "typeorm"
|
||||
|
||||
/**
|
||||
* API Level DTOs + Validation rules
|
||||
@@ -67,6 +74,10 @@ export class FilterableProductProps {
|
||||
@FeatureFlagDecorators(SalesChannelFeatureFlag.key, [IsOptional(), IsArray()])
|
||||
sales_channel_id?: string[]
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
discount_condition_id?: string
|
||||
|
||||
@IsOptional()
|
||||
@ValidateNested()
|
||||
@Type(() => DateComparisonOperator)
|
||||
@@ -83,6 +94,15 @@ export class FilterableProductProps {
|
||||
deleted_at?: DateComparisonOperator
|
||||
}
|
||||
|
||||
export type ProductSelector =
|
||||
| FilterableProductProps
|
||||
| (Selector<Product> & {
|
||||
q?: string
|
||||
discount_condition_id?: string
|
||||
price_list_id?: string[] | FindOperator<PriceList>
|
||||
sales_channel_id?: string[] | FindOperator<SalesChannel>
|
||||
})
|
||||
|
||||
/**
|
||||
* Service Level DTOs
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user