diff --git a/.changeset/big-chefs-dream.md b/.changeset/big-chefs-dream.md new file mode 100644 index 0000000000000..caf4dd22c9ff9 --- /dev/null +++ b/.changeset/big-chefs-dream.md @@ -0,0 +1,6 @@ +--- +"@medusajs/pricing": patch +"@medusajs/product": patch +--- + +feat(product, pricing, utils): Transaction issues and reference issues + refactoring diff --git a/.github/actions/setup-server/action.yml b/.github/actions/setup-server/action.yml index 3b494931f01b9..806d266a219a9 100644 --- a/.github/actions/setup-server/action.yml +++ b/.github/actions/setup-server/action.yml @@ -5,7 +5,7 @@ inputs: node-version: description: "Node version" required: false - default: "16.20.2" + default: "16.10.0" cache-extension: description: "Extension for fetching cached dependencies" required: true diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index c10d96f5a7a60..5abe7f362b4cf 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Assert changed @@ -48,7 +48,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Assert changed @@ -171,7 +171,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies @@ -223,7 +223,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies @@ -275,7 +275,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies diff --git a/.github/workflows/codegen-test.yml b/.github/workflows/codegen-test.yml index 81afbb1f04d33..9f0a5913173c1 100644 --- a/.github/workflows/codegen-test.yml +++ b/.github/workflows/codegen-test.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies diff --git a/.github/workflows/docs-freshness-check.yml b/.github/workflows/docs-freshness-check.yml index bf0268921e629..45e88dbb60357 100644 --- a/.github/workflows/docs-freshness-check.yml +++ b/.github/workflows/docs-freshness-check.yml @@ -24,7 +24,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies diff --git a/.github/workflows/docs-new-announcement.yml b/.github/workflows/docs-new-announcement.yml index c4f68a8eca26e..457feaf4553fa 100644 --- a/.github/workflows/docs-new-announcement.yml +++ b/.github/workflows/docs-new-announcement.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies diff --git a/.github/workflows/docs-remove-announcement.yml b/.github/workflows/docs-remove-announcement.yml index 0f141bd8d2b27..06bf788c9cbb5 100644 --- a/.github/workflows/docs-remove-announcement.yml +++ b/.github/workflows/docs-remove-announcement.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies diff --git a/.github/workflows/generate-references.yml b/.github/workflows/generate-references.yml index 046cfe9f52616..a6bbb23354567 100644 --- a/.github/workflows/generate-references.yml +++ b/.github/workflows/generate-references.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies @@ -89,7 +89,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies diff --git a/.github/workflows/oas-test.yml b/.github/workflows/oas-test.yml index ec4e1a17eb189..f5f253bbf3fd8 100644 --- a/.github/workflows/oas-test.yml +++ b/.github/workflows/oas-test.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Install dependencies diff --git a/.github/workflows/release-notifications.yml b/.github/workflows/release-notifications.yml index 3c677890752d2..1551a3c4c0f79 100644 --- a/.github/workflows/release-notifications.yml +++ b/.github/workflows/release-notifications.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Node.js environment uses: actions/setup-node@v3 with: - node-version: "16.20.2" + node-version: "16.10.0" cache: "yarn" - name: Post to Slack channel diff --git a/integration-tests/environment-helpers/bootstrap-app.js b/integration-tests/environment-helpers/bootstrap-app.js index 1ea6169a42f76..6fc21793fc27a 100644 --- a/integration-tests/environment-helpers/bootstrap-app.js +++ b/integration-tests/environment-helpers/bootstrap-app.js @@ -54,6 +54,7 @@ module.exports = { const shutdown = async () => { await Promise.all([ + container.dispose(), expressServer.close(), db?.destroy(), pgConnection?.context?.destroy(), diff --git a/integration-tests/plugins/__tests__/product/admin/create-product.ts b/integration-tests/plugins/__tests__/product/admin/create-product.ts index ef1af2b397282..8fa4dbd136bfe 100644 --- a/integration-tests/plugins/__tests__/product/admin/create-product.ts +++ b/integration-tests/plugins/__tests__/product/admin/create-product.ts @@ -22,7 +22,7 @@ const env = { MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN: true, } -describe.skip("[Product & Pricing Module] POST /admin/products", () => { +describe("[Product & Pricing Module] POST /admin/products", () => { let dbConnection let appContainer let shutdownServer diff --git a/integration-tests/plugins/__tests__/product/admin/update-product-variant.spec.ts b/integration-tests/plugins/__tests__/product/admin/update-product-variant.spec.ts index 7fc8bc53e18c1..b65d75560ac4b 100644 --- a/integration-tests/plugins/__tests__/product/admin/update-product-variant.spec.ts +++ b/integration-tests/plugins/__tests__/product/admin/update-product-variant.spec.ts @@ -26,7 +26,7 @@ const env = { MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN: true, } -describe.skip("[Product & Pricing Module] POST /admin/products/:id/variants/:id", () => { +describe("[Product & Pricing Module] POST /admin/products/:id/variants/:id", () => { let dbConnection let appContainer let shutdownServer diff --git a/integration-tests/plugins/__tests__/product/admin/update-product.spec.ts b/integration-tests/plugins/__tests__/product/admin/update-product.spec.ts index c88f37457e786..547a531ed9166 100644 --- a/integration-tests/plugins/__tests__/product/admin/update-product.spec.ts +++ b/integration-tests/plugins/__tests__/product/admin/update-product.spec.ts @@ -24,7 +24,7 @@ const env = { MEDUSA_FF_ISOLATE_PRODUCT_DOMAIN: true, } -describe.skip("[Product & Pricing Module] POST /admin/products/:id", () => { +describe("[Product & Pricing Module] POST /admin/products/:id", () => { let dbConnection let appContainer let shutdownServer @@ -172,9 +172,7 @@ describe.skip("[Product & Pricing Module] POST /admin/products/:id", () => { ], } - console.log("I am here first") await api.post(`/admin/products/${product.id}`, data, adminHeaders) - console.log("I am here") const response = await api.get( `/admin/products/${product.id}`, diff --git a/integration-tests/plugins/__tests__/workflows/product/update-product.ts b/integration-tests/plugins/__tests__/workflows/product/update-product.ts index 26e0863f60ff8..987637892015b 100644 --- a/integration-tests/plugins/__tests__/workflows/product/update-product.ts +++ b/integration-tests/plugins/__tests__/workflows/product/update-product.ts @@ -12,7 +12,7 @@ import { getContainer } from "../../../../environment-helpers/use-container" import { initDb, useDb } from "../../../../environment-helpers/use-db" import { simpleProductFactory } from "../../../../factories" -jest.setTimeout(30000) +jest.setTimeout(100000) describe("UpdateProduct workflow", function () { let dbConnection diff --git a/integration-tests/plugins/jest.config.js b/integration-tests/plugins/jest.config.js index 0134f98931790..17a23c30b82af 100644 --- a/integration-tests/plugins/jest.config.js +++ b/integration-tests/plugins/jest.config.js @@ -9,7 +9,7 @@ module.exports = { `/www/`, `/dist/`, `/node_modules/`, - "/node_modules", + `/node_modules/`, `__tests__/fixtures`, `__testfixtures__`, `.cache`, diff --git a/integration-tests/plugins/package.json b/integration-tests/plugins/package.json index 0b1b18de06da5..e3c07aa393c05 100644 --- a/integration-tests/plugins/package.json +++ b/integration-tests/plugins/package.json @@ -5,7 +5,7 @@ "license": "MIT", "private": true, "scripts": { - "test:integration": "node --expose-gc ./../../node_modules/.bin/jest --silent=false --runInBand --bail --logHeapUsage --forceExit", + "test:integration": "node --expose-gc ./../../node_modules/.bin/jest --silent=false --runInBand --bail --detectOpenHandles --logHeapUsage --clearMocks --no-compilation-cache --forceExit", "build": "babel src -d dist --extensions \".ts,.js\"" }, "dependencies": { diff --git a/packages/pricing/src/repositories/pricing.ts b/packages/pricing/src/repositories/pricing.ts index f2766b7efa142..b27c0be1266cb 100644 --- a/packages/pricing/src/repositories/pricing.ts +++ b/packages/pricing/src/repositories/pricing.ts @@ -27,7 +27,7 @@ export class PricingRepository pricingContext: PricingContext = { context: {} }, sharedContext: Context = {} ): Promise { - const manager = this.getActiveManager() + const manager = this.getActiveManager(sharedContext) const knex = manager.getKnex() const context = pricingContext.context || {} diff --git a/packages/pricing/src/services/currency.ts b/packages/pricing/src/services/currency.ts index cf806794c4630..53a87037dcb45 100644 --- a/packages/pricing/src/services/currency.ts +++ b/packages/pricing/src/services/currency.ts @@ -9,8 +9,6 @@ import { import { Currency } from "@models" import { CurrencyRepository } from "@repositories" -import { doNotForceTransaction, shouldForceTransaction } from "@medusajs/utils" - type InjectedDependencies = { currencyRepository: DAL.RepositoryService } @@ -75,7 +73,7 @@ export default class CurrencyService { return queryOptions } - @InjectTransactionManager(shouldForceTransaction, "currencyRepository_") + @InjectTransactionManager("currencyRepository_") async create( data: PricingTypes.CreateCurrencyDTO[], @MedusaContext() sharedContext: Context = {} @@ -86,7 +84,7 @@ export default class CurrencyService { )) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "currencyRepository_") + @InjectTransactionManager("currencyRepository_") async update( data: PricingTypes.UpdateCurrencyDTO[], @MedusaContext() sharedContext: Context = {} @@ -97,7 +95,7 @@ export default class CurrencyService { )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "currencyRepository_") + @InjectTransactionManager("currencyRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/pricing/src/services/money-amount.ts b/packages/pricing/src/services/money-amount.ts index 9ef7655561c03..78ce4cfa13379 100644 --- a/packages/pricing/src/services/money-amount.ts +++ b/packages/pricing/src/services/money-amount.ts @@ -9,8 +9,6 @@ import { import { MoneyAmount } from "@models" import { MoneyAmountRepository } from "@repositories" -import { doNotForceTransaction, shouldForceTransaction } from "@medusajs/utils" - type InjectedDependencies = { moneyAmountRepository: DAL.RepositoryService } @@ -73,7 +71,7 @@ export default class MoneyAmountService< )) as [TEntity[], number] } - @InjectTransactionManager(shouldForceTransaction, "moneyAmountRepository_") + @InjectTransactionManager("moneyAmountRepository_") async create( data: PricingTypes.CreateMoneyAmountDTO[], @MedusaContext() sharedContext: Context = {} @@ -84,7 +82,7 @@ export default class MoneyAmountService< )) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "moneyAmountRepository_") + @InjectTransactionManager("moneyAmountRepository_") async update( data: PricingTypes.UpdateMoneyAmountDTO[], @MedusaContext() sharedContext: Context = {} @@ -95,7 +93,7 @@ export default class MoneyAmountService< )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "moneyAmountRepository_") + @InjectTransactionManager("moneyAmountRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/pricing/src/services/price-rule.ts b/packages/pricing/src/services/price-rule.ts index 0fc7c8f2c0a3c..7735d2a0c5c4a 100644 --- a/packages/pricing/src/services/price-rule.ts +++ b/packages/pricing/src/services/price-rule.ts @@ -4,9 +4,7 @@ import { InjectTransactionManager, MedusaContext, ModulesSdkUtils, - doNotForceTransaction, retrieveEntity, - shouldForceTransaction, } from "@medusajs/utils" import { PriceRule } from "@models" import { PriceRuleRepository } from "@repositories" @@ -65,7 +63,7 @@ export default class PriceRuleService { )) as [TEntity[], number] } - @InjectTransactionManager(shouldForceTransaction, "priceRuleRepository_") + @InjectTransactionManager("priceRuleRepository_") async create( data: PricingTypes.CreatePriceRuleDTO[], @MedusaContext() sharedContext: Context = {} @@ -76,7 +74,7 @@ export default class PriceRuleService { )) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "priceRuleRepository_") + @InjectTransactionManager("priceRuleRepository_") async update( data: PricingTypes.UpdatePriceRuleDTO[], @MedusaContext() sharedContext: Context = {} @@ -87,7 +85,7 @@ export default class PriceRuleService { )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "priceRuleRepository_") + @InjectTransactionManager("priceRuleRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/pricing/src/services/price-set-money-amount-rules.ts b/packages/pricing/src/services/price-set-money-amount-rules.ts index a8c2eb4151c78..883d580fe1c81 100644 --- a/packages/pricing/src/services/price-set-money-amount-rules.ts +++ b/packages/pricing/src/services/price-set-money-amount-rules.ts @@ -8,8 +8,6 @@ import { } from "@medusajs/utils" import { PriceSetMoneyAmountRules } from "@models" -import { doNotForceTransaction, shouldForceTransaction } from "@medusajs/utils" - type InjectedDependencies = { priceSetMoneyAmountRulesRepository: DAL.RepositoryService } @@ -79,10 +77,7 @@ export default class PriceSetMoneyAmountRulesService< return queryOptions } - @InjectTransactionManager( - shouldForceTransaction, - "priceSetMoneyAmountRulesRepository_" - ) + @InjectTransactionManager("priceSetMoneyAmountRulesRepository_") async create( data: PricingTypes.CreatePriceSetMoneyAmountRulesDTO[], @MedusaContext() sharedContext: Context = {} @@ -93,10 +88,7 @@ export default class PriceSetMoneyAmountRulesService< )) as TEntity[] } - @InjectTransactionManager( - shouldForceTransaction, - "priceSetMoneyAmountRulesRepository_" - ) + @InjectTransactionManager("priceSetMoneyAmountRulesRepository_") async update( data: PricingTypes.UpdatePriceSetMoneyAmountRulesDTO[], @MedusaContext() sharedContext: Context = {} @@ -107,10 +99,7 @@ export default class PriceSetMoneyAmountRulesService< )) as TEntity[] } - @InjectTransactionManager( - doNotForceTransaction, - "priceSetMoneyAmountRulesRepository_" - ) + @InjectTransactionManager("priceSetMoneyAmountRulesRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/pricing/src/services/price-set-money-amount.ts b/packages/pricing/src/services/price-set-money-amount.ts index de1a45aa323ec..b660c7238506d 100644 --- a/packages/pricing/src/services/price-set-money-amount.ts +++ b/packages/pricing/src/services/price-set-money-amount.ts @@ -1,12 +1,11 @@ import { Context, DAL, FindConfig, PricingTypes } from "@medusajs/types" import { + doNotForceTransaction, InjectManager, InjectTransactionManager, MedusaContext, ModulesSdkUtils, - doNotForceTransaction, retrieveEntity, - shouldForceTransaction, } from "@medusajs/utils" import { PriceSet, PriceSetMoneyAmount } from "@models" import { PriceSetMoneyAmountRepository } from "@repositories" @@ -79,36 +78,29 @@ export default class PriceSetMoneyAmountService< return queryOptions } - @InjectTransactionManager( - shouldForceTransaction, - "priceSetMoneyAmountRepository_" - ) + @InjectTransactionManager("priceSetMoneyAmountRepository_") async create( data: PricingTypes.CreatePriceSetMoneyAmountDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { return (await ( - this.priceSetMoneyAmountRepository_ as unknown as PriceSetMoneyAmountRepository + this + .priceSetMoneyAmountRepository_ as unknown as PriceSetMoneyAmountRepository ).create(data, sharedContext)) as TEntity[] } - @InjectTransactionManager( - shouldForceTransaction, - "priceSetMoneyAmountRepository_" - ) + @InjectTransactionManager("priceSetMoneyAmountRepository_") async update( data: PricingTypes.UpdatePriceSetMoneyAmountDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { return (await ( - this.priceSetMoneyAmountRepository_ as unknown as PriceSetMoneyAmountRepository + this + .priceSetMoneyAmountRepository_ as unknown as PriceSetMoneyAmountRepository ).update(data, sharedContext)) as TEntity[] } - @InjectTransactionManager( - doNotForceTransaction, - "priceSetMoneyAmountRepository_" - ) + @InjectTransactionManager("priceSetMoneyAmountRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/pricing/src/services/price-set-rule-type.ts b/packages/pricing/src/services/price-set-rule-type.ts index fbe719ca90d47..2f376084989ea 100644 --- a/packages/pricing/src/services/price-set-rule-type.ts +++ b/packages/pricing/src/services/price-set-rule-type.ts @@ -4,9 +4,7 @@ import { InjectTransactionManager, MedusaContext, ModulesSdkUtils, - doNotForceTransaction, retrieveEntity, - shouldForceTransaction, } from "@medusajs/utils" import { PriceSet, PriceSetRuleType } from "@models" import { PriceSetRuleTypeRepository } from "src/repositories/price-set-rule-type" @@ -15,7 +13,9 @@ type InjectedDependencies = { priceSetRuleTypeRepository: DAL.RepositoryService } -export default class PriceSetRuleTypeService { +export default class PriceSetRuleTypeService< + TEntity extends PriceSetRuleType = PriceSetRuleType +> { protected readonly priceSetRuleTypeRepository_: DAL.RepositoryService constructor({ priceSetRuleTypeRepository }: InjectedDependencies) { @@ -28,7 +28,10 @@ export default class PriceSetRuleTypeService = {}, @MedusaContext() sharedContext: Context = {} ): Promise { - return (await retrieveEntity({ + return (await retrieveEntity< + PriceSetRuleType, + PricingTypes.PriceSetRuleTypeDTO + >({ id: priceSetId, entityName: PriceSet.name, repository: this.priceSetRuleTypeRepository_, @@ -74,29 +77,27 @@ export default class PriceSetRuleTypeService { - return (await (this.priceSetRuleTypeRepository_ as PriceSetRuleTypeRepository).create( - data, - sharedContext - )) as TEntity[] + return (await ( + this.priceSetRuleTypeRepository_ as PriceSetRuleTypeRepository + ).create(data, sharedContext)) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "priceSetRuleTypeRepository_") + @InjectTransactionManager("priceSetRuleTypeRepository_") async update( data: PricingTypes.UpdatePriceSetRuleTypeDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { - return (await (this.priceSetRuleTypeRepository_ as PriceSetRuleTypeRepository).update( - data, - sharedContext - )) as TEntity[] + return (await ( + this.priceSetRuleTypeRepository_ as PriceSetRuleTypeRepository + ).update(data, sharedContext)) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "priceSetRuleTypeRepository_") + @InjectTransactionManager("priceSetRuleTypeRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/pricing/src/services/price-set.ts b/packages/pricing/src/services/price-set.ts index b3fe10189f9f4..ab9f552ccc83f 100644 --- a/packages/pricing/src/services/price-set.ts +++ b/packages/pricing/src/services/price-set.ts @@ -4,9 +4,7 @@ import { InjectTransactionManager, MedusaContext, ModulesSdkUtils, - doNotForceTransaction, retrieveEntity, - shouldForceTransaction, } from "@medusajs/utils" import { PriceSet } from "@models" import { PriceSetRepository } from "@repositories" @@ -65,7 +63,7 @@ export default class PriceSetService { )) as [TEntity[], number] } - @InjectTransactionManager(shouldForceTransaction, "priceSetRepository_") + @InjectTransactionManager("priceSetRepository_") async create( data: Omit[], @MedusaContext() sharedContext: Context = {} @@ -76,7 +74,7 @@ export default class PriceSetService { )) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "priceSetRepository_") + @InjectTransactionManager("priceSetRepository_") async update( data: Omit[], @MedusaContext() sharedContext: Context = {} @@ -87,7 +85,7 @@ export default class PriceSetService { )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "priceSetRepository_") + @InjectTransactionManager("priceSetRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/pricing/src/services/pricing-module.ts b/packages/pricing/src/services/pricing-module.ts index fc3dcc3641d2a..a0b853c4fc93e 100644 --- a/packages/pricing/src/services/pricing-module.ts +++ b/packages/pricing/src/services/pricing-module.ts @@ -1,4 +1,5 @@ import { + AddPricesDTO, Context, CreateMoneyAmountDTO, DAL, @@ -35,16 +36,13 @@ import { } from "@services" import { + groupBy, InjectManager, InjectTransactionManager, MedusaContext, MedusaError, - groupBy, removeNullish, - shouldForceTransaction, } from "@medusajs/utils" - -import { AddPricesDTO } from "@medusajs/types" import { joinerConfig } from "../joiner-config" import { PricingRepositoryService } from "../types" @@ -231,7 +229,8 @@ export default class PricingModuleService< { id: priceSets.filter((p) => !!p).map((p) => p!.id) }, { relations: ["rule_types", "money_amounts", "price_rules"], - } + }, + sharedContext ) return (Array.isArray(data) ? dbPriceSets : dbPriceSets[0]) as unknown as @@ -239,7 +238,7 @@ export default class PricingModuleService< | PriceSetDTO[] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") protected async create_( data: PricingTypes.CreatePriceSetDTO[], @MedusaContext() sharedContext: Context = {} @@ -286,74 +285,89 @@ export default class PricingModuleService< ) } - const priceSets = await Promise.all( - data.map(async (d) => { - const { rules, prices, ...rest } = d - const [priceSet] = await this.priceSetService_.create( - [rest], - sharedContext - ) + // Bulk create price sets + const priceSetData = data.map(({ rules, prices, ...rest }) => rest) + const createdPriceSets = await this.priceSetService_.create( + priceSetData, + sharedContext + ) - if (rules?.length) { - const priceSetRuleTypesCreate = rules!.map((r) => ({ - rule_type: ruleTypeMap.get(r.rule_attribute), - price_set: priceSet, - })) + // Price set rule types + const ruleTypeData = data.flatMap( + (item, index) => + item.rules?.map((rule) => ({ + rule_type: ruleTypeMap.get(rule.rule_attribute), + price_set: createdPriceSets[index], + })) || [] + ) + if (ruleTypeData.length > 0) { + await this.priceSetRuleTypeService_.create( + ruleTypeData as unknown as PricingTypes.CreatePriceSetRuleTypeDTO[], + sharedContext + ) + } - await this.priceSetRuleTypeService_.create( - priceSetRuleTypesCreate as unknown as PricingTypes.CreatePriceSetRuleTypeDTO[], - sharedContext - ) - } + // Money amounts + const moneyAmountData = data.flatMap((item) => item.prices || []) + const createdMoneyAmounts = await this.moneyAmountService_.create( + moneyAmountData, + sharedContext + ) - if (prices?.length) { - for (const ma of prices) { - const [moneyAmount] = await this.moneyAmountService_.create( - [ma] as unknown as CreateMoneyAmountDTO[], - sharedContext - ) - - const cleanRules = ma.rules ? removeNullish(ma.rules) : {} - - const numberOfRules = Object.entries(cleanRules).length - - const [priceSetMoneyAmount] = - await this.priceSetMoneyAmountService_.create( - [ - { - price_set: priceSet, - money_amount: moneyAmount, - title: "test", - number_rules: numberOfRules, - }, - ] as unknown as PricingTypes.CreatePriceSetMoneyAmountDTO[], - sharedContext - ) - - if (numberOfRules) { - const priceSetRulesCreate = Object.entries(cleanRules).map( - ([k, v]) => ({ - price_set_money_amount: priceSetMoneyAmount, - rule_type: ruleTypeMap.get(k), - price_set: priceSet, - value: v, - price_list_id: "test", - }) - ) - - await this.priceRuleService_.create( - priceSetRulesCreate as unknown as PricingTypes.CreatePriceRuleDTO[], - sharedContext - ) - } - } + let moneyAmountIndex = 0 + const priceSetMoneyAmountData: unknown[] = [] + const priceRulesData: unknown[] = [] + + for (const [index, item] of data.entries()) { + for (const ma of item.prices || []) { + const cleanRules = ma.rules ? removeNullish(ma.rules) : {} + const numberOfRules = Object.entries(cleanRules).length + + const priceSetMoneyAmount = { + price_set: createdPriceSets[index], + money_amount: createdMoneyAmounts[moneyAmountIndex++], + title: "test", // TODO: accept title + number_rules: numberOfRules, + } + priceSetMoneyAmountData.push(priceSetMoneyAmount) + + for (const [k, v] of Object.entries(cleanRules)) { + priceRulesData.push({ + price_set_money_amount: null, // Updated later + rule_type: ruleTypeMap.get(k), + price_set: createdPriceSets[index], + value: v, + price_list_id: "test", + }) } + } + } - return priceSet - }) - ) + // Bulk create price set money amounts + const createdPriceSetMoneyAmounts = + await this.priceSetMoneyAmountService_.create( + priceSetMoneyAmountData as PricingTypes.CreatePriceSetMoneyAmountDTO[], + sharedContext + ) + + // Update price set money amount references + for (let i = 0, j = 0; i < priceSetMoneyAmountData.length; i++) { + const rulesCount = (priceSetMoneyAmountData[i] as any).number_rules + for (let k = 0; k < rulesCount; k++, j++) { + ;(priceRulesData[j] as any).price_set_money_amount = + createdPriceSetMoneyAmounts[i] + } + } - return priceSets + // Price rules + if (priceRulesData.length > 0) { + await this.priceRuleService_.create( + priceRulesData as PricingTypes.CreatePriceRuleDTO[], + sharedContext + ) + } + + return createdPriceSets } async addRules( @@ -380,14 +394,15 @@ export default class PricingModuleService< | PricingTypes.PriceSetDTO } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") protected async addRules_( inputs: PricingTypes.AddRulesDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { const priceSets = await this.priceSetService_.list( { id: inputs.map((d) => d.priceSetId) }, - { relations: ["rule_types"] } + { relations: ["rule_types"] }, + sharedContext ) const priceSetRuleTypeMap: Map> = new Map( @@ -491,11 +506,12 @@ export default class PricingModuleService< return (await this.list( { id: input.map((d) => d.priceSetId) }, - { relations: ["money_amounts"] } + { relations: ["money_amounts"] }, + sharedContext )) as unknown as PricingTypes.PriceSetDTO[] | PricingTypes.PriceSetDTO } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") protected async addPrices_( input: AddPricesDTO[], @MedusaContext() sharedContext: Context = {} @@ -543,73 +559,93 @@ export default class PricingModuleService< } }) - for (const { priceSetId, prices } of input) { - await Promise.all( - prices.map(async (ma) => { - const [moneyAmount] = await this.moneyAmountService_.create( - [ma] as unknown as CreateMoneyAmountDTO[], - sharedContext - ) - - const numberOfRules = Object.entries(ma?.rules ?? {}).length - - const [priceSetMoneyAmount] = - await this.priceSetMoneyAmountService_.create( - [ - { - price_set: priceSetId, - money_amount: moneyAmount, - title: "test", - number_rules: numberOfRules, - }, - ] as unknown as PricingTypes.CreatePriceSetMoneyAmountDTO[], - sharedContext - ) - - if (numberOfRules) { - const priceSetRulesCreate = Object.entries(ma.rules!).map( - ([k, v]) => ({ - price_set_money_amount: priceSetMoneyAmount, - rule_type: ruleTypeMap.get(priceSetId)!.get(k), - price_set: priceSetId, - value: v, - price_list_id: "test", - }) - ) - - await this.priceRuleService_.create( - priceSetRulesCreate as unknown as PricingTypes.CreatePriceRuleDTO[], - sharedContext - ) - } + // Money amounts + const moneyAmountsBulkData = input.flatMap((entry) => entry.prices) + const createdMoneyAmounts = await this.moneyAmountService_.create( + moneyAmountsBulkData as unknown as CreateMoneyAmountDTO[], + sharedContext + ) - return moneyAmount + // Price set money amounts + let maCursor = 0 + const priceSetMoneyAmountsBulkData = input.flatMap( + ({ priceSetId, prices }) => + prices.map(() => { + const ma = createdMoneyAmounts[maCursor] + const numberOfRules = Object.entries( + prices[maCursor]?.rules ?? {} + ).length + maCursor++ + return { + price_set: priceSetId, + money_amount: ma, + title: "test", // TODO: accept title + number_rules: numberOfRules, + } }) + ) + const createdPriceSetMoneyAmounts = + await this.priceSetMoneyAmountService_.create( + priceSetMoneyAmountsBulkData as unknown as PricingTypes.CreatePriceSetMoneyAmountDTO[], + sharedContext ) - } - return priceSets + // Price rules + let rulesCursor = 0 + const priceRulesBulkData = input.flatMap(({ priceSetId, prices }) => + prices.flatMap((ma) => { + const rules = ma.rules ?? {} + const priceSetMoneyAmount = createdPriceSetMoneyAmounts[rulesCursor] + rulesCursor++ + return Object.entries(rules).map(([k, v]) => ({ + price_set_money_amount: priceSetMoneyAmount, + rule_type: ruleTypeMap.get(priceSetId)!.get(k), + price_set: priceSetId, + value: v, + price_list_id: "test", // TODO: accept title + })) + }) + ) + + if (priceRulesBulkData.length > 0) { + await this.priceRuleService_.create( + priceRulesBulkData as unknown as PricingTypes.CreatePriceRuleDTO[], + sharedContext + ) + } } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async removeRules( data: PricingTypes.RemovePriceSetRulesDTO[], @MedusaContext() sharedContext: Context = {} ): Promise { - const priceSets = await this.priceSetService_.list({ - id: data.map((d) => d.id), - }) + const priceSets = await this.priceSetService_.list( + { + id: data.map((d) => d.id), + }, + undefined, + sharedContext + ) const priceSetIds = priceSets.map((ps) => ps.id) - const ruleTypes = await this.ruleTypeService_.list({ - rule_attribute: data.map((d) => d.rules || []).flat(), - }) + const ruleTypes = await this.ruleTypeService_.list( + { + rule_attribute: data.map((d) => d.rules || []).flat(), + }, + undefined, + sharedContext + ) const ruleTypeIds = ruleTypes.map((rt) => rt.id) - const priceSetRuleTypes = await this.priceSetRuleTypeService_.list({ - price_set_id: priceSetIds, - rule_type_id: ruleTypeIds, - }) + const priceSetRuleTypes = await this.priceSetRuleTypeService_.list( + { + price_set_id: priceSetIds, + rule_type_id: ruleTypeIds, + }, + undefined, + sharedContext + ) const priceRules = await this.priceRuleService_.list( { @@ -618,7 +654,8 @@ export default class PricingModuleService< }, { select: ["price_set_money_amount"], - } + }, + sharedContext ) await this.priceSetRuleTypeService_.delete( @@ -632,7 +669,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async update( data: PricingTypes.UpdatePriceSetDTO[], @MedusaContext() sharedContext: Context = {} @@ -647,7 +684,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} @@ -718,7 +755,7 @@ export default class PricingModuleService< ] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createMoneyAmounts( data: PricingTypes.CreateMoneyAmountDTO[], @MedusaContext() sharedContext: Context = {} @@ -736,7 +773,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updateMoneyAmounts( data: PricingTypes.UpdateMoneyAmountDTO[], @MedusaContext() sharedContext: Context = {} @@ -754,7 +791,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deleteMoneyAmounts( ids: string[], @MedusaContext() sharedContext: Context = {} @@ -822,7 +859,7 @@ export default class PricingModuleService< ] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createCurrencies( data: PricingTypes.CreateCurrencyDTO[], @MedusaContext() sharedContext: Context = {} @@ -837,7 +874,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updateCurrencies( data: PricingTypes.UpdateCurrencyDTO[], @MedusaContext() sharedContext: Context = {} @@ -852,7 +889,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deleteCurrencies( currencyCodes: string[], @MedusaContext() sharedContext: Context = {} @@ -920,7 +957,7 @@ export default class PricingModuleService< ] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createRuleTypes( data: PricingTypes.CreateRuleTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -935,7 +972,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updateRuleTypes( data: PricingTypes.UpdateRuleTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -950,7 +987,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deleteRuleTypes( ruleTypeIds: string[], @MedusaContext() sharedContext: Context = {} @@ -1062,7 +1099,7 @@ export default class PricingModuleService< ] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createPriceSetMoneyAmountRules( data: PricingTypes.CreatePriceSetMoneyAmountRulesDTO[], @MedusaContext() sharedContext: Context = {} @@ -1079,7 +1116,7 @@ export default class PricingModuleService< }) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updatePriceSetMoneyAmountRules( data: PricingTypes.UpdatePriceSetMoneyAmountRulesDTO[], @MedusaContext() sharedContext: Context = {} @@ -1096,7 +1133,7 @@ export default class PricingModuleService< }) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deletePriceSetMoneyAmountRules( ids: string[], @MedusaContext() sharedContext: Context = {} @@ -1167,7 +1204,7 @@ export default class PricingModuleService< ] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createPriceRules( data: PricingTypes.CreatePriceRuleDTO[], @MedusaContext() sharedContext: Context = {} @@ -1182,7 +1219,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updatePriceRules( data: PricingTypes.UpdatePriceRuleDTO[], @MedusaContext() sharedContext: Context = {} @@ -1197,7 +1234,7 @@ export default class PricingModuleService< ) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deletePriceRules( priceRuleIds: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/pricing/src/services/rule-type.ts b/packages/pricing/src/services/rule-type.ts index 7dadbbc070128..7f0e34561683b 100644 --- a/packages/pricing/src/services/rule-type.ts +++ b/packages/pricing/src/services/rule-type.ts @@ -8,8 +8,6 @@ import { } from "@medusajs/utils" import { RuleType } from "@models" -import { doNotForceTransaction, shouldForceTransaction } from "@medusajs/utils" - type InjectedDependencies = { ruleTypeRepository: DAL.RepositoryService } @@ -65,7 +63,7 @@ export default class RuleTypeService { )) as [TEntity[], number] } - @InjectTransactionManager(shouldForceTransaction, "ruleTypeRepository_") + @InjectTransactionManager("ruleTypeRepository_") async create( data: PricingTypes.CreateRuleTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -76,7 +74,7 @@ export default class RuleTypeService { )) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "ruleTypeRepository_") + @InjectTransactionManager("ruleTypeRepository_") async update( data: PricingTypes.UpdateRuleTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -87,7 +85,7 @@ export default class RuleTypeService { )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "ruleTypeRepository_") + @InjectTransactionManager("ruleTypeRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts index d1227ef5a5015..856dd05a5ee83 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/product-collections.spec.ts @@ -69,11 +69,6 @@ describe("ProductModuleService product collections", () => { productCollectionOne = productCollections[0] productCollectionTwo = productCollections[1] - - await testManager.persistAndFlush([ - productCollectionOne, - productCollectionTwo, - ]) }) afterEach(async () => { @@ -339,16 +334,17 @@ describe("ProductModuleService product collections", () => { relations: ["products"], }) + expect(productCollection.products).toHaveLength(2) expect(productCollection).toEqual( expect.objectContaining({ - products: [ + products: expect.arrayContaining([ expect.objectContaining({ id: productOne.id, }), expect.objectContaining({ id: productTwo.id, }), - ], + ]), }) ) }) diff --git a/packages/product/integration-tests/__tests__/services/product-option/index.ts b/packages/product/integration-tests/__tests__/services/product-option/index.ts index 514f03a1a3258..61f552c29261a 100644 --- a/packages/product/integration-tests/__tests__/services/product-option/index.ts +++ b/packages/product/integration-tests/__tests__/services/product-option/index.ts @@ -121,7 +121,9 @@ describe("ProductOption Service", () => { }) it("should return product option and count when filtered", async () => { - const [optionResults, count] = await service.listAndCount({ id: "option-2" }) + const [optionResults, count] = await service.listAndCount({ + id: "option-2", + }) expect(count).toEqual(1) expect(optionResults).toEqual([ @@ -132,7 +134,10 @@ describe("ProductOption Service", () => { }) it("should return product option and count when using skip and take", async () => { - const [optionResults, count] = await service.listAndCount({}, { skip: 1, take: 1 }) + const [optionResults, count] = await service.listAndCount( + {}, + { skip: 1, take: 1 } + ) expect(count).toEqual(2) expect(optionResults).toEqual([ @@ -143,10 +148,13 @@ describe("ProductOption Service", () => { }) it("should return requested fields", async () => { - const [optionResults, count] = await service.listAndCount({}, { - take: 1, - select: ["title"], - }) + const [optionResults, count] = await service.listAndCount( + {}, + { + take: 1, + select: ["title"], + } + ) const serialized = JSON.parse(JSON.stringify(optionResults)) @@ -164,13 +172,11 @@ describe("ProductOption Service", () => { const optionValue = "Option 1" it("should return option for the given id", async () => { - const option = await service.retrieve( - optionId, - ) + const option = await service.retrieve(optionId) expect(option).toEqual( expect.objectContaining({ - id: optionId + id: optionId, }) ) }) @@ -184,7 +190,9 @@ describe("ProductOption Service", () => { error = e } - expect(error.message).toEqual('ProductOption with id: does-not-exist was not found') + expect(error.message).toEqual( + "ProductOption with id: does-not-exist was not found" + ) }) it("should throw an error when an id is not provided", async () => { @@ -200,21 +208,16 @@ describe("ProductOption Service", () => { }) it("should return option based on config select param", async () => { - const option = await service.retrieve( - optionId, - { - select: ["id", "title"], - } - ) + const option = await service.retrieve(optionId, { + select: ["id", "title"], + }) const serialized = JSON.parse(JSON.stringify(option)) - expect(serialized).toEqual( - { - id: optionId, - title: optionValue, - } - ) + expect(serialized).toEqual({ + id: optionId, + title: optionValue, + }) }) }) @@ -222,13 +225,10 @@ describe("ProductOption Service", () => { const optionId = "option-1" it("should delete the product option given an ID successfully", async () => { - - await service.delete( - [optionId], - ) + await service.delete([optionId]) const options = await service.list({ - id: optionId + id: optionId, }) expect(options).toHaveLength(0) @@ -239,12 +239,12 @@ describe("ProductOption Service", () => { const optionId = "option-1" it("should update the title of the option successfully", async () => { - await service.update( - [{ + await service.update([ + { id: optionId, title: "UK", - }] - ) + }, + ]) const productOption = await service.retrieve(optionId) @@ -252,23 +252,23 @@ describe("ProductOption Service", () => { }) it("should update the relationship of the option successfully", async () => { - await service.update( - [{ + await service.update([ + { id: optionId, product_id: productTwo.id, - }] - ) + }, + ]) const productOption = await service.retrieve(optionId, { - relations: ["product"] + relations: ["product"], }) expect(productOption).toEqual( expect.objectContaining({ id: optionId, product: expect.objectContaining({ - id: productTwo.id - }) + id: productTwo.id, + }), }) ) }) @@ -278,43 +278,98 @@ describe("ProductOption Service", () => { try { await service.update([ - { - id: "does-not-exist", - title: "UK", - } - ]) + { + id: "does-not-exist", + title: "UK", + }, + ]) } catch (e) { error = e } - expect(error.message).toEqual('ProductOption with id "does-not-exist" not found') + expect(error.message).toEqual( + 'ProductOption with id "does-not-exist" not found' + ) }) }) describe("create", () => { it("should create a option successfully", async () => { - await service.create( - [{ + await service.create([ + { title: "UK", - product: productOne - }] - ) - - const [productOption] = await service.list({ - title: "UK" - }, { - relations: ["product"], - }) + product: productOne, + }, + ]) + const [productOption] = await service.list( + { + title: "UK", + }, + { + relations: ["product"], + } + ) expect(productOption).toEqual( expect.objectContaining({ title: "UK", product: expect.objectContaining({ - id: productOne.id - }) + id: productOne.id, + }), }) ) }) }) + + describe("upsert", function () { + it("should create an option and update another option successfully", async () => { + const productOption = ( + await service.create([ + { + title: "UK", + product: productOne, + }, + ]) + )[0] + + const optionToUpdate = { + id: productOption.id, + title: "US", + } + + const newOption = { + title: "US2", + product_id: productOne.id, + } + + await service.upsert([optionToUpdate, newOption]) + + const productOptions = await service.list( + { + title: "US%", + }, + { + relations: ["product"], + } + ) + + expect(JSON.parse(JSON.stringify(productOptions))).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + title: "US", + product: expect.objectContaining({ + id: productOne.id, + }), + }), + expect.objectContaining({ + title: newOption.title, + product: expect.objectContaining({ + id: productOne.id, + }), + }), + ]) + ) + }) + }) }) diff --git a/packages/product/src/repositories/product-collection.ts b/packages/product/src/repositories/product-collection.ts index a672d6f6a3342..26ef2631de06b 100644 --- a/packages/product/src/repositories/product-collection.ts +++ b/packages/product/src/repositories/product-collection.ts @@ -1,9 +1,9 @@ import { Context, DAL, ProductTypes } from "@medusajs/types" import { DALUtils, MedusaError } from "@medusajs/utils" import { - LoadStrategy, FilterQuery as MikroFilterQuery, FindOptions as MikroOptions, + LoadStrategy, } from "@mikro-orm/core" import { SqlEntityManager } from "@mikro-orm/postgresql" import { ProductCollection } from "@models" @@ -31,8 +31,7 @@ export class ProductCollectionRepository extends DALUtils.MikroOrmBaseRepository findOptions: DAL.FindOptions = { where: {} }, context: Context = {} ): Promise { - const manager = (context.transactionManager ?? - this.manager_) as SqlEntityManager + const manager = this.getActiveManager(context) const findOptions_ = { ...findOptions } findOptions_.options ??= {} @@ -52,8 +51,7 @@ export class ProductCollectionRepository extends DALUtils.MikroOrmBaseRepository findOptions: DAL.FindOptions = { where: {} }, context: Context = {} ): Promise<[ProductCollection[], number]> { - const manager = (context.transactionManager ?? - this.manager_) as SqlEntityManager + const manager = this.getActiveManager(context) const findOptions_ = { ...findOptions } findOptions_.options ??= {} diff --git a/packages/product/src/repositories/product-option.ts b/packages/product/src/repositories/product-option.ts index f06ae8b538f99..5f7c10e8d85df 100644 --- a/packages/product/src/repositories/product-option.ts +++ b/packages/product/src/repositories/product-option.ts @@ -1,9 +1,9 @@ import { Context, DAL, ProductTypes } from "@medusajs/types" import { DALUtils, MedusaError } from "@medusajs/utils" import { - LoadStrategy, FilterQuery as MikroFilterQuery, FindOptions as MikroOptions, + LoadStrategy, } from "@mikro-orm/core" import { SqlEntityManager } from "@mikro-orm/postgresql" import { Product, ProductOption } from "@models" @@ -149,4 +149,62 @@ export class ProductOptionRepository extends DALUtils.MikroOrmAbstractBaseReposi return productOptions } + + async upsert( + data: + | ProductTypes.CreateProductOptionDTO[] + | ProductTypes.UpdateProductOptionDTO[], + context: Context = {} + ): Promise { + const manager = this.getActiveManager(context) + + const optionIds = data.map((optionData) => optionData.id).filter((o) => o) + + let existingOptions + let existingOptionsMap = new Map() + + if (optionIds.length) { + existingOptions = await this.find( + { + where: { + id: { + $in: optionIds, + }, + }, + }, + context + ) + + existingOptionsMap = new Map( + existingOptions.map((option) => [option.id, option]) + ) + } + + const upsertedOptions: ProductOption[] = [] + const optionsToCreate: ProductOption[] = [] + const optionsToUpdate: ProductOption[] = [] + + data.forEach((option) => { + const existingOption = existingOptionsMap.get(option.id) + if (existingOption) { + const updatedOption = manager.assign(existingOption, option) + optionsToUpdate.push(updatedOption) + } else { + const newOption = manager.create(ProductOption, option) + optionsToCreate.push(newOption) + } + }) + + if (optionsToCreate.length) { + manager.persist(optionsToCreate) + upsertedOptions.push(...optionsToCreate) + } + + if (optionsToUpdate.length) { + manager.persist(optionsToUpdate) + upsertedOptions.push(...optionsToUpdate) + } + + return upsertedOptions + } } diff --git a/packages/product/src/repositories/product-tag.ts b/packages/product/src/repositories/product-tag.ts index fb04e13d6bf4b..88628bdd8b9e7 100644 --- a/packages/product/src/repositories/product-tag.ts +++ b/packages/product/src/repositories/product-tag.ts @@ -2,7 +2,6 @@ import { FilterQuery as MikroFilterQuery, FindOptions as MikroOptions, LoadStrategy, - RequiredEntityData, } from "@mikro-orm/core" import { ProductTag } from "@models" import { @@ -141,26 +140,28 @@ export class ProductTagRepository extends DALUtils.MikroOrmBaseRepository { ) const upsertedTags: ProductTag[] = [] - const tagsToCreate: RequiredEntityData[] = [] + const tagsToCreate: ProductTag[] = [] + const tagsToUpdate: ProductTag[] = [] tags.forEach((tag) => { const aTag = existingTagsMap.get(tag.value) if (aTag) { - upsertedTags.push(aTag) + const updatedTag = manager.assign(aTag, tag) + tagsToUpdate.push(updatedTag) } else { - const newTag = (manager as SqlEntityManager).create(ProductTag, tag) + const newTag = manager.create(ProductTag, tag) tagsToCreate.push(newTag) } }) if (tagsToCreate.length) { - const newTags: ProductTag[] = [] - tagsToCreate.forEach((tag) => { - newTags.push((manager as SqlEntityManager).create(ProductTag, tag)) - }) + manager.persist(tagsToCreate) + upsertedTags.push(...tagsToCreate) + } - manager.persist(newTags) - upsertedTags.push(...newTags) + if (tagsToUpdate.length) { + manager.persist(tagsToUpdate) + upsertedTags.push(...tagsToUpdate) } return upsertedTags diff --git a/packages/product/src/repositories/product-type.ts b/packages/product/src/repositories/product-type.ts index b23989f538d70..d2f3e2171067e 100644 --- a/packages/product/src/repositories/product-type.ts +++ b/packages/product/src/repositories/product-type.ts @@ -2,7 +2,6 @@ import { FilterQuery as MikroFilterQuery, FindOptions as MikroOptions, LoadStrategy, - RequiredEntityData, } from "@mikro-orm/core" import { ProductType } from "@models" import { @@ -87,26 +86,28 @@ export class ProductTypeRepository extends DALUtils.MikroOrmBaseRepository { ) const upsertedTypes: ProductType[] = [] - const typesToCreate: RequiredEntityData[] = [] + const typesToCreate: ProductType[] = [] + const typesToUpdate: ProductType[] = [] types.forEach((type) => { const aType = existingTypesMap.get(type.value) if (aType) { - upsertedTypes.push(aType) + const updatedType = manager.assign(aType, type) + typesToUpdate.push(updatedType) } else { - const newType = (manager as SqlEntityManager).create(ProductType, type) + const newType = manager.create(ProductType, type) typesToCreate.push(newType) } }) if (typesToCreate.length) { - const newTypes: ProductType[] = [] - typesToCreate.forEach((type) => { - newTypes.push((manager as SqlEntityManager).create(ProductType, type)) - }) + manager.persist(typesToCreate) + upsertedTypes.push(...typesToCreate) + } - manager.persist(newTypes) - upsertedTypes.push(...newTypes) + if (typesToUpdate.length) { + manager.persist(typesToUpdate) + upsertedTypes.push(...typesToUpdate) } return upsertedTypes diff --git a/packages/product/src/services/product-category.ts b/packages/product/src/services/product-category.ts index 1886fdd9b30a5..a2ef62a041f06 100644 --- a/packages/product/src/services/product-category.ts +++ b/packages/product/src/services/product-category.ts @@ -9,8 +9,6 @@ import { MedusaError, ModulesSdkUtils, } from "@medusajs/utils" - -import { shouldForceTransaction } from "../utils" import { ProductCategoryServiceTypes } from "../types" type InjectedDependencies = { @@ -114,10 +112,7 @@ export default class ProductCategoryService< )) as [TEntity[], number] } - @InjectTransactionManager( - shouldForceTransaction, - "productCategoryRepository_" - ) + @InjectTransactionManager("productCategoryRepository_") async create( data: ProductCategoryServiceTypes.CreateProductCategoryDTO, @MedusaContext() sharedContext: Context = {} @@ -127,10 +122,7 @@ export default class ProductCategoryService< ).create(data, sharedContext)) as TEntity } - @InjectTransactionManager( - shouldForceTransaction, - "productCategoryRepository_" - ) + @InjectTransactionManager("productCategoryRepository_") async update( id: string, data: ProductCategoryServiceTypes.UpdateProductCategoryDTO, @@ -141,10 +133,7 @@ export default class ProductCategoryService< ).update(id, data, sharedContext)) as TEntity } - @InjectTransactionManager( - shouldForceTransaction, - "productCategoryRepository_" - ) + @InjectTransactionManager("productCategoryRepository_") async delete( id: string, @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/src/services/product-collection.ts b/packages/product/src/services/product-collection.ts index 93f5d1ca70c11..08199ab1ae400 100644 --- a/packages/product/src/services/product-collection.ts +++ b/packages/product/src/services/product-collection.ts @@ -6,8 +6,6 @@ import { ModulesSdkUtils, retrieveEntity, } from "@medusajs/utils" - -import { shouldForceTransaction } from "../utils" import { ProductCollectionRepository } from "../repositories" import { ProductCollection } from "@models" @@ -85,10 +83,7 @@ export default class ProductCollectionService< return queryOptions } - @InjectTransactionManager( - shouldForceTransaction, - "productCollectionRepository_" - ) + @InjectTransactionManager("productCollectionRepository_") async create( data: ProductTypes.CreateProductCollectionDTO[], @MedusaContext() sharedContext: Context = {} @@ -98,10 +93,7 @@ export default class ProductCollectionService< ).create(data, sharedContext)) as TEntity[] } - @InjectTransactionManager( - shouldForceTransaction, - "productCollectionRepository_" - ) + @InjectTransactionManager("productCollectionRepository_") async update( data: ProductTypes.UpdateProductCollectionDTO[], @MedusaContext() sharedContext: Context = {} @@ -111,10 +103,7 @@ export default class ProductCollectionService< ).update(data, sharedContext)) as TEntity[] } - @InjectTransactionManager( - shouldForceTransaction, - "productCollectionRepository_" - ) + @InjectTransactionManager("productCollectionRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/src/services/product-image.ts b/packages/product/src/services/product-image.ts index 7149c7c0ca5eb..134ba87a7d8be 100644 --- a/packages/product/src/services/product-image.ts +++ b/packages/product/src/services/product-image.ts @@ -1,7 +1,6 @@ import { Image } from "@models" import { Context, DAL } from "@medusajs/types" import { InjectTransactionManager, MedusaContext } from "@medusajs/utils" -import { doNotForceTransaction } from "../utils" import { ProductImageRepository } from "@repositories" type InjectedDependencies = { @@ -15,7 +14,7 @@ export default class ProductImageService { this.productImageRepository_ = productImageRepository } - @InjectTransactionManager(doNotForceTransaction, "productImageRepository_") + @InjectTransactionManager("productImageRepository_") async upsert( urls: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/src/services/product-module-service.ts b/packages/product/src/services/product-module-service.ts index e632324873a50..21182de4ed1b7 100644 --- a/packages/product/src/services/product-module-service.ts +++ b/packages/product/src/services/product-module-service.ts @@ -62,8 +62,6 @@ import { MedusaContext, MedusaError, } from "@medusajs/utils" - -import { shouldForceTransaction } from "../utils" import { entityNameToLinkableKeysMap, joinerConfig, @@ -277,7 +275,7 @@ export default class ProductModuleService< return [JSON.parse(JSON.stringify(tags)), count] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createTags( data: ProductTypes.CreateProductTagDTO[], @MedusaContext() sharedContext: Context = {} @@ -290,7 +288,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(productTags)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updateTags( data: ProductTypes.UpdateProductTagDTO[], @MedusaContext() sharedContext: Context = {} @@ -303,7 +301,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(productTags)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deleteTags( productTagIds: string[], @MedusaContext() sharedContext: Context = {} @@ -356,7 +354,7 @@ export default class ProductModuleService< return [JSON.parse(JSON.stringify(types)), count] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createTypes( data: ProductTypes.CreateProductTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -369,7 +367,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(productTypes)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updateTypes( data: ProductTypes.UpdateProductTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -382,7 +380,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(productTypes)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deleteTypes( productTypeIds: string[], @MedusaContext() sharedContext: Context = {} @@ -436,7 +434,7 @@ export default class ProductModuleService< return [JSON.parse(JSON.stringify(productOptions)), count] } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createOptions( data: ProductTypes.CreateProductOptionDTO[], @MedusaContext() sharedContext: Context = {} @@ -453,7 +451,7 @@ export default class ProductModuleService< }) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updateOptions( data: ProductTypes.UpdateProductOptionDTO[], @MedusaContext() sharedContext: Context = {} @@ -470,7 +468,7 @@ export default class ProductModuleService< }) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deleteOptions( productOptionIds: string[], @MedusaContext() sharedContext: Context = {} @@ -523,7 +521,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(collections)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createCollections( data: ProductTypes.CreateProductCollectionDTO[], @MedusaContext() sharedContext: Context = {} @@ -543,7 +541,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(productCollections)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updateCollections( data: ProductTypes.UpdateProductCollectionDTO[], @MedusaContext() sharedContext: Context = {} @@ -563,7 +561,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(productCollections)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deleteCollections( productCollectionIds: string[], @MedusaContext() sharedContext: Context = {} @@ -611,7 +609,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(categories)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async createCategory( data: CreateProductCategoryDTO, @MedusaContext() sharedContext: Context = {} @@ -629,7 +627,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(productCategory)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async updateCategory( categoryId: string, data: UpdateProductCategoryDTO, @@ -649,7 +647,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(productCategory)) } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async deleteCategory( categoryId: string, @MedusaContext() sharedContext: Context = {} @@ -677,6 +675,7 @@ export default class ProductModuleService< return JSON.parse(JSON.stringify(categories)) } + @InjectManager("baseRepository_") async create( data: ProductTypes.CreateProductDTO[], sharedContext?: Context @@ -698,6 +697,7 @@ export default class ProductModuleService< return createdProducts } + @InjectManager("baseRepository_") async update( data: ProductTypes.UpdateProductDTO[], @MedusaContext() sharedContext: Context = {} @@ -720,7 +720,7 @@ export default class ProductModuleService< return updatedProducts } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") protected async create_( data: ProductTypes.CreateProductDTO[], @MedusaContext() sharedContext: Context = {} @@ -829,7 +829,7 @@ export default class ProductModuleService< return products } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") protected async update_( data: ProductTypes.UpdateProductDTO[], @MedusaContext() sharedContext: Context = {} @@ -859,14 +859,11 @@ export default class ProductModuleService< )[] >() - const productOptionsMap = new Map< - string, - ProductTypes.CreateProductOptionDTO[] - >() + const productOptionsMap = new Map() const productsData = await Promise.all( data.map(async (product) => { - const { variants, options, ...productData } = product + const { variants, ...productData } = product if (!isDefined(productData.id)) { throw new MedusaError( @@ -876,7 +873,6 @@ export default class ProductModuleService< } productVariantsMap.set(productData.id, variants ?? []) - productOptionsMap.set(productData.id, options ?? []) if (productData.is_giftcard) { productData.discountable = false @@ -894,6 +890,15 @@ export default class ProductModuleService< productData, sharedContext ) + await this.upsertAndAssignOptionsToProductData( + productData, + sharedContext + ) + + productOptionsMap.set( + productData.id, + (productData.options ?? []) as TProductOption[] + ) return productData as UpdateProductDTO }) @@ -908,20 +913,6 @@ export default class ProductModuleService< products.map((product) => [product.id, product]) ) - const productOptionsData = [...productOptionsMap] - .map(([id, options]) => - options.map((option) => ({ - ...option, - product: productByIdMap.get(id)!, - })) - ) - .flat() - - const productOptions = await this.productOptionService_.create( - productOptionsData, - sharedContext - ) - const productVariantIdsToDelete: string[] = [] const productVariantsToCreateMap = new Map< string, @@ -937,6 +928,7 @@ export default class ProductModuleService< const variantsToCreate: ProductTypes.CreateProductVariantDTO[] = [] const variantsToUpdate: ProductTypes.UpdateProductVariantDTO[] = [] const existingVariants = existingProductVariantsMap.get(productId) + const productOptions = productOptionsMap.get(productId)! variants.forEach((variant) => { const isVariantIdDefined = "id" in variant && isDefined(variant.id) @@ -955,7 +947,7 @@ export default class ProductModuleService< } }) - if (variantOptions) { + if (variantOptions?.length) { variant.options = variantOptions } }) @@ -1010,6 +1002,18 @@ export default class ProductModuleService< return products } + protected async upsertAndAssignOptionsToProductData( + productData: ProductTypes.CreateProductDTO | ProductTypes.UpdateProductDTO, + sharedContext: Context = {} + ) { + if (productData.options?.length) { + productData.options = await this.productOptionService_.upsert( + productData.options, + sharedContext + ) + } + } + protected async upsertAndAssignImagesToProductData( productData: ProductTypes.CreateProductDTO | ProductTypes.UpdateProductDTO, sharedContext: Context = {} @@ -1060,7 +1064,7 @@ export default class ProductModuleService< } } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async delete( productIds: string[], @MedusaContext() sharedContext: Context = {} @@ -1075,6 +1079,7 @@ export default class ProductModuleService< ) } + @InjectManager("baseRepository_") async softDelete< TReturnableLinkableKeys extends string = Lowercase< keyof typeof LinkableKeys @@ -1116,7 +1121,7 @@ export default class ProductModuleService< return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0 } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") protected async softDelete_( productIds: string[], @MedusaContext() sharedContext: Context = {} @@ -1124,6 +1129,7 @@ export default class ProductModuleService< return await this.productService_.softDelete(productIds, sharedContext) } + @InjectManager("baseRepository_") async restore< TReturnableLinkableKeys extends string = Lowercase< keyof typeof LinkableKeys @@ -1152,7 +1158,7 @@ export default class ProductModuleService< return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0 } - @InjectManager("baseRepository_") + @InjectTransactionManager("baseRepository_") async restoreVariants< TReturnableLinkableKeys extends string = Lowercase< keyof typeof LinkableKeys @@ -1179,7 +1185,7 @@ export default class ProductModuleService< return mappedCascadedEntitiesMap ? mappedCascadedEntitiesMap : void 0 } - @InjectTransactionManager(shouldForceTransaction, "baseRepository_") + @InjectTransactionManager("baseRepository_") async restore_( productIds: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/src/services/product-option.ts b/packages/product/src/services/product-option.ts index c5d796ccdec97..0a2b3910330e4 100644 --- a/packages/product/src/services/product-option.ts +++ b/packages/product/src/services/product-option.ts @@ -2,15 +2,13 @@ import { ProductOption } from "@models" import { Context, DAL, FindConfig, ProductTypes } from "@medusajs/types" import { ProductOptionRepository } from "@repositories" import { - InjectTransactionManager, InjectManager, + InjectTransactionManager, MedusaContext, ModulesSdkUtils, retrieveEntity, } from "@medusajs/utils" -import { doNotForceTransaction, shouldForceTransaction } from "../utils" - type InjectedDependencies = { productOptionRepository: DAL.RepositoryService } @@ -80,7 +78,7 @@ export default class ProductOptionService< return queryOptions } - @InjectTransactionManager(shouldForceTransaction, "productOptionRepository_") + @InjectTransactionManager("productOptionRepository_") async create( data: ProductTypes.CreateProductOptionOnlyDTO[], @MedusaContext() sharedContext: Context = {} @@ -92,7 +90,7 @@ export default class ProductOptionService< })) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "productOptionRepository_") + @InjectTransactionManager("productOptionRepository_") async update( data: ProductTypes.UpdateProductOptionDTO[], @MedusaContext() sharedContext: Context = {} @@ -102,11 +100,22 @@ export default class ProductOptionService< ).update(data, sharedContext)) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "productOptionRepository_") + @InjectTransactionManager("productOptionRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} ): Promise { return await this.productOptionRepository_.delete(ids, sharedContext) } + + @InjectTransactionManager("productOptionRepository_") + async upsert( + data: + | ProductTypes.CreateProductOptionDTO[] + | ProductTypes.UpdateProductOptionDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + return (await (this.productOptionRepository_ as ProductOptionRepository) + .upsert!(data, sharedContext)) as TEntity[] + } } diff --git a/packages/product/src/services/product-tag.ts b/packages/product/src/services/product-tag.ts index 6c5aa39606d51..90a33aa352732 100644 --- a/packages/product/src/services/product-tag.ts +++ b/packages/product/src/services/product-tag.ts @@ -2,22 +2,22 @@ import { ProductTag } from "@models" import { Context, CreateProductTagDTO, - UpdateProductTagDTO, - UpsertProductTagDTO, DAL, FindConfig, ProductTypes, + UpdateProductTagDTO, + UpsertProductTagDTO, } from "@medusajs/types" import { - InjectTransactionManager, InjectManager, + InjectTransactionManager, MedusaContext, ModulesSdkUtils, retrieveEntity, } from "@medusajs/utils" import { ProductTagRepository } from "@repositories" -import { doNotForceTransaction, shouldForceTransaction } from "../utils" +import { shouldForceTransaction } from "../utils" type InjectedDependencies = { productTagRepository: DAL.RepositoryService @@ -84,7 +84,7 @@ export default class ProductTagService< return queryOptions } - @InjectTransactionManager(shouldForceTransaction, "productTagRepository_") + @InjectTransactionManager("productTagRepository_") async create( data: CreateProductTagDTO[], @MedusaContext() sharedContext: Context = {} @@ -95,7 +95,7 @@ export default class ProductTagService< )) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "productTagRepository_") + @InjectTransactionManager("productTagRepository_") async update( data: UpdateProductTagDTO[], @MedusaContext() sharedContext: Context = {} @@ -106,7 +106,7 @@ export default class ProductTagService< )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "productTagRepository_") + @InjectTransactionManager("productTagRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} @@ -114,7 +114,7 @@ export default class ProductTagService< await this.productTagRepository_.delete(ids, sharedContext) } - @InjectTransactionManager(doNotForceTransaction, "productTagRepository_") + @InjectTransactionManager("productTagRepository_") async upsert( data: UpsertProductTagDTO[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/src/services/product-type.ts b/packages/product/src/services/product-type.ts index bbf1182aa5b22..2d510a3430f82 100644 --- a/packages/product/src/services/product-type.ts +++ b/packages/product/src/services/product-type.ts @@ -2,11 +2,11 @@ import { ProductType } from "@models" import { Context, CreateProductTypeDTO, - UpsertProductTypeDTO, - UpdateProductTypeDTO, DAL, FindConfig, ProductTypes, + UpdateProductTypeDTO, + UpsertProductTypeDTO, } from "@medusajs/types" import { ProductTypeRepository } from "@repositories" import { @@ -17,7 +17,7 @@ import { retrieveEntity, } from "@medusajs/utils" -import { doNotForceTransaction, shouldForceTransaction } from "../utils" +import { shouldForceTransaction } from "../utils" type InjectedDependencies = { productTypeRepository: DAL.RepositoryService @@ -87,7 +87,7 @@ export default class ProductTypeService< return queryOptions } - @InjectTransactionManager(doNotForceTransaction, "productTypeRepository_") + @InjectTransactionManager("productTypeRepository_") async upsert( types: UpsertProductTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -96,7 +96,7 @@ export default class ProductTypeService< .upsert!(types, sharedContext)) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "productTypeRepository_") + @InjectTransactionManager("productTypeRepository_") async create( data: CreateProductTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -107,7 +107,7 @@ export default class ProductTypeService< )) as TEntity[] } - @InjectTransactionManager(shouldForceTransaction, "productTypeRepository_") + @InjectTransactionManager("productTypeRepository_") async update( data: UpdateProductTypeDTO[], @MedusaContext() sharedContext: Context = {} @@ -118,7 +118,7 @@ export default class ProductTypeService< )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "productTypeRepository_") + @InjectTransactionManager("productTypeRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/src/services/product-variant.ts b/packages/product/src/services/product-variant.ts index d41300ccb3840..de7a776f67f80 100644 --- a/packages/product/src/services/product-variant.ts +++ b/packages/product/src/services/product-variant.ts @@ -12,7 +12,6 @@ import { import { ProductVariantServiceTypes } from "../types/services" import ProductService from "./product" -import { doNotForceTransaction } from "../utils" type InjectedDependencies = { productVariantRepository: DAL.RepositoryService @@ -86,7 +85,7 @@ export default class ProductVariantService< )) as [TEntity[], number] } - @InjectTransactionManager(doNotForceTransaction, "productVariantRepository_") + @InjectTransactionManager("productVariantRepository_") async create( productOrId: TProduct | string, data: ProductTypes.CreateProductVariantOnlyDTO[], @@ -119,7 +118,7 @@ export default class ProductVariantService< })) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "productVariantRepository_") + @InjectTransactionManager("productVariantRepository_") async update( productOrId: TProduct | string, data: ProductVariantServiceTypes.UpdateProductVariantDTO[], @@ -145,7 +144,7 @@ export default class ProductVariantService< })) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "productVariantRepository_") + @InjectTransactionManager("productVariantRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} @@ -155,7 +154,7 @@ export default class ProductVariantService< }) } - @InjectTransactionManager(doNotForceTransaction, "productVariantRepository_") + @InjectTransactionManager("productVariantRepository_") async softDelete( ids: string[], @MedusaContext() sharedContext: Context = {} @@ -165,7 +164,7 @@ export default class ProductVariantService< }) } - @InjectTransactionManager(doNotForceTransaction, "productVariantRepository_") + @InjectTransactionManager("productVariantRepository_") async restore( ids: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/src/services/product.ts b/packages/product/src/services/product.ts index 7c6c88967570e..15d3c010b74e1 100644 --- a/packages/product/src/services/product.ts +++ b/packages/product/src/services/product.ts @@ -8,17 +8,16 @@ import { import { InjectManager, InjectTransactionManager, + isDefined, MedusaContext, MedusaError, ModulesSdkUtils, ProductUtils, - isDefined, } from "@medusajs/utils" import { Product } from "@models" import { ProductRepository } from "@repositories" import { ProductServiceTypes } from "../types/services" -import { doNotForceTransaction } from "../utils" type InjectedDependencies = { productRepository: DAL.RepositoryService @@ -118,7 +117,7 @@ export default class ProductService { )) as [TEntity[], number] } - @InjectTransactionManager(doNotForceTransaction, "productRepository_") + @InjectTransactionManager("productRepository_") async create( data: ProductTypes.CreateProductOnlyDTO[], @MedusaContext() sharedContext: Context = {} @@ -138,7 +137,7 @@ export default class ProductService { )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "productRepository_") + @InjectTransactionManager("productRepository_") async update( data: ProductServiceTypes.UpdateProductDTO[], @MedusaContext() sharedContext: Context = {} @@ -154,7 +153,7 @@ export default class ProductService { )) as TEntity[] } - @InjectTransactionManager(doNotForceTransaction, "productRepository_") + @InjectTransactionManager("productRepository_") async delete( ids: string[], @MedusaContext() sharedContext: Context = {} @@ -164,7 +163,7 @@ export default class ProductService { }) } - @InjectTransactionManager(doNotForceTransaction, "productRepository_") + @InjectTransactionManager("productRepository_") async softDelete( productIds: string[], @MedusaContext() sharedContext: Context = {} @@ -174,7 +173,7 @@ export default class ProductService { }) } - @InjectTransactionManager(doNotForceTransaction, "productRepository_") + @InjectTransactionManager("productRepository_") async restore( productIds: string[], @MedusaContext() sharedContext: Context = {} diff --git a/packages/product/src/utils/index.ts b/packages/product/src/utils/index.ts index 8188a4c10e6bd..9e37c3cbf0c24 100644 --- a/packages/product/src/utils/index.ts +++ b/packages/product/src/utils/index.ts @@ -1,7 +1,5 @@ -import { MODULE_RESOURCE_TYPE } from "@medusajs/types" - -export function shouldForceTransaction(target: any): boolean { - return target.moduleDeclaration?.resources === MODULE_RESOURCE_TYPE.ISOLATED +export function shouldForceTransaction(): boolean { + return true } export function doNotForceTransaction(): boolean { diff --git a/packages/utils/src/dal/utils.ts b/packages/utils/src/dal/utils.ts index 457a7ab0eb426..de3e0aa8ad239 100644 --- a/packages/utils/src/dal/utils.ts +++ b/packages/utils/src/dal/utils.ts @@ -28,9 +28,12 @@ export async function transactionWrapper( Object.assign(options, { isolationLevel }) } + const freshManager = this.getFreshManager + ? this.getFreshManager() + : this.manager_ const transactionMethod = - this.manager_.transaction ?? this.manager_.transactional - return await transactionMethod.bind(this.manager_)(task, options) + freshManager.transaction ?? freshManager.transactional + return await transactionMethod.bind(freshManager)(task, options) } /** diff --git a/packages/utils/src/modules-sdk/decorators/inject-manager.ts b/packages/utils/src/modules-sdk/decorators/inject-manager.ts index e0c007d9c9fc2..8650a613952c4 100644 --- a/packages/utils/src/modules-sdk/decorators/inject-manager.ts +++ b/packages/utils/src/modules-sdk/decorators/inject-manager.ts @@ -16,13 +16,12 @@ export function InjectManager(managerProperty?: string): MethodDecorator { const argIndex = target.MedusaContextIndex_[propertyKey] descriptor.value = function (...args: any[]) { - const context: SharedContext | Context = args[argIndex] ?? {} - const resourceWithManager = (!managerProperty + const context: SharedContext | Context = { ...(args[argIndex] ?? {}) } + const resourceWithManager = !managerProperty ? this - : this[managerProperty]) + : this[managerProperty] - context.manager = - context.manager ?? resourceWithManager.getFreshManager() + context.manager = context.manager ?? resourceWithManager.getFreshManager() args[argIndex] = context return originalMethod.apply(this, args) diff --git a/packages/utils/src/modules-sdk/decorators/inject-transaction-manager.ts b/packages/utils/src/modules-sdk/decorators/inject-transaction-manager.ts index 4cb557ea59aa2..30d9ca8550484 100644 --- a/packages/utils/src/modules-sdk/decorators/inject-transaction-manager.ts +++ b/packages/utils/src/modules-sdk/decorators/inject-transaction-manager.ts @@ -1,7 +1,10 @@ import { Context, SharedContext } from "@medusajs/types" +import { isString } from "../../common" export function InjectTransactionManager( - shouldForceTransaction: (target: any) => boolean = () => false, + shouldForceTransactionOrManagerProperty: + | string + | ((target: any) => boolean) = () => false, managerProperty?: string ): MethodDecorator { return function ( @@ -16,6 +19,14 @@ export function InjectTransactionManager( } const originalMethod = descriptor.value + const shouldForceTransaction = !isString( + shouldForceTransactionOrManagerProperty + ) + ? shouldForceTransactionOrManagerProperty + : () => false + managerProperty = isString(shouldForceTransactionOrManagerProperty) + ? shouldForceTransactionOrManagerProperty + : managerProperty const argIndex = target.MedusaContextIndex_[propertyKey] descriptor.value = async function (...args: any[]) { @@ -31,7 +42,7 @@ export function InjectTransactionManager( : this[managerProperty] ).transaction( async (transactionManager) => { - args[argIndex] = args[argIndex] ?? {} + args[argIndex] = { ...(args[argIndex] ?? {}) } args[argIndex].transactionManager = transactionManager return await originalMethod.apply(this, args) diff --git a/packages/workflows/src/handlers/product/update-products-variants-prices.ts b/packages/workflows/src/handlers/product/update-products-variants-prices.ts index 9934e645fdfd3..49c2f049e3063 100644 --- a/packages/workflows/src/handlers/product/update-products-variants-prices.ts +++ b/packages/workflows/src/handlers/product/update-products-variants-prices.ts @@ -2,6 +2,7 @@ import { ProductTypes, WorkflowTypes } from "@medusajs/types" import { MedusaError } from "@medusajs/utils" import { WorkflowArguments } from "../../helper" +import { ModuleRegistrationName } from "@medusajs/modules-sdk" type ProductHandle = string type VariantIndexAndPrices = { @@ -37,6 +38,8 @@ export async function updateProductsVariantsPrices({ products.map((p) => [p.handle!, p]) ) + const regionIds = new Set() + for (const mapData of productsHandleVariantsIndexPricesMap.entries()) { const [handle, variantData] = mapData @@ -55,9 +58,10 @@ export async function updateProductsVariantsPrices({ prices: item.prices, }) - variantPricesMap.set(variant.id, []) + const prices: any[] = [] + variantPricesMap.set(variant.id, prices) - item.prices.forEach(async (price) => { + item.prices.forEach((price) => { const obj = { amount: price.amount, currency_code: price.currency_code, @@ -65,38 +69,59 @@ export async function updateProductsVariantsPrices({ } if (price.region_id) { - const region = await regionService.retrieve(price.region_id) - obj.currency_code = region.currency_code - obj.rules = { - region_id: price.region_id, - } + regionIds.add(price.region_id) + ;(obj as any).region_id = price.region_id } - const variantPrices = variantPricesMap.get(variant.id) - variantPrices?.push(obj) + prices.push(obj) }) }) } - if (featureFlagRouter.isFeatureEnabled("isolate_pricing_domain")) { - const remoteLink = container.resolve("remoteLink") - const pricingModuleService = container.resolve("pricingModuleService") + if (regionIds.size) { + const regions = await regionService.list({ + id: [...regionIds], + }) + const regionMap = new Map(regions.map((r) => [r.id, r])) - for (let { variantId } of variantIdsPricesData) { - const priceSet = await pricingModuleService.create({ - rules: [{ rule_attribute: "region_id" }], - prices: variantPricesMap.get(variantId), - }) + for (const [, prices] of variantPricesMap.entries()) { + prices.forEach((price) => { + if (price.region_id) { + const region = regionMap.get(price.region_id) + price.currency_code = region?.currency_code + price.rules = { + region_id: price.region_id, + } - await remoteLink.create({ - productService: { - variant_id: variantId, - }, - pricingService: { - price_set_id: priceSet.id, - }, + delete price.region_id + } }) } + } + + if (featureFlagRouter.isFeatureEnabled("isolate_pricing_domain")) { + const remoteLink = container.resolve("remoteLink") + const pricingModuleService = container.resolve( + ModuleRegistrationName.PRICING + ) + + const priceSetsToCreate = variantIdsPricesData.map(({ variantId }) => ({ + rules: [{ rule_attribute: "region_id" }], + prices: variantPricesMap.get(variantId), + })) + + const priceSets = await pricingModuleService.create(priceSetsToCreate) + + const links = priceSets.map((priceSet, index) => ({ + productService: { + variant_id: variantIdsPricesData[index].variantId, + }, + pricingService: { + price_set_id: priceSet.id, + }, + })) + + await remoteLink.create(links) } else { await productVariantServiceTx.updateVariantPrices(variantIdsPricesData) }