fix(medusa): Price selection strategy bug with customer groups without customers (#4578)
* change up condition for joining price lists * add changeset * naming * update tests
This commit is contained in:
5
.changeset/new-items-warn.md
Normal file
5
.changeset/new-items-warn.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
fix(medusa): update how price lists are filtered in price selection strategy
|
||||
@@ -1299,9 +1299,11 @@ describe("/admin/price-lists", () => {
|
||||
|
||||
expect(response.status).toBe(200)
|
||||
expect(response.data).toEqual({
|
||||
ids: product1.variants.map((variant, i) => {
|
||||
return getCustomPriceIdFromVariant(variant.id, i)
|
||||
}),
|
||||
ids: expect.arrayContaining(
|
||||
product1.variants.map((variant, i) => {
|
||||
return getCustomPriceIdFromVariant(variant.id, i)
|
||||
})
|
||||
),
|
||||
object: "money-amount",
|
||||
deleted: true,
|
||||
})
|
||||
|
||||
@@ -262,6 +262,12 @@ describe("/admin/products", () => {
|
||||
product_id: productData.id,
|
||||
sku: `test-product_filtering_by_variant_id-${i}`,
|
||||
title: `test-product_filtering_by_variant_id-${i}`,
|
||||
options: [
|
||||
{
|
||||
option_id: "test-product_filtering_by_variant_id-option",
|
||||
value: `Large-${i}`,
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,16 @@ const { useDb, initDb } = require("../../../environment-helpers/use-db")
|
||||
|
||||
const adminSeeder = require("../../../helpers/admin-seeder")
|
||||
const promotionsSeeder = require("../../../helpers/price-selection-seeder")
|
||||
const {
|
||||
Product,
|
||||
ShippingProfileType,
|
||||
ShippingProfile,
|
||||
ProductOption,
|
||||
CustomerGroup,
|
||||
PriceList,
|
||||
ProductVariant,
|
||||
Customer,
|
||||
} = require("@medusajs/medusa")
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
@@ -449,6 +459,55 @@ describe("Promotions", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("should only join price lists with customer groups for the customer, not empty groups", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const authResponse = await api.post("/store/auth", {
|
||||
email: "test5@email-pl.com",
|
||||
password: "test",
|
||||
})
|
||||
|
||||
const [authCookie] = authResponse.headers["set-cookie"][0].split(";")
|
||||
|
||||
const res = await api
|
||||
.get("/store/products/test-product-pl?cart_id=test-cart", {
|
||||
headers: {
|
||||
Cookie: authCookie,
|
||||
},
|
||||
})
|
||||
.catch((error) => console.log(error))
|
||||
|
||||
const variant = res.data.product.variants[0]
|
||||
|
||||
expect(variant.prices.length).toEqual(2)
|
||||
|
||||
expect(variant).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test-variant-pl",
|
||||
calculated_price: 110,
|
||||
})
|
||||
)
|
||||
|
||||
expect(variant.prices.length).toEqual(2)
|
||||
expect(variant.prices).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "test-price1120",
|
||||
region_id: "test-region",
|
||||
currency_code: "usd",
|
||||
amount: 120,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-price2120",
|
||||
region_id: "test-region",
|
||||
currency_code: "usd",
|
||||
price_list_id: "pl_current_pl",
|
||||
amount: 110,
|
||||
}),
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it("fetches product with groups and quantities in money amounts with login", async () => {
|
||||
const api = useApi()
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ module.exports = async (dataSource, data = {}) => {
|
||||
id: "test-price2",
|
||||
region_id: "test-region",
|
||||
currency_code: "usd",
|
||||
amount: 130,
|
||||
amount: 90,
|
||||
price_list_id: "pl_2",
|
||||
},
|
||||
{
|
||||
@@ -881,4 +881,110 @@ module.exports = async (dataSource, data = {}) => {
|
||||
})
|
||||
|
||||
await manager.save(cart_region1)
|
||||
|
||||
const customerPl = await manager.create(Customer, {
|
||||
id: "test-customer-5-pl",
|
||||
email: "test5@email-pl.com",
|
||||
first_name: "John",
|
||||
last_name: "Deere",
|
||||
password_hash:
|
||||
"c2NyeXB0AAEAAAABAAAAAVMdaddoGjwU1TafDLLlBKnOTQga7P2dbrfgf3fB+rCD/cJOMuGzAvRdKutbYkVpuJWTU39P7OpuWNkUVoEETOVLMJafbI8qs8Qx/7jMQXkN", // password matching "test"
|
||||
has_account: true,
|
||||
})
|
||||
await manager.save(customerPl)
|
||||
|
||||
const pPl = await manager.create(Product, {
|
||||
id: "test-product-pl",
|
||||
handle: "test-product-pl",
|
||||
title: "Test product",
|
||||
profile_id: defaultProfile.id,
|
||||
description: "test-product-description1",
|
||||
collection_id: "test-collection",
|
||||
tags: [],
|
||||
})
|
||||
|
||||
await manager.save(pPl)
|
||||
|
||||
await manager.save(ProductOption, {
|
||||
id: "test-option-pl",
|
||||
title: "test-option",
|
||||
product_id: "test-product-pl",
|
||||
})
|
||||
|
||||
const c_group_pl = await manager.create(CustomerGroup, {
|
||||
id: "test-group-pl",
|
||||
name: "test-group-pl",
|
||||
})
|
||||
await manager.save(c_group_pl)
|
||||
|
||||
customerPl.groups = [c_group_pl]
|
||||
await manager.save(customerPl)
|
||||
|
||||
const c_group_not_pl = await manager.create(CustomerGroup, {
|
||||
id: "test-group-not-pl",
|
||||
name: "test-group-not-pl",
|
||||
})
|
||||
await manager.save(c_group_not_pl)
|
||||
|
||||
const priceListPl = await manager.create(PriceList, {
|
||||
id: "pl_current_pl",
|
||||
name: "Past winter sale",
|
||||
description: "Winter sale for key accounts.",
|
||||
type: "sale",
|
||||
status: "active",
|
||||
})
|
||||
|
||||
priceListPl.customer_groups = [c_group_pl]
|
||||
await manager.save(priceListPl)
|
||||
|
||||
const priceListNotPL = await manager.create(PriceList, {
|
||||
id: "pl_current_not_pl",
|
||||
name: "Past winter sale",
|
||||
description: "Winter sale for key accounts.",
|
||||
type: "sale",
|
||||
status: "active",
|
||||
})
|
||||
priceListNotPL.customer_groups = [c_group_not_pl]
|
||||
|
||||
await manager.save(priceListNotPL)
|
||||
|
||||
const variantPl = await manager.create(ProductVariant, {
|
||||
id: "test-variant-pl",
|
||||
inventory_quantity: 10,
|
||||
title: "Test variant",
|
||||
sku: "test-sku-pl",
|
||||
status: "published",
|
||||
product_id: "test-product-pl",
|
||||
prices: [
|
||||
{
|
||||
id: "test-price120",
|
||||
region_id: "test-region",
|
||||
currency_code: "usd",
|
||||
amount: 90,
|
||||
price_list_id: "pl_current_not_pl",
|
||||
},
|
||||
{
|
||||
id: "test-price1120",
|
||||
region_id: "test-region",
|
||||
currency_code: "usd",
|
||||
amount: 120,
|
||||
},
|
||||
{
|
||||
id: "test-price2120",
|
||||
region_id: "test-region",
|
||||
currency_code: "usd",
|
||||
amount: 110,
|
||||
price_list_id: "pl_current_pl",
|
||||
},
|
||||
],
|
||||
options: [
|
||||
{
|
||||
id: "test-variant-option-pl",
|
||||
value: "Default variant",
|
||||
option_id: "test-option-pl",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await manager.save(variantPl)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NextFunction, Request, Response } from "express"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
import { Logger } from "../../types/global"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { formatException } from "../../utils"
|
||||
|
||||
const QUERY_RUNNER_RELEASED = "QueryRunnerAlreadyReleasedError"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import partition from "lodash/partition"
|
||||
import {
|
||||
Brackets,
|
||||
In,
|
||||
@@ -7,16 +6,18 @@ import {
|
||||
ObjectLiteral,
|
||||
WhereExpressionBuilder,
|
||||
} from "typeorm"
|
||||
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"
|
||||
import { dataSource } from "../loaders/database"
|
||||
import { MoneyAmount } from "../models"
|
||||
import {
|
||||
PriceListPriceCreateInput,
|
||||
PriceListPriceUpdateInput,
|
||||
} from "../types/price-list"
|
||||
|
||||
import { MoneyAmount } from "../models"
|
||||
import { ProductVariantPrice } from "../types/product-variant"
|
||||
import { isString } from "../utils"
|
||||
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"
|
||||
import { dataSource } from "../loaders/database"
|
||||
import { groupBy } from "lodash"
|
||||
import { isString } from "../utils"
|
||||
import partition from "lodash/partition"
|
||||
|
||||
type Price = Partial<
|
||||
Omit<MoneyAmount, "created_at" | "updated_at" | "deleted_at">
|
||||
@@ -316,12 +317,9 @@ export const MoneyAmountRepository = dataSource
|
||||
"cgc",
|
||||
"cgc.customer_group_id = cgroup.id"
|
||||
)
|
||||
.andWhere(
|
||||
"(cgc.customer_group_id is null OR cgc.customer_id = :customer_id)",
|
||||
{
|
||||
customer_id,
|
||||
}
|
||||
)
|
||||
.andWhere("(cgroup.id is null OR cgc.customer_id = :customer_id)", {
|
||||
customer_id,
|
||||
})
|
||||
} else {
|
||||
qb.leftJoin("price_list.customer_groups", "cgroup").andWhere(
|
||||
"cgroup.id is null"
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { ICacheService } from "@medusajs/types"
|
||||
import { isDefined } from "medusa-core-utils"
|
||||
import { EntityManager } from "typeorm"
|
||||
import {
|
||||
AbstractPriceSelectionStrategy,
|
||||
PriceSelectionContext,
|
||||
PriceSelectionResult,
|
||||
PriceType,
|
||||
} from "../interfaces"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
import { TaxServiceRate } from "../types/tax-service"
|
||||
|
||||
import { EntityManager } from "typeorm"
|
||||
import { FlagRouter } from "../utils/flag-router"
|
||||
import { ICacheService } from "@medusajs/types"
|
||||
import { MoneyAmountRepository } from "../repositories/money-amount"
|
||||
import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing"
|
||||
import { TaxServiceRate } from "../types/tax-service"
|
||||
import { isDefined } from "medusa-core-utils"
|
||||
|
||||
class PriceSelectionStrategy extends AbstractPriceSelectionStrategy {
|
||||
protected manager_: EntityManager
|
||||
|
||||
Reference in New Issue
Block a user