feat(core-flows,pricing,medusa,pricing,types,utils): Price List Prices can have their own rules (#5752)
**What** - Add price-rules for prices in price-lists - make rules object optional when creating prices **Why** - more price granularity Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com>
This commit is contained in:
co-authored by
Philip Korsholm
parent
f05c20695e
commit
079f0da83f
@@ -36,7 +36,6 @@ describe("PriceSet Service", () => {
|
||||
id: "money-amount-USD",
|
||||
currency_code: "EUR",
|
||||
amount: 100,
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
+156
-10
@@ -18,7 +18,7 @@ const defaultRules = {
|
||||
region_id: ["DE", "DK"],
|
||||
}
|
||||
|
||||
const defaultPriceListPrices = [
|
||||
const defaultPriceListPrices: PricingTypes.PriceListPriceDTO[] = [
|
||||
{
|
||||
amount: 232,
|
||||
currency_code: "PLN",
|
||||
@@ -170,63 +170,63 @@ describe("PricingModule Service - Calculate Price", () => {
|
||||
title: "psma PLN",
|
||||
price_set: "price-set-PLN",
|
||||
money_amount: "money-amount-PLN",
|
||||
number_rules: 0,
|
||||
rules_count: 0,
|
||||
},
|
||||
{
|
||||
id: "psma-company_id-EUR",
|
||||
title: "psma EUR - company_id",
|
||||
price_set: "price-set-EUR",
|
||||
money_amount: "money-amount-company_id-EUR",
|
||||
number_rules: 1,
|
||||
rules_count: 1,
|
||||
},
|
||||
{
|
||||
id: "psma-company_id-PLN",
|
||||
title: "psma PLN - company_id",
|
||||
price_set: "price-set-PLN",
|
||||
money_amount: "money-amount-company_id-PLN",
|
||||
number_rules: 1,
|
||||
rules_count: 1,
|
||||
},
|
||||
{
|
||||
id: "psma-region_id-PLN",
|
||||
title: "psma PLN - region_id",
|
||||
price_set: "price-set-PLN",
|
||||
money_amount: "money-amount-region_id-PLN",
|
||||
number_rules: 1,
|
||||
rules_count: 1,
|
||||
},
|
||||
{
|
||||
id: "psma-region_id+company_id-PLN",
|
||||
title: "psma region_id + company_id",
|
||||
price_set: "price-set-PLN",
|
||||
money_amount: "money-amount-region_id+company_id-PLN",
|
||||
number_rules: 2,
|
||||
rules_count: 2,
|
||||
},
|
||||
{
|
||||
id: "psma-region_id-PLN-5-qty",
|
||||
title: "psma PLN - region_id 5 qty",
|
||||
price_set: "price-set-PLN",
|
||||
money_amount: "money-amount-region_id-PLN-5-qty",
|
||||
number_rules: 1,
|
||||
rules_count: 1,
|
||||
},
|
||||
{
|
||||
id: "psma-region_id_company_id-PL-EUR",
|
||||
title: "psma PLN - region_id PL with EUR currency",
|
||||
price_set: "price-set-PLN",
|
||||
money_amount: "money-amount-region_id-PL-EUR",
|
||||
number_rules: 2,
|
||||
rules_count: 2,
|
||||
},
|
||||
{
|
||||
id: "psma-region_id_company_id-PL-EUR-4-qty",
|
||||
title: "psma PLN - region_id PL with EUR currency for quantity 4",
|
||||
price_set: "price-set-PLN",
|
||||
money_amount: "money-amount-region_id-PL-EUR-4-qty",
|
||||
number_rules: 2,
|
||||
rules_count: 2,
|
||||
},
|
||||
{
|
||||
id: "psma-region_id_company_id-PL-EUR-customer-group",
|
||||
title: "psma PLN - region_id PL with EUR currency for customer group",
|
||||
price_set: "price-set-PLN",
|
||||
money_amount: "money-amount-region_id-PL-EUR-customer-group",
|
||||
number_rules: 3,
|
||||
rules_count: 3,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1846,6 +1846,152 @@ describe("PricingModule Service - Calculate Price", () => {
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should return price list prices when price list conditions match within prices", async () => {
|
||||
await createPriceLists(service, {}, { region_id: ["DE", "PL"] }, [
|
||||
...defaultPriceListPrices,
|
||||
{
|
||||
amount: 111,
|
||||
currency_code: "PLN",
|
||||
price_set_id: "price-set-PLN",
|
||||
rules: {
|
||||
region_id: "DE",
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const priceSetsResult = await service.calculatePrices(
|
||||
{ id: ["price-set-EUR", "price-set-PLN"] },
|
||||
{
|
||||
context: {
|
||||
currency_code: "PLN",
|
||||
region_id: "DE",
|
||||
customer_group_id: "vip-customer-group-id",
|
||||
company_id: "medusa-company-id",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(priceSetsResult).toEqual([
|
||||
{
|
||||
id: "price-set-EUR",
|
||||
is_calculated_price_price_list: false,
|
||||
calculated_amount: null,
|
||||
is_original_price_price_list: false,
|
||||
original_amount: null,
|
||||
currency_code: null,
|
||||
calculated_price: {
|
||||
money_amount_id: null,
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
original_price: {
|
||||
money_amount_id: null,
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "price-set-PLN",
|
||||
is_calculated_price_price_list: true,
|
||||
calculated_amount: 111,
|
||||
is_original_price_price_list: false,
|
||||
original_amount: 400,
|
||||
currency_code: "PLN",
|
||||
calculated_price: {
|
||||
money_amount_id: expect.any(String),
|
||||
price_list_id: expect.any(String),
|
||||
price_list_type: "sale",
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
original_price: {
|
||||
money_amount_id: expect.any(String),
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: 1,
|
||||
max_quantity: 5,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("should not return price list prices when price list conditions are met but price rules are not", async () => {
|
||||
await createPriceLists(service, {}, { region_id: ["DE", "PL"] }, [
|
||||
...defaultPriceListPrices,
|
||||
{
|
||||
amount: 111,
|
||||
currency_code: "PLN",
|
||||
price_set_id: "price-set-PLN",
|
||||
rules: {
|
||||
region_id: "PL",
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const priceSetsResult = await service.calculatePrices(
|
||||
{ id: ["price-set-EUR", "price-set-PLN"] },
|
||||
{
|
||||
context: {
|
||||
currency_code: "PLN",
|
||||
region_id: "DE",
|
||||
customer_group_id: "vip-customer-group-id",
|
||||
company_id: "medusa-company-id",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
expect(priceSetsResult).toEqual([
|
||||
{
|
||||
id: "price-set-EUR",
|
||||
is_calculated_price_price_list: false,
|
||||
calculated_amount: null,
|
||||
is_original_price_price_list: false,
|
||||
original_amount: null,
|
||||
currency_code: null,
|
||||
calculated_price: {
|
||||
money_amount_id: null,
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
original_price: {
|
||||
money_amount_id: null,
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "price-set-PLN",
|
||||
is_calculated_price_price_list: true,
|
||||
calculated_amount: 232,
|
||||
is_original_price_price_list: false,
|
||||
original_amount: 400,
|
||||
currency_code: "PLN",
|
||||
calculated_price: {
|
||||
money_amount_id: expect.any(String),
|
||||
price_list_id: expect.any(String),
|
||||
price_list_type: "sale",
|
||||
min_quantity: null,
|
||||
max_quantity: null,
|
||||
},
|
||||
original_price: {
|
||||
money_amount_id: expect.any(String),
|
||||
price_list_id: null,
|
||||
price_list_type: null,
|
||||
min_quantity: 1,
|
||||
max_quantity: 5,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+355
-5
@@ -28,6 +28,16 @@ describe("PriceList Service", () => {
|
||||
await createCurrencies(testManager)
|
||||
await createPriceSets(testManager)
|
||||
await createPriceLists(testManager)
|
||||
await service.createRuleTypes([
|
||||
{
|
||||
name: "Region ID",
|
||||
rule_attribute: "region_id",
|
||||
},
|
||||
{
|
||||
name: "Customer Group ID",
|
||||
rule_attribute: "customer_group_id",
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -35,7 +45,7 @@ describe("PriceList Service", () => {
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
it("list priceLists", async () => {
|
||||
it("should list priceLists", async () => {
|
||||
const priceListResult = await service.listPriceLists()
|
||||
|
||||
expect(priceListResult).toEqual([
|
||||
@@ -48,7 +58,7 @@ describe("PriceList Service", () => {
|
||||
])
|
||||
})
|
||||
|
||||
it("list pricelists by id", async () => {
|
||||
it("should list pricelists by id", async () => {
|
||||
const priceListResult = await service.listPriceLists({
|
||||
id: ["price-list-1"],
|
||||
})
|
||||
@@ -279,8 +289,7 @@ describe("PriceList Service", () => {
|
||||
await service.updatePriceLists([
|
||||
{
|
||||
id: "does-not-exist",
|
||||
number_rules: 2,
|
||||
rules: {},
|
||||
rules_count: 2,
|
||||
},
|
||||
])
|
||||
} catch (e) {
|
||||
@@ -293,7 +302,7 @@ describe("PriceList Service", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
describe("createPriceLists", () => {
|
||||
it("should create a priceList successfully", async () => {
|
||||
const [created] = await service.createPriceLists([
|
||||
{
|
||||
@@ -393,5 +402,346 @@ describe("PriceList Service", () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should create a price list with granular rules within prices", async () => {
|
||||
const [created] = await service.createPriceLists([
|
||||
{
|
||||
title: "test",
|
||||
description: "test",
|
||||
starts_at: "10/01/2023",
|
||||
ends_at: "10/30/2023",
|
||||
rules: {
|
||||
customer_group_id: [
|
||||
"vip-customer-group-id",
|
||||
"another-vip-customer-group-id",
|
||||
],
|
||||
region_id: ["DE", "DK"],
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
currency_code: "EUR",
|
||||
price_set_id: "price-set-1",
|
||||
rules: {
|
||||
region_id: "DE",
|
||||
},
|
||||
},
|
||||
{
|
||||
amount: 600,
|
||||
currency_code: "EUR",
|
||||
price_set_id: "price-set-1",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const [priceList] = await service.listPriceLists(
|
||||
{
|
||||
id: [created.id],
|
||||
},
|
||||
{
|
||||
relations: [
|
||||
"price_set_money_amounts.money_amount",
|
||||
"price_set_money_amounts.price_set",
|
||||
"price_set_money_amounts.price_rules",
|
||||
"price_list_rules.price_list_rule_values",
|
||||
"price_list_rules.rule_type",
|
||||
],
|
||||
select: [
|
||||
"id",
|
||||
"price_set_money_amounts.price_rules.value",
|
||||
"price_set_money_amounts.rules_count",
|
||||
"price_set_money_amounts.money_amount.amount",
|
||||
"price_set_money_amounts.money_amount.currency_code",
|
||||
"price_set_money_amounts.money_amount.price_list_id",
|
||||
"price_list_rules.price_list_rule_values.value",
|
||||
"price_list_rules.rule_type.rule_attribute",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
expect(priceList).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
price_set_money_amounts: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
rules_count: 1,
|
||||
price_rules: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: "DE",
|
||||
}),
|
||||
]),
|
||||
price_list: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
}),
|
||||
money_amount: expect.objectContaining({
|
||||
amount: 400,
|
||||
currency_code: "EUR",
|
||||
}),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
rules_count: 0,
|
||||
price_rules: [],
|
||||
price_list: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
}),
|
||||
money_amount: expect.objectContaining({
|
||||
amount: 600,
|
||||
currency_code: "EUR",
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
price_list_rules: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
rule_type: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
rule_attribute: "customer_group_id",
|
||||
}),
|
||||
price_list_rule_values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: "vip-customer-group-id",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: "another-vip-customer-group-id",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
rule_type: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
rule_attribute: "region_id",
|
||||
}),
|
||||
price_list_rule_values: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: "DE",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
value: "DK",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw error when rule type does not exist", async () => {
|
||||
const error = await service
|
||||
.createPriceLists([
|
||||
{
|
||||
title: "test",
|
||||
description: "test",
|
||||
rules: {
|
||||
region_id: ["DE", "DK"],
|
||||
missing_1: ["test-missing-1"],
|
||||
},
|
||||
prices: [
|
||||
{
|
||||
amount: 400,
|
||||
currency_code: "EUR",
|
||||
price_set_id: "price-set-1",
|
||||
rules: {
|
||||
region_id: "DE",
|
||||
missing_2: "test-missing-2",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
.catch((e) => e)
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"Cannot find RuleTypes with rule_attribute - missing_1, missing_2"
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("addPriceListPrices", () => {
|
||||
it("should add a price to a priceList successfully", async () => {
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
priceListId: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
price_set_id: "price-set-1",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const [priceList] = await service.listPriceLists(
|
||||
{
|
||||
id: ["price-list-1"],
|
||||
},
|
||||
{
|
||||
relations: [
|
||||
"price_set_money_amounts.money_amount",
|
||||
"price_set_money_amounts.price_set",
|
||||
"price_set_money_amounts.price_rules",
|
||||
"price_list_rules.price_list_rule_values",
|
||||
"price_list_rules.rule_type",
|
||||
],
|
||||
select: [
|
||||
"id",
|
||||
"price_set_money_amounts.price_rules.value",
|
||||
"price_set_money_amounts.rules_count",
|
||||
"price_set_money_amounts.money_amount.amount",
|
||||
"price_set_money_amounts.money_amount.currency_code",
|
||||
"price_set_money_amounts.money_amount.price_list_id",
|
||||
"price_list_rules.price_list_rule_values.value",
|
||||
"price_list_rules.rule_type.rule_attribute",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
expect(priceList).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
price_set_money_amounts: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
rules_count: 0,
|
||||
price_list: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
}),
|
||||
money_amount: expect.objectContaining({
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
price_list_rules: [],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should fail to add a price with non-existing rule-types in the price-set to a priceList", async () => {
|
||||
await service.createRuleTypes([
|
||||
{
|
||||
name: "twitter_handle",
|
||||
rule_attribute: "twitter_handle",
|
||||
},
|
||||
])
|
||||
|
||||
let error
|
||||
try {
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
priceListId: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
price_set_id: "price-set-1",
|
||||
rules: {
|
||||
twitter_handle: "owjuhl",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
} catch (err) {
|
||||
error = err
|
||||
}
|
||||
|
||||
expect(error.message).toEqual(
|
||||
"" +
|
||||
`Invalid rule type configuration: Price set rules doesn't exist for rule_attribute "twitter_handle" in price set price-set-1`
|
||||
)
|
||||
})
|
||||
|
||||
it("should add a price with rules to a priceList successfully", async () => {
|
||||
await service.createRuleTypes([
|
||||
{
|
||||
name: "region_id",
|
||||
rule_attribute: "region_id",
|
||||
},
|
||||
])
|
||||
|
||||
const r = await service.addRules([
|
||||
{
|
||||
priceSetId: "price-set-1",
|
||||
rules: [{ attribute: "region_id" }],
|
||||
},
|
||||
])
|
||||
|
||||
await service.addPriceListPrices([
|
||||
{
|
||||
priceListId: "price-list-1",
|
||||
prices: [
|
||||
{
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
price_set_id: "price-set-1",
|
||||
rules: {
|
||||
region_id: "EU",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const [priceList] = await service.listPriceLists(
|
||||
{
|
||||
id: ["price-list-1"],
|
||||
},
|
||||
{
|
||||
relations: [
|
||||
"price_set_money_amounts.money_amount",
|
||||
"price_set_money_amounts.price_set",
|
||||
"price_set_money_amounts.price_rules",
|
||||
"price_set_money_amounts.price_rules.rule_type",
|
||||
"price_list_rules.price_list_rule_values",
|
||||
"price_list_rules.rule_type",
|
||||
],
|
||||
select: [
|
||||
"id",
|
||||
"price_set_money_amounts.price_rules.value",
|
||||
"price_set_money_amounts.price_rules.rule_type.rule_attribute",
|
||||
"price_set_money_amounts.rules_count",
|
||||
"price_set_money_amounts.money_amount.amount",
|
||||
"price_set_money_amounts.money_amount.currency_code",
|
||||
"price_set_money_amounts.money_amount.price_list_id",
|
||||
"price_list_rules.price_list_rule_values.value",
|
||||
"price_list_rules.rule_type.rule_attribute",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
expect(priceList).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
price_set_money_amounts: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
rules_count: 1,
|
||||
price_list: expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
}),
|
||||
price_rules: [
|
||||
expect.objectContaining({
|
||||
value: "EU",
|
||||
rule_type: expect.objectContaining({
|
||||
rule_attribute: "region_id",
|
||||
}),
|
||||
}),
|
||||
],
|
||||
money_amount: expect.objectContaining({
|
||||
amount: 123,
|
||||
currency_code: "EUR",
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
price_list_rules: [],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+1
-2
@@ -6,7 +6,7 @@ import {
|
||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||
import { PriceSet } from "@models"
|
||||
|
||||
import { PriceSetRuleType, initialize } from "../../../../src"
|
||||
import { initialize, PriceSetRuleType } from "../../../../src"
|
||||
import { seedPriceData } from "../../../__fixtures__/seed-price-data"
|
||||
import { DB_URL, MikroOrmWrapper } from "../../../utils"
|
||||
|
||||
@@ -400,7 +400,6 @@ describe("PricingModule Service - PriceSet", () => {
|
||||
{
|
||||
amount: 150,
|
||||
currency_code: "USD",
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user