From 9c7c1d48c7779023172d5e7003674b2d7107b733 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Tue, 30 Sep 2025 14:38:04 +0200 Subject: [PATCH] fix(): Pricing preference context loss (#13626) **What** The context reference is being mutated by the repository leading to an empty context. Also, the filter is built from the pricing context instead of pricing context -> context leading to always fetch all preferences all the time --- .changeset/cyan-horses-join.md | 7 ++ .../src/pricing/common/price-preference.ts | 8 +- .../types/src/pricing/common/price-rule.ts | 19 ++-- .../pricing/src/repositories/pricing.ts | 2 +- .../pricing/src/services/pricing-module.ts | 99 ++++++++++++++++--- packages/plugins/draft-order/package.json | 4 +- yarn.lock | 37 +------ 7 files changed, 111 insertions(+), 65 deletions(-) create mode 100644 .changeset/cyan-horses-join.md diff --git a/.changeset/cyan-horses-join.md b/.changeset/cyan-horses-join.md new file mode 100644 index 0000000000..fb3db8543b --- /dev/null +++ b/.changeset/cyan-horses-join.md @@ -0,0 +1,7 @@ +--- +"@medusajs/pricing": patch +"@medusajs/draft-order": patch +"@medusajs/types": patch +--- + +fix(): Pricing preference context loss diff --git a/packages/core/types/src/pricing/common/price-preference.ts b/packages/core/types/src/pricing/common/price-preference.ts index 8ecc9d66a9..6c476b4320 100644 --- a/packages/core/types/src/pricing/common/price-preference.ts +++ b/packages/core/types/src/pricing/common/price-preference.ts @@ -1,4 +1,4 @@ -import { BaseFilterable } from "../../dal" +import { BaseFilterable, OperatorMap } from "../../dal" /** * @interface @@ -83,13 +83,13 @@ export interface FilterablePricePreferenceProps /** * The IDs to filter the price preferences by. */ - id?: string[] + id?: string | string[] | OperatorMap /** * Attributes to filter price preferences by. */ - attribute?: string | string[] + attribute?: string | string[] | OperatorMap /** * Values to filter price preferences by. */ - value?: string | string[] + value?: string | string[] | OperatorMap } diff --git a/packages/core/types/src/pricing/common/price-rule.ts b/packages/core/types/src/pricing/common/price-rule.ts index bcf981b88b..1b216f7a2b 100644 --- a/packages/core/types/src/pricing/common/price-rule.ts +++ b/packages/core/types/src/pricing/common/price-rule.ts @@ -1,4 +1,4 @@ -import { BaseFilterable } from "../../dal" +import { BaseFilterable, OperatorMap } from "../../dal" import { PriceSetDTO } from "./price-set" /** @@ -123,15 +123,20 @@ export interface FilterablePriceRuleProps /** * The IDs to filter price rules by. */ - id?: string[] + id?: string | string[] | OperatorMap /** * The names to filter price rules by. */ - name?: string[] + name?: string | string[] | OperatorMap /** * The IDs to filter the price rule's associated price set. */ - price_set_id?: string[] + price_set_id?: string | string[] | OperatorMap + + /** + * The IDs to filter the price rule's associated price. + */ + price_id?: string | string[] | OperatorMap } /** @@ -145,21 +150,21 @@ export type PricingRuleOperatorValues = "gt" | "lt" | "eq" | "lte" | "gte" export interface PriceRule { /** * The attribute to compare. - * + * * @example * amount */ attribute: string /** * The operator to use in the comparison. - * + * * @example * gt */ operator: PricingRuleOperatorValues /** * The value to compare against. - * + * * @example * 100 */ diff --git a/packages/modules/pricing/src/repositories/pricing.ts b/packages/modules/pricing/src/repositories/pricing.ts index bd7eef0de9..343b224285 100644 --- a/packages/modules/pricing/src/repositories/pricing.ts +++ b/packages/modules/pricing/src/repositories/pricing.ts @@ -69,7 +69,7 @@ export class PricingRepository ): Promise { const manager = this.getActiveManager(sharedContext) const knex = manager.getKnex() - const context = pricingContext.context || {} + const context = { ...(pricingContext.context || {}) } // Extract quantity and currency from context const quantity = context.quantity diff --git a/packages/modules/pricing/src/services/pricing-module.ts b/packages/modules/pricing/src/services/pricing-module.ts index 91356bdd8b..546dfe8357 100644 --- a/packages/modules/pricing/src/services/pricing-module.ts +++ b/packages/modules/pricing/src/services/pricing-module.ts @@ -9,6 +9,7 @@ import { FindConfig, InferEntityType, InternalModuleDeclaration, + MedusaContainer, ModuleJoinerConfig, ModulesSdkTypes, PricePreferenceDTO, @@ -95,6 +96,7 @@ export default class PricingModuleService extends BaseClass implements PricingTypes.IPricingModuleService { + protected readonly container_: MedusaContainer protected baseRepository_: DAL.RepositoryService protected readonly pricingRepository_: PricingRepositoryService & { clearAvailableAttributes?: () => Promise @@ -134,6 +136,7 @@ export default class PricingModuleService // @ts-ignore super(...arguments) + this.container_ = arguments[0] this.baseRepository_ = baseRepository this.pricingRepository_ = pricingRepository this.priceSetService_ = priceSetService @@ -327,6 +330,62 @@ export default class PricingModuleService return [serializedPriceSets, count] } + @InjectManager() + // @ts-expect-error + async listPriceRules( + filters: PricingTypes.FilterablePriceRuleProps, + config: FindConfig = {}, + sharedContext?: Context + ): Promise { + const priceRules = await this.listPriceRules_( + filters, + config, + sharedContext + ) + + return await this.baseRepository_.serialize( + priceRules + ) + } + + protected async listPriceRules_( + filters: PricingTypes.FilterablePriceRuleProps, + config: FindConfig = {}, + sharedContext?: Context + ): Promise[]> { + return await this.priceRuleService_.list(filters, config, sharedContext) + } + + @InjectManager() + // @ts-expect-error + async listPricePreferences( + filters: PricingTypes.FilterablePricePreferenceProps, + config: FindConfig = {}, + sharedContext?: Context + ): Promise { + const pricePreferences = await this.listPricePreferences_( + filters, + { ...config, select: [...(config.select || []), "id"] }, + sharedContext + ) + + return await this.baseRepository_.serialize< + PricingTypes.PricePreferenceDTO[] + >(pricePreferences) + } + + protected async listPricePreferences_( + filters: PricingTypes.FilterablePricePreferenceProps, + config: FindConfig = {}, + sharedContext?: Context + ): Promise[]> { + return await this.pricePreferenceService_.list( + filters, + config, + sharedContext + ) + } + @InjectManager() async calculatePrices( pricingFilters: PricingFilters, @@ -395,29 +454,37 @@ export default class PricingModuleService ) // We use the price rules to get the right preferences for the price - const priceRulesForPrices = await this.priceRuleService_.list( + const priceRulesForPrices = await this.listPriceRules( { price_id: priceIds }, - {} + {}, + sharedContext ) const priceRulesPriceMap = groupBy(priceRulesForPrices, "price_id") // Note: For now the preferences are intentionally kept very simple and explicit - they use either the region or currency, // so we hard-code those as the possible filters here. This can be made more flexible if needed later on. - const pricingPreferences = await this.pricePreferenceService_.list( - { - $or: Object.entries(pricingContext) - .filter(([key, val]) => { - return key === "region_id" || key === "currency_code" - }) - .map(([key, val]) => ({ - attribute: key, - value: val, - })), - }, - {}, - sharedContext - ) + const preferenceContext = Object.entries( + pricingContext.context ?? {} + ).filter(([key, val]) => { + return key === "region_id" || key === "currency_code" + }) + let pricingPreferences: InferEntityType[] = [] + if (preferenceContext.length) { + const preferenceFilters = preferenceContext.length + ? { + $or: preferenceContext.map(([key, val]) => ({ + attribute: key, + value: val, + })), + } + : {} + pricingPreferences = await this.listPricePreferences_( + preferenceFilters as PricingTypes.FilterablePricePreferenceProps, + {}, + sharedContext + ) + } const calculatedPrices: PricingTypes.CalculatedPriceSet[] = [] diff --git a/packages/plugins/draft-order/package.json b/packages/plugins/draft-order/package.json index d4fa3fc6e4..4cd1bb766b 100644 --- a/packages/plugins/draft-order/package.json +++ b/packages/plugins/draft-order/package.json @@ -51,7 +51,7 @@ "@medusajs/icons": "2.10.3", "@medusajs/test-utils": "2.10.3", "@medusajs/types": "2.10.3", - "@medusajs/ui": "4.0.21", + "@medusajs/ui": "4.0.23", "@medusajs/ui-preset": "2.10.3", "@swc/core": "1.5.7", "@types/lodash": "^4.17.15", @@ -74,7 +74,7 @@ "@medusajs/framework": "2.10.3", "@medusajs/icons": "2.10.3", "@medusajs/test-utils": "2.10.3", - "@medusajs/ui": "4.0.21", + "@medusajs/ui": "4.0.23", "lodash": "^4.17.21", "react-router-dom": "6.20.1" }, diff --git a/yarn.lock b/yarn.lock index 717647a3c9..c8d2161f09 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6417,7 +6417,7 @@ __metadata: "@medusajs/js-sdk": 2.10.3 "@medusajs/test-utils": 2.10.3 "@medusajs/types": 2.10.3 - "@medusajs/ui": 4.0.21 + "@medusajs/ui": 4.0.23 "@medusajs/ui-preset": 2.10.3 "@swc/core": 1.5.7 "@tanstack/react-query": 5.64.2 @@ -6445,7 +6445,7 @@ __metadata: "@medusajs/framework": 2.10.3 "@medusajs/icons": 2.10.3 "@medusajs/test-utils": 2.10.3 - "@medusajs/ui": 4.0.21 + "@medusajs/ui": 4.0.23 lodash: ^4.17.21 react-router-dom: 6.20.1 languageName: unknown @@ -6663,15 +6663,6 @@ __metadata: languageName: unknown linkType: soft -"@medusajs/icons@npm:2.10.1": - version: 2.10.1 - resolution: "@medusajs/icons@npm:2.10.1" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - checksum: 280e7174376a1e36db5376a2efee659a6b4f1002bd9136a6966c039587373688c4f1239830873c9d592bc570592ddc014037d7af6d9d075e5093ae6c74d0f351 - languageName: node - linkType: hard - "@medusajs/index@2.10.3, @medusajs/index@workspace:packages/modules/index": version: 0.0.0-use.local resolution: "@medusajs/index@workspace:packages/modules/index" @@ -7404,30 +7395,6 @@ __metadata: languageName: unknown linkType: soft -"@medusajs/ui@npm:4.0.21": - version: 4.0.21 - resolution: "@medusajs/ui@npm:4.0.21" - dependencies: - "@medusajs/icons": 2.10.1 - "@tanstack/react-table": 8.20.5 - clsx: ^1.2.1 - copy-to-clipboard: ^3.3.3 - cva: 1.0.0-beta.1 - prism-react-renderer: ^2.0.6 - prismjs: ^1.29.0 - radix-ui: 1.1.2 - react-aria: ^3.33.1 - react-currency-input-field: ^3.6.11 - react-stately: ^3.31.1 - sonner: ^1.5.0 - tailwind-merge: ^2.2.1 - peerDependencies: - react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc - react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc - checksum: 9e5af539201aa2e50090134a492a992e01233943a39c7d21e8193469c4d379c6a0fce55cc2541cb4ce4e93813bb76015e9256043769a551a8c0cad338774512d - languageName: node - linkType: hard - "@medusajs/user@2.10.3, @medusajs/user@workspace:^, @medusajs/user@workspace:packages/modules/user": version: 0.0.0-use.local resolution: "@medusajs/user@workspace:packages/modules/user"