Skip to content

Commit

Permalink
feat(pricing, types): add price rule operators to price calculations (#…
Browse files Browse the repository at this point in the history
…10350)

what:

- adds price rule operators when doing price calculations
- rules now accepts a key where the value can be an array of objects `({ operator: string, value: number })`
  - validation for available types of operator and value to be a number
```
await service.createPriceSets({
  prices: [
    {
      amount: 50,
      currency_code: "usd",
      rules: {
        region_id: "de",
        cart_total: [
          { operator: "gte", value: 400 },
          { operator: "lte", value: 500 },
        ]
      },
    },
  ]
})
```
- price calculations will now account for the operators - lte, gte, lt, gt when the price context is a number

RESOLVES CMRC-747
  • Loading branch information
riqwan authored Nov 28, 2024
1 parent 805fe4b commit 324b4ab
Show file tree
Hide file tree
Showing 8 changed files with 462 additions and 33 deletions.
6 changes: 6 additions & 0 deletions .changeset/kind-moons-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@medusajs/pricing": patch
"@medusajs/types": patch
---

feat(pricing, types): add price rule operators to price calculations
9 changes: 8 additions & 1 deletion packages/core/types/src/pricing/common/price-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
MoneyAmountDTO,
UpdateMoneyAmountDTO,
} from "./money-amount"
import { PricingRuleOperatorValues } from "./price-rule"

export interface PricingRepositoryService {
calculatePrices(
Expand Down Expand Up @@ -206,6 +207,11 @@ export interface CalculatedPriceSet {
}
}

export interface RuleWithOperator {
operator: PricingRuleOperatorValues
value: number
}

/**
* @interface
*
Expand All @@ -214,7 +220,8 @@ export interface CalculatedPriceSet {
* Each key of the object is a the attribute, and its value
* is the values of the rule.
*/
export interface CreatePriceSetPriceRules extends Record<string, string> {}
export interface CreatePriceSetPriceRules
extends Record<string, string | RuleWithOperator[]> {}

/**
* @interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SqlEntityManager } from "@mikro-orm/postgresql"
import { defaultPriceRuleData } from "./data"

export * from "./data"
export * from "./operators"

export async function createPriceRules(
manager: SqlEntityManager,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { RuleWithOperator } from "@medusajs/types"

export const withOperator = (
border,
min = 400,
max = 800
): RuleWithOperator[] => {
if (border === "betweenEquals") {
return [
{ operator: "gte", value: min },
{ operator: "lte", value: max },
]
} else if (border === "between") {
return [
{ operator: "gt", value: min },
{ operator: "lt", value: max },
]
} else if (border === "excludingMin") {
return [
{ operator: "gt", value: min },
{ operator: "lte", value: max },
]
} else if (border === "excludingMax") {
return [
{ operator: "gte", value: min },
{ operator: "lt", value: max },
]
} else if (border === "gt") {
return [{ operator: "gt", value: min }]
} else if (border === "lt") {
return [{ operator: "lt", value: min }]
} else if (border === "lte") {
return [{ operator: "lte", value: min }]
} else if (border === "gte") {
return [{ operator: "gte", value: min }]
} else {
return []
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
PriceListType,
} from "@medusajs/framework/utils"
import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
import { withOperator } from "../../../__fixtures__/price-rule"
import { seedPriceData } from "../../../__fixtures__/seed-price-data"

jest.setTimeout(30000)
Expand Down Expand Up @@ -1857,6 +1858,180 @@ moduleIntegrationTestRunner<IPricingModuleService>({
})
})
})

describe("calculatePrices", () => {
let priceSet1

it("should return accurate prices when using custom price rule operators", async () => {
priceSet1 = await service.createPriceSets({
prices: [
{
amount: 50,
currency_code: "usd",
rules: {
region_id: "de",
cart_total: withOperator("between", 300, 400),
},
},
{
amount: 100,
currency_code: "usd",
rules: {
region_id: "de",
cart_total: withOperator("betweenEquals", 400, 500),
},
},
{
amount: 150,
currency_code: "usd",
rules: {
region_id: "de",
cart_total: withOperator("excludingMin", 500, 600),
},
},
{
amount: 200,
currency_code: "usd",
rules: {
region_id: "de",
cart_total: withOperator("excludingMax", 600, 700),
},
},
],
})

let priceSetsResult = await service.calculatePrices(
{ id: [priceSet1.id] },
{
context: {
currency_code: "usd",
region_id: "de",
cart_total: 350,
},
}
)

expect(priceSetsResult).toEqual([
expect.objectContaining({
is_calculated_price_price_list: false,
is_calculated_price_tax_inclusive: false,
calculated_amount: 50,
raw_calculated_amount: {
value: "50",
precision: 20,
},
is_original_price_price_list: false,
is_original_price_tax_inclusive: false,
original_amount: 50,
raw_original_amount: {
value: "50",
precision: 20,
},
currency_code: "usd",
calculated_price: expect.objectContaining({
id: expect.any(String),
price_list_id: null,
price_list_type: null,
min_quantity: null,
max_quantity: null,
}),
original_price: {
id: expect.any(String),
price_list_id: null,
price_list_type: null,
min_quantity: null,
max_quantity: null,
},
}),
])

priceSetsResult = await service.calculatePrices(
{ id: [priceSet1.id] },
{
context: {
currency_code: "usd",
region_id: "de",
cart_total: 300,
},
}
)

expect(priceSetsResult).toEqual([])

priceSetsResult = await service.calculatePrices(
{ id: [priceSet1.id] },
{
context: {
currency_code: "usd",
region_id: "de",
cart_total: 400,
},
}
)

expect(priceSetsResult).toEqual([
expect.objectContaining({ calculated_amount: 100 }),
])

priceSetsResult = await service.calculatePrices(
{ id: [priceSet1.id] },
{
context: {
currency_code: "usd",
region_id: "de",
cart_total: 500,
},
}
)

expect(priceSetsResult).toEqual([
expect.objectContaining({ calculated_amount: 100 }),
])

priceSetsResult = await service.calculatePrices(
{ id: [priceSet1.id] },
{
context: {
currency_code: "usd",
region_id: "de",
cart_total: 501,
},
}
)

expect(priceSetsResult).toEqual([
expect.objectContaining({ calculated_amount: 150 }),
])

priceSetsResult = await service.calculatePrices(
{ id: [priceSet1.id] },
{
context: {
currency_code: "usd",
region_id: "de",
cart_total: 601,
},
}
)

expect(priceSetsResult).toEqual([
expect.objectContaining({ calculated_amount: 200 }),
])

priceSetsResult = await service.calculatePrices(
{ id: [priceSet1.id] },
{
context: {
currency_code: "usd",
region_id: "de",
cart_total: 900,
},
}
)

expect(priceSetsResult).toEqual([])
})
})
})
},
})
Loading

0 comments on commit 324b4ab

Please sign in to comment.