From 1772e80ed1ecab27741be80204f5df92eaa3f2b4 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Wed, 15 Nov 2023 21:24:29 +0100 Subject: [PATCH] feat(pricing,types): price list API + price calculations with price lists (#5498) **what:** **PriceList Service APIs:** - createPriceList - updatePriceList - addPriceListPrices - removePriceListRules - setPriceListRules - deletePriceList - listPriceLists - listAndCountPriceLists **Price Calculations** - Returns prices with price list prices - Returns a new shape with calculated and original prices Co-authored-by: Philip Korsholm <88927411+pKorsholm@users.noreply.github.com> --- .changeset/nervous-berries-sip.md | 6 + .../admin/price-lists/create-price-list.ts | 5 +- .../admin/price-lists/update-price-list.ts | 7 +- .../routes/store/products/list-products.ts | 6 +- packages/medusa/src/models/price-list.ts | 10 +- packages/medusa/src/services/pricing.ts | 42 +- .../__fixtures__/price-list-rules/data.ts | 12 + .../__fixtures__/price-list-rules/index.ts | 22 + .../__fixtures__/price-list/data.ts | 14 + .../__fixtures__/price-list/index.ts | 22 + .../services/money-amount/index.spec.ts | 78 +- .../services/price-list-rule/index.spec.ts | 243 +++ .../services/price-list/index.spec.ts | 237 +-- .../services/price-set/index.spec.ts | 12 +- .../pricing-module/calculate-price.spec.ts | 1225 +++++++++++++++- .../pricing-module/money-amount.spec.ts | 71 +- .../pricing-module/price-list-rule.spec.ts | 348 +++++ .../pricing-module/price-list.spec.ts | 397 +++++ .../services/pricing-module/price-set.spec.ts | 22 +- packages/pricing/src/loaders/container.ts | 16 + .../migrations/.snapshot-medusa-pricing.json | 363 ++++- .../src/migrations/Migration20230929122253.ts | 34 + .../src/migrations/Migration20231101232834.ts | 86 ++ packages/pricing/src/models/index.ts | 3 + packages/pricing/src/models/money-amount.ts | 37 +- .../src/models/price-list-rule-value.ts | 31 + .../pricing/src/models/price-list-rule.ts | 57 + packages/pricing/src/models/price-list.ts | 110 ++ packages/pricing/src/models/price-rule.ts | 5 - .../src/models/price-set-money-amount.ts | 11 +- packages/pricing/src/models/price-set.ts | 2 +- packages/pricing/src/repositories/index.ts | 3 + .../src/repositories/price-list-rule-value.ts | 143 ++ .../src/repositories/price-list-rule.ts | 161 ++ .../pricing/src/repositories/price-list.ts | 133 ++ .../repositories/price-set-money-amount.ts | 26 +- .../pricing/src/repositories/price-set.ts | 3 +- packages/pricing/src/repositories/pricing.ts | 86 +- packages/pricing/src/services/index.ts | 3 + .../src/services/price-list-rule-value.ts | 117 ++ .../pricing/src/services/price-list-rule.ts | 103 ++ packages/pricing/src/services/price-list.ts | 98 ++ .../pricing/src/services/pricing-module.ts | 742 +++++++++- .../pricing/src/types/repositories/index.ts | 2 + .../repositories/price-list-rule-value.ts | 12 + .../src/types/repositories/price-list.ts | 11 + packages/types/src/pricing/common/index.ts | 1 + .../types/src/pricing/common/money-amount.ts | 24 +- .../types/src/pricing/common/price-list.ts | 137 ++ .../types/src/pricing/common/price-rule.ts | 1 - .../pricing/common/price-set-money-amount.ts | 12 +- .../types/src/pricing/common/price-set.ts | 96 +- packages/types/src/pricing/service.ts | 1303 +++++++++-------- packages/utils/src/common/array-difference.ts | 15 + packages/utils/src/common/index.ts | 5 +- packages/utils/src/pricing/index.ts | 40 +- packages/utils/src/pricing/price-list.ts | 9 + packages/utils/src/pricing/rule-type.ts | 37 + 58 files changed, 5745 insertions(+), 1112 deletions(-) create mode 100644 .changeset/nervous-berries-sip.md create mode 100644 packages/pricing/integration-tests/__fixtures__/price-list-rules/data.ts create mode 100644 packages/pricing/integration-tests/__fixtures__/price-list-rules/index.ts create mode 100644 packages/pricing/integration-tests/__fixtures__/price-list/data.ts create mode 100644 packages/pricing/integration-tests/__fixtures__/price-list/index.ts create mode 100644 packages/pricing/integration-tests/__tests__/services/price-list-rule/index.spec.ts create mode 100644 packages/pricing/integration-tests/__tests__/services/pricing-module/price-list-rule.spec.ts create mode 100644 packages/pricing/integration-tests/__tests__/services/pricing-module/price-list.spec.ts create mode 100644 packages/pricing/src/migrations/Migration20231101232834.ts create mode 100644 packages/pricing/src/models/price-list-rule-value.ts create mode 100644 packages/pricing/src/models/price-list-rule.ts create mode 100644 packages/pricing/src/models/price-list.ts create mode 100644 packages/pricing/src/repositories/price-list-rule-value.ts create mode 100644 packages/pricing/src/repositories/price-list-rule.ts create mode 100644 packages/pricing/src/repositories/price-list.ts create mode 100644 packages/pricing/src/services/price-list-rule-value.ts create mode 100644 packages/pricing/src/services/price-list-rule.ts create mode 100644 packages/pricing/src/services/price-list.ts create mode 100644 packages/pricing/src/types/repositories/price-list-rule-value.ts create mode 100644 packages/pricing/src/types/repositories/price-list.ts create mode 100644 packages/types/src/pricing/common/price-list.ts create mode 100644 packages/utils/src/common/array-difference.ts create mode 100644 packages/utils/src/pricing/price-list.ts create mode 100644 packages/utils/src/pricing/rule-type.ts diff --git a/.changeset/nervous-berries-sip.md b/.changeset/nervous-berries-sip.md new file mode 100644 index 0000000000..bb8041abb8 --- /dev/null +++ b/.changeset/nervous-berries-sip.md @@ -0,0 +1,6 @@ +--- +"@medusajs/pricing": patch +"@medusajs/types": patch +--- + +feat(pricing,types): price list API + price calculations with price lists diff --git a/packages/medusa/src/api/routes/admin/price-lists/create-price-list.ts b/packages/medusa/src/api/routes/admin/price-lists/create-price-list.ts index 395c255072..fc9fc6e712 100644 --- a/packages/medusa/src/api/routes/admin/price-lists/create-price-list.ts +++ b/packages/medusa/src/api/routes/admin/price-lists/create-price-list.ts @@ -9,21 +9,20 @@ import { import { AdminPriceListPricesCreateReq, CreatePriceListInput, - PriceListStatus, - PriceListType, } from "../../../../types/price-list" +import { PriceListStatus, PriceListType } from "@medusajs/utils" import { Type } from "class-transformer" import { Request } from "express" import { EntityManager } from "typeorm" import TaxInclusivePricingFeatureFlag from "../../../../loaders/feature-flags/tax-inclusive-pricing" +import { PriceList } from "../../../../models" import PriceListService from "../../../../services/price-list" import { FeatureFlagDecorators } from "../../../../utils/feature-flag-decorators" import { defaultAdminPriceListFields, defaultAdminPriceListRelations, } from "./index" -import { PriceList } from "../../../../models" /** * @oas [post] /admin/price-lists diff --git a/packages/medusa/src/api/routes/admin/price-lists/update-price-list.ts b/packages/medusa/src/api/routes/admin/price-lists/update-price-list.ts index 371585cfde..9ba8c3a561 100644 --- a/packages/medusa/src/api/routes/admin/price-lists/update-price-list.ts +++ b/packages/medusa/src/api/routes/admin/price-lists/update-price-list.ts @@ -1,3 +1,4 @@ +import { PriceListStatus, PriceListType } from "@medusajs/utils" import { IsArray, IsBoolean, @@ -7,11 +8,7 @@ import { ValidateNested, } from "class-validator" import { defaultAdminPriceListFields, defaultAdminPriceListRelations } from "." -import { - AdminPriceListPricesUpdateReq, - PriceListStatus, - PriceListType, -} from "../../../../types/price-list" +import { AdminPriceListPricesUpdateReq } from "../../../../types/price-list" import { Type } from "class-transformer" import { EntityManager } from "typeorm" diff --git a/packages/medusa/src/api/routes/store/products/list-products.ts b/packages/medusa/src/api/routes/store/products/list-products.ts index 590fd8d695..92a1792c1c 100644 --- a/packages/medusa/src/api/routes/store/products/list-products.ts +++ b/packages/medusa/src/api/routes/store/products/list-products.ts @@ -250,13 +250,11 @@ export default async (req, res) => { } } - const isIsolateProductDomain = featureFlagRouter.isFeatureEnabled( - MedusaV2Flag.key - ) + const isMedusaV2Enabled = featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key) const promises: Promise[] = [] - if (isIsolateProductDomain) { + if (isMedusaV2Enabled) { promises.push( listAndCountProductWithIsolatedProductModule( req, diff --git a/packages/medusa/src/models/price-list.ts b/packages/medusa/src/models/price-list.ts index b06f9adeca..9a62b763e4 100644 --- a/packages/medusa/src/models/price-list.ts +++ b/packages/medusa/src/models/price-list.ts @@ -1,3 +1,4 @@ +import { PriceListStatus, PriceListType } from "@medusajs/utils" import { BeforeInsert, Column, @@ -7,14 +8,13 @@ import { OneToMany, } from "typeorm" import { DbAwareColumn, resolveDbType } from "../utils/db-aware-column" -import { PriceListStatus, PriceListType } from "../types/price-list" +import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity" +import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing" +import { FeatureFlagColumn } from "../utils/feature-flag-decorators" +import { generateEntityId } from "../utils/generate-entity-id" import { CustomerGroup } from "./customer-group" import { MoneyAmount } from "./money-amount" -import { SoftDeletableEntity } from "../interfaces/models/soft-deletable-entity" -import { generateEntityId } from "../utils/generate-entity-id" -import { FeatureFlagColumn } from "../utils/feature-flag-decorators" -import TaxInclusivePricingFeatureFlag from "../loaders/feature-flags/tax-inclusive-pricing" @Entity() export class PriceList extends SoftDeletableEntity { diff --git a/packages/medusa/src/services/pricing.ts b/packages/medusa/src/services/pricing.ts index 61d3191a79..de1265db5f 100644 --- a/packages/medusa/src/services/pricing.ts +++ b/packages/medusa/src/services/pricing.ts @@ -1,9 +1,10 @@ import { - CalculatedPriceSetDTO, + CalculatedPriceSet, IPricingModuleService, PriceSetMoneyAmountDTO, RemoteQueryFunction, } from "@medusajs/types" + import { FlagRouter, MedusaV2Flag, @@ -11,6 +12,7 @@ import { removeNullish, } from "@medusajs/utils" import { ProductVariantService, RegionService, TaxProviderService } from "." + import { IPriceSelectionStrategy, PriceSelectionContext, @@ -222,19 +224,19 @@ class PricingService extends TransactionBaseService { context.price_selection ) - let priceSets: CalculatedPriceSetDTO[] = [] + let calculatedPrices: CalculatedPriceSet[] = [] if (queryContext.currency_code) { - priceSets = (await this.pricingModuleService.calculatePrices( + calculatedPrices = (await this.pricingModuleService.calculatePrices( { id: priceSetIds }, { context: queryContext as any, } - )) as unknown as CalculatedPriceSetDTO[] + )) as unknown as CalculatedPriceSet[] } - const priceSetMap = new Map( - priceSets.map((priceSet) => [priceSet.id, priceSet]) + const calculatedPriceMap = new Map( + calculatedPrices.map((priceSet) => [priceSet.id, priceSet]) ) const pricingResultMap = new Map() @@ -257,14 +259,28 @@ class PricingService extends TransactionBaseService { } if (priceSetId) { - const prices = priceSetMap.get(priceSetId) + const calculatedPrice: CalculatedPriceSet | undefined = + calculatedPriceMap.get(priceSetId) - if (prices) { - pricingResult.prices = [prices] as MoneyAmount[] - pricingResult.original_price = prices.amount - pricingResult.calculated_price = prices.amount + if (calculatedPrice) { + pricingResult.prices = [ + { + id: calculatedPrice.calculated_price?.money_amount_id, + currency_code: calculatedPrice.currency_code, + amount: calculatedPrice.calculated_amount, + min_quantity: calculatedPrice.calculated_price?.min_quantity, + max_quantity: calculatedPrice.calculated_price?.max_quantity, + price_list_id: calculatedPrice.calculated_price?.price_list_id, + }, + ] as MoneyAmount[] + + pricingResult.original_price = calculatedPrice?.original_amount + pricingResult.calculated_price = calculatedPrice?.calculated_amount + pricingResult.calculated_price_type = + calculatedPrice?.calculated_price?.price_list_type } } + pricingResultMap.set(variantId, pricingResult) }) @@ -286,7 +302,7 @@ class PricingService extends TransactionBaseService { .withTransaction(this.activeManager_) .calculateVariantPrice(data, context.price_selection) - const pricingResultMap = new Map() + const pricingResultMap = new Map() for (const [variantId, pricing] of variantsPricing.entries()) { const pricingResult: ProductVariantPricing = { @@ -668,6 +684,7 @@ class PricingService extends TransactionBaseService { { relations: [ "money_amount", + "price_list", "price_set", "price_rules", "price_rules.rule_type", @@ -691,6 +708,7 @@ class PricingService extends TransactionBaseService { const moneyAmount = { ...priceSetMoneyAmount.money_amount, region_id: null as null | string, + price_list_id: priceSetMoneyAmount.price_list?.id, } if (regionId) { diff --git a/packages/pricing/integration-tests/__fixtures__/price-list-rules/data.ts b/packages/pricing/integration-tests/__fixtures__/price-list-rules/data.ts new file mode 100644 index 0000000000..b40f9279fa --- /dev/null +++ b/packages/pricing/integration-tests/__fixtures__/price-list-rules/data.ts @@ -0,0 +1,12 @@ +export const defaultPriceListRuleData = [ + { + id: "price-list-rule-1", + price_list: "price-list-1", + rule_type: "rule-type-1", + }, + { + id: "price-list-rule-2", + price_list: "price-list-1", + rule_type: "rule-type-2", + }, +] diff --git a/packages/pricing/integration-tests/__fixtures__/price-list-rules/index.ts b/packages/pricing/integration-tests/__fixtures__/price-list-rules/index.ts new file mode 100644 index 0000000000..07af1818cd --- /dev/null +++ b/packages/pricing/integration-tests/__fixtures__/price-list-rules/index.ts @@ -0,0 +1,22 @@ +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { PriceListRule } from "@models" +import { defaultPriceListRuleData } from "./data" + +export * from "./data" + +export async function createPriceListRules( + manager: SqlEntityManager, + priceListRuleData: any[] = defaultPriceListRuleData +): Promise { + const priceListRules: PriceListRule[] = [] + + for (let data of priceListRuleData) { + const plr = manager.create(PriceListRule, data) + + priceListRules.push(plr) + } + + await manager.persistAndFlush(priceListRules) + + return priceListRules +} diff --git a/packages/pricing/integration-tests/__fixtures__/price-list/data.ts b/packages/pricing/integration-tests/__fixtures__/price-list/data.ts new file mode 100644 index 0000000000..fd9610ea03 --- /dev/null +++ b/packages/pricing/integration-tests/__fixtures__/price-list/data.ts @@ -0,0 +1,14 @@ +export const defaultPriceListData = [ + { + id: "price-list-1", + title: "Price List 1", + description: "test", + number_rules: 0, + }, + { + id: "price-list-2", + title: "Price List 2", + description: "test", + number_rules: 0, + }, +] diff --git a/packages/pricing/integration-tests/__fixtures__/price-list/index.ts b/packages/pricing/integration-tests/__fixtures__/price-list/index.ts new file mode 100644 index 0000000000..cf28eda46c --- /dev/null +++ b/packages/pricing/integration-tests/__fixtures__/price-list/index.ts @@ -0,0 +1,22 @@ +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { PriceList } from "@models" +import { defaultPriceListData } from "./data" + +export * from "./data" + +export async function createPriceLists( + manager: SqlEntityManager, + priceListData: any[] = defaultPriceListData +): Promise { + const priceLists: PriceList[] = [] + + for (let data of priceListData) { + const pl = manager.create(PriceList, data) + + priceLists.push(pl) + } + + await manager.persistAndFlush(priceLists) + + return priceLists +} diff --git a/packages/pricing/integration-tests/__tests__/services/money-amount/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/money-amount/index.spec.ts index 5f44e56677..849cba3c08 100644 --- a/packages/pricing/integration-tests/__tests__/services/money-amount/index.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/money-amount/index.spec.ts @@ -42,20 +42,22 @@ describe("MoneyAmount Service", () => { it("should list all moneyAmounts", async () => { const moneyAmountsResult = await service.list() - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-USD", - amount: "500", - }), - expect.objectContaining({ - id: "money-amount-EUR", - amount: "400", - }), - expect.objectContaining({ - id: "money-amount-CAD", - amount: "600", - }), - ]) + expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "money-amount-USD", + amount: 500, + }), + expect.objectContaining({ + id: "money-amount-EUR", + amount: 400, + }), + expect.objectContaining({ + id: "money-amount-CAD", + amount: 600, + }), + ]) + ) }) it("should list moneyAmounts by id", async () => { @@ -63,7 +65,7 @@ describe("MoneyAmount Service", () => { id: ["money-amount-USD"], }) - expect(moneyAmountsResult).toEqual([ + expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual([ expect.objectContaining({ id: "money-amount-USD", }), @@ -76,7 +78,7 @@ describe("MoneyAmount Service", () => { id: ["money-amount-USD"], }, { - select: ["id", "min_quantity", "currency.code"], + select: ["id", "min_quantity", "currency.code", "amount"], relations: ["currency"], } ) @@ -86,6 +88,7 @@ describe("MoneyAmount Service", () => { expect(serialized).toEqual([ { id: "money-amount-USD", + amount: 500, min_quantity: "1", currency_code: "USD", currency: { @@ -101,7 +104,7 @@ describe("MoneyAmount Service", () => { currency_code: ["USD"], }, { - select: ["id", "min_quantity", "currency.code"], + select: ["id", "min_quantity", "currency.code", "amount"], relations: ["currency"], } ) @@ -113,6 +116,7 @@ describe("MoneyAmount Service", () => { id: "money-amount-USD", min_quantity: "1", currency_code: "USD", + amount: 500, currency: { code: "USD", }, @@ -126,17 +130,19 @@ describe("MoneyAmount Service", () => { const [moneyAmountsResult, count] = await service.listAndCount() expect(count).toEqual(3) - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-USD", - }), - expect.objectContaining({ - id: "money-amount-EUR", - }), - expect.objectContaining({ - id: "money-amount-CAD", - }), - ]) + expect(JSON.parse(JSON.stringify(moneyAmountsResult))).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "money-amount-USD", + }), + expect.objectContaining({ + id: "money-amount-EUR", + }), + expect.objectContaining({ + id: "money-amount-CAD", + }), + ]) + ) }) it("should return moneyAmounts and count when filtered", async () => { @@ -158,7 +164,7 @@ describe("MoneyAmount Service", () => { id: ["money-amount-USD"], }, { - select: ["id", "min_quantity", "currency.code"], + select: ["id", "min_quantity", "currency.code", "amount"], relations: ["currency"], } ) @@ -169,6 +175,7 @@ describe("MoneyAmount Service", () => { expect(serialized).toEqual([ { id: "money-amount-USD", + amount: 500, min_quantity: "1", currency_code: "USD", currency: { @@ -197,7 +204,7 @@ describe("MoneyAmount Service", () => { {}, { take: 1, - select: ["id"], + select: ["id", "amount"], } ) @@ -207,6 +214,7 @@ describe("MoneyAmount Service", () => { expect(serialized).toEqual([ { id: "money-amount-USD", + amount: 500, }, ]) }) @@ -214,7 +222,7 @@ describe("MoneyAmount Service", () => { describe("retrieve", () => { const id = "money-amount-USD" - const amount = "500" + const amount = 500 it("should return moneyAmount for the given id", async () => { const moneyAmount = await service.retrieve(id) @@ -291,9 +299,9 @@ describe("MoneyAmount Service", () => { }, ]) - const moneyAmount = await service.retrieve(id) + const moneyAmount = JSON.parse(JSON.stringify(await service.retrieve(id))) - expect(moneyAmount.amount).toEqual("700") + expect(moneyAmount.amount).toEqual(700) }) it("should update the currency of the moneyAmount successfully", async () => { @@ -346,11 +354,11 @@ describe("MoneyAmount Service", () => { id: ["money-amount-TESM"], }) - expect(moneyAmount).toEqual( + expect(JSON.parse(JSON.stringify(moneyAmount))).toEqual( expect.objectContaining({ id: "money-amount-TESM", currency_code: "USD", - amount: "333", + amount: 333, min_quantity: "1", max_quantity: "4", }) diff --git a/packages/pricing/integration-tests/__tests__/services/price-list-rule/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/price-list-rule/index.spec.ts new file mode 100644 index 0000000000..14ded5bd20 --- /dev/null +++ b/packages/pricing/integration-tests/__tests__/services/price-list-rule/index.spec.ts @@ -0,0 +1,243 @@ +import { SqlEntityManager } from "@mikro-orm/postgresql" + +import { PriceListRuleRepository } from "@repositories" +import { PriceListRuleService } from "@services" + +import { createPriceLists } from "../../../__fixtures__/price-list" +import { createPriceListRules } from "../../../__fixtures__/price-list-rules" +import { createRuleTypes } from "../../../__fixtures__/rule-type" +import { MikroOrmWrapper } from "../../../utils" + +jest.setTimeout(30000) + +describe("PriceListRule Service", () => { + let service: PriceListRuleService + let testManager: SqlEntityManager + let repositoryManager: SqlEntityManager + + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() + + const priceListRuleRepository = new PriceListRuleRepository({ + manager: repositoryManager, + }) + + service = new PriceListRuleService({ + priceListRuleRepository, + }) + + testManager = await MikroOrmWrapper.forkManager() + await createRuleTypes(testManager) + await createPriceLists(testManager) + await createPriceListRules(testManager) + }) + + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) + + describe("list", () => { + it("should list all priceListRules", async () => { + const priceListRuleResult = await service.list() + + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) + }) + + it("should list priceListRules scoped by priceListRule id", async () => { + const priceListRuleResult = await service.list({ + id: ["price-list-rule-1"], + }) + + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + ]) + }) + }) + + describe("listAndCount", () => { + it("should return pricelistrules and count", async () => { + const [priceListRuleResult, count] = await service.listAndCount() + + expect(count).toEqual(2) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) + }) + + it("should return pricelistrules and count when filtered", async () => { + const [priceListRuleResult, count] = await service.listAndCount({ + id: ["price-list-rule-1"], + }) + + expect(count).toEqual(1) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + ]) + }) + + it("should return pricelistrules and count when using skip and take", async () => { + const [priceListRuleResult, count] = await service.listAndCount( + {}, + { skip: 1, take: 1 } + ) + + expect(count).toEqual(2) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [priceListRuleResult, count] = await service.listAndCount( + {}, + { + take: 1, + select: ["id"], + } + ) + + const serialized = JSON.parse(JSON.stringify(priceListRuleResult)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + { + id: "price-list-rule-1", + }, + ]) + }) + }) + + describe("retrieve", () => { + const id = "price-list-rule-1" + + it("should return priceList for the given id", async () => { + const priceListRuleResult = await service.retrieve(id) + + expect(priceListRuleResult).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when priceListRule with id does not exist", async () => { + let error + + try { + await service.retrieve("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "PriceListRule with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.retrieve(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual('"priceListRuleId" must be defined') + }) + }) + + describe("delete", () => { + const id = "price-list-rule-1" + + it("should delete the pricelists given an id successfully", async () => { + await service.delete([id]) + + const priceListResult = await service.list({ + id: [id], + }) + + expect(priceListResult).toHaveLength(0) + }) + }) + + describe("update", () => { + const id = "price-list-rule-2" + + it("should update the value of the priceListRule successfully", async () => { + await service.update([ + { + id, + price_list_id: "price-list-1", + }, + ]) + + const priceList = await service.retrieve(id, { + relations: ["price_list"], + }) + + expect(priceList.price_list.id).toEqual("price-list-1") + }) + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.update([ + { + id: "does-not-exist", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'PriceListRule with id(s) "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should create a priceListRule successfully", async () => { + const [created] = await service.create([ + { + price_list_id: "price-list-2", + rule_type_id: "rule-type-1", + }, + ]) + + const [priceListRule] = await service.list( + { + id: [created.id], + }, + { + relations: ["price_list", "rule_type"], + select: ["price_list.id", "rule_type.id"], + } + ) + + expect(priceListRule.price_list.id).toEqual("price-list-2") + expect(priceListRule.rule_type.id).toEqual("rule-type-1") + }) + }) +}) diff --git a/packages/pricing/integration-tests/__tests__/services/price-list/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/price-list/index.spec.ts index 3e817b97b8..994036e7ba 100644 --- a/packages/pricing/integration-tests/__tests__/services/price-list/index.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/price-list/index.spec.ts @@ -1,37 +1,31 @@ -import { SqlEntityManager } from "@mikro-orm/postgresql" - -import { Currency, MoneyAmount } from "@models" -import { MoneyAmountRepository } from "@repositories" -import { MoneyAmountService } from "@services" - -import { createCurrencies } from "../../../__fixtures__/currency" -import { createMoneyAmounts } from "../../../__fixtures__/money-amount" +import { PriceListRepository } from "@repositories" +import { PriceListService } from "@services" import { MikroOrmWrapper } from "../../../utils" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { createPriceLists } from "../../../__fixtures__/price-list" + jest.setTimeout(30000) -describe("MoneyAmount Service", () => { - let service: MoneyAmountService +describe("PriceList Service", () => { + let service: PriceListService let testManager: SqlEntityManager let repositoryManager: SqlEntityManager - let data!: MoneyAmount[] - let currencyData!: Currency[] beforeEach(async () => { await MikroOrmWrapper.setupDatabase() repositoryManager = await MikroOrmWrapper.forkManager() - const moneyAmountRepository = new MoneyAmountRepository({ + const priceListRepository = new PriceListRepository({ manager: repositoryManager, }) - service = new MoneyAmountService({ - moneyAmountRepository, + service = new PriceListService({ + priceListRepository, }) testManager = await MikroOrmWrapper.forkManager() - currencyData = await createCurrencies(testManager) - data = await createMoneyAmounts(testManager) + await createPriceLists(testManager) }) afterEach(async () => { @@ -39,136 +33,76 @@ describe("MoneyAmount Service", () => { }) describe("list", () => { - it("list moneyAmounts", async () => { - const moneyAmountsResult = await service.list() + it("should return list priceLists", async () => { + const priceListResult = await service.list() - expect(moneyAmountsResult).toEqual([ + expect(priceListResult).toEqual([ expect.objectContaining({ - id: "money-amount-USD", - amount: "500", + id: "price-list-1", }), expect.objectContaining({ - id: "money-amount-EUR", - amount: "400", - }), - expect.objectContaining({ - id: "money-amount-CAD", - amount: "600", + id: "price-list-2", }), ]) }) - it("list moneyAmounts by id", async () => { - const moneyAmountsResult = await service.list({ - id: ["money-amount-USD"], + it("should list pricelists by id", async () => { + const priceListResult = await service.list({ + id: ["price-list-1"], }) - expect(moneyAmountsResult).toEqual([ + expect(priceListResult).toEqual([ expect.objectContaining({ - id: "money-amount-USD", + id: "price-list-1", }), ]) }) - - it("list moneyAmounts with relations and selects", async () => { - const moneyAmountsResult = await service.list( - { - id: ["money-amount-USD"], - }, - { - select: ["id", "min_quantity", "currency.code"], - relations: ["currency"], - } - ) - - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(serialized).toEqual([ - { - id: "money-amount-USD", - min_quantity: "1", - currency_code: "USD", - currency: { - code: "USD", - }, - }, - ]) - }) }) describe("listAndCount", () => { - it("should return moneyAmounts and count", async () => { - const [moneyAmountsResult, count] = await service.listAndCount() + it("should return pricelists and count", async () => { + const [priceListResult, count] = await service.listAndCount() - expect(count).toEqual(3) - expect(moneyAmountsResult).toEqual([ + expect(count).toEqual(2) + expect(priceListResult).toEqual([ expect.objectContaining({ - id: "money-amount-USD", + id: "price-list-1", }), expect.objectContaining({ - id: "money-amount-EUR", - }), - expect.objectContaining({ - id: "money-amount-CAD", + id: "price-list-2", }), ]) }) - it("should return moneyAmounts and count when filtered", async () => { - const [moneyAmountsResult, count] = await service.listAndCount({ - id: ["money-amount-USD"], + it("should return pricelists and count when filtered", async () => { + const [priceListResult, count] = await service.listAndCount({ + id: ["price-list-1"], }) expect(count).toEqual(1) - expect(moneyAmountsResult).toEqual([ + expect(priceListResult).toEqual([ expect.objectContaining({ - id: "money-amount-USD", + id: "price-list-1", }), ]) }) - it("list moneyAmounts with relations and selects", async () => { - const [moneyAmountsResult, count] = await service.listAndCount( - { - id: ["money-amount-USD"], - }, - { - select: ["id", "min_quantity", "currency.code"], - relations: ["currency"], - } - ) - - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) - - expect(count).toEqual(1) - expect(serialized).toEqual([ - { - id: "money-amount-USD", - min_quantity: "1", - currency_code: "USD", - currency: { - code: "USD", - }, - }, - ]) - }) - - it("should return moneyAmounts and count when using skip and take", async () => { - const [moneyAmountsResult, count] = await service.listAndCount( + it("should return pricelists and count when using skip and take", async () => { + const [priceListResult, count] = await service.listAndCount( {}, { skip: 1, take: 1 } ) - expect(count).toEqual(3) - expect(moneyAmountsResult).toEqual([ + expect(count).toEqual(2) + expect(priceListResult).toEqual([ expect.objectContaining({ - id: "money-amount-EUR", + id: "price-list-2", }), ]) }) it("should return requested fields", async () => { - const [moneyAmountsResult, count] = await service.listAndCount( + const [priceListResult, count] = await service.listAndCount( {}, { take: 1, @@ -176,32 +110,31 @@ describe("MoneyAmount Service", () => { } ) - const serialized = JSON.parse(JSON.stringify(moneyAmountsResult)) + const serialized = JSON.parse(JSON.stringify(priceListResult)) - expect(count).toEqual(3) + expect(count).toEqual(2) expect(serialized).toEqual([ { - id: "money-amount-USD", + id: "price-list-1", }, ]) }) }) describe("retrieve", () => { - const id = "money-amount-USD" - const amount = "500" + const id = "price-list-1" - it("should return moneyAmount for the given id", async () => { - const moneyAmount = await service.retrieve(id) + it("should return priceList for the given id", async () => { + const priceListResult = await service.retrieve(id) - expect(moneyAmount).toEqual( + expect(priceListResult).toEqual( expect.objectContaining({ id, }) ) }) - it("should throw an error when moneyAmount with id does not exist", async () => { + it("should throw an error when priceList with id does not exist", async () => { let error try { @@ -211,7 +144,7 @@ describe("MoneyAmount Service", () => { } expect(error.message).toEqual( - "MoneyAmount with id: does-not-exist was not found" + "PriceList with id: does-not-exist was not found" ) }) @@ -224,65 +157,39 @@ describe("MoneyAmount Service", () => { error = e } - expect(error.message).toEqual('"moneyAmountId" must be defined') - }) - - it("should return moneyAmount based on config select param", async () => { - const moneyAmount = await service.retrieve(id, { - select: ["id", "amount"], - }) - - const serialized = JSON.parse(JSON.stringify(moneyAmount)) - - expect(serialized).toEqual({ - id, - amount, - }) + expect(error.message).toEqual('"priceListId" must be defined') }) }) describe("delete", () => { - const id = "money-amount-USD" + const id = "price-list-1" - it("should delete the moneyAmounts given an id successfully", async () => { + it("should delete the pricelists given an id successfully", async () => { await service.delete([id]) - const moneyAmounts = await service.list({ + const priceListResult = await service.list({ id: [id], }) - expect(moneyAmounts).toHaveLength(0) + expect(priceListResult).toHaveLength(0) }) }) describe("update", () => { - const id = "money-amount-USD" + const id = "price-list-2" - it("should update the amount of the moneyAmount successfully", async () => { + it("should update the starts_at date of the priceList successfully", async () => { + const updateDate = new Date() await service.update([ { id, - amount: 700, + starts_at: updateDate.toISOString(), }, ]) - const moneyAmount = await service.retrieve(id) + const priceList = await service.retrieve(id) - expect(moneyAmount.amount).toEqual("700") - }) - - it("should update the currency of the moneyAmount successfully", async () => { - await service.update([ - { - id, - currency_code: "EUR", - }, - ]) - - const moneyAmount = await service.retrieve(id) - - expect(moneyAmount.currency_code).toEqual("EUR") - expect(moneyAmount.currency?.code).toEqual("EUR") + expect(priceList.starts_at).toEqual(updateDate) }) it("should throw an error when a id does not exist", async () => { @@ -292,7 +199,6 @@ describe("MoneyAmount Service", () => { await service.update([ { id: "does-not-exist", - amount: 666, }, ]) } catch (e) { @@ -300,36 +206,25 @@ describe("MoneyAmount Service", () => { } expect(error.message).toEqual( - 'MoneyAmount with id "does-not-exist" not found' + 'PriceList with id "does-not-exist" not found' ) }) }) describe("create", () => { - it("should create a moneyAmount successfully", async () => { - await service.create([ + it("should create a priceList successfully", async () => { + const [created] = await service.create([ { - id: "money-amount-TESM", - currency_code: "USD", - amount: 333, - min_quantity: 1, - max_quantity: 4, + title: "test", + description: "test", }, ]) - const [moneyAmount] = await service.list({ - id: ["money-amount-TESM"], + const [priceList] = await service.list({ + id: [created.id], }) - expect(moneyAmount).toEqual( - expect.objectContaining({ - id: "money-amount-TESM", - currency_code: "USD", - amount: "333", - min_quantity: "1", - max_quantity: "4", - }) - ) + expect(priceList.title).toEqual("test") }) }) }) diff --git a/packages/pricing/integration-tests/__tests__/services/price-set/index.spec.ts b/packages/pricing/integration-tests/__tests__/services/price-set/index.spec.ts index eea3953ad3..6fbb38bba3 100644 --- a/packages/pricing/integration-tests/__tests__/services/price-set/index.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/price-set/index.spec.ts @@ -124,9 +124,9 @@ describe("PriceSet Service", () => { { id: "price-set-1", money_amounts: [ - { + expect.objectContaining({ id: "money-amount-USD", - }, + }), ], }, ]) @@ -151,9 +151,9 @@ describe("PriceSet Service", () => { { id: "price-set-1", money_amounts: [ - { + expect.objectContaining({ id: "money-amount-USD", - }, + }), ], }, ]) @@ -227,9 +227,9 @@ describe("PriceSet Service", () => { { id: "price-set-1", money_amounts: [ - { + expect.objectContaining({ id: "money-amount-USD", - }, + }), ], }, ]) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/calculate-price.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/calculate-price.spec.ts index 0dc4414cf9..659ac3e350 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/calculate-price.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/calculate-price.spec.ts @@ -2,18 +2,54 @@ import { CreatePriceRuleDTO, CreatePriceSetDTO, IPricingModuleService, + PricingTypes, } from "@medusajs/types" +import { PriceListType } from "@medusajs/utils" import { SqlEntityManager } from "@mikro-orm/postgresql" import { PriceSet } from "@models" - import { initialize } from "../../../../src" - +import { seedPriceData } from "../../../__fixtures__/seed-price-data" import { DB_URL, MikroOrmWrapper } from "../../../utils" -import { seedPriceData } from "../../../__fixtures__/seed-price-data" - jest.setTimeout(30000) +const defaultRules = { + customer_group_id: ["vip-customer-group-id", "another-vip-customer-group-id"], + region_id: ["DE", "DK"], +} + +const defaultPriceListPrices = [ + { + amount: 232, + currency_code: "PLN", + price_set_id: "price-set-PLN", + }, + { + amount: 455, + currency_code: "EUR", + price_set_id: "price-set-EUR", + }, +] +const createPriceLists = async ( + service, + priceListOverride: Partial< + Omit + > = {}, + rules: object = defaultRules, + prices = defaultPriceListPrices +) => { + return await service.createPriceLists([ + { + title: "Test Price List", + description: "test description", + type: PriceListType.SALE, + rules, + prices, + ...priceListOverride, + }, + ]) +} + describe("PricingModule Service - Calculate Price", () => { let service: IPricingModuleService let testManager: SqlEntityManager @@ -367,17 +403,47 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: "1000", + is_calculated_price_price_list: false, + calculated_amount: 1000, + is_original_price_price_list: false, + original_amount: 1000, currency_code: "PLN", - min_quantity: "1", - max_quantity: "10", + calculated_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, }, ]) }) @@ -393,17 +459,47 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: "300", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, currency_code: "PLN", - min_quantity: "1", - max_quantity: "4", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, }, ]) }) @@ -419,17 +515,47 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: "1000", + is_calculated_price_price_list: false, + calculated_amount: 1000, + is_original_price_price_list: false, + original_amount: 1000, currency_code: "PLN", - min_quantity: "1", - max_quantity: "10", + calculated_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, }, ]) }) @@ -445,17 +571,47 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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, + }, }, ]) }) @@ -471,17 +627,47 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: "300", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, currency_code: "PLN", - min_quantity: "1", - max_quantity: "4", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, }, ]) }) @@ -497,17 +683,47 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: "1000", + is_calculated_price_price_list: false, + calculated_amount: 1000, + is_original_price_price_list: false, + original_amount: 1000, currency_code: "PLN", - min_quantity: "1", - max_quantity: "10", + calculated_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, }, ]) }) @@ -523,10 +739,25 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-PLN", - amount: "250", + is_calculated_price_price_list: false, + calculated_amount: 250, + is_original_price_price_list: false, + original_amount: 250, currency_code: "PLN", - min_quantity: "4", - max_quantity: "10", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN-5-qty", + price_list_id: null, + price_list_type: null, + min_quantity: 4, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN-5-qty", + price_list_id: null, + price_list_type: null, + min_quantity: 4, + max_quantity: 10, + }, }, ]) }) @@ -547,17 +778,47 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: "300", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, currency_code: "PLN", - min_quantity: "1", - max_quantity: "4", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, }, ]) }) @@ -578,18 +839,48 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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, + }, }, // Currency Code + Region value + customer group id { id: "price-set-PLN", - amount: "100", + is_calculated_price_price_list: false, + calculated_amount: 100, + is_original_price_price_list: false, + original_amount: 100, currency_code: "EUR", - min_quantity: "1", - max_quantity: "3", + calculated_price: { + money_amount_id: "money-amount-region_id-PL-EUR-customer-group", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 3, + }, + original_price: { + money_amount_id: "money-amount-region_id-PL-EUR-customer-group", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 3, + }, }, ]) }) @@ -610,18 +901,48 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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, + }, }, // PLN price set is not setup for EUR currency_code so it will default to a null set { id: "price-set-PLN", - amount: "300", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, currency_code: "PLN", - min_quantity: "1", - max_quantity: "4", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, }, ]) }) @@ -642,18 +963,48 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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, + }, }, // PLN price set is not setup for EUR currency_code so it will default to a null set { id: "price-set-PLN", - amount: "1000", + is_calculated_price_price_list: false, + calculated_amount: 1000, + is_original_price_price_list: false, + original_amount: 1000, currency_code: "PLN", - min_quantity: "1", - max_quantity: "10", + calculated_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, + original_price: { + money_amount_id: "money-amount-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 10, + }, }, ]) }) @@ -669,17 +1020,47 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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, + }, }, ]) }) @@ -695,19 +1076,705 @@ describe("PricingModule Service - Calculate Price", () => { expect(priceSetsResult).toEqual([ { id: "price-set-EUR", - amount: null, + is_calculated_price_price_list: false, + calculated_amount: null, + is_original_price_price_list: false, + original_amount: null, currency_code: null, - min_quantity: null, - max_quantity: 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", - amount: "300", + is_calculated_price_price_list: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, currency_code: "PLN", - min_quantity: "1", - max_quantity: "4", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, }, ]) }) + + describe("Price Lists", () => { + it("should return price list prices when price list conditions match", async () => { + await createPriceLists(service) + + 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, + }, + }, + ]) + }) + + it("should return price list prices when price list dont have rules, but context is loaded", async () => { + await createPriceLists(service, {}, {}) + + 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, + }, + }, + ]) + }) + + it("should return price list prices when price list dont have any rules", async () => { + await createPriceLists(service, {}, {}) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + }, + } + ) + + 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: 1000, + 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: 10, + }, + }, + ]) + }) + + it("should return price list prices when price list conditions match for override", async () => { + await createPriceLists(service, { type: PriceListType.OVERRIDE }) + + 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: true, + original_amount: 232, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "override", + min_quantity: null, + max_quantity: null, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: expect.any(String), + price_list_type: "override", + min_quantity: null, + max_quantity: null, + }, + }, + ]) + }) + + it("should not return price list prices when price list conditions only partially match", async () => { + await createPriceLists(service) + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "PL", + customer_group_id: "vip-customer-group-id", + company_id: "does-not-exist", + }, + } + ) + + 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: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: "money-amount-region_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + }, + ]) + }) + + it("should not return price list prices when price list conditions dont match", async () => { + await createPriceLists(service) + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR", "price-set-PLN"] }, + { + context: { + currency_code: "PLN", + region_id: "PL", + customer_group_id: "does-not-exist", + company_id: "does-not-exist", + }, + } + ) + + 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: false, + calculated_amount: 300, + is_original_price_price_list: false, + original_amount: 300, + currency_code: "PLN", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + original_price: { + money_amount_id: expect.any(String), + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 4, + }, + }, + ]) + }) + + it("should return price list prices for pricelist with valid pricing interval", async () => { + const yesterday = ((today) => + new Date(today.setDate(today.getDate() - 1)))(new Date()) + const tomorrow = ((today) => + new Date(today.setDate(today.getDate() + 1)))(new Date()) + + await createPriceLists( + service, + { + starts_at: yesterday.toISOString(), + ends_at: tomorrow.toISOString(), + }, + {} + ) + + 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, + }, + }, + ]) + }) + + it("should not return price list prices for upcoming pricelist", async () => { + const tomorrow = ((today) => + new Date(today.setDate(today.getDate() + 1)))(new Date()) + + const tenDaysFromToday = ((today) => + new Date(today.setDate(today.getDate() + 10)))(new Date()) + + await createPriceLists( + service, + { + starts_at: tomorrow.toISOString(), + ends_at: tenDaysFromToday.toISOString(), + }, + {} + ) + + 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: false, + calculated_amount: 400, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-company_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + original_price: { + money_amount_id: "money-amount-company_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should not return price list prices for expired pricelist", async () => { + const yesterday = ((today) => + new Date(today.setDate(today.getDate() - 1)))(new Date()) + const tenDaysAgo = ((today) => + new Date(today.setDate(today.getDate() - 10)))(new Date()) + + await createPriceLists( + service, + { + starts_at: tenDaysAgo.toISOString(), + ends_at: yesterday.toISOString(), + }, + {} + ) + + 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: false, + calculated_amount: 400, + is_original_price_price_list: false, + original_amount: 400, + currency_code: "PLN", + calculated_price: { + money_amount_id: "money-amount-company_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + original_price: { + money_amount_id: "money-amount-company_id-PLN", + price_list_id: null, + price_list_type: null, + min_quantity: 1, + max_quantity: 5, + }, + }, + ]) + }) + + it("should return price list prices for price list with customer groupst", async () => { + const [{ id }] = await createPriceLists( + service, + {}, + { + customer_group_id: [ + "vip-customer-group-id", + "vip-customer-group-id-1", + ], + }, + [ + { + amount: 200, + currency_code: "EUR", + price_set_id: "price-set-EUR", + }, + ] + ) + + const priceSetsResult = await service.calculatePrices( + { id: ["price-set-EUR"] }, + { + context: { + currency_code: "EUR", + customer_group_id: "vip-customer-group-id", + }, + } + ) + + expect(priceSetsResult).toEqual([ + { + id: "price-set-EUR", + is_calculated_price_price_list: true, + calculated_amount: 200, + is_original_price_price_list: false, + original_amount: null, + currency_code: "EUR", + calculated_price: { + money_amount_id: expect.any(String), + price_list_id: id, + price_list_type: "sale", + 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, + }, + }, + ]) + }) + }) }) }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/money-amount.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/money-amount.spec.ts index fb2889b9b4..010dd05219 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/money-amount.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/money-amount.spec.ts @@ -42,20 +42,22 @@ describe("PricingModule Service - MoneyAmount", () => { it("list moneyAmounts", async () => { const moneyAmountsResult = await service.listMoneyAmounts() - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-USD", - amount: "500", - }), - expect.objectContaining({ - id: "money-amount-EUR", - amount: "400", - }), - expect.objectContaining({ - id: "money-amount-CAD", - amount: "600", - }), - ]) + expect(moneyAmountsResult).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "money-amount-USD", + amount: 500, + }), + expect.objectContaining({ + id: "money-amount-EUR", + amount: 400, + }), + expect.objectContaining({ + id: "money-amount-CAD", + amount: 600, + }), + ]) + ) }) it("should list moneyAmounts by id", async () => { @@ -86,6 +88,7 @@ describe("PricingModule Service - MoneyAmount", () => { expect(serialized).toEqual([ { id: "money-amount-USD", + amount: null, min_quantity: "1", currency_code: "USD", currency: { @@ -102,17 +105,19 @@ describe("PricingModule Service - MoneyAmount", () => { await service.listAndCountMoneyAmounts() expect(count).toEqual(3) - expect(moneyAmountsResult).toEqual([ - expect.objectContaining({ - id: "money-amount-USD", - }), - expect.objectContaining({ - id: "money-amount-EUR", - }), - expect.objectContaining({ - id: "money-amount-CAD", - }), - ]) + expect(moneyAmountsResult).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "money-amount-USD", + }), + expect.objectContaining({ + id: "money-amount-EUR", + }), + expect.objectContaining({ + id: "money-amount-CAD", + }), + ]) + ) }) it("should return moneyAmounts and count when filtered", async () => { @@ -136,7 +141,7 @@ describe("PricingModule Service - MoneyAmount", () => { id: ["money-amount-USD"], }, { - select: ["id", "min_quantity", "currency.code"], + select: ["id", "min_quantity", "currency.code", "amount"], relations: ["currency"], } ) @@ -147,6 +152,7 @@ describe("PricingModule Service - MoneyAmount", () => { expect(serialized).toEqual([ { id: "money-amount-USD", + amount: 500, min_quantity: "1", currency_code: "USD", currency: { @@ -184,6 +190,7 @@ describe("PricingModule Service - MoneyAmount", () => { expect(serialized).toEqual([ { id: "money-amount-USD", + amount: null, }, ]) }) @@ -191,7 +198,7 @@ describe("PricingModule Service - MoneyAmount", () => { describe("retrieveMoneyAmount", () => { const id = "money-amount-USD" - const amount = "500" + const amount = 500 it("should return moneyAmount for the given id", async () => { const moneyAmount = await service.retrieveMoneyAmount(id) @@ -268,9 +275,13 @@ describe("PricingModule Service - MoneyAmount", () => { }, ]) - const moneyAmount = await service.retrieveMoneyAmount(id) + const moneyAmount = JSON.parse( + JSON.stringify( + await service.retrieveMoneyAmount(id, { select: ["amount"] }) + ) + ) - expect(moneyAmount.amount).toEqual("700") + expect(moneyAmount.amount).toEqual(700) }) it("should update the currency of the moneyAmount successfully", async () => { @@ -329,7 +340,7 @@ describe("PricingModule Service - MoneyAmount", () => { expect.objectContaining({ id: "money-amount-TESM", currency_code: "USD", - amount: "333", + amount: 333, min_quantity: "1", max_quantity: "4", }) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list-rule.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list-rule.spec.ts new file mode 100644 index 0000000000..b530b0ec3b --- /dev/null +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list-rule.spec.ts @@ -0,0 +1,348 @@ +import { DB_URL, MikroOrmWrapper } from "../../../utils" + +import { IPricingModuleService } from "@medusajs/types" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { initialize } from "../../../../src" +import { createPriceLists } from "../../../__fixtures__/price-list" +import { createPriceListRules } from "../../../__fixtures__/price-list-rules" +import { createRuleTypes } from "../../../__fixtures__/rule-type" + +jest.setTimeout(30000) + +describe("PriceListRule Service", () => { + let service: IPricingModuleService + let testManager: SqlEntityManager + let repositoryManager: SqlEntityManager + + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + repositoryManager = await MikroOrmWrapper.forkManager() + + service = await initialize({ + database: { + clientUrl: DB_URL, + schema: process.env.MEDUSA_PRICING_DB_SCHEMA, + }, + }) + + testManager = await MikroOrmWrapper.forkManager() + await createRuleTypes(testManager) + await createPriceLists(testManager) + await createPriceListRules(testManager) + }) + + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) + + describe("list", () => { + it("should list priceListRules", async () => { + const priceListRuleResult = await service.listPriceListRules() + + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) + }) + + it("should list priceListRules by pricelist id", async () => { + const priceListRuleResult = await service.listPriceListRules({ + id: ["price-list-rule-1"], + }) + + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + ]) + }) + }) + + describe("listAndCount", () => { + it("should return pricelistrules and count", async () => { + const [priceListRuleResult, count] = + await service.listAndCountPriceListRules() + + expect(count).toEqual(2) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) + }) + + it("should return pricelistrules and count when filtered", async () => { + const [priceListRuleResult, count] = + await service.listAndCountPriceListRules({ + id: ["price-list-rule-1"], + }) + + expect(count).toEqual(1) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-1", + }), + ]) + }) + + it("should return pricelistrules and count when using skip and take", async () => { + const [priceListRuleResult, count] = + await service.listAndCountPriceListRules({}, { skip: 1, take: 1 }) + + expect(count).toEqual(2) + expect(priceListRuleResult).toEqual([ + expect.objectContaining({ + id: "price-list-rule-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [priceListRuleResult, count] = + await service.listAndCountPriceListRules( + {}, + { + take: 1, + select: ["id"], + } + ) + + const serialized = JSON.parse(JSON.stringify(priceListRuleResult)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + { + id: "price-list-rule-1", + }, + ]) + }) + }) + + describe("retrieve", () => { + const id = "price-list-rule-1" + + it("should return priceList for the given id", async () => { + const priceListRuleResult = await service.retrievePriceListRule(id) + + expect(priceListRuleResult).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when priceListRule with id does not exist", async () => { + let error + + try { + await service.retrievePriceListRule("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "PriceListRule with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.retrievePriceListRule(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual('"priceListRuleId" must be defined') + }) + }) + + describe("delete", () => { + const id = "price-list-rule-1" + + it("should delete the pricelists given an id successfully", async () => { + await service.deletePriceListRules([id]) + + const priceListResult = await service.listPriceListRules({ + id: [id], + }) + + expect(priceListResult).toHaveLength(0) + }) + }) + + describe("update", () => { + const id = "price-list-rule-2" + + it("should update the value of the priceListRule successfully", async () => { + await service.updatePriceListRules([ + { + id, + price_list_id: "price-list-2", + rule_type_id: "rule-type-2", + }, + ]) + + const priceList = await service.retrievePriceListRule(id, { + relations: ["price_list", "rule_type"], + }) + + expect(priceList.price_list.id).toEqual("price-list-2") + expect(priceList.rule_type.id).toEqual("rule-type-2") + }) + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.updatePriceListRules([ + { + id: "does-not-exist", + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'PriceListRule with id(s) "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should create a priceListRule successfully", async () => { + const [created] = await service.createPriceListRules([ + { + price_list_id: "price-list-2", + rule_type_id: "rule-type-2", + }, + ]) + + const [priceListRule] = await service.listPriceListRules( + { + id: [created.id], + }, + { + relations: ["price_list", "rule_type"], + } + ) + + expect(priceListRule.price_list.id).toEqual("price-list-2") + expect(priceListRule.rule_type.id).toEqual("rule-type-2") + }) + }) + + describe("setPriceListRules", () => { + it("should add a priceListRule to a priceList", async () => { + await createRuleTypes(testManager, [ + { + id: "rule-type-3", + name: "test", + rule_attribute: "sales_channel", + }, + ]) + + await service.setPriceListRules({ + priceListId: "price-list-1", + rules: { + sales_channel: "sc-1", + }, + }) + + const [priceList] = await service.listPriceLists( + { + id: ["price-list-1"], + }, + { + relations: [ + "price_list_rules", + "price_list_rules.price_list_rule_values", + ], + } + ) + + expect(priceList.price_list_rules).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + rule_type: "rule-type-3", + price_list_rule_values: [ + expect.objectContaining({ value: "sc-1" }), + ], + }), + ]) + ) + }) + + it("should multiple priceListRules to a priceList", async () => { + await createRuleTypes(testManager, [ + { + id: "rule-type-3", + name: "test", + rule_attribute: "sales_channel", + }, + ]) + + await service.setPriceListRules({ + priceListId: "price-list-1", + rules: { + sales_channel: ["sc-1", "sc-2"], + }, + }) + + const [priceList] = await service.listPriceLists( + { + id: ["price-list-1"], + }, + { + relations: [ + "price_list_rules", + "price_list_rules.price_list_rule_values", + ], + } + ) + + expect(priceList.price_list_rules).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + rule_type: "rule-type-3", + price_list_rule_values: expect.arrayContaining([ + expect.objectContaining({ value: "sc-1" }), + expect.objectContaining({ value: "sc-2" }), + ]), + }), + ]) + ) + }) + }) + + describe("removePriceListRules", () => { + it("should remove a priceListRule from a priceList", async () => { + await service.removePriceListRules({ + priceListId: "price-list-1", + rules: ["currency_code"], + }) + + const [priceList] = await service.listPriceLists( + { + id: ["price-list-1"], + }, + { + relations: ["price_list_rules"], + } + ) + + expect(priceList.price_list_rules).toEqual([ + expect.objectContaining({ rule_type: "rule-type-2" }), + ]) + }) + }) +}) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list.spec.ts new file mode 100644 index 0000000000..3096a2107c --- /dev/null +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-list.spec.ts @@ -0,0 +1,397 @@ +import { IPricingModuleService } from "@medusajs/types" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { initialize } from "../../../../src" + +import { createCurrencies } from "../../../__fixtures__/currency" +import { createPriceLists } from "../../../__fixtures__/price-list" +import { createPriceSets } from "../../../__fixtures__/price-set" +import { DB_URL, MikroOrmWrapper } from "../../../utils" + +jest.setTimeout(30000) + +describe("PriceList Service", () => { + let service: IPricingModuleService + let testManager: SqlEntityManager + + beforeEach(async () => { + await MikroOrmWrapper.setupDatabase() + await MikroOrmWrapper.forkManager() + + service = await initialize({ + database: { + clientUrl: DB_URL, + schema: process.env.MEDUSA_PRICING_DB_SCHEMA, + }, + }) + + testManager = await MikroOrmWrapper.forkManager() + await createCurrencies(testManager) + await createPriceSets(testManager) + await createPriceLists(testManager) + }) + + afterEach(async () => { + await MikroOrmWrapper.clearDatabase() + }) + + describe("list", () => { + it("list priceLists", async () => { + const priceListResult = await service.listPriceLists() + + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-1", + }), + expect.objectContaining({ + id: "price-list-2", + }), + ]) + }) + + it("list pricelists by id", async () => { + const priceListResult = await service.listPriceLists({ + id: ["price-list-1"], + }) + + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-1", + }), + ]) + }) + }) + + describe("listAndCount", () => { + it("should return pricelists and count", async () => { + const [priceListResult, count] = await service.listAndCountPriceLists() + + expect(count).toEqual(2) + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-1", + }), + expect.objectContaining({ + id: "price-list-2", + }), + ]) + }) + + it("should return pricelists and count when filtered", async () => { + const [priceListResult, count] = await service.listAndCountPriceLists({ + id: ["price-list-1"], + }) + + expect(count).toEqual(1) + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-1", + }), + ]) + }) + + it("should return pricelists and count when using skip and take", async () => { + const [priceListResult, count] = await service.listAndCountPriceLists( + {}, + { skip: 1, take: 1 } + ) + + expect(count).toEqual(2) + expect(priceListResult).toEqual([ + expect.objectContaining({ + id: "price-list-2", + }), + ]) + }) + + it("should return requested fields", async () => { + const [priceListResult, count] = await service.listAndCountPriceLists( + {}, + { + take: 1, + select: ["id"], + } + ) + + const serialized = JSON.parse(JSON.stringify(priceListResult)) + + expect(count).toEqual(2) + expect(serialized).toEqual([ + { + id: "price-list-1", + }, + ]) + }) + }) + + describe("retrieve", () => { + const id = "price-list-1" + + it("should return priceList for the given id", async () => { + const priceListResult = await service.retrievePriceList(id) + + expect(priceListResult).toEqual( + expect.objectContaining({ + id, + }) + ) + }) + + it("should throw an error when priceList with id does not exist", async () => { + let error + + try { + await service.retrievePriceList("does-not-exist") + } catch (e) { + error = e + } + + expect(error.message).toEqual( + "PriceList with id: does-not-exist was not found" + ) + }) + + it("should throw an error when a id is not provided", async () => { + let error + + try { + await service.retrievePriceList(undefined as unknown as string) + } catch (e) { + error = e + } + + expect(error.message).toEqual('"priceListId" must be defined') + }) + }) + + describe("delete", () => { + const id = "price-list-1" + + it("should delete the pricelists given an id successfully", async () => { + await service.deletePriceLists([id]) + + const priceListResult = await service.listPriceLists({ + id: [id], + }) + + expect(priceListResult).toHaveLength(0) + }) + }) + + describe("update", () => { + const id = "price-list-2" + + it("should update the starts_at date of the priceList successfully", 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", + }, + ], + }, + ]) + + const updateDate = new Date() + await service.updatePriceLists([ + { + id: created.id, + starts_at: updateDate.toISOString(), + rules: { + new_rule: ["new-rule-value"], + }, + }, + ]) + + const [priceList] = await service.listPriceLists( + { + id: [created.id], + }, + { + relations: [ + "price_set_money_amounts.money_amount", + "price_set_money_amounts.price_set", + "price_list_rules.price_list_rule_values", + "price_list_rules.rule_type", + ], + select: [ + "id", + "starts_at", + "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), + starts_at: updateDate, + price_set_money_amounts: expect.arrayContaining([ + expect.objectContaining({ + price_list: expect.objectContaining({ + id: expect.any(String), + }), + money_amount: expect.objectContaining({ + amount: 400, + currency_code: "EUR", + }), + }), + ]), + price_list_rules: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(String), + rule_type: expect.objectContaining({ + id: expect.any(String), + rule_attribute: "new_rule", + }), + price_list_rule_values: [ + expect.objectContaining({ + id: expect.any(String), + value: "new-rule-value", + }), + ], + }), + ]), + }) + ) + }) + + it("should throw an error when a id does not exist", async () => { + let error + + try { + await service.updatePriceLists([ + { + id: "does-not-exist", + number_rules: 2, + rules: {}, + }, + ]) + } catch (e) { + error = e + } + + expect(error.message).toEqual( + 'PriceList with id "does-not-exist" not found' + ) + }) + }) + + describe("create", () => { + it("should create a priceList successfully", 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", + }, + ], + }, + ]) + + const [priceList] = await service.listPriceLists( + { + id: [created.id], + }, + { + relations: [ + "price_set_money_amounts.money_amount", + "price_set_money_amounts.price_set", + "price_list_rules.price_list_rule_values", + "price_list_rules.rule_type", + ], + select: [ + "id", + "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({ + price_list: expect.objectContaining({ + id: expect.any(String), + }), + money_amount: expect.objectContaining({ + amount: 400, + 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", + }), + ]), + }), + ]), + }) + ) + }) + }) +}) diff --git a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set.spec.ts b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set.spec.ts index b13f703219..158f7af075 100644 --- a/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set.spec.ts +++ b/packages/pricing/integration-tests/__tests__/services/pricing-module/price-set.spec.ts @@ -106,7 +106,9 @@ describe("PricingModule Service - PriceSet", () => { expect(serialized).toEqual([ { id: "price-set-1", - money_amounts: [{ id: "money-amount-USD", amount: "500" }], + money_amounts: [ + expect.objectContaining({ id: "money-amount-USD", amount: 500 }), + ], }, ]) }) @@ -160,7 +162,7 @@ describe("PricingModule Service - PriceSet", () => { expect(serialized).toEqual([ { id: "price-set-1", - money_amounts: [{ id: "money-amount-USD" }], + money_amounts: [expect.objectContaining({ id: "money-amount-USD" })], }, ]) }) @@ -375,7 +377,7 @@ describe("PricingModule Service - PriceSet", () => { ], money_amounts: [ expect.objectContaining({ - amount: "100", + amount: 100, currency_code: "USD", }), ], @@ -413,11 +415,11 @@ describe("PricingModule Service - PriceSet", () => { ], money_amounts: expect.arrayContaining([ expect.objectContaining({ - amount: "100", + amount: 100, currency_code: "USD", }), expect.objectContaining({ - amount: "150", + amount: 150, currency_code: "USD", }), ]), @@ -450,7 +452,7 @@ describe("PricingModule Service - PriceSet", () => { ], money_amounts: [ expect.objectContaining({ - amount: "100", + amount: 100, currency_code: "USD", }), ], @@ -544,7 +546,7 @@ describe("PricingModule Service - PriceSet", () => { ], money_amounts: [ expect.objectContaining({ - amount: "500", + amount: 500, currency_code: "EUR", }), ], @@ -608,7 +610,7 @@ describe("PricingModule Service - PriceSet", () => { id: "price-set-1", money_amounts: expect.arrayContaining([ expect.objectContaining({ - amount: "100", + amount: 100, currency_code: "USD", }), ]), @@ -650,7 +652,7 @@ describe("PricingModule Service - PriceSet", () => { id: "price-set-1", money_amounts: expect.arrayContaining([ expect.objectContaining({ - amount: "100", + amount: 100, currency_code: "USD", }), ]), @@ -659,7 +661,7 @@ describe("PricingModule Service - PriceSet", () => { id: "price-set-2", money_amounts: expect.arrayContaining([ expect.objectContaining({ - amount: "150", + amount: 150, currency_code: "EUR", }), ]), diff --git a/packages/pricing/src/loaders/container.ts b/packages/pricing/src/loaders/container.ts index c8b304eec5..37ef7d0616 100644 --- a/packages/pricing/src/loaders/container.ts +++ b/packages/pricing/src/loaders/container.ts @@ -32,6 +32,13 @@ export default async ({ priceSetMoneyAmountService: asClass( defaultServices.PriceSetMoneyAmountService ).singleton(), + priceListService: asClass(defaultServices.PriceListService).singleton(), + priceListRuleService: asClass( + defaultServices.PriceListRuleService + ).singleton(), + priceListRuleValueService: asClass( + defaultServices.PriceListRuleValueService + ).singleton(), }) if (customRepositories) { @@ -75,5 +82,14 @@ function loadDefaultRepositories({ container }) { priceSetMoneyAmountRepository: asClass( defaultRepositories.PriceSetMoneyAmountRepository ).singleton(), + priceListRepository: asClass( + defaultRepositories.PriceListRepository + ).singleton(), + priceListRuleRepository: asClass( + defaultRepositories.PriceListRuleRepository + ).singleton(), + priceListRuleValueRepository: asClass( + defaultRepositories.PriceListRuleValueRepository + ).singleton(), }) } diff --git a/packages/pricing/src/migrations/.snapshot-medusa-pricing.json b/packages/pricing/src/migrations/.snapshot-medusa-pricing.json index 1d5cad716c..6b7a26da5b 100644 --- a/packages/pricing/src/migrations/.snapshot-medusa-pricing.json +++ b/packages/pricing/src/migrations/.snapshot-medusa-pricing.json @@ -146,6 +146,151 @@ } } }, + { + "columns": { + "id": { + "name": "id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "title": { + "name": "title", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "description": { + "name": "description", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "status": { + "name": "status", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'draft'", + "enumItems": [ + "active", + "draft" + ], + "mappedType": "enum" + }, + "type": { + "name": "type", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'sale'", + "enumItems": [ + "sale", + "override" + ], + "mappedType": "enum" + }, + "starts_at": { + "name": "starts_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "ends_at": { + "name": "ends_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + }, + "number_rules": { + "name": "number_rules", + "type": "integer", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "0", + "mappedType": "integer" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" + } + }, + "name": "price_list", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "deleted_at" + ], + "composite": false, + "keyName": "IDX_price_list_deleted_at", + "primary": false, + "unique": false + }, + { + "keyName": "price_list_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": {} + }, { "columns": { "id": { @@ -221,6 +366,15 @@ "nullable": false, "default": "0", "mappedType": "integer" + }, + "price_list_id": { + "name": "price_list_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "text" } }, "name": "price_set_money_amount", @@ -244,6 +398,15 @@ "primary": false, "unique": false }, + { + "columnNames": [ + "price_list_id" + ], + "composite": false, + "keyName": "IDX_price_rule_price_list_id", + "primary": false, + "unique": false + }, { "keyName": "price_set_money_amount_pkey", "columnNames": [ @@ -281,6 +444,19 @@ "referencedTableName": "public.money_amount", "deleteRule": "cascade", "updateRule": "cascade" + }, + "price_set_money_amount_price_list_id_foreign": { + "constraintName": "price_set_money_amount_price_list_id_foreign", + "columnNames": [ + "price_list_id" + ], + "localTableName": "public.price_set_money_amount", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.price_list", + "deleteRule": "cascade", + "updateRule": "cascade" } } }, @@ -604,15 +780,6 @@ "primary": false, "nullable": false, "mappedType": "text" - }, - "price_list_id": { - "name": "price_list_id", - "type": "text", - "unsigned": false, - "autoincrement": false, - "primary": false, - "nullable": false, - "mappedType": "text" } }, "name": "price_rule", @@ -696,6 +863,184 @@ "updateRule": "cascade" } } + }, + { + "columns": { + "id": { + "name": "id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "rule_type_id": { + "name": "rule_type_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "priority": { + "name": "priority", + "type": "integer", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "0", + "mappedType": "integer" + }, + "price_list_id": { + "name": "price_list_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + } + }, + "name": "price_list_rule", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "rule_type_id" + ], + "composite": false, + "keyName": "IDX_price_list_rule_rule_type_id", + "primary": false, + "unique": false + }, + { + "columnNames": [ + "price_list_id" + ], + "composite": false, + "keyName": "IDX_price_list_rule_price_list_id", + "primary": false, + "unique": false + }, + { + "keyName": "IDX_price_list_rule_rule_type_id_price_list_id_unique", + "columnNames": [ + "price_list_id", + "rule_type_id" + ], + "composite": true, + "primary": false, + "unique": true + }, + { + "keyName": "price_list_rule_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": { + "price_list_rule_rule_type_id_foreign": { + "constraintName": "price_list_rule_rule_type_id_foreign", + "columnNames": [ + "rule_type_id" + ], + "localTableName": "public.price_list_rule", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.rule_type", + "updateRule": "cascade" + }, + "price_list_rule_price_list_id_foreign": { + "constraintName": "price_list_rule_price_list_id_foreign", + "columnNames": [ + "price_list_id" + ], + "localTableName": "public.price_list_rule", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.price_list", + "updateRule": "cascade" + } + } + }, + { + "columns": { + "id": { + "name": "id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "price_list_rule_id": { + "name": "price_list_rule_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, + "value": { + "name": "value", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + } + }, + "name": "price_list_rule_value", + "schema": "public", + "indexes": [ + { + "columnNames": [ + "price_list_rule_id" + ], + "composite": false, + "keyName": "IDX_price_list_rule_price_list_rule_value_id", + "primary": false, + "unique": false + }, + { + "keyName": "price_list_rule_value_pkey", + "columnNames": [ + "id" + ], + "composite": false, + "primary": true, + "unique": true + } + ], + "checks": [], + "foreignKeys": { + "price_list_rule_value_price_list_rule_id_foreign": { + "constraintName": "price_list_rule_value_price_list_rule_id_foreign", + "columnNames": [ + "price_list_rule_id" + ], + "localTableName": "public.price_list_rule_value", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.price_list_rule", + "deleteRule": "cascade", + "updateRule": "cascade" + } + } } ] } diff --git a/packages/pricing/src/migrations/Migration20230929122253.ts b/packages/pricing/src/migrations/Migration20230929122253.ts index f4d0b843ad..81ecf7e577 100644 --- a/packages/pricing/src/migrations/Migration20230929122253.ts +++ b/packages/pricing/src/migrations/Migration20230929122253.ts @@ -101,5 +101,39 @@ export class Migration20230929122253 extends Migration { this.addSql( 'alter table "price_rule" add constraint "price_rule_price_set_money_amount_id_foreign" foreign key ("price_set_money_amount_id") references "price_set_money_amount" ("id") on update cascade on delete cascade;' ) + + this.addSql( + 'create table if not exists "price_list" ("id" text not null, "status" text check ("status" in (\'active\', \'draft\')) not null default \'draft\', "starts_at" timestamptz null, "ends_at" timestamptz null, "number_rules" integer not null default 0, constraint "price_list_pkey" primary key ("id"));' + ) + + this.addSql( + 'create table "price_list_rule" ("id" text not null, "rule_type_id" text not null, "value" text not null, "price_list_id" text not null, constraint "price_list_rule_pkey" primary key ("id"));' + ) + + this.addSql( + 'create index "IDX_price_list_rule_rule_type_id" on "price_list_rule" ("rule_type_id");' + ) + this.addSql( + 'create index "IDX_price_list_rule_price_list_id" on "price_list_rule" ("price_list_id");' + ) + + this.addSql( + 'alter table "price_list_rule" add constraint "price_list_rule_rule_type_id_foreign" foreign key ("rule_type_id") references "rule_type" ("id") on update cascade;' + ) + this.addSql( + 'alter table "price_list_rule" add constraint "price_list_rule_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade;' + ) + + this.addSql( + 'alter table "price_set_money_amount" add column "price_list_id" text null;' + ) + this.addSql( + 'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete set null;' + ) + this.addSql( + 'create index "IDX_price_rule_price_list_id" on "price_set_money_amount" ("price_list_id");' + ) + + this.addSql('alter table "price_rule" drop column "price_list_id";') } } diff --git a/packages/pricing/src/migrations/Migration20231101232834.ts b/packages/pricing/src/migrations/Migration20231101232834.ts new file mode 100644 index 0000000000..aa4e269711 --- /dev/null +++ b/packages/pricing/src/migrations/Migration20231101232834.ts @@ -0,0 +1,86 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20231101232834 extends Migration { + async up(): Promise { + this.addSql( + 'create table "price_list_rule_value" ("id" text not null, "value" text not null, "price_list_rule_id" text not null, constraint "price_list_rule_value_pkey" primary key ("id"));' + ) + this.addSql( + 'create index "IDX_price_list_rule_price_list_rule_value_id" on "price_list_rule_value" ("price_list_rule_id");' + ) + + this.addSql( + 'alter table "price_list_rule_value" add constraint "price_list_rule_value_price_list_rule_id_foreign" foreign key ("price_list_rule_id") references "price_list_rule" ("id") on update cascade on delete cascade;' + ) + + this.addSql( + `ALTER TABLE price_list ADD COLUMN IF NOT EXISTS number_rules integer not null default 0` + ) + + this.addSql( + 'alter table "price_list" add column if not exists "title" text not null, add column if not exists "description" text not null, add column if not exists "type" text check ("type" in (\'sale\', \'override\')) not null default \'sale\';' + ) + + this.addSql( + 'alter table "price_set_money_amount" drop constraint "price_set_money_amount_price_list_id_foreign";' + ) + + this.addSql( + 'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete cascade;' + ) + + this.addSql( + 'alter table "price_list" add column if not exists "title" text not null, add column if not exists "description" text not null, add column if not exists "type" text check ("type" in (\'sale\', \'override\')) not null default \'sale\', add column if not exists "created_at" timestamptz not null default now(), add column if not exists "updated_at" timestamptz not null default now(), add column if not exists "deleted_at" timestamptz null;' + ) + + this.addSql( + 'create index if not exists "IDX_price_list_deleted_at" on "price_list" ("deleted_at");' + ) + + this.addSql( + 'alter table "price_list_rule" drop constraint if exists "IDX_price_list_rule_rule_type_id_price_list_id_unique"' + ) + + this.addSql( + 'alter table "price_list_rule" add constraint "IDX_price_list_rule_rule_type_id_price_list_id_unique" unique ("price_list_id", "rule_type_id");' + ) + } + + async down(): Promise { + this.addSql('drop table if exists "price_list_rule_value" cascade;') + + this.addSql(`ALTER TABLE price_list DROP COLUMN IF EXISTS number_rules`) + + this.addSql('alter table "price_list" drop column if exists "title";') + + this.addSql('alter table "price_list" drop column if exists "description";') + + this.addSql('alter table "price_list" drop column if exists "type";') + + this.addSql( + 'alter table "price_set_money_amount" drop constraint "price_set_money_amount_price_list_id_foreign";' + ) + + this.addSql( + 'alter table "price_set_money_amount" add constraint "price_set_money_amount_price_list_id_foreign" foreign key ("price_list_id") references "price_list" ("id") on update cascade on delete set null;' + ) + + this.addSql('drop index if exists "IDX_price_list_deleted_at";') + + this.addSql('alter table "price_list" drop column if exists "title";') + + this.addSql('alter table "price_list" drop column if exists "description";') + + this.addSql('alter table "price_list" drop column if exists "type";') + + this.addSql('alter table "price_list" drop column if exists "created_at";') + + this.addSql('alter table "price_list" drop column if exists "updated_at";') + + this.addSql('alter table "price_list" drop column if exists "deleted_at";') + + this.addSql( + 'alter table "price_list_rule" drop constraint if exists "IDX_price_list_rule_rule_type_id_price_list_id_unique";' + ) + } +} diff --git a/packages/pricing/src/models/index.ts b/packages/pricing/src/models/index.ts index 10f5df9460..327a6bff11 100644 --- a/packages/pricing/src/models/index.ts +++ b/packages/pricing/src/models/index.ts @@ -1,5 +1,8 @@ export { default as Currency } from "./currency" export { default as MoneyAmount } from "./money-amount" +export { default as PriceList } from "./price-list" +export { default as PriceListRule } from "./price-list-rule" +export { default as PriceListRuleValue } from "./price-list-rule-value" export { default as PriceRule } from "./price-rule" export { default as PriceSet } from "./price-set" export { default as PriceSetMoneyAmount } from "./price-set-money-amount" diff --git a/packages/pricing/src/models/money-amount.ts b/packages/pricing/src/models/money-amount.ts index 2f4b77758e..dc6c2b5a47 100644 --- a/packages/pricing/src/models/money-amount.ts +++ b/packages/pricing/src/models/money-amount.ts @@ -3,17 +3,27 @@ import { BeforeCreate, Collection, Entity, + Index, ManyToMany, ManyToOne, + OneToOne, + OptionalProps, PrimaryKey, Property, } from "@mikro-orm/core" import Currency from "./currency" +import { PriceSetMoneyAmount } from "./index" import PriceSet from "./price-set" @Entity() class MoneyAmount { + [OptionalProps]?: + | "created_at" + | "updated_at" + | "deleted_at" + | "price_set_money_amount" + @PrimaryKey({ columnType: "text" }) id!: string @@ -26,6 +36,12 @@ class MoneyAmount { }) price_sets = new Collection(this) + @OneToOne({ + entity: () => PriceSetMoneyAmount, + mappedBy: (psma) => psma.money_amount, + }) + price_set_money_amount: PriceSetMoneyAmount + @ManyToOne(() => Currency, { nullable: true, index: "IDX_money_amount_currency_code", @@ -33,7 +49,7 @@ class MoneyAmount { }) currency?: Currency - @Property({ columnType: "numeric", nullable: true }) + @Property({ columnType: "numeric", nullable: true, serializer: Number }) amount?: number @Property({ columnType: "numeric", nullable: true }) @@ -42,6 +58,25 @@ class MoneyAmount { @Property({ columnType: "numeric", nullable: true }) max_quantity?: number | null + @Property({ + onCreate: () => new Date(), + columnType: "timestamptz", + defaultRaw: "now()", + }) + created_at: Date + + @Property({ + onCreate: () => new Date(), + onUpdate: () => new Date(), + columnType: "timestamptz", + defaultRaw: "now()", + }) + updated_at: Date + + @Index({ name: "IDX_money_amount_deleted_at" }) + @Property({ columnType: "timestamptz", nullable: true }) + deleted_at: Date + @BeforeCreate() onCreate() { this.id = generateEntityId(this.id, "ma") diff --git a/packages/pricing/src/models/price-list-rule-value.ts b/packages/pricing/src/models/price-list-rule-value.ts new file mode 100644 index 0000000000..c2c9e5d9ac --- /dev/null +++ b/packages/pricing/src/models/price-list-rule-value.ts @@ -0,0 +1,31 @@ +import { + BeforeCreate, + Entity, + ManyToOne, + PrimaryKey, + Property, +} from "@mikro-orm/core" + +import PriceListRule from "./price-list-rule" +import { generateEntityId } from "@medusajs/utils" + +@Entity() +export default class PriceListRuleValue { + @PrimaryKey({ columnType: "text" }) + id!: string + + @ManyToOne(() => PriceListRule, { + onDelete: "cascade", + fieldName: 'price_list_rule_id', + index: "IDX_price_list_rule_price_list_rule_value_id", + }) + price_list_rule: PriceListRule + + @Property({ columnType: "text" }) + value: string + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "plrv") + } +} diff --git a/packages/pricing/src/models/price-list-rule.ts b/packages/pricing/src/models/price-list-rule.ts new file mode 100644 index 0000000000..edbdbde218 --- /dev/null +++ b/packages/pricing/src/models/price-list-rule.ts @@ -0,0 +1,57 @@ +import { + BeforeCreate, + Cascade, + Collection, + Entity, + ManyToOne, + OneToMany, + OptionalProps, + PrimaryKey, + Unique, +} from "@mikro-orm/core" + +import { generateEntityId } from "@medusajs/utils" +import PriceList from "./price-list" +import PriceListRuleValue from "./price-list-rule-value" +import RuleType from "./rule-type" + +type OptionalFields = "id" +type OptionalRelations = "rule_type" | "price_list" + +@Entity() +@Unique({ + name: "IDX_price_list_rule_rule_type_id_price_list_id_unique", + properties: ["price_list", "rule_type"], +}) +export default class PriceListRule { + [OptionalProps]: OptionalFields | OptionalRelations + + @PrimaryKey({ columnType: "text" }) + id!: string + + @ManyToOne({ + entity: () => RuleType, + fieldName: "rule_type_id", + name: "price_rule_rule_type_id_unique", + index: "IDX_price_list_rule_rule_type_id", + }) + rule_type: RuleType + + @OneToMany(() => PriceListRuleValue, (plrv) => plrv.price_list_rule, { + cascade: [Cascade.REMOVE], + }) + price_list_rule_values = new Collection(this) + + @ManyToOne({ + entity: () => PriceList, + fieldName: "price_list_id", + name: "price_rule_price_list_id", + index: "IDX_price_list_rule_price_list_id", + }) + price_list: PriceList + + @BeforeCreate() + beforeCreate() { + this.id = generateEntityId(this.id, "plrule") + } +} diff --git a/packages/pricing/src/models/price-list.ts b/packages/pricing/src/models/price-list.ts new file mode 100644 index 0000000000..fb8f0dfd90 --- /dev/null +++ b/packages/pricing/src/models/price-list.ts @@ -0,0 +1,110 @@ +import { generateEntityId } from "@medusajs/utils" +import { + BeforeCreate, + Cascade, + Collection, + Entity, + Enum, + Index, + ManyToMany, + OneToMany, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" + +import { PriceListStatus, PriceListType } from "@medusajs/utils" +import PriceListRule from "./price-list-rule" +import PriceSetMoneyAmount from "./price-set-money-amount" +import RuleType from "./rule-type" + +type OptionalFields = + | "status" + | "type" + | "number_rules" + | "starts_at" + | "ends_at" + | "created_at" + | "updated_at" + | "deleted_at" + +type OptionalRelations = + | "price_set_money_amounts" + | "rule_types" + | "price_list_rules" + +@Entity() +export default class PriceList { + [OptionalProps]: OptionalFields | OptionalRelations + + @PrimaryKey({ columnType: "text" }) + id!: string + + @Property({ columnType: "text" }) + title: string + + @Property({ columnType: "text" }) + description: string + + @Enum({ items: () => PriceListStatus, default: PriceListStatus.DRAFT }) + status?: PriceListStatus + + @Enum({ items: () => PriceListType, default: PriceListType.SALE }) + type?: PriceListType + + @Property({ + columnType: "timestamptz", + nullable: true, + }) + starts_at?: Date | null + + @Property({ + columnType: "timestamptz", + nullable: true, + }) + ends_at?: Date | null + + @OneToMany(() => PriceSetMoneyAmount, (psma) => psma.price_list, { + cascade: [Cascade.REMOVE], + }) + price_set_money_amounts = new Collection(this) + + @OneToMany(() => PriceListRule, (pr) => pr.price_list, { + cascade: [Cascade.REMOVE], + }) + price_list_rules = new Collection(this) + + @ManyToMany({ + entity: () => RuleType, + pivotEntity: () => PriceListRule, + cascade: [Cascade.REMOVE], + }) + rule_types = new Collection(this) + + @Property({ columnType: "integer", default: 0 }) + number_rules?: number + + @Property({ + onCreate: () => new Date(), + columnType: "timestamptz", + defaultRaw: "now()", + }) + created_at?: Date + + @Property({ + onCreate: () => new Date(), + onUpdate: () => new Date(), + columnType: "timestamptz", + defaultRaw: "now()", + }) + updated_at?: Date + + @Index({ name: "IDX_price_list_deleted_at" }) + @Property({ columnType: "timestamptz", nullable: true }) + deleted_at?: Date + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "plist") + } +} diff --git a/packages/pricing/src/models/price-rule.ts b/packages/pricing/src/models/price-rule.ts index 06c82c7027..aab8908a65 100644 --- a/packages/pricing/src/models/price-rule.ts +++ b/packages/pricing/src/models/price-rule.ts @@ -57,11 +57,6 @@ export default class PriceRule { }) price_set_money_amount: PriceSetMoneyAmount - @Property({ columnType: "text" }) - price_list_id!: string - - // TODO: Add price list - @BeforeCreate() beforeCreate() { this.id = generateEntityId(this.id, "prule") diff --git a/packages/pricing/src/models/price-set-money-amount.ts b/packages/pricing/src/models/price-set-money-amount.ts index b9cfc0926e..729f4db96e 100644 --- a/packages/pricing/src/models/price-set-money-amount.ts +++ b/packages/pricing/src/models/price-set-money-amount.ts @@ -5,12 +5,14 @@ import { Entity, ManyToOne, OneToMany, + OneToOne, PrimaryKey, PrimaryKeyType, Property, } from "@mikro-orm/core" import MoneyAmount from "./money-amount" +import PriceList from "./price-list" import PriceRule from "./price-rule" import PriceSet from "./price-set" import PriceSetMoneyAmountRules from "./price-set-money-amount-rules" @@ -29,7 +31,7 @@ export default class PriceSetMoneyAmount { }) price_set?: PriceSet - @ManyToOne(() => MoneyAmount, { + @OneToOne(() => MoneyAmount, { onDelete: "cascade", index: "IDX_price_set_money_amount_money_amount_id", }) @@ -50,6 +52,13 @@ export default class PriceSetMoneyAmount { }) price_set_money_amount_rules = new Collection(this) + @ManyToOne(() => PriceList, { + index: "IDX_price_rule_price_list_id", + onDelete: "cascade", + nullable: true, + }) + price_list?: PriceList + @BeforeCreate() onCreate() { this.id = generateEntityId(this.id, "psma") diff --git a/packages/pricing/src/models/price-set.ts b/packages/pricing/src/models/price-set.ts index 20c62aeeb7..3b4d7a50ea 100644 --- a/packages/pricing/src/models/price-set.ts +++ b/packages/pricing/src/models/price-set.ts @@ -18,7 +18,7 @@ import RuleType from "./rule-type" @Entity() export default class PriceSet { - [OptionalProps]?: "price_set_money_amounts" | "rule_types" + [OptionalProps]?: "price_set_money_amounts" | "rule_types" | "money_amounts" @PrimaryKey({ columnType: "text" }) id!: string diff --git a/packages/pricing/src/repositories/index.ts b/packages/pricing/src/repositories/index.ts index d0ca202ac1..0cf8d09aae 100644 --- a/packages/pricing/src/repositories/index.ts +++ b/packages/pricing/src/repositories/index.ts @@ -1,6 +1,9 @@ export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" export { CurrencyRepository } from "./currency" export { MoneyAmountRepository } from "./money-amount" +export { PriceListRepository } from "./price-list" +export { PriceListRuleRepository } from "./price-list-rule" +export { PriceListRuleValueRepository } from "./price-list-rule-value" export { PriceRuleRepository } from "./price-rule" export { PriceSetRepository } from "./price-set" export { PriceSetMoneyAmountRepository } from "./price-set-money-amount" diff --git a/packages/pricing/src/repositories/price-list-rule-value.ts b/packages/pricing/src/repositories/price-list-rule-value.ts new file mode 100644 index 0000000000..1c9d14158f --- /dev/null +++ b/packages/pricing/src/repositories/price-list-rule-value.ts @@ -0,0 +1,143 @@ +import { Context, DAL } from "@medusajs/types" +import { DALUtils, MedusaError, arrayDifference } from "@medusajs/utils" +import { + LoadStrategy, + FilterQuery as MikroFilterQuery, + FindOptions as MikroOptions, +} from "@mikro-orm/core" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { PriceListRuleValue } from "@models" +import { + CreatePriceListRuleValueDTO, + UpdatePriceListRuleValueDTO, +} from "../types" + +export class PriceListRuleValueRepository extends DALUtils.MikroOrmBaseRepository { + protected readonly manager_: SqlEntityManager + + constructor({ manager }: { manager: SqlEntityManager }) { + // @ts-ignore + // eslint-disable-next-line prefer-rest-params + super(...arguments) + this.manager_ = manager + } + + async find( + findOptions: DAL.FindOptions = { where: {} }, + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const findOptions_ = { ...findOptions } + findOptions_.options ??= {} + + Object.assign(findOptions_.options, { + strategy: LoadStrategy.SELECT_IN, + }) + + return await manager.find( + PriceListRuleValue, + findOptions_.where as MikroFilterQuery, + findOptions_.options as MikroOptions + ) + } + + async findAndCount( + findOptions: DAL.FindOptions = { where: {} }, + context: Context = {} + ): Promise<[PriceListRuleValue[], number]> { + const manager = this.getActiveManager(context) + + const findOptions_ = { ...findOptions } + findOptions_.options ??= {} + + Object.assign(findOptions_.options, { + strategy: LoadStrategy.SELECT_IN, + }) + + return await manager.findAndCount( + PriceListRuleValue, + findOptions_.where as MikroFilterQuery, + findOptions_.options as MikroOptions + ) + } + + async delete(ids: string[], context: Context = {}): Promise { + const manager = this.getActiveManager(context) + + await manager.nativeDelete(PriceListRuleValue, { id: { $in: ids } }, {}) + } + + async create( + data: CreatePriceListRuleValueDTO[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const priceListRuleValues = data.map((priceRuleValueData) => { + const { price_list_rule_id: priceListRuleId, ...priceRuleValue } = + priceRuleValueData + + if (priceListRuleId) { + priceRuleValue.price_list_rule = priceListRuleId + } + + return manager.create(PriceListRuleValue, priceRuleValue) + }) + + manager.persist(priceListRuleValues) + + return priceListRuleValues + } + + async update( + data: UpdatePriceListRuleValueDTO[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + const priceValueIds = data.map( + (priceRuleValueData) => priceRuleValueData.id + ) + const existingPriceValues = await this.find( + { + where: { + id: { + $in: priceValueIds, + }, + }, + }, + context + ) + + const dataAndExistingIdDifference = arrayDifference( + data.map((d) => d.id), + existingPriceValues.map((pv) => pv.id) + ) + + if (dataAndExistingIdDifference.length) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `PriceListRuleValue with id(s) "${dataAndExistingIdDifference.join( + ", " + )}" not found` + ) + } + + const existingValuesMap = new Map( + existingPriceValues.map<[string, PriceListRuleValue]>((priceValue) => [ + priceValue.id, + priceValue, + ]) + ) + + const priceValues = data.map((priceRuleValueData) => { + const existingPriceValue = existingValuesMap.get(priceRuleValueData.id)! + + return manager.assign(existingPriceValue, priceRuleValueData) + }) + + manager.persist(priceValues) + + return priceValues + } +} diff --git a/packages/pricing/src/repositories/price-list-rule.ts b/packages/pricing/src/repositories/price-list-rule.ts new file mode 100644 index 0000000000..f21435c289 --- /dev/null +++ b/packages/pricing/src/repositories/price-list-rule.ts @@ -0,0 +1,161 @@ +import { + Context, + CreatePriceListRuleDTO, + DAL, + UpdatePriceListRuleDTO, +} from "@medusajs/types" +import { DALUtils, MedusaError, arrayDifference } from "@medusajs/utils" +import { + LoadStrategy, + FilterQuery as MikroFilterQuery, + FindOptions as MikroOptions, +} from "@mikro-orm/core" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { PriceListRule } from "@models" + +export class PriceListRuleRepository extends DALUtils.MikroOrmBaseRepository { + protected readonly manager_: SqlEntityManager + + constructor({ manager }: { manager: SqlEntityManager }) { + // @ts-ignore + // eslint-disable-next-line prefer-rest-params + super(...arguments) + this.manager_ = manager + } + + async find( + findOptions: DAL.FindOptions = { where: {} }, + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const findOptions_ = { ...findOptions } + findOptions_.options ??= {} + + Object.assign(findOptions_.options, { + strategy: LoadStrategy.SELECT_IN, + }) + + return await manager.find( + PriceListRule, + findOptions_.where as MikroFilterQuery, + findOptions_.options as MikroOptions + ) + } + + async findAndCount( + findOptions: DAL.FindOptions = { where: {} }, + context: Context = {} + ): Promise<[PriceListRule[], number]> { + const manager = this.getActiveManager(context) + + const findOptions_ = { ...findOptions } + findOptions_.options ??= {} + + Object.assign(findOptions_.options, { + strategy: LoadStrategy.SELECT_IN, + }) + + return await manager.findAndCount( + PriceListRule, + findOptions_.where as MikroFilterQuery, + findOptions_.options as MikroOptions + ) + } + + async delete(ids: string[], context: Context = {}): Promise { + const manager = this.getActiveManager(context) + await manager.nativeDelete(PriceListRule, { id: { $in: ids } }, {}) + } + + async create( + data: CreatePriceListRuleDTO[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const priceListRule = data.map((priceListRule) => { + const { + price_list_id: priceListId, + rule_type_id: ruleTypeId, + ...createData + } = priceListRule + + if (priceListId) { + createData.price_list = priceListId + } + + if (ruleTypeId) { + createData.rule_type = ruleTypeId + } + + return manager.create(PriceListRule, createData) + }) + + manager.persist(priceListRule) + + return priceListRule + } + + async update( + data: UpdatePriceListRuleDTO[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + const priceListIds = data.map((priceListRule) => priceListRule.id) + const existingPriceListRules = await this.find( + { + where: { + id: { + $in: priceListIds, + }, + }, + }, + context + ) + + const dataAndExistingIdDifference = arrayDifference( + data.map((d) => d.id), + existingPriceListRules.map((plr) => plr.id) + ) + + if (dataAndExistingIdDifference.length) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `PriceListRule with id(s) "${dataAndExistingIdDifference.join( + ", " + )}" not found` + ) + } + + const existingPriceListRuleMap = new Map( + existingPriceListRules.map<[string, PriceListRule]>((priceList) => [ + priceList.id, + priceList, + ]) + ) + + const priceListRules = data.map((priceListRule) => { + const { price_list_id, rule_type_id, ...priceListRuleData } = + priceListRule + + const existingPriceListRule = existingPriceListRuleMap.get( + priceListRule.id + )! + + if (price_list_id) { + priceListRuleData.price_list = price_list_id + } + + if (rule_type_id) { + priceListRuleData.rule_type = rule_type_id + } + + return manager.assign(existingPriceListRule, priceListRuleData) + }) + + manager.persist(priceListRules) + + return priceListRules + } +} diff --git a/packages/pricing/src/repositories/price-list.ts b/packages/pricing/src/repositories/price-list.ts new file mode 100644 index 0000000000..b88d85ba20 --- /dev/null +++ b/packages/pricing/src/repositories/price-list.ts @@ -0,0 +1,133 @@ +import { Context, DAL, UpdatePriceListDTO } from "@medusajs/types" +import { DALUtils, MedusaError } from "@medusajs/utils" +import { + LoadStrategy, + FilterQuery as MikroFilterQuery, + FindOptions as MikroOptions, +} from "@mikro-orm/core" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import { PriceList } from "@models" +import { CreatePriceListDTO } from "../types" + +export class PriceListRepository extends DALUtils.MikroOrmBaseRepository { + protected readonly manager_: SqlEntityManager + + constructor({ manager }: { manager: SqlEntityManager }) { + // @ts-ignore + // eslint-disable-next-line prefer-rest-params + super(...arguments) + this.manager_ = manager + } + + async find( + findOptions: DAL.FindOptions = { where: {} }, + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const findOptions_ = { ...findOptions } + findOptions_.options ??= {} + + Object.assign(findOptions_.options, { + strategy: LoadStrategy.SELECT_IN, + }) + + return await manager.find( + PriceList, + findOptions_.where as MikroFilterQuery, + findOptions_.options as MikroOptions + ) + } + + async findAndCount( + findOptions: DAL.FindOptions = { where: {} }, + context: Context = {} + ): Promise<[PriceList[], number]> { + const manager = this.getActiveManager(context) + + const findOptions_ = { ...findOptions } + findOptions_.options ??= {} + + Object.assign(findOptions_.options, { + strategy: LoadStrategy.SELECT_IN, + }) + + return await manager.findAndCount( + PriceList, + findOptions_.where as MikroFilterQuery, + findOptions_.options as MikroOptions + ) + } + + async delete(ids: string[], context: Context = {}): Promise { + const manager = this.getActiveManager(context) + await manager.nativeDelete(PriceList, { id: { $in: ids } }, {}) + } + + async create( + data: CreatePriceListDTO[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const priceLists = data.map((priceListData) => { + return manager.create(PriceList, priceListData) + }) + + manager.persist(priceLists) + + return priceLists + } + + async update( + data: Omit[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + const priceListIds = data.map((priceListData) => priceListData.id) + const existingPriceLists = await this.find( + { + where: { + id: { + $in: priceListIds, + }, + }, + }, + context + ) + + const existingPriceListMap = new Map( + existingPriceLists.map<[string, PriceList]>((priceList) => [ + priceList.id, + priceList, + ]) + ) + + const priceLists = data.map((priceListData) => { + const existingPriceList = existingPriceListMap.get(priceListData.id) + + if (!existingPriceList) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `PriceList with id "${priceListData.id}" not found` + ) + } + + if (!!priceListData.starts_at) { + priceListData.starts_at = new Date( + priceListData.starts_at + ).toISOString() + } + + if (!!priceListData.ends_at) { + priceListData.ends_at = new Date(priceListData.ends_at).toISOString() + } + + return manager.assign(existingPriceList, priceListData) + }) + + manager.persist(priceLists) + + return priceLists + } +} diff --git a/packages/pricing/src/repositories/price-set-money-amount.ts b/packages/pricing/src/repositories/price-set-money-amount.ts index 06e4e2d1ad..f8fb35a247 100644 --- a/packages/pricing/src/repositories/price-set-money-amount.ts +++ b/packages/pricing/src/repositories/price-set-money-amount.ts @@ -74,13 +74,16 @@ export class PriceSetMoneyAmountRepository extends DALUtils.MikroOrmBaseReposito ): Promise { const manager = this.getActiveManager(context) - const currencies = data.map((currencyData) => { - return manager.create(PriceSetMoneyAmount, currencyData as PriceSetMoneyAmount) + const psma = data.map((psmaData) => { + return manager.create( + PriceSetMoneyAmount, + psmaData as unknown as PriceSetMoneyAmount + ) }) - manager.persist(currencies) + manager.persist(psma) - return currencies + return psma } async update( @@ -101,23 +104,22 @@ export class PriceSetMoneyAmountRepository extends DALUtils.MikroOrmBaseReposito ) const existingPSMAMap = new Map( - existingPriceSetMoneyAmounts.map<[string, PriceSetMoneyAmount]>((psma) => [ - psma.id, - psma, - ]) + existingPriceSetMoneyAmounts.map<[string, PriceSetMoneyAmount]>( + (psma) => [psma.id, psma] + ) ) - const priceSetMoneyAmounts = data.map((currencyData) => { - const existingPSMA = existingPSMAMap.get(currencyData.id) + const priceSetMoneyAmounts = data.map((psmaData) => { + const existingPSMA = existingPSMAMap.get(psmaData.id) if (!existingPSMA) { throw new MedusaError( MedusaError.Types.NOT_FOUND, - `PriceSetMoneyAmount with id "${currencyData.id}" not found` + `PriceSetMoneyAmount with id "${psmaData.id}" not found` ) } - return manager.assign(existingPSMA, currencyData) + return manager.assign(existingPSMA, psmaData) }) manager.persist(priceSetMoneyAmounts) diff --git a/packages/pricing/src/repositories/price-set.ts b/packages/pricing/src/repositories/price-set.ts index fd4b9442b3..b8c4adcfb0 100644 --- a/packages/pricing/src/repositories/price-set.ts +++ b/packages/pricing/src/repositories/price-set.ts @@ -1,3 +1,4 @@ +import { UpdatePriceListDTO } from "@medusajs/types" import { Context, DAL, @@ -84,7 +85,7 @@ export class PriceSetRepository extends DALUtils.MikroOrmBaseRepository { } async update( - data: UpdatePriceSetDTO[], + data: Omit[], context: Context = {} ): Promise { const manager = this.getActiveManager(context) diff --git a/packages/pricing/src/repositories/pricing.ts b/packages/pricing/src/repositories/pricing.ts index b27c0be126..6aadce3886 100644 --- a/packages/pricing/src/repositories/pricing.ts +++ b/packages/pricing/src/repositories/pricing.ts @@ -57,43 +57,96 @@ export class PricingRepository return [] } + const date = new Date().toISOString() // Gets all the price set money amounts where rules match for each of the contexts // that the price set is configured for const psmaSubQueryKnex = knex({ psma: "price_set_money_amount", }) .select({ - id: "psma.id", - price_set_id: "psma.price_set_id", - money_amount_id: "psma.money_amount_id", - number_rules: "psma.number_rules", + id: "psma1.id", + price_set_id: "psma1.price_set_id", + money_amount_id: "psma1.money_amount_id", + number_rules: "psma1.number_rules", + price_list_id: "psma1.price_list_id", + pl_number_rules: "pl.number_rules", + pl_type: "pl.type", }) - .leftJoin("price_set_money_amount as psma1", "psma1.id", "psma.id") - .leftJoin("price_rule as pr", "pr.price_set_money_amount_id", "psma.id") + .leftJoin("price_set_money_amount as psma1", "psma1.id", "psma1.id") + .leftJoin("price_rule as pr", "pr.price_set_money_amount_id", "psma1.id") + .leftJoin("price_list as pl", "pl.id", "psma1.price_list_id") + .leftJoin("price_list_rule as plr", "plr.price_list_id", "pl.id") + .leftJoin( + "price_list_rule_value as plrv", + "plrv.price_list_rule_id", + "plr.id" + ) + .leftJoin("rule_type as plrt", "plrt.id", "plr.rule_type_id") .leftJoin("rule_type as rt", "rt.id", "pr.rule_type_id") + .orderBy("pl.number_rules", "desc") .orderBy("number_rules", "desc") - .orWhere("psma1.number_rules", "=", 0) - .groupBy("psma.id") - .having(knex.raw("count(DISTINCT rt.rule_attribute) = psma.number_rules")) + .orderBy([ + { column: "number_rules", order: "desc" }, + { column: "pl.number_rules", order: "desc" }, + ]) + .groupBy("psma1.id", "pl.id") + .having( + knex.raw( + "count(DISTINCT rt.rule_attribute) = psma1.number_rules AND psma1.price_list_id IS NULL" + ) + ) + .orHaving( + knex.raw( + "count(DISTINCT plrt.rule_attribute) = pl.number_rules AND psma1.price_list_id IS NOT NULL" + ) + ) + psmaSubQueryKnex.orWhere((q) => { + for (const [key, value] of Object.entries(context)) { + q.orWhere({ + "rt.rule_attribute": key, + "pr.value": value, + }) + } - for (const [key, value] of Object.entries(context)) { - psmaSubQueryKnex.orWhere({ - "rt.rule_attribute": key, - "pr.value": value, - }) - } + q.orWhere("psma1.number_rules", "=", 0) + q.whereNull("psma1.price_list_id") + }) + + psmaSubQueryKnex.orWhere((q) => { + q.whereNotNull("psma1.price_list_id") + .andWhere(function () { + this.whereNull("pl.starts_at").orWhere("pl.starts_at", "<=", date) + }) + .andWhere(function () { + this.whereNull("pl.ends_at").orWhere("pl.ends_at", ">=", date) + }) + .andWhere(function () { + for (const [key, value] of Object.entries(context)) { + this.orWhere({ + "plrt.rule_attribute": key, + }) + this.whereIn("plrv.value", [value]) + } + + this.orWhere("pl.number_rules", "=", 0) + }) + }) const priceSetQueryKnex = knex({ ps: "price_set", }) .select({ - id: "ps.id", + id: "ma.id", + price_set_id: "ps.id", amount: "ma.amount", min_quantity: "ma.min_quantity", max_quantity: "ma.max_quantity", currency_code: "ma.currency_code", default_priority: "rt.default_priority", number_rules: "psma.number_rules", + pl_number_rules: "psma.pl_number_rules", + price_list_type: "psma.pl_type", + price_list_id: "psma.price_list_id", }) .join(psmaSubQueryKnex.as("psma"), "psma.price_set_id", "ps.id") .join("money_amount as ma", "ma.id", "psma.money_amount_id") @@ -102,6 +155,7 @@ export class PricingRepository .whereIn("ps.id", pricingFilters.id) .andWhere("ma.currency_code", "=", currencyCode) .orderBy([ + { column: "price_list_id", order: "asc" }, { column: "number_rules", order: "desc" }, { column: "default_priority", order: "desc" }, ]) diff --git a/packages/pricing/src/services/index.ts b/packages/pricing/src/services/index.ts index e4b65c5288..7e67ff0a55 100644 --- a/packages/pricing/src/services/index.ts +++ b/packages/pricing/src/services/index.ts @@ -1,5 +1,8 @@ export { default as CurrencyService } from "./currency" export { default as MoneyAmountService } from "./money-amount" +export { default as PriceListService } from "./price-list" +export { default as PriceListRuleService } from "./price-list-rule" +export { default as PriceListRuleValueService } from "./price-list-rule-value" export { default as PriceRuleService } from "./price-rule" export { default as PriceSetService } from "./price-set" export { default as PriceSetMoneyAmountService } from "./price-set-money-amount" diff --git a/packages/pricing/src/services/price-list-rule-value.ts b/packages/pricing/src/services/price-list-rule-value.ts new file mode 100644 index 0000000000..5d273094b6 --- /dev/null +++ b/packages/pricing/src/services/price-list-rule-value.ts @@ -0,0 +1,117 @@ +import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types" +import { + InjectManager, + InjectTransactionManager, + MedusaContext, + ModulesSdkUtils, + doNotForceTransaction, + retrieveEntity, + shouldForceTransaction, +} from "@medusajs/utils" +import { PriceListRuleValue } from "@models" +import { PriceListRuleValueRepository } from "@repositories" + +import { CreatePriceListRuleValueDTO } from "../types" + +type InjectedDependencies = { + priceListRuleValueRepository: DAL.RepositoryService +} + +export default class PriceListRuleValueService< + TEntity extends PriceListRuleValue = PriceListRuleValue +> { + protected readonly priceListRuleValueRepository_: DAL.RepositoryService + + constructor({ priceListRuleValueRepository }: InjectedDependencies) { + this.priceListRuleValueRepository_ = priceListRuleValueRepository + } + + @InjectManager("priceListRuleValueRepository_") + async retrieve( + priceSetId: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await retrieveEntity< + PriceListRuleValue, + PricingTypes.PriceListRuleValueDTO + >({ + id: priceSetId, + entityName: PriceListRuleValue.name, + repository: this.priceListRuleValueRepository_, + config, + sharedContext, + })) as TEntity + } + + @InjectManager("priceListRuleValueRepository_") + async list( + filters: PricingTypes.FilterablePriceListRuleValueProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const queryOptions = ModulesSdkUtils.buildQuery( + filters, + config + ) + + return (await this.priceListRuleValueRepository_.find( + queryOptions, + sharedContext + )) as TEntity[] + } + + @InjectManager("priceListRuleValueRepository_") + async listAndCount( + filters: PricingTypes.FilterablePriceListRuleValueProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[TEntity[], number]> { + const queryOptions = ModulesSdkUtils.buildQuery( + filters, + config + ) + + return (await this.priceListRuleValueRepository_.findAndCount( + queryOptions, + sharedContext + )) as [TEntity[], number] + } + + @InjectTransactionManager( + shouldForceTransaction, + "priceListRuleValueRepository_" + ) + async create( + data: CreatePriceListRuleValueDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await ( + this.priceListRuleValueRepository_ as PriceListRuleValueRepository + ).create(data, sharedContext)) as TEntity[] + } + + @InjectTransactionManager( + shouldForceTransaction, + "priceListRuleValueRepository_" + ) + async update( + data: PricingTypes.UpdatePriceListRuleValueDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await ( + this.priceListRuleValueRepository_ as PriceListRuleValueRepository + ).update(data, sharedContext)) as TEntity[] + } + + @InjectTransactionManager( + doNotForceTransaction, + "priceListRuleValueRepository_" + ) + async delete( + ids: string[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + await this.priceListRuleValueRepository_.delete(ids, sharedContext) + } +} diff --git a/packages/pricing/src/services/price-list-rule.ts b/packages/pricing/src/services/price-list-rule.ts new file mode 100644 index 0000000000..c60db371ee --- /dev/null +++ b/packages/pricing/src/services/price-list-rule.ts @@ -0,0 +1,103 @@ +import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types" +import { + InjectManager, + InjectTransactionManager, + MedusaContext, + ModulesSdkUtils, + doNotForceTransaction, + retrieveEntity, + shouldForceTransaction, +} from "@medusajs/utils" +import { PriceListRule } from "@models" +import { PriceListRuleRepository } from "@repositories" + +type InjectedDependencies = { + priceListRuleRepository: DAL.RepositoryService +} + +export default class PriceListRuleService< + TEntity extends PriceListRule = PriceListRule +> { + protected readonly priceListRuleRepository_: DAL.RepositoryService + + constructor({ priceListRuleRepository }: InjectedDependencies) { + this.priceListRuleRepository_ = priceListRuleRepository + } + + @InjectManager("priceListRuleRepository_") + async retrieve( + priceSetId: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await retrieveEntity({ + id: priceSetId, + entityName: PriceListRule.name, + repository: this.priceListRuleRepository_, + config, + sharedContext, + })) as TEntity + } + + @InjectManager("priceListRuleRepository_") + async list( + filters: PricingTypes.FilterablePriceListRuleProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const queryOptions = ModulesSdkUtils.buildQuery( + filters, + config + ) + + return (await this.priceListRuleRepository_.find( + queryOptions, + sharedContext + )) as TEntity[] + } + + @InjectManager("priceListRuleRepository_") + async listAndCount( + filters: PricingTypes.FilterablePriceListRuleProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[TEntity[], number]> { + const queryOptions = ModulesSdkUtils.buildQuery( + filters, + config + ) + + return (await this.priceListRuleRepository_.findAndCount( + queryOptions, + sharedContext + )) as [TEntity[], number] + } + + @InjectTransactionManager(shouldForceTransaction, "priceListRuleRepository_") + async create( + data: PricingTypes.CreatePriceListRuleDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await ( + this.priceListRuleRepository_ as PriceListRuleRepository + ).create(data, sharedContext)) as TEntity[] + } + + @InjectTransactionManager(shouldForceTransaction, "priceListRuleRepository_") + async update( + data: PricingTypes.UpdatePriceListRuleDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await ( + this.priceListRuleRepository_ as PriceListRuleRepository + ).update(data, sharedContext)) as TEntity[] + } + + @InjectTransactionManager(doNotForceTransaction, "priceListRuleRepository_") + async delete( + ids: string[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + await this.priceListRuleRepository_.delete(ids, sharedContext) + } +} diff --git a/packages/pricing/src/services/price-list.ts b/packages/pricing/src/services/price-list.ts new file mode 100644 index 0000000000..b42cceb688 --- /dev/null +++ b/packages/pricing/src/services/price-list.ts @@ -0,0 +1,98 @@ +import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types" +import { + InjectManager, + InjectTransactionManager, + MedusaContext, + ModulesSdkUtils, + doNotForceTransaction, + retrieveEntity, + shouldForceTransaction, +} from "@medusajs/utils" +import { PriceList } from "@models" +import { PriceListRepository } from "@repositories" +import { CreatePriceListDTO } from "../types" + +type InjectedDependencies = { + priceListRepository: DAL.RepositoryService +} + +export default class PriceListService { + protected readonly priceListRepository_: DAL.RepositoryService + + constructor({ priceListRepository }: InjectedDependencies) { + this.priceListRepository_ = priceListRepository + } + + @InjectManager("priceListRepository_") + async retrieve( + priceListId: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await retrieveEntity({ + id: priceListId, + entityName: PriceList.name, + repository: this.priceListRepository_, + config, + sharedContext, + })) as TEntity + } + + @InjectManager("priceListRepository_") + async list( + filters: PricingTypes.FilterablePriceListProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const queryOptions = ModulesSdkUtils.buildQuery(filters, config) + + return (await this.priceListRepository_.find( + queryOptions, + sharedContext + )) as TEntity[] + } + + @InjectManager("priceListRepository_") + async listAndCount( + filters: PricingTypes.FilterablePriceListProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[TEntity[], number]> { + const queryOptions = ModulesSdkUtils.buildQuery(filters, config) + + return (await this.priceListRepository_.findAndCount( + queryOptions, + sharedContext + )) as [TEntity[], number] + } + + @InjectTransactionManager(shouldForceTransaction, "priceListRepository_") + async create( + data: CreatePriceListDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await (this.priceListRepository_ as PriceListRepository).create( + data, + sharedContext + )) as TEntity[] + } + + @InjectTransactionManager(shouldForceTransaction, "priceListRepository_") + async update( + data: Omit[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await (this.priceListRepository_ as PriceListRepository).update( + data, + sharedContext + )) as TEntity[] + } + + @InjectTransactionManager(doNotForceTransaction, "priceListRepository_") + async delete( + ids: string[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + await this.priceListRepository_.delete(ids, sharedContext) + } +} diff --git a/packages/pricing/src/services/pricing-module.ts b/packages/pricing/src/services/pricing-module.ts index a0b853c4fc..101ad70055 100644 --- a/packages/pricing/src/services/pricing-module.ts +++ b/packages/pricing/src/services/pricing-module.ts @@ -2,6 +2,7 @@ import { AddPricesDTO, Context, CreateMoneyAmountDTO, + CreatePriceListRuleDTO, DAL, FindConfig, InternalModuleDeclaration, @@ -12,10 +13,14 @@ import { PricingTypes, RuleTypeDTO, } from "@medusajs/types" +import { PriceListType } from "@medusajs/utils" import { Currency, MoneyAmount, + PriceList, + PriceListRule, + PriceListRuleValue, PriceRule, PriceSet, PriceSetMoneyAmount, @@ -27,6 +32,9 @@ import { import { CurrencyService, MoneyAmountService, + PriceListRuleService, + PriceListRuleValueService, + PriceListService, PriceRuleService, PriceSetMoneyAmountRulesService, PriceSetMoneyAmountService, @@ -44,7 +52,7 @@ import { removeNullish, } from "@medusajs/utils" import { joinerConfig } from "../joiner-config" -import { PricingRepositoryService } from "../types" +import { CreatePriceListRuleValueDTO, PricingRepositoryService } from "../types" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -57,6 +65,9 @@ type InjectedDependencies = { priceRuleService: PriceRuleService priceSetRuleTypeService: PriceSetRuleTypeService priceSetMoneyAmountService: PriceSetMoneyAmountService + priceListService: PriceListService + priceListRuleService: PriceListRuleService + priceListRuleValueService: PriceListRuleValueService } export default class PricingModuleService< @@ -67,7 +78,10 @@ export default class PricingModuleService< TPriceSetMoneyAmountRules extends PriceSetMoneyAmountRules = PriceSetMoneyAmountRules, TPriceRule extends PriceRule = PriceRule, TPriceSetRuleType extends PriceSetRuleType = PriceSetRuleType, - TPriceSetMoneyAmount extends PriceSetMoneyAmount = PriceSetMoneyAmount + TPriceSetMoneyAmount extends PriceSetMoneyAmount = PriceSetMoneyAmount, + TPriceList extends PriceList = PriceList, + TPriceListRule extends PriceListRule = PriceListRule, + TPriceListRuleValue extends PriceListRuleValue = PriceListRuleValue > implements PricingTypes.IPricingModuleService { protected baseRepository_: DAL.RepositoryService @@ -80,6 +94,9 @@ export default class PricingModuleService< protected readonly priceRuleService_: PriceRuleService protected readonly priceSetRuleTypeService_: PriceSetRuleTypeService protected readonly priceSetMoneyAmountService_: PriceSetMoneyAmountService + protected readonly priceListService_: PriceListService + protected readonly priceListRuleService_: PriceListRuleService + protected readonly priceListRuleValueService_: PriceListRuleValueService constructor( { @@ -93,6 +110,9 @@ export default class PricingModuleService< priceRuleService, priceSetRuleTypeService, priceSetMoneyAmountService, + priceListService, + priceListRuleService, + priceListRuleValueService, }: InjectedDependencies, protected readonly moduleDeclaration: InternalModuleDeclaration ) { @@ -107,6 +127,9 @@ export default class PricingModuleService< this.priceRuleService_ = priceRuleService this.priceSetRuleTypeService_ = priceSetRuleTypeService this.priceSetMoneyAmountService_ = priceSetMoneyAmountService + this.priceListService_ = priceListService + this.priceListRuleService_ = priceListRuleService + this.priceListRuleValueService_ = priceListRuleValueService } __joinerConfig(): ModuleJoinerConfig { @@ -124,21 +147,53 @@ export default class PricingModuleService< pricingContext, sharedContext ) - const pricesSetPricesMap = groupBy(results, "id") + const pricesSetPricesMap = groupBy(results, "price_set_id") const calculatedPrices = pricingFilters.id.map( - (priceSetId: string): PricingTypes.CalculatedPriceSetDTO => { + (priceSetId: string): PricingTypes.CalculatedPriceSet => { // This is where we select prices, for now we just do a first match based on the database results // which is prioritized by number_rules first for exact match and then deafult_priority of the rule_type // inject custom price selection here - const price = pricesSetPricesMap.get(priceSetId)?.[0] + const prices = pricesSetPricesMap.get(priceSetId) || [] + const priceListPrice = prices?.find((p) => !!p.price_list_id) + const defaultPrice = prices?.find((p) => !!!p.price_list_id) + + let calculatedPrice: PricingTypes.CalculatedPriceSetDTO = defaultPrice + let originalPrice: PricingTypes.CalculatedPriceSetDTO = defaultPrice + + if (priceListPrice) { + calculatedPrice = priceListPrice + + if (priceListPrice.price_list_type === PriceListType.OVERRIDE) { + originalPrice = priceListPrice + } + } return { id: priceSetId, - amount: price?.amount || null, - currency_code: price?.currency_code || null, - min_quantity: price?.min_quantity || null, - max_quantity: price?.max_quantity || null, + is_calculated_price_price_list: !!calculatedPrice?.price_list_id, + calculated_amount: parseInt(calculatedPrice?.amount || "") || null, + + is_original_price_price_list: !!originalPrice?.price_list_id, + original_amount: parseInt(originalPrice?.amount || "") || null, + + currency_code: calculatedPrice?.currency_code || null, + + calculated_price: { + money_amount_id: calculatedPrice?.id || null, + price_list_id: calculatedPrice?.price_list_id || null, + price_list_type: calculatedPrice?.price_list_type || null, + min_quantity: parseInt(calculatedPrice?.min_quantity || "") || null, + max_quantity: parseInt(calculatedPrice?.max_quantity || "") || null, + }, + + original_price: { + money_amount_id: originalPrice?.id || null, + price_list_id: originalPrice?.price_list_id || null, + price_list_type: originalPrice?.price_list_type || null, + min_quantity: parseInt(originalPrice?.min_quantity || "") || null, + max_quantity: parseInt(originalPrice?.max_quantity || "") || null, + }, } } ) @@ -1241,4 +1296,673 @@ export default class PricingModuleService< ): Promise { await this.priceRuleService_.delete(priceRuleIds, sharedContext) } + + @InjectManager("baseRepository_") + async retrievePriceList( + id: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const priceList = await this.priceListService_.retrieve( + id, + config, + sharedContext + ) + + return this.baseRepository_.serialize( + priceList, + { + populate: true, + } + ) + } + + @InjectManager("baseRepository_") + async listPriceLists( + filters: PricingTypes.FilterablePriceListProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const priceLists = await this.priceListService_.list( + filters, + config, + sharedContext + ) + + return this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ) + } + + @InjectManager("baseRepository_") + async listAndCountPriceLists( + filters: PricingTypes.FilterablePriceListProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[PricingTypes.PriceListDTO[], number]> { + const [priceLists, count] = await this.priceListService_.listAndCount( + filters, + config, + sharedContext + ) + + return [ + await this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ), + count, + ] + } + + @InjectManager("baseRepository_") + async createPriceLists( + data: PricingTypes.CreatePriceListDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const priceLists = await this.createPriceLists_(data, sharedContext) + + return this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ) + } + + @InjectTransactionManager("baseRepository_") + protected async createPriceLists_( + data: PricingTypes.CreatePriceListDTO[], + @MedusaContext() sharedContext: Context = {} + ) { + const createdPriceLists: PricingTypes.PriceListDTO[] = [] + const ruleAttributes = data + .map((priceListData) => Object.keys(priceListData.rules || {})) + .flat() + + const ruleTypes = await this.listRuleTypes({ + rule_attribute: ruleAttributes, + }) + + const ruleTypeMap: Map = new Map( + ruleTypes.map((rt) => [rt.rule_attribute, rt]) + ) + + for (const priceListData of data) { + const { rules = {}, prices = [], ...priceListOnlyData } = priceListData + + const [createdPriceList] = (await this.priceListService_.create( + [ + { + ...priceListOnlyData, + number_rules: Object.keys(rules).length, + }, + ], + sharedContext + )) as unknown as PricingTypes.PriceListDTO[] + + createdPriceLists.push(createdPriceList) + + for (const [ruleAttribute, ruleValues = []] of Object.entries(rules)) { + // Find or create rule type + let ruleType = ruleTypeMap.get(ruleAttribute) + + if (!ruleType) { + ;[ruleType] = await this.createRuleTypes( + [ + { + name: ruleAttribute, + rule_attribute: ruleAttribute, + }, + ], + sharedContext + ) + + ruleTypeMap.set(ruleAttribute, ruleType) + } + + // Create the rule + const [priceListRule] = await this.priceListRuleService_.create( + [ + { + price_list: createdPriceList, + rule_type: ruleType?.id || ruleType, + }, + ], + sharedContext + ) + + // Create the values for the rule + for (const ruleValue of ruleValues as string[]) { + await this.priceListRuleValueService_.create( + [ + { + price_list_rule: priceListRule, + value: ruleValue, + }, + ], + sharedContext + ) + } + } + + for (const price of prices) { + const { price_set_id: priceSetId, ...moneyAmountData } = price + + const [moneyAmount] = await this.moneyAmountService_.create( + [moneyAmountData], + sharedContext + ) + + await this.priceSetMoneyAmountService_.create( + [ + { + price_set: priceSetId, + price_list: createdPriceList, + money_amount: moneyAmount, + title: "test", + number_rules: 0, + }, + ] as unknown as PricingTypes.CreatePriceSetMoneyAmountDTO[], + sharedContext + ) + } + } + + return createdPriceLists + } + + @InjectTransactionManager("baseRepository_") + async updatePriceLists( + data: PricingTypes.UpdatePriceListDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const priceLists = await this.updatePriceLists_(data, sharedContext) + + return this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ) + } + + @InjectTransactionManager("baseRepository_") + protected async updatePriceLists_( + data: PricingTypes.UpdatePriceListDTO[], + @MedusaContext() sharedContext: Context = {} + ) { + const updatedPriceLists: PricingTypes.PriceListDTO[] = [] + const priceListIds = data.map((d) => d.id) + const ruleAttributes = data + .map((priceListData) => Object.keys(priceListData.rules || {})) + .flat() + + const existingPriceLists = await this.listPriceLists( + { id: priceListIds }, + { relations: ["price_list_rules"] }, + sharedContext + ) + + const priceListRuleIds = existingPriceLists + .map((pl) => (pl.price_list_rules || []).map((plr) => plr.id)) + .flat() + + const existingPriceListRules = await this.listPriceListRules( + { + id: priceListRuleIds, + }, + {}, + sharedContext + ) + + if (existingPriceListRules.length) { + await this.deletePriceListRules( + existingPriceListRules.map((plr) => plr.id), + sharedContext + ) + } + + const ruleTypes = await this.listRuleTypes( + { + rule_attribute: ruleAttributes, + }, + {}, + sharedContext + ) + + const ruleTypeMap: Map = new Map( + ruleTypes.map((rt) => [rt.rule_attribute, rt]) + ) + + for (const priceListData of data) { + const { rules = {}, ...priceListOnlyData } = priceListData + + const [updatedPriceList] = (await this.priceListService_.update( + [ + { + ...priceListOnlyData, + number_rules: Object.keys(rules).length, + }, + ], + sharedContext + )) as unknown as PricingTypes.PriceListDTO[] + + updatedPriceLists.push(updatedPriceList) + + for (const [ruleAttribute, ruleValues = []] of Object.entries(rules)) { + let ruleType = ruleTypeMap.get(ruleAttribute) + + if (!ruleType) { + ;[ruleType] = await this.createRuleTypes( + [ + { + name: ruleAttribute, + rule_attribute: ruleAttribute, + }, + ], + sharedContext + ) + + ruleTypeMap.set(ruleAttribute, ruleType) + } + + const [priceListRule] = await this.priceListRuleService_.create( + [ + { + price_list: updatedPriceList, + rule_type: ruleType?.id || ruleType, + }, + ], + sharedContext + ) + + for (const ruleValue of ruleValues as string[]) { + await this.priceListRuleValueService_.create( + [ + { + price_list_rule: priceListRule, + value: ruleValue, + }, + ], + sharedContext + ) + } + } + } + + return updatedPriceLists + } + + @InjectTransactionManager("baseRepository_") + async deletePriceLists( + priceListIds: string[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + await this.priceListService_.delete(priceListIds, sharedContext) + } + + @InjectManager("baseRepository_") + async retrievePriceListRule( + id: string, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const priceList = await this.priceListRuleService_.retrieve( + id, + config, + sharedContext + ) + + return this.baseRepository_.serialize( + priceList, + { + populate: true, + } + ) + } + + @InjectManager("baseRepository_") + async listPriceListRules( + filters: PricingTypes.FilterablePriceListRuleProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const priceLists = await this.priceListRuleService_.list( + filters, + config, + sharedContext + ) + + return this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ) + } + + @InjectManager("baseRepository_") + async listAndCountPriceListRules( + filters: PricingTypes.FilterablePriceListRuleProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ): Promise<[PricingTypes.PriceListRuleDTO[], number]> { + const [priceLists, count] = await this.priceListRuleService_.listAndCount( + filters, + config, + sharedContext + ) + + return [ + await this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ), + count, + ] + } + + @InjectManager("baseRepository_") + async createPriceListRules( + data: PricingTypes.CreatePriceListRuleDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const priceLists = await this.createPriceListRules_(data, sharedContext) + + return this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ) + } + + @InjectTransactionManager("baseRepository_") + async createPriceListRules_( + data: PricingTypes.CreatePriceListRuleDTO[], + @MedusaContext() sharedContext: Context = {} + ) { + return await this.priceListRuleService_.create(data, sharedContext) + } + + @InjectTransactionManager("baseRepository_") + async updatePriceListRules( + data: PricingTypes.UpdatePriceListRuleDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const priceLists = await this.priceListRuleService_.update( + data, + sharedContext + ) + + return this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ) + } + + @InjectTransactionManager("baseRepository_") + async deletePriceListRules( + priceListRuleIds: string[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + await this.priceListRuleService_.delete(priceListRuleIds, sharedContext) + } + + @InjectManager("baseRepository_") + async addPriceListPrices( + data: PricingTypes.AddPriceListPricesDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return await this.addPriceListPrices_(data, sharedContext) + } + + @InjectTransactionManager("baseRepository_") + protected async addPriceListPrices_( + data: PricingTypes.AddPriceListPricesDTO[], + sharedContext: Context = {} + ): Promise { + const priceLists = await this.listPriceLists( + { id: data.map((d) => d.priceListId) }, + {}, + sharedContext + ) + + const priceListMap = new Map(priceLists.map((p) => [p.id, p])) + + for (const { priceListId, prices } of data) { + const priceList = priceListMap.get(priceListId) + + if (!priceList) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Price list with id: ${priceListId} not found` + ) + } + + await Promise.all( + prices.map(async (price) => { + const [moneyAmount] = await this.moneyAmountService_.create( + [price] as unknown as CreateMoneyAmountDTO[], + sharedContext + ) + + const psma = await this.priceSetMoneyAmountService_.create( + [ + { + price_set: price.price_set_id, + money_amount: moneyAmount.id, + title: "test", + price_list: priceList.id, + }, + ], + sharedContext + ) + + return psma + }) + ) + } + + return priceLists + } + + @InjectManager("baseRepository_") + async setPriceListRules( + data: PricingTypes.SetPriceListRulesDTO, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const [priceList] = await this.setPriceListRules_([data], sharedContext) + + return priceList + } + + @InjectTransactionManager("baseRepository_") + protected async setPriceListRules_( + data: PricingTypes.SetPriceListRulesDTO[], + sharedContext: Context = {} + ): Promise { + const priceLists = await this.priceListService_.list( + { id: data.map((d) => d.priceListId) }, + { relations: ["price_list_rules", "price_list_rules.rule_type"] }, + sharedContext + ) + + const priceListMap = new Map(priceLists.map((p) => [p.id, p])) + + const ruleTypes = await this.listRuleTypes({ + rule_attribute: data.map((d) => Object.keys(d.rules)).flat(), + }) + + const ruleTypeMap = new Map(ruleTypes.map((rt) => [rt.rule_attribute, rt])) + + const ruleIdsToUpdate: string[] = [] + const rulesToCreate: CreatePriceListRuleDTO[] = [] + + const priceRuleValues = new Map>() + + for (const { priceListId, rules } of data) { + const priceList = priceListMap.get(priceListId) + + if (!priceList) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Price list with id: ${priceListId} not found` + ) + } + + const priceListRulesMap: Map = new Map( + priceList.price_list_rules + .getItems() + .map((p) => [p.rule_type.rule_attribute, p]) + ) + + const priceListRuleValues = new Map() + await Promise.all( + Object.entries(rules).map(async ([key, value]) => { + const ruleType = ruleTypeMap.get(key) + if (!ruleType) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Rule type with attribute: ${key} not found` + ) + } + + const rule = priceListRulesMap.get(key) + + priceListRuleValues.set( + ruleType.id, + Array.isArray(value) ? value : [value] + ) + + if (!rule) { + rulesToCreate.push({ + rule_type: ruleType.id, + price_list: priceListId, + }) + } else { + ruleIdsToUpdate.push(rule.id) + } + }) + ) + + priceRuleValues.set(priceListId, priceListRuleValues) + } + + const [createdRules, priceListValuesToDelete] = await Promise.all([ + this.priceListRuleService_.create(rulesToCreate), + this.priceListRuleValueService_.list({ + price_list_rule_id: ruleIdsToUpdate, + }), + ]) + + const priceListRuleValuesToCreate: CreatePriceListRuleValueDTO[] = [] + + for (const { id, price_list, rule_type } of createdRules) { + const ruleValues = priceRuleValues.get( + price_list.id ?? (price_list as unknown as string) + ) + if (!ruleValues) { + continue + } + + const values = ruleValues.get( + rule_type.id ?? (rule_type as unknown as string) + ) + if (!values) { + continue + } + + values.forEach((v) => { + priceListRuleValuesToCreate.push({ + price_list_rule: id, + value: v, + }) + }) + } + + await Promise.all([ + this.priceListRuleValueService_.delete( + priceListValuesToDelete.map((p) => p.id) + ), + this.priceListRuleValueService_.create(priceListRuleValuesToCreate), + ]) + + return this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ) + } + + @InjectManager("baseRepository_") + async removePriceListRules( + data: PricingTypes.RemovePriceListRulesDTO, + @MedusaContext() sharedContext: Context = {} + ): Promise { + const [priceList] = await this.removePriceListRules_([data], sharedContext) + + return priceList + } + + @InjectTransactionManager("baseRepository_") + protected async removePriceListRules_( + data: PricingTypes.RemovePriceListRulesDTO[], + sharedContext: Context = {} + ): Promise { + const priceLists = await this.priceListService_.list( + { id: data.map((d) => d.priceListId) }, + { relations: ["price_list_rules", "price_list_rules.rule_type"] }, + sharedContext + ) + + const priceListMap = new Map(priceLists.map((p) => [p.id, p])) + + const idsToDelete: string[] = [] + for (const { priceListId, rules } of data) { + const priceList = priceListMap.get(priceListId) + + if (!priceList) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Price list with id: ${priceListId} not found` + ) + } + + const priceListRulesMap: Map = new Map( + priceList.price_list_rules + .getItems() + .map((p) => [p.rule_type.rule_attribute, p]) + ) + + await Promise.all( + rules.map(async (rule_attribute) => { + const rule = priceListRulesMap.get(rule_attribute) + if (rule) { + idsToDelete.push(rule.id) + } + }) + ) + } + + await this.priceListRuleService_.delete(idsToDelete) + + return this.baseRepository_.serialize( + priceLists, + { + populate: true, + } + ) + } } diff --git a/packages/pricing/src/types/repositories/index.ts b/packages/pricing/src/types/repositories/index.ts index d4f24d5a82..bd0562e143 100644 --- a/packages/pricing/src/types/repositories/index.ts +++ b/packages/pricing/src/types/repositories/index.ts @@ -1 +1,3 @@ +export * from "./price-list" +export * from "./price-list-rule-value" export * from "./pricing" diff --git a/packages/pricing/src/types/repositories/price-list-rule-value.ts b/packages/pricing/src/types/repositories/price-list-rule-value.ts new file mode 100644 index 0000000000..f00222a299 --- /dev/null +++ b/packages/pricing/src/types/repositories/price-list-rule-value.ts @@ -0,0 +1,12 @@ +import { PriceListRule } from "@models" + +export interface CreatePriceListRuleValueDTO { + price_list_rule_id?: string + price_list_rule: PriceListRule | string + value: string +} + +export interface UpdatePriceListRuleValueDTO { + id: string + value: string +} diff --git a/packages/pricing/src/types/repositories/price-list.ts b/packages/pricing/src/types/repositories/price-list.ts new file mode 100644 index 0000000000..ca8993dd21 --- /dev/null +++ b/packages/pricing/src/types/repositories/price-list.ts @@ -0,0 +1,11 @@ +import { PriceListStatus, PriceListType } from "@medusajs/utils" + +export interface CreatePriceListDTO { + title: string + description: string + starts_at?: string + ends_at?: string + status?: PriceListStatus + type?: PriceListType + number_rules?: number +} diff --git a/packages/types/src/pricing/common/index.ts b/packages/types/src/pricing/common/index.ts index 4a7578a1b3..10fb2e4419 100644 --- a/packages/types/src/pricing/common/index.ts +++ b/packages/types/src/pricing/common/index.ts @@ -6,3 +6,4 @@ export * from "./price-set-money-amount" export * from "./price-set-money-amount-rules" export * from "./price-set-rule-type" export * from "./rule-type" +export * from "./price-list" diff --git a/packages/types/src/pricing/common/money-amount.ts b/packages/types/src/pricing/common/money-amount.ts index 420824e0b4..eb891aac19 100644 --- a/packages/types/src/pricing/common/money-amount.ts +++ b/packages/types/src/pricing/common/money-amount.ts @@ -1,11 +1,12 @@ -import { BaseFilterable } from "../../dal" -import { CreateCurrencyDTO, CurrencyDTO } from "./currency" +import { BaseFilterable } from "../../dal"; +import { CreateCurrencyDTO, CurrencyDTO } from "./currency"; +import { PriceSetMoneyAmountDTO } from "./price-set-money-amount"; /** * @interface - * + * * A money amount's data. A money amount represents a price. - * + * * @prop id - The ID of the money amount. * @prop currency_code - The currency code of this money amount. * @prop currency - The money amount's currency. Since this is a relation, it will only be retrieved if it's passed to the `relations` array of the find-configuration options. @@ -20,13 +21,14 @@ export interface MoneyAmountDTO { amount?: number min_quantity?: number max_quantity?: number + price_set_money_amount?: PriceSetMoneyAmountDTO } /** * @interface - * + * * The money amount to create. - * + * * @prop id - The ID of the money amount. * @prop currency_code - The currency code of this money amount. * @prop currency - The currency of this money amount. @@ -38,16 +40,16 @@ export interface CreateMoneyAmountDTO { id?: string currency_code: string currency?: CreateCurrencyDTO - amount?: number + amount: number min_quantity?: number | null max_quantity?: number | null } /** * * @interface - * + * * The data to update in a money amount. The `id` is used to identify which money amount to update. - * + * * @prop id - The ID of the money amount to update. * @prop currency_code - The code of the currency to associate with the money amount. * @prop currency - The currency to associte with the money amount. @@ -65,9 +67,9 @@ export interface UpdateMoneyAmountDTO { /** * @interface - * + * * Filters to apply on a money amount. - * + * * @prop id - IDs to filter money amounts by. * @prop currency_code - Currency codes to filter money amounts by. */ diff --git a/packages/types/src/pricing/common/price-list.ts b/packages/types/src/pricing/common/price-list.ts new file mode 100644 index 0000000000..3194a1313a --- /dev/null +++ b/packages/types/src/pricing/common/price-list.ts @@ -0,0 +1,137 @@ +import { CreateMoneyAmountDTO, MoneyAmountDTO } from "./money-amount" + +import { BaseFilterable } from "../../dal" +import { PriceSetMoneyAmountDTO } from "./price-set-money-amount" +import { RuleTypeDTO } from "./rule-type" + +export enum PriceListStatus { + ACTIVE = "active", + DRAFT = "draft", +} + +export enum PriceListType { + SALE = "sale", + OVERRIDE = "override", +} + +export interface PriceListDTO { + id: string + title?: string + starts_at?: string | null + status?: PriceListStatus + ends_at?: string | null + number_rules?: number + price_set_money_amounts?: PriceSetMoneyAmountDTO[] + money_amounts?: MoneyAmountDTO[] + rule_types?: RuleTypeDTO[] + rules?: PriceListRuleDTO[] + price_list_rules?: PriceListRuleDTO[] +} + +export interface PriceListPriceDTO extends CreateMoneyAmountDTO { + price_set_id: string +} + +export interface CreatePriceListRules extends Record {} + +export interface CreatePriceListDTO { + title: string + description: string + starts_at?: string + ends_at?: string + status?: PriceListStatus + type?: PriceListType + number_rules?: number + rules?: CreatePriceListRules + prices?: PriceListPriceDTO[] +} + +export interface UpdatePriceListDTO { + id: string + title?: string + starts_at?: string + ends_at?: string + status?: PriceListStatus + number_rules?: number + rules?: CreatePriceListRules +} + +export interface FilterablePriceListProps + extends BaseFilterable { + id?: string[] + starts_at?: string[] + ends_at?: string[] + status?: PriceListStatus[] + number_rules?: number[] +} + +export interface FilterablePriceListRuleProps + extends BaseFilterable { + id?: string[] + value?: string[] + rule_type?: string[] + price_list_id?: string[] +} + +export interface FilterablePriceListRuleValueProps + extends BaseFilterable { + id?: string[] + value?: string[] + price_list_rule_id?: string[] +} + +export interface PriceListRuleDTO { + id: string + value: string + rule_type: RuleTypeDTO + price_list: PriceListDTO + price_list_rule_values?: PriceListRuleValueDTO[] +} + +export interface CreatePriceListRuleDTO { + rule_type_id?: string + rule_type?: string | RuleTypeDTO + price_list_id?: string + price_list?: string | PriceListDTO +} + +export interface UpdatePriceListRuleDTO { + id: string + price_list_id?: string + rule_type_id?: string + price_list?: string + rule_type?: string +} + +export interface PriceListRuleValueDTO { + id: string + value: string + price_list_rule: PriceListRuleDTO +} + +export interface CreatePriceListRuleValueDTO { + value: string + price_list_rule_id?: string + price_list_rule?: PriceListRuleDTO | string +} + +export interface UpdatePriceListRuleValueDTO { + id: string + value: string + price_list_rule_id: string +} + +export interface AddPriceListPricesDTO { + priceListId: string + prices: PriceListPriceDTO[] +} + +export interface SetPriceListRulesDTO { + priceListId: string + rules: Record +} + +export interface RemovePriceListRulesDTO { + priceListId: string + rules: string[] +} diff --git a/packages/types/src/pricing/common/price-rule.ts b/packages/types/src/pricing/common/price-rule.ts index a0e970ef1f..b6dd6fad5a 100644 --- a/packages/types/src/pricing/common/price-rule.ts +++ b/packages/types/src/pricing/common/price-rule.ts @@ -64,7 +64,6 @@ export interface CreatePriceRuleDTO { value: string priority?: number price_set_money_amount_id: string - price_list_id: string } /** diff --git a/packages/types/src/pricing/common/price-set-money-amount.ts b/packages/types/src/pricing/common/price-set-money-amount.ts index 479d2818d2..4432c2f16c 100644 --- a/packages/types/src/pricing/common/price-set-money-amount.ts +++ b/packages/types/src/pricing/common/price-set-money-amount.ts @@ -1,13 +1,14 @@ import { BaseFilterable } from "../../dal" import { MoneyAmountDTO } from "./money-amount" +import { PriceListDTO } from "./price-list" import { PriceRuleDTO } from "./price-rule" import { PriceSetDTO } from "./price-set" /** * @interface - * + * * A price set money amount's data. - * + * * @prop id - The ID of a price set money amount. * @prop title - The title of the price set money amount. * @prop price_set - The price set associated with the price set money amount. It may only be available if the relation `price_set` is expanded. @@ -17,6 +18,7 @@ export interface PriceSetMoneyAmountDTO { id: string title?: string price_set?: PriceSetDTO + price_list?: PriceListDTO price_set_id?: string price_rules?: PriceRuleDTO[] money_amount?: MoneyAmountDTO @@ -32,14 +34,15 @@ export interface UpdatePriceSetMoneyAmountDTO { export interface CreatePriceSetMoneyAmountDTO { title?: string price_set?: PriceSetDTO | string + price_list?: PriceListDTO | string money_amount?: MoneyAmountDTO | string } /** * @interface - * + * * Filters to apply on price set money amounts. - * + * * @prop id - The IDs to filter the price set money amounts by. * @prop price_set_id - The IDs to filter the price set money amount's associated price set. */ @@ -47,4 +50,5 @@ export interface FilterablePriceSetMoneyAmountProps extends BaseFilterable { id?: string[] price_set_id?: string[] + price_list_id?: string[] } diff --git a/packages/types/src/pricing/common/price-set.ts b/packages/types/src/pricing/common/price-set.ts index 2cae002e31..ae4d21ef87 100644 --- a/packages/types/src/pricing/common/price-set.ts +++ b/packages/types/src/pricing/common/price-set.ts @@ -1,20 +1,16 @@ -import { BaseFilterable } from "../../dal" -import { - CreateMoneyAmountDTO, - FilterableMoneyAmountProps, - MoneyAmountDTO, -} from "./money-amount" -import { RuleTypeDTO } from "./rule-type" +import { BaseFilterable } from "../../dal"; +import { CreateMoneyAmountDTO, FilterableMoneyAmountProps, MoneyAmountDTO } from "./money-amount"; +import { RuleTypeDTO } from "./rule-type"; /** * @interface - * + * * The context to calculate prices. For example, you can specify the currency code to calculate prices in. - * - * @prop context - + * + * @prop context - * an object whose keys are the name of the context attribute. Its value can be a string or a number. For example, you can pass the `currency_code` property with its value being the currency code to calculate the price in. * Another example is passing the `quantity` property to calculate the price for that specified quantity, which finds a price set whose `min_quantity` and `max_quantity` conditions match the specified quantity. - * + * */ export interface PricingContext { context?: Record @@ -22,9 +18,9 @@ export interface PricingContext { /** * @interface - * + * * Filters to apply on prices. - * + * * @prop id - IDs to filter prices. */ export interface PricingFilters { @@ -33,13 +29,13 @@ export interface PricingFilters { /** * @interface - * + * * A price set's data. - * + * * @prop id - The ID of the price set. * @prop money_amounts - The prices that belong to this price set. * @prop rule_types - The rule types applied on this price set. - * + * */ export interface PriceSetDTO { id: string @@ -49,9 +45,9 @@ export interface PriceSetDTO { /** * @interface - * + * * A calculated price set's data. - * + * * @prop id - The ID of the price set. * @prop amount - The calculated amount. It can possibly be `null` if there's no price set up for the provided context. * @prop currency_code - The currency code of the calculated price. It can possibly be `null`. @@ -60,17 +56,47 @@ export interface PriceSetDTO { */ export interface CalculatedPriceSetDTO { id: string - amount: number | null + price_set_id: string + amount: string | null currency_code: string | null - min_quantity: number | null - max_quantity: number | null + min_quantity: string | null + max_quantity: string | null + price_list_type: string | null + price_list_id: string | null +} + +export interface CalculatedPriceSet { + id: string + is_calculated_price_price_list?: boolean + calculated_amount: number | null + + is_original_price_price_list?: boolean + original_amount: number | null + + currency_code: string | null + + calculated_price?: { + money_amount_id: string | null + price_list_id: string | null + price_list_type: string | null + min_quantity: number | null + max_quantity: number | null + } + + original_price?: { + money_amount_id: string | null + price_list_id: string | null + price_list_type: string | null + min_quantity: number | null + max_quantity: number | null + } } /** * @interface - * + * * The rules to add to a price set. - * + * * @prop priceSetId - The ID of the price set to add the rules to. * @prop rules - The rules to add to a price set. The value of `attribute` is the value of the rule's `rule_attribute` attribute. */ @@ -81,9 +107,9 @@ export interface AddRulesDTO { /** * @interface - * + * * The prices to create part of a price set. - * + * * @prop rules - The rules to add to the price. The object's keys are rule types' `rule_attribute` attribute, and values are the value of that rule associated with this price. */ export interface CreatePricesDTO extends CreateMoneyAmountDTO { @@ -92,9 +118,9 @@ export interface CreatePricesDTO extends CreateMoneyAmountDTO { /** * @interface - * + * * The prices to add to a price set. - * + * * @prop priceSetId - The ID of the price set to add prices to. * @prop prices - The prices to add to the price set. */ @@ -105,9 +131,9 @@ export interface AddPricesDTO { /** * @interface - * + * * The rules to remove from a price set. - * + * * @prop id - The ID of the price set. * @prop rules - The rules to remove. Each string is the `rule_attribute` of a rule to remove. */ @@ -118,9 +144,9 @@ export interface RemovePriceSetRulesDTO { /** * @interface - * + * * A price set to create. - * + * * @prop rules - The rules to associate with the price set. The value of `attribute` is the value of the rule's `rule_attribute` attribute. * @prop prices -The prices to create and add to this price set. */ @@ -131,9 +157,9 @@ export interface CreatePriceSetDTO { /** * @interface - * + * * The data to update in a price set. The `id` is used to identify which price set to update. - * + * * @prop id - A string indicating the ID of the price set to update. */ export interface UpdatePriceSetDTO { @@ -142,9 +168,9 @@ export interface UpdatePriceSetDTO { /** * @interface - * + * * Filters to apply on price sets. - * + * * @prop id - IDs to filter price sets by. * @prop money_amounts - Filters to apply on a price set's associated money amounts. */ diff --git a/packages/types/src/pricing/service.ts b/packages/types/src/pricing/service.ts index a5c6e29beb..0285f50837 100644 --- a/packages/types/src/pricing/service.ts +++ b/packages/types/src/pricing/service.ts @@ -1,9 +1,12 @@ import { + AddPriceListPricesDTO, AddPricesDTO, AddRulesDTO, CalculatedPriceSetDTO, CreateCurrencyDTO, CreateMoneyAmountDTO, + CreatePriceListDTO, + CreatePriceListRuleDTO, CreatePriceRuleDTO, CreatePriceSetDTO, CreatePriceSetMoneyAmountRulesDTO, @@ -11,31 +14,39 @@ import { CurrencyDTO, FilterableCurrencyProps, FilterableMoneyAmountProps, + FilterablePriceListProps, + FilterablePriceListRuleProps, FilterablePriceRuleProps, FilterablePriceSetMoneyAmountProps, FilterablePriceSetMoneyAmountRulesProps, FilterablePriceSetProps, FilterableRuleTypeProps, MoneyAmountDTO, + PriceListDTO, + PriceListRuleDTO, PriceRuleDTO, PriceSetDTO, PriceSetMoneyAmountDTO, PriceSetMoneyAmountRulesDTO, PricingContext, PricingFilters, + RemovePriceListRulesDTO, RemovePriceSetRulesDTO, RuleTypeDTO, + SetPriceListRulesDTO, UpdateCurrencyDTO, UpdateMoneyAmountDTO, + UpdatePriceListDTO, + UpdatePriceListRuleDTO, UpdatePriceRuleDTO, UpdatePriceSetDTO, UpdatePriceSetMoneyAmountRulesDTO, UpdateRuleTypeDTO, } from "./common" -import { Context } from "../shared-context" import { FindConfig } from "../common" import { ModuleJoinerConfig } from "../modules-sdk" +import { Context } from "../shared-context" export interface IPricingModuleService { /** @@ -47,22 +58,22 @@ export interface IPricingModuleService { * This method is used to calculate prices based on the provided filters and context. * * @param {PricingFilters} filters - The filters to apply on prices. - * @param {PricingContext} context - + * @param {PricingContext} context - * The context used to select the prices. For example, you can specify the region ID in this context, and only prices having the same value * will be retrieved. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The calculated price matching the context and filters provided. - * + * * @example * When you calculate prices, you must at least specify the currency code: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" * async function calculatePrice (priceSetId: string, currencyCode: string) { * const pricingService = await initializePricingModule() - * + * * const price = await pricingService.calculatePrices( * { id: [priceSetId] }, * { @@ -71,20 +82,20 @@ export interface IPricingModuleService { * } * } * ) - * + * * // do something with the price or return it * } * ``` - * + * * To calculate prices for specific minimum and/or maximum quantity: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" * async function calculatePrice (priceSetId: string, currencyCode: string) { * const pricingService = await initializePricingModule() - * + * * const price = await pricingService.calculatePrices( * { id: [priceSetId] }, * { @@ -94,20 +105,20 @@ export interface IPricingModuleService { * } * } * ) - * + * * // do something with the price or return it * } * ``` - * + * * To calculate prices for custom rule types: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" * async function calculatePrice (priceSetId: string, currencyCode: string) { * const pricingService = await initializePricingModule() - * + * * const price = await pricingService.calculatePrices( * { id: [priceSetId] }, * { @@ -117,7 +128,7 @@ export interface IPricingModuleService { * } * } * ) - * + * * // do something with the price or return it * } * ``` @@ -132,48 +143,48 @@ export interface IPricingModuleService { * This method is used to retrieves a price set by its ID. * * @param {string} id - The ID of the price set to retrieve. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price set is retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price set. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The retrieved price set. - * + * * @example * A simple example that retrieves a price set by its ID: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSet (priceSetId: string) { * const pricingService = await initializePricingModule() - * + * * const priceSet = await pricingService.retrieve( * priceSetId * ) - * + * * // do something with the price set or return it * } * ``` - * + * * To specify relations that should be retrieved: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSet (priceSetId: string) { * const pricingService = await initializePricingModule() - * + * * const priceSet = await pricingService.retrieve( * priceSetId, * { * relations: ["money_amounts"] * } * ) - * + * * // do something with the price set or return it * } * ``` @@ -188,44 +199,44 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of price sets based on optional filters and configuration. * * @param {FilterablePriceSetProps} filters - The filters to apply on the retrieved price lists. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price sets are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price set. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of price sets. - * + * * @example - * + * * To retrieve a list of price sets using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSets (priceSetIds: string[]) { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.list( * { * id: priceSetIds * }, * ) - * + * * // do something with the price sets or return them * } * ``` - * + * * To specify relations that should be retrieved within the price sets: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSets (priceSetIds: string[]) { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.list( * { * id: priceSetIds @@ -234,21 +245,21 @@ export interface IPricingModuleService { * relations: ["money_amounts"] * } * ) - * + * * // do something with the price sets or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSets (priceSetIds: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.list( * { * id: priceSetIds @@ -259,21 +270,21 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the price sets or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSets (priceSetIds: string[], moneyAmountIds: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.list( * { * $and: [ @@ -293,7 +304,7 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the price sets or return them * } * ``` @@ -306,46 +317,46 @@ export interface IPricingModuleService { /** * This method is used to retrieve a paginated list of price sets along with the total count of available price sets satisfying the provided filters. - * + * * @param {FilterablePriceSetProps} filters - The filters to apply on the retrieved price lists. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price sets are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price set. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise<[PriceSetDTO[], number]>} The list of price sets along with their total count. - * + * * @example - * + * * To retrieve a list of prices sets using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSets (priceSetIds: string[]) { * const pricingService = await initializePricingModule() - * + * * const [priceSets, count] = await pricingService.listAndCount( * { * id: priceSetIds * }, * ) - * + * * // do something with the price sets or return them * } * ``` - * + * * To specify relations that should be retrieved within the price sets: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSets (priceSetIds: string[]) { * const pricingService = await initializePricingModule() - * + * * const [priceSets, count] = await pricingService.listAndCount( * { * id: priceSetIds @@ -354,21 +365,21 @@ export interface IPricingModuleService { * relations: ["money_amounts"] * } * ) - * + * * // do something with the price sets or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSets (priceSetIds: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [priceSets, count] = await pricingService.listAndCount( * { * id: priceSetIds @@ -379,21 +390,21 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the price sets or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSets (priceSetIds: string[], moneyAmountIds: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [priceSets, count] = await pricingService.listAndCount( * { * $and: [ @@ -413,7 +424,7 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the price sets or return them * } * ``` @@ -426,22 +437,22 @@ export interface IPricingModuleService { /** * This method is used to create a new price set. - * + * * @param {CreatePriceSetDTO} data - The attributes of the price set to create. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The created price set. - * + * * @example * To create a default price set, don't pass any rules. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function createPriceSet () { * const pricingService = await initializePricingModule() - * + * * const priceSet = await pricingService.create( * { * rules: [], @@ -463,21 +474,21 @@ export interface IPricingModuleService { * ], * }, * ) - * + * * // do something with the price set or return it * } * ``` - * + * * To create a price set and associate it with rule types: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function createPriceSet () { * const pricingService = await initializePricingModule() - * + * * const priceSet = await pricingService.create( * { * rules: [{ rule_attribute: "region_id" }, { rule_attribute: "city" }], @@ -507,7 +518,7 @@ export interface IPricingModuleService { * ], * }, * ) - * + * * // do something with the price set or return it * } * ``` @@ -516,24 +527,24 @@ export interface IPricingModuleService { /** * @overload - * + * * This method is used to create multiple price sets. - * + * * @param {CreatePriceSetDTO[]} data - The price sets to create. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of created price sets. - * + * * @example * To create price sets with a default price, don't pass any rules and make sure to pass the `currency_code` of the price. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function createPriceSets () { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.create([ * { * rules: [], @@ -546,21 +557,21 @@ export interface IPricingModuleService { * ], * }, * ]) - * + * * // do something with the price sets or return them * } * ``` - * + * * To create price sets and associate them with rule types: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function createPriceSets () { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.create([ * { * rules: [{ rule_attribute: "region_id" }, { rule_attribute: "city" }], @@ -592,7 +603,7 @@ export interface IPricingModuleService { * ], * }, * ]) - * + * * // do something with the price sets or return them * } * ``` @@ -604,11 +615,11 @@ export interface IPricingModuleService { /** * @ignore - * @privateRemarks + * @privateRemarks * The update method shouldn't be documented at the moment - * + * * This method is used to update existing price sets. - * + * * @param {UpdatePriceSetDTO[]} data - The price sets to update, each having the attributes that should be updated in a price set. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of updated price sets. @@ -620,19 +631,19 @@ export interface IPricingModuleService { /** * This method remove rules from a price set. - * + * * @param {RemovePriceSetRulesDTO[]} data - The rules to remove per price set. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} Resolves when rules are successfully removed. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function removePriceSetRule (priceSetId: string, ruleAttributes: []) { * const pricingService = await initializePricingModule() - * + * * await pricingService.removeRules([ * { * id: priceSetId, @@ -652,15 +663,15 @@ export interface IPricingModuleService { * @param {string[]} ids - The IDs of the price sets to delete. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} Resolves when the price sets are successfully deleted. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function removePriceSetRule (priceSetIds: string[]) { * const pricingService = await initializePricingModule() - * + * * await pricingService.delete(priceSetIds) * } */ @@ -672,19 +683,19 @@ export interface IPricingModuleService { * @param {AddPricesDTO} data - The data defining the price set to add the prices to, along with the prices to add. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The price set that the prices were added to. - * + * * @example - * + * * To add a default price to a price set, don't pass it any rules and make sure to pass it the `currency_code`: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function addPricesToPriceSet (priceSetId: string) { * const pricingService = await initializePricingModule() - * + * * const priceSet = await pricingService.addPrices({ * priceSetId, * prices: [ @@ -695,21 +706,21 @@ export interface IPricingModuleService { * }, * ], * }) - * + * * // do something with the price set or return it * } * ``` - * + * * To add prices with rules: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function addPricesToPriceSet (priceSetId: string) { * const pricingService = await initializePricingModule() - * + * * const priceSet = await pricingService.addPrices({ * priceSetId, * prices: [ @@ -739,7 +750,7 @@ export interface IPricingModuleService { * } * ], * }) - * + * * // do something with the price set or return it * } * ``` @@ -752,19 +763,19 @@ export interface IPricingModuleService { * @param {AddPricesDTO[]} data - The data defining the prices to add per price set. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of the price sets that prices were added to. - * + * * @example - * + * * To add a default price to a price set, don't pass it any rules and make sure to pass it the `currency_code`: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function addPricesToPriceSet (priceSetId: string) { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.addPrices([{ * priceSetId, * prices: [ @@ -775,21 +786,21 @@ export interface IPricingModuleService { * }, * ], * }]) - * + * * // do something with the price sets or return them * } * ``` - * + * * To add prices with rules: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function addPricesToPriceSet (priceSetId: string) { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.addPrices([{ * priceSetId, * prices: [ @@ -819,7 +830,7 @@ export interface IPricingModuleService { * } * ], * }]) - * + * * // do something with the price sets or return them * } * ``` @@ -835,22 +846,22 @@ export interface IPricingModuleService { * @param {AddRulesDTO} data - The data defining the price set to add the rules to, along with the rules to add. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The price set that the rules were added to. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function addRulesToPriceSet (priceSetId: string) { * const pricingService = await initializePricingModule() - * + * * const priceSet = await pricingService.addRules({ * priceSetId, * rules: [{ * attribute: "region_id" * }] * }) - * + * * // do something with the price set or return it * } */ @@ -862,22 +873,22 @@ export interface IPricingModuleService { * @param {AddRulesDTO[]} data - The data defining the rules to add per price set. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of the price sets that the rules were added to. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function addRulesToPriceSet (priceSetId: string) { * const pricingService = await initializePricingModule() - * + * * const priceSets = await pricingService.addRules([{ * priceSetId, * rules: [{ * attribute: "region_id" * }] * }]) - * + * * // do something with the price sets or return them * } */ @@ -887,48 +898,48 @@ export interface IPricingModuleService { * This method retrieves a money amount by its ID. * * @param {string} id - The ID of the money amount to retrieve. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how a money amount is retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a money amount. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The retrieved money amount. - * + * * @example * To retrieve a money amount by its ID: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmount (moneyAmountId: string) { * const pricingService = await initializePricingModule() - * + * * const moneyAmount = await pricingService.retrieveMoneyAmount( * moneyAmountId, * ) - * + * * // do something with the money amount or return it * } * ``` - * + * * To retrieve relations along with the money amount: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmount (moneyAmountId: string) { * const pricingService = await initializePricingModule() - * + * * const moneyAmount = await pricingService.retrieveMoneyAmount( * moneyAmountId, * { * relations: ["currency"] * } * ) - * + * * // do something with the money amount or return it * } * ``` @@ -943,44 +954,44 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of money amounts based on optional filters and configuration. * * @param {FilterableMoneyAmountProps} filters - The filtes to apply on the retrieved money amounts. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the money amounts are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a money amount. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of money amounts. - * + * * @example - * + * * To retrieve a list of money amounts using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts (moneyAmountIds: string[]) { * const pricingService = await initializePricingModule() - * + * * const moneyAmounts = await pricingService.listMoneyAmounts( * { * id: moneyAmountIds * } * ) - * + * * // do something with the money amounts or return them * } * ``` - * + * * To specify relations that should be retrieved within the money amounts: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts (moneyAmountIds: string[]) { * const pricingService = await initializePricingModule() - * + * * const moneyAmounts = await pricingService.listMoneyAmounts( * { * id: moneyAmountIds @@ -989,21 +1000,21 @@ export interface IPricingModuleService { * relations: ["currency"] * } * ) - * + * * // do something with the money amounts or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts (moneyAmountIds: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const moneyAmounts = await pricingService.listMoneyAmounts( * { * id: moneyAmountIds @@ -1014,21 +1025,21 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the money amounts or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts (moneyAmountIds: string[], currencyCode: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const moneyAmounts = await pricingService.listMoneyAmounts( * { * $and: [ @@ -1046,7 +1057,7 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the money amounts or return them * } * ``` @@ -1061,44 +1072,44 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of money amounts along with the total count of available money amounts satisfying the provided filters. * * @param {FilterableMoneyAmountProps} filters - The filters to apply on the retrieved money amounts. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the money amounts are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a money amount. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise<[MoneyAmountDTO[], number]>} The list of money amounts along with their total count. - * + * * @example - * + * * To retrieve a list of money amounts using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts (moneyAmountIds: string[]) { * const pricingService = await initializePricingModule() - * + * * const [moneyAmounts, count] = await pricingService.listAndCountMoneyAmounts( * { * id: moneyAmountIds * } * ) - * + * * // do something with the money amounts or return them * } * ``` - * + * * To specify relations that should be retrieved within the money amounts: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts (moneyAmountIds: string[]) { * const pricingService = await initializePricingModule() - * + * * const [moneyAmounts, count] = await pricingService.listAndCountMoneyAmounts( * { * id: moneyAmountIds @@ -1107,21 +1118,21 @@ export interface IPricingModuleService { * relations: ["currency"] * } * ) - * + * * // do something with the money amounts or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts (moneyAmountIds: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [moneyAmounts, count] = await pricingService.listAndCountMoneyAmounts( * { * id: moneyAmountIds @@ -1132,21 +1143,21 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the money amounts or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts (moneyAmountIds: string[], currencyCode: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [moneyAmounts, count] = await pricingService.listAndCountMoneyAmounts( * { * $and: [ @@ -1164,7 +1175,7 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the money amounts or return them * } * ``` @@ -1181,15 +1192,15 @@ export interface IPricingModuleService { * @param {CreateMoneyAmountDTO[]} data - The money amounts to create. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of created money amounts. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveMoneyAmounts () { * const pricingService = await initializePricingModule() - * + * * const moneyAmounts = await pricingService.createMoneyAmounts([ * { * amount: 500, @@ -1202,7 +1213,7 @@ export interface IPricingModuleService { * max_quantity: 4 * } * ]) - * + * * // do something with the money amounts or return them * } */ @@ -1217,22 +1228,22 @@ export interface IPricingModuleService { * @param {UpdateMoneyAmountDTO[]} data - The money amounts to update, each having the attributes that should be updated in a money amount. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of updated money amounts. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function updateMoneyAmounts (moneyAmountId: string, amount: number) { * const pricingService = await initializePricingModule() - * + * * const moneyAmounts = await pricingService.updateMoneyAmounts([ * { - * id: moneyAmountId, + * id: moneyAmountId, * amount * } * ]) - * + * * // do something with the money amounts or return them * } */ @@ -1247,15 +1258,15 @@ export interface IPricingModuleService { * @param {string[]} ids - The IDs of the money amounts to delete. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} Resolves when the money amounts are successfully deleted. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function deleteMoneyAmounts (moneyAmountIds: string[]) { * const pricingService = await initializePricingModule() - * + * * await pricingService.deleteMoneyAmounts( * moneyAmountIds * ) @@ -1267,48 +1278,48 @@ export interface IPricingModuleService { * This method retrieves a currency by its code and and optionally based on the provided configurations. * * @param {string} code - The code of the currency to retrieve. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the currency is retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a currency. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The retrieved currency. - * + * * @example * A simple example that retrieves a currency by its code: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveCurrency (code: string) { * const pricingService = await initializePricingModule() - * + * * const currency = await pricingService.retrieveCurrency( * code * ) - * + * * // do something with the currency or return it * } * ``` - * + * * To specify attributes that should be retrieved: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveCurrency (code: string) { * const pricingService = await initializePricingModule() - * + * * const currency = await pricingService.retrieveCurrency( * code, * { * select: ["symbol_native"] * } * ) - * + * * // do something with the currency or return it * } * ``` @@ -1323,44 +1334,44 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of currencies based on optional filters and configuration. * * @param {FilterableCurrencyProps} filters - The filters to apply on the retrieved currencies. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the currencies are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a currency. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of currencies. - * + * * @example - * + * * To retrieve a list of currencies using their codes: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveCurrencies (codes: string[]) { * const pricingService = await initializePricingModule() - * + * * const currencies = await pricingService.listCurrencies( * { * code: codes * }, * ) - * + * * // do something with the currencies or return them * } * ``` - * + * * To specify attributes that should be retrieved within the money amounts: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveCurrencies (codes: string[]) { * const pricingService = await initializePricingModule() - * + * * const currencies = await pricingService.listCurrencies( * { * code: codes @@ -1369,21 +1380,21 @@ export interface IPricingModuleService { * select: ["symbol_native"] * } * ) - * + * * // do something with the currencies or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveCurrencies (codes: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const currencies = await pricingService.listCurrencies( * { * code: codes @@ -1394,7 +1405,7 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the currencies or return them * } * ``` @@ -1409,44 +1420,44 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of currencies along with the total count of available currencies satisfying the provided filters. * * @param {FilterableCurrencyProps} filters - The filters to apply on the retrieved currencies. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the currencies are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a currency. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise<[CurrencyDTO[], number]>} The list of currencies along with the total count. - * + * * @example - * + * * To retrieve a list of currencies using their codes: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveCurrencies (codes: string[]) { * const pricingService = await initializePricingModule() - * + * * const [currencies, count] = await pricingService.listAndCountCurrencies( * { * code: codes * }, * ) - * + * * // do something with the currencies or return them * } * ``` - * + * * To specify attributes that should be retrieved within the money amounts: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveCurrencies (codes: string[]) { * const pricingService = await initializePricingModule() - * + * * const [currencies, count] = await pricingService.listAndCountCurrencies( * { * code: codes @@ -1455,21 +1466,21 @@ export interface IPricingModuleService { * select: ["symbol_native"] * } * ) - * + * * // do something with the currencies or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveCurrencies (codes: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [currencies, count] = await pricingService.listAndCountCurrencies( * { * code: codes @@ -1480,7 +1491,7 @@ export interface IPricingModuleService { * take * } * ) - * + * * // do something with the currencies or return them * } * ``` @@ -1497,15 +1508,15 @@ export interface IPricingModuleService { * @param {CreateCurrencyDTO[]} data - The currencies to create. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of created currencies. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function createCurrencies () { * const pricingService = await initializePricingModule() - * + * * const currencies = await pricingService.createCurrencies([ * { * code: "USD", @@ -1514,7 +1525,7 @@ export interface IPricingModuleService { * name: "US Dollar", * } * ]) - * + * * // do something with the currencies or return them * } */ @@ -1529,22 +1540,22 @@ export interface IPricingModuleService { * @param {UpdateCurrencyDTO[]} data - The currencies to update, each having the attributes that should be updated in a currency. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of updated currencies. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function updateCurrencies () { * const pricingService = await initializePricingModule() - * + * * const currencies = await pricingService.updateCurrencies([ * { * code: "USD", * symbol: "$", * } * ]) - * + * * // do something with the currencies or return them * } */ @@ -1559,17 +1570,17 @@ export interface IPricingModuleService { * @param {string[]} currencyCodes - Currency codes of the currencies to delete. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} Resolves once the currencies are deleted. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function deleteCurrencies () { * const pricingService = await initializePricingModule() - * + * * await pricingService.deleteCurrencies(["USD"]) - * + * * } */ deleteCurrencies( @@ -1581,43 +1592,43 @@ export interface IPricingModuleService { * This method is used to retrieve a rule type by its ID and and optionally based on the provided configurations. * * @param {string} id - The ID of the rule type to retrieve. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the rule type is retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a rule type. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The retrieved rule type. - * + * * @example * A simple example that retrieves a rule type by its code: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleType (ruleTypeId: string) { * const pricingService = await initializePricingModule() - * + * * const ruleType = await pricingService.retrieveRuleType(ruleTypeId) - * + * * // do something with the rule type or return it * } * ``` - * + * * To specify attributes that should be retrieved: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleType (ruleTypeId: string) { * const pricingService = await initializePricingModule() - * + * * const ruleType = await pricingService.retrieveRuleType(ruleTypeId, { * select: ["name"] * }) - * + * * // do something with the rule type or return it * } * ``` @@ -1632,44 +1643,44 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of rule types based on optional filters and configuration. * * @param {FilterableRuleTypeProps} filters - The filters to apply on the retrieved rule types. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the rule types are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a rule type. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of rule types. - * + * * @example - * + * * To retrieve a list of rule types using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleTypes (ruleTypeId: string) { * const pricingService = await initializePricingModule() - * + * * const ruleTypes = await pricingService.listRuleTypes({ * id: [ * ruleTypeId * ] * }) - * + * * // do something with the rule types or return them * } * ``` - * + * * To specify attributes that should be retrieved within the rule types: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleTypes (ruleTypeId: string) { * const pricingService = await initializePricingModule() - * + * * const ruleTypes = await pricingService.listRuleTypes({ * id: [ * ruleTypeId @@ -1677,21 +1688,21 @@ export interface IPricingModuleService { * }, { * select: ["name"] * }) - * + * * // do something with the rule types or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleTypes (ruleTypeId: string, skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const ruleTypes = await pricingService.listRuleTypes({ * id: [ * ruleTypeId @@ -1701,21 +1712,21 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the rule types or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleTypes (ruleTypeId: string[], name: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const ruleTypes = await pricingService.listRuleTypes({ * $and: [ * { @@ -1730,7 +1741,7 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the rule types or return them * } * ``` @@ -1745,44 +1756,44 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of rule types along with the total count of available rule types satisfying the provided filters. * * @param {FilterableRuleTypeProps} filters - The filters to apply on the retrieved rule types. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the rule types are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a rule type. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise<[RuleTypeDTO[], number]>} The list of rule types along with their total count. - * + * * @example - * + * * To retrieve a list of rule types using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleTypes (ruleTypeId: string) { * const pricingService = await initializePricingModule() - * + * * const [ruleTypes, count] = await pricingService.listAndCountRuleTypes({ * id: [ * ruleTypeId * ] * }) - * + * * // do something with the rule types or return them * } * ``` - * + * * To specify attributes that should be retrieved within the rule types: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleTypes (ruleTypeId: string) { * const pricingService = await initializePricingModule() - * + * * const [ruleTypes, count] = await pricingService.listAndCountRuleTypes({ * id: [ * ruleTypeId @@ -1790,21 +1801,21 @@ export interface IPricingModuleService { * }, { * select: ["name"] * }) - * + * * // do something with the rule types or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleTypes (ruleTypeId: string, skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [ruleTypes, count] = await pricingService.listAndCountRuleTypes({ * id: [ * ruleTypeId @@ -1814,21 +1825,21 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the rule types or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrieveRuleTypes (ruleTypeId: string[], name: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [ruleTypes, count] = await pricingService.listAndCountRuleTypes({ * $and: [ * { @@ -1843,7 +1854,7 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the rule types or return them * } * ``` @@ -1860,22 +1871,22 @@ export interface IPricingModuleService { * @param {CreateRuleTypeDTO[]} data - The rule types to create. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of created rule types. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function createRuleTypes () { * const pricingService = await initializePricingModule() - * + * * const ruleTypes = await pricingService.createRuleTypes([ * { * name: "Region", * rule_attribute: "region_id" * } * ]) - * + * * // do something with the rule types or return them * } */ @@ -1890,22 +1901,22 @@ export interface IPricingModuleService { * @param {UpdateRuleTypeDTO[]} data - The rule types to update, each having the attributes that should be updated in a rule type. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of updated rule types. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function updateRuleTypes (ruleTypeId: string) { * const pricingService = await initializePricingModule() - * + * * const ruleTypes = await pricingService.updateRuleTypes([ * { * id: ruleTypeId, * name: "Region", * } * ]) - * + * * // do something with the rule types or return them * } */ @@ -1920,15 +1931,15 @@ export interface IPricingModuleService { * @param {string[]} ruleTypeIds - The IDs of the rule types to delete. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} Resolves once the rule types are deleted. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function deleteRuleTypes (ruleTypeId: string) { * const pricingService = await initializePricingModule() - * + * * await pricingService.deleteRuleTypes([ruleTypeId]) * } */ @@ -1938,43 +1949,43 @@ export interface IPricingModuleService { * This method is used to a price set money amount rule by its ID based on the provided configuration. * * @param {string} id - The ID of the price set money amount rule to retrieve. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price set money amount rule is retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price set money amount rule. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The retrieved price set money amount rule. - * + * * @example * A simple example that retrieves a price set money amount rule by its ID: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRule (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmountRule = await pricingService.retrievePriceSetMoneyAmountRules(id) - * + * * // do something with the price set money amount rule or return it * } * ``` - * + * * To specify relations that should be retrieved: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRule (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmountRule = await pricingService.retrievePriceSetMoneyAmountRules(id, { * relations: ["price_set_money_amount"] * }) - * + * * // do something with the price set money amount rule or return it * } * ``` @@ -1989,62 +2000,62 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of price set money amount rules based on optional filters and configuration. * * @param {FilterablePriceSetMoneyAmountRulesProps} filters - The filters to apply on the retrieved price set money amount rules. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price set money amount rules are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price set money amount rule. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of price set money amount rules. - * + * * @example - * + * * To retrieve a list of price set money amount rules using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRules (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmountRules = await pricingService.listPriceSetMoneyAmountRules({ * id: [id] * }) - * + * * // do something with the price set money amount rules or return them * } * ``` - * + * * To specify relations that should be retrieved within the price set money amount rules: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRules (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmountRules = await pricingService.listPriceSetMoneyAmountRules({ * id: [id] * }, { * relations: ["price_set_money_amount"] * }) - * + * * // do something with the price set money amount rules or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRules (id: string, skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmountRules = await pricingService.listPriceSetMoneyAmountRules({ * id: [id] * }, { @@ -2052,21 +2063,21 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price set money amount rules or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRules (ids: string[], ruleTypeId: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmountRules = await pricingService.listPriceSetMoneyAmountRules({ * $and: [ * { @@ -2081,7 +2092,7 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price set money amount rules or return them * } * ``` @@ -2093,66 +2104,66 @@ export interface IPricingModuleService { ): Promise /** - * This method is used to retrieve a paginated list of price set money amount rules along with the total count of + * This method is used to retrieve a paginated list of price set money amount rules along with the total count of * available price set money amount rules satisfying the provided filters. * * @param {FilterablePriceSetMoneyAmountRulesProps} filters - The filters to apply on the retrieved price set money amount rules. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price set money amount rules are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price set money amount rule. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise<[PriceSetMoneyAmountRulesDTO[], number]>} The list of price set money amount rules and their total count. - * + * * @example - * + * * To retrieve a list of price set money amounts using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRules (id: string) { * const pricingService = await initializePricingModule() - * + * * const [priceSetMoneyAmountRules, count] = await pricingService.listAndCountPriceSetMoneyAmountRules({ * id: [id] * }) - * + * * // do something with the price set money amount rules or return them * } * ``` - * + * * To specify relations that should be retrieved within the price set money amount rules: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRules (id: string) { * const pricingService = await initializePricingModule() - * + * * const [priceSetMoneyAmountRules, count] = await pricingService.listAndCountPriceSetMoneyAmountRules({ * id: [id] * }, { * relations: ["price_set_money_amount"], * }) - * + * * // do something with the price set money amount rules or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRules (id: string, skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [priceSetMoneyAmountRules, count] = await pricingService.listAndCountPriceSetMoneyAmountRules({ * id: [id] * }, { @@ -2160,21 +2171,21 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price set money amount rules or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmountRules (ids: string[], ruleTypeId: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [priceSetMoneyAmountRules, count] = await pricingService.listAndCountPriceSetMoneyAmountRules({ * $and: [ * { @@ -2189,7 +2200,7 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price set money amount rules or return them * } * ``` @@ -2204,62 +2215,62 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of price set money amounts based on optional filters and configuration. * * @param {FilterablePriceSetMoneyAmountProps} filters - The filters to apply on the retrieved price set money amounts. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price set money amounts are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price set money amount. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of price set money amounts. - * + * * @example - * + * * To retrieve a list of price set money amounts using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmounts (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmounts = await pricingService.listPriceSetMoneyAmounts({ * id: [id] * }) - * + * * // do something with the price set money amounts or return them * } * ``` - * + * * To specify relations that should be retrieved within the price set money amounts: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmounts (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmounts = await pricingService.listPriceSetMoneyAmounts({ * id: [id] * }, { * relations: ["price_rules"] * }) - * + * * // do something with the price set money amounts or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmounts (id: string, skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmounts = await pricingService.listPriceSetMoneyAmounts({ * id: [id] * }, { @@ -2267,21 +2278,21 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price set money amounts or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmounts (ids: string[], titles: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmounts = await pricingService.listPriceSetMoneyAmounts({ * $and: [ * { @@ -2296,7 +2307,7 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price set money amounts or return them * } * ``` @@ -2305,69 +2316,69 @@ export interface IPricingModuleService { filters?: FilterablePriceSetMoneyAmountProps, config?: FindConfig, sharedContext?: Context - ): Promise + ): Promise /** - * This method is used to retrieve a paginated list of price set money amounts along with the total count of + * This method is used to retrieve a paginated list of price set money amounts along with the total count of * available price set money amounts satisfying the provided filters. * * @param {FilterablePriceSetMoneyAmountProps} filters - The filters to apply on the retrieved price set money amounts. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price set money amounts are retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price set money amount. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise<[PriceSetMoneyAmountDTO[], number]>} The list of price set money amounts and their total count. - * + * * @example - * + * * To retrieve a list of price set money amounts using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmounts (id: string) { * const pricingService = await initializePricingModule() - * + * * const [priceSetMoneyAmounts, count] = await pricingService.listAndCountPriceSetMoneyAmounts({ * id: [id] * }) - * + * * // do something with the price set money amounts or return them * } * ``` - * + * * To specify relations that should be retrieved within the price set money amounts: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmounts (id: string) { * const pricingService = await initializePricingModule() - * + * * const [priceSetMoneyAmounts, count] = await pricingService.listAndCountPriceSetMoneyAmounts({ * id: [id] * }, { * relations: ["price_rules"], * }) - * + * * // do something with the price set money amounts or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmounts (id: string, skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [priceSetMoneyAmounts, count] = await pricingService.listAndCountPriceSetMoneyAmounts({ * id: [id] * }, { @@ -2375,21 +2386,21 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price set money amounts or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceSetMoneyAmounts (ids: string[], titles: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [priceSetMoneyAmounts, count] = await pricingService.listAndCountPriceSetMoneyAmounts({ * $and: [ * { @@ -2404,7 +2415,7 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price set money amounts or return them * } * ``` @@ -2413,8 +2424,7 @@ export interface IPricingModuleService { filters?: FilterablePriceSetMoneyAmountProps, config?: FindConfig, sharedContext?: Context - ): Promise<[PriceSetMoneyAmountDTO[], number]> - + ): Promise<[PriceSetMoneyAmountDTO[], number]> /** * This method is used to create new price set money amount rules. A price set money amount rule creates an association between a price set money amount and @@ -2423,15 +2433,15 @@ export interface IPricingModuleService { * @param {CreatePriceSetMoneyAmountRulesDTO[]} data - The price set money amount rules to create. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of created price set money amount rules. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function createPriceSetMoneyAmountRules (priceSetMoneyAmountId: string, ruleTypeId: string, value: string) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmountRules = await pricingService.createPriceSetMoneyAmountRules([ * { * price_set_money_amount: priceSetMoneyAmountId, @@ -2439,7 +2449,7 @@ export interface IPricingModuleService { * value * } * ]) - * + * * // do something with the price set money amount rules or return them * } */ @@ -2451,26 +2461,26 @@ export interface IPricingModuleService { /** * This method is used to update price set money amount rules, each with their provided data. * - * @param {UpdatePriceSetMoneyAmountRulesDTO[]} data - + * @param {UpdatePriceSetMoneyAmountRulesDTO[]} data - * The price set money amounts to update, each having the attributes to update in a price set money amount. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of updated price set money amount rules. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function updatePriceSetMoneyAmountRules (id: string, value: string) { * const pricingService = await initializePricingModule() - * + * * const priceSetMoneyAmountRules = await pricingService.updatePriceSetMoneyAmountRules([ * { * id, * value * } * ]) - * + * * // do something with the price set money amount rules or return them * } */ @@ -2485,15 +2495,15 @@ export interface IPricingModuleService { * @param {string[]} ids - The IDs of the price set money amount rules to delete. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} Resolves once the price set money amount rules are deleted. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function deletePriceSetMoneyAmountRule (id: string) { * const pricingService = await initializePricingModule() - * + * * await pricingService.deletePriceSetMoneyAmountRules([id]) * } */ @@ -2506,43 +2516,43 @@ export interface IPricingModuleService { * This method is used to retrieve a price rule by its ID. * * @param {string} id - The ID of the price rule to retrieve. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price rule is retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price rule. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The retrieved price rule. - * + * * @example * A simple example that retrieves a price rule by its ID: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRule (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceRule = await pricingService.retrievePriceRule(id) - * + * * // do something with the price rule or return it * } * ``` - * + * * To specify relations that should be retrieved: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRule (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceRule = await pricingService.retrievePriceRule(id, { * relations: ["price_set"] * }) - * + * * // do something with the price rule or return it * } * ``` @@ -2557,62 +2567,62 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of price rules based on optional filters and configuration. * * @param {FilterablePriceRuleProps} filters - The filters to apply on the retrieved price rules. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price rule is retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price rule. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of price rules. - * + * * @example - * + * * To retrieve a list of price rules using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRules (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceRules = await pricingService.listPriceRules({ * id: [id] * }) - * + * * // do something with the price rules or return them * } * ``` - * + * * To specify relations that should be retrieved within the price rules: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRules (id: string) { * const pricingService = await initializePricingModule() - * + * * const priceRules = await pricingService.listPriceRules({ * id: [id], * }, { * relations: ["price_set"] * }) - * + * * // do something with the price rules or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRules (id: string, skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const priceRules = await pricingService.listPriceRules({ * id: [id], * }, { @@ -2620,21 +2630,21 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price rules or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRules (ids: string[], name: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const priceRules = await pricingService.listPriceRules({ * $and: [ * { @@ -2649,7 +2659,7 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price rules or return them * } * ``` @@ -2664,62 +2674,62 @@ export interface IPricingModuleService { * This method is used to retrieve a paginated list of price rules along with the total count of available price rules satisfying the provided filters. * * @param {FilterablePriceRuleProps} filters - The filters to apply on the retrieved price rules. - * @param {FindConfig} config - + * @param {FindConfig} config - * The configurations determining how the price rule is retrieved. Its properties, such as `select` or `relations`, accept the * attributes or relations associated with a price rule. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of price rules along with their total count. - * + * * @example - * + * * To retrieve a list of price rules using their IDs: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRules (id: string) { * const pricingService = await initializePricingModule() - * + * * const [priceRules, count] = await pricingService.listAndCountPriceRules({ * id: [id] * }) - * + * * // do something with the price rules or return them * } * ``` - * + * * To specify relations that should be retrieved within the price rules: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRules (id: string) { * const pricingService = await initializePricingModule() - * + * * const [priceRules, count] = await pricingService.listAndCountPriceRules({ * id: [id], * }, { * relations: ["price_set"] * }) - * + * * // do something with the price rules or return them * } * ``` - * + * * By default, only the first `15` records are retrieved. You can control pagination by specifying the `skip` and `take` properties of the `config` parameter: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRules (id: string, skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [priceRules, count] = await pricingService.listAndCountPriceRules({ * id: [id], * }, { @@ -2727,21 +2737,21 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price rules or return them * } * ``` - * + * * You can also use the `$and` or `$or` properties of the `filter` parameter to use and/or conditions in your filters. For example: - * + * * ```ts - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function retrievePriceRules (ids: string[], name: string[], skip: number, take: number) { * const pricingService = await initializePricingModule() - * + * * const [priceRules, count] = await pricingService.listAndCountPriceRules({ * $and: [ * { @@ -2756,7 +2766,7 @@ export interface IPricingModuleService { * skip, * take * }) - * + * * // do something with the price rules or return them * } * ``` @@ -2773,22 +2783,22 @@ export interface IPricingModuleService { * @param {CreatePriceRuleDTO[]} data - The price rules to create. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of created price rules. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function createPriceRules ( * id: string, - * priceSetId: string, - * ruleTypeId: string, - * value: string, - * priceSetMoneyAmountId: string, + * priceSetId: string, + * ruleTypeId: string, + * value: string, + * priceSetMoneyAmountId: string, * priceListId: string * ) { * const pricingService = await initializePricingModule() - * + * * const priceRules = await pricingService.createPriceRules([ * { * id, @@ -2799,7 +2809,7 @@ export interface IPricingModuleService { * price_list_id: priceListId * } * ]) - * + * * // do something with the price rules or return them * } */ @@ -2814,25 +2824,25 @@ export interface IPricingModuleService { * @param {UpdatePriceRuleDTO[]} data - The price rules to update, each having attributes that should be updated in a price rule. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} The list of updated price rules. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function updatePriceRules ( * id: string, - * priceSetId: string, + * priceSetId: string, * ) { * const pricingService = await initializePricingModule() - * + * * const priceRules = await pricingService.updatePriceRules([ * { * id, * price_set_id: priceSetId, * } * ]) - * + * * // do something with the price rules or return them * } */ @@ -2847,17 +2857,17 @@ export interface IPricingModuleService { * @param {string[]} priceRuleIds - The IDs of the price rules to delete. * @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module. * @returns {Promise} Resolves once the price rules are deleted. - * + * * @example - * import { + * import { * initialize as initializePricingModule, * } from "@medusajs/pricing" - * + * * async function deletePriceRules ( * id: string, * ) { * const pricingService = await initializePricingModule() - * + * * await pricingService.deletePriceRules([id]) * } */ @@ -2865,4 +2875,85 @@ export interface IPricingModuleService { priceRuleIds: string[], sharedContext?: Context ): Promise + + retrievePriceList( + id: string, + config?: FindConfig, + sharedContext?: Context + ): Promise + + listPriceLists( + filters?: FilterablePriceListProps, + config?: FindConfig, + sharedContext?: Context + ): Promise + + listAndCountPriceLists( + filters?: FilterablePriceListProps, + config?: FindConfig, + sharedContext?: Context + ): Promise<[PriceListDTO[], number]> + + createPriceLists( + data: CreatePriceListDTO[], + sharedContext?: Context + ): Promise + + updatePriceLists( + data: UpdatePriceListDTO[], + sharedContext?: Context + ): Promise + + deletePriceLists( + priceListIds: string[], + sharedContext?: Context + ): Promise + + retrievePriceListRule( + id: string, + config?: FindConfig, + sharedContext?: Context + ): Promise + + listPriceListRules( + filters?: FilterablePriceListRuleProps, + config?: FindConfig, + sharedContext?: Context + ): Promise + + listAndCountPriceListRules( + filters?: FilterablePriceListRuleProps, + config?: FindConfig, + sharedContext?: Context + ): Promise<[PriceListRuleDTO[], number]> + + createPriceListRules( + data: CreatePriceListRuleDTO[], + sharedContext?: Context + ): Promise + + updatePriceListRules( + data: UpdatePriceListRuleDTO[], + sharedContext?: Context + ): Promise + + deletePriceListRules( + priceListRuleIds: string[], + sharedContext?: Context + ): Promise + + addPriceListPrices( + data: AddPriceListPricesDTO[], + sharedContext?: Context + ): Promise + + setPriceListRules( + data: SetPriceListRulesDTO, + sharedContext?: Context + ): Promise + + removePriceListRules( + data: RemovePriceListRulesDTO, + sharedContext?: Context + ): Promise } diff --git a/packages/utils/src/common/array-difference.ts b/packages/utils/src/common/array-difference.ts new file mode 100644 index 0000000000..5aef456e7e --- /dev/null +++ b/packages/utils/src/common/array-difference.ts @@ -0,0 +1,15 @@ +type ArrayDifferenceElement = string | number + +export function arrayDifference( + mainArray: ArrayDifferenceElement[], + differingArray: ArrayDifferenceElement[] +): ArrayDifferenceElement[] { + const mainArraySet = new Set(mainArray) + const differingArraySet = new Set(differingArray) + + const difference = [...mainArraySet].filter( + (element) => !differingArraySet.has(element) + ) + + return difference +} diff --git a/packages/utils/src/common/index.ts b/packages/utils/src/common/index.ts index e8d05ecf90..d901a1baf1 100644 --- a/packages/utils/src/common/index.ts +++ b/packages/utils/src/common/index.ts @@ -1,6 +1,9 @@ +export * from "./array-difference" export * from "./build-query" +export * from "./camel-to-snake-case" export * from "./container" export * from "./deduplicate" +export * from "./deep-equal-obj" export * from "./errors" export * from "./generate-entity-id" export * from "./get-config-file" @@ -24,11 +27,9 @@ export * from "./set-metadata" export * from "./simple-hash" export * from "./string-to-select-relation-object" export * from "./stringify-circular" -export * from "./camel-to-snake-case" export * from "./to-camel-case" export * from "./to-kebab-case" export * from "./to-pascal-case" export * from "./transaction" export * from "./upper-case-first" export * from "./wrap-handler" -export * from "./deep-equal-obj" diff --git a/packages/utils/src/pricing/index.ts b/packages/utils/src/pricing/index.ts index 28ab69825a..f62072ad0d 100644 --- a/packages/utils/src/pricing/index.ts +++ b/packages/utils/src/pricing/index.ts @@ -1,38 +1,2 @@ -import { MedusaError } from "../common" - -export const ReservedPricingRuleAttributes = [ - "quantity", - "currency_code", - "price_list_id", -] - -type RuleAttributeInput = string | undefined - -export const getInvalidRuleAttributes = ( - ruleAttributes: RuleAttributeInput[] -): string[] => { - const invalidRuleAttributes: string[] = [] - - for (const attribute of ReservedPricingRuleAttributes) { - if (ruleAttributes.indexOf(attribute) > -1) { - invalidRuleAttributes.push(attribute) - } - } - - return invalidRuleAttributes -} - -export const validateRuleAttributes = ( - ruleAttributes: RuleAttributeInput[] -): void => { - const invalidRuleAttributes = getInvalidRuleAttributes(ruleAttributes) - - if (invalidRuleAttributes.length) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - `Can't create rule_attribute with reserved keywords [${ReservedPricingRuleAttributes.join( - ", " - )}] - ${invalidRuleAttributes.join(", ")}` - ) - } -} +export * from "./price-list" +export * from "./rule-type" diff --git a/packages/utils/src/pricing/price-list.ts b/packages/utils/src/pricing/price-list.ts new file mode 100644 index 0000000000..a91488a9d5 --- /dev/null +++ b/packages/utils/src/pricing/price-list.ts @@ -0,0 +1,9 @@ +export enum PriceListStatus { + ACTIVE = "active", + DRAFT = "draft", +} + +export enum PriceListType { + SALE = "sale", + OVERRIDE = "override", +} diff --git a/packages/utils/src/pricing/rule-type.ts b/packages/utils/src/pricing/rule-type.ts new file mode 100644 index 0000000000..ad386e8610 --- /dev/null +++ b/packages/utils/src/pricing/rule-type.ts @@ -0,0 +1,37 @@ +import { MedusaError } from "../common" +type RuleAttributeInput = string | undefined + +export const ReservedPricingRuleAttributes = [ + "quantity", + "currency_code", + "price_list_id", +] + +export const getInvalidRuleAttributes = ( + ruleAttributes: RuleAttributeInput[] +): string[] => { + const invalidRuleAttributes: string[] = [] + + for (const attribute of ReservedPricingRuleAttributes) { + if (ruleAttributes.indexOf(attribute) > -1) { + invalidRuleAttributes.push(attribute) + } + } + + return invalidRuleAttributes +} + +export const validateRuleAttributes = ( + ruleAttributes: RuleAttributeInput[] +): void => { + const invalidRuleAttributes = getInvalidRuleAttributes(ruleAttributes) + + if (invalidRuleAttributes.length) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Can't create rule_attribute with reserved keywords [${ReservedPricingRuleAttributes.join( + ", " + )}] - ${invalidRuleAttributes.join(", ")}` + ) + } +}