diff --git a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts index ff3b0c3c47..c4a8d58916 100644 --- a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts @@ -1,4 +1,5 @@ import { + addShippingMethodToWorkflow, addToCartWorkflow, createCartWorkflow, createPaymentCollectionForCartWorkflow, @@ -15,6 +16,7 @@ import { ModuleRegistrationName, Modules } from "@medusajs/modules-sdk" import { ICartModuleService, ICustomerModuleService, + IFulfillmentModuleService, IPaymentModuleService, IPricingModuleService, IProductModuleService, @@ -40,6 +42,7 @@ medusaIntegrationTestRunner({ let productModule: IProductModuleService let pricingModule: IPricingModuleService let paymentModule: IPaymentModuleService + let fulfillmentModule: IFulfillmentModuleService let remoteLink, remoteQuery let defaultRegion @@ -57,6 +60,9 @@ medusaIntegrationTestRunner({ productModule = appContainer.resolve(ModuleRegistrationName.PRODUCT) pricingModule = appContainer.resolve(ModuleRegistrationName.PRICING) paymentModule = appContainer.resolve(ModuleRegistrationName.PAYMENT) + fulfillmentModule = appContainer.resolve( + ModuleRegistrationName.FULFILLMENT + ) remoteLink = appContainer.resolve("remoteLink") remoteQuery = appContainer.resolve("remoteQuery") }) @@ -978,6 +984,99 @@ medusaIntegrationTestRunner({ }) }) }) + describe("AddShippingMethodToCartWorkflow", () => { + it("should add shipping method to cart", async () => { + let cart = await cartModuleService.create({ + currency_code: "usd", + }) + + const shippingProfile = + await fulfillmentModule.createShippingProfiles({ + name: "Test", + type: "default", + }) + const fulfillmentSet = await fulfillmentModule.create({ + name: "Test", + type: "test-type", + }) + const serviceZone = await fulfillmentModule.createServiceZones({ + name: "Test", + fulfillment_set_id: fulfillmentSet.id, + geo_zones: [ + { + type: "country", + country_code: "us", + }, + ], + }) + + const shippingOption = await fulfillmentModule.createShippingOptions({ + name: "Test shipping option", + service_zone_id: serviceZone.id, + shipping_profile_id: shippingProfile.id, + provider_id: "manual_test-provider", + price_type: "flat", + type: { + label: "Test type", + description: "Test description", + code: "test-code", + }, + }) + + const priceSet = await pricingModule.create({ + prices: [ + { + amount: 3000, + currency_code: "usd", + }, + ], + }) + + await remoteLink.create([ + { + [Modules.FULFILLMENT]: { + shipping_option_id: shippingOption.id, + }, + [Modules.PRICING]: { + price_set_id: priceSet.id, + }, + }, + ]) + + cart = await cartModuleService.retrieve(cart.id, { + select: ["id", "region_id", "currency_code"], + }) + + await addShippingMethodToWorkflow(appContainer).run({ + input: { + options: [ + { + id: shippingOption.id, + }, + ], + cart_id: cart.id, + currency_code: cart.currency_code, + }, + }) + + cart = await cartModuleService.retrieve(cart.id, { + relations: ["shipping_methods"], + }) + + expect(cart).toEqual( + expect.objectContaining({ + id: cart.id, + currency_code: "usd", + shipping_methods: expect.arrayContaining([ + expect.objectContaining({ + amount: 3000, + name: "Test shipping option", + }), + ]), + }) + ) + }) + }) }) }, }) diff --git a/integration-tests/modules/__tests__/fulfillment/index.spec.ts b/integration-tests/modules/__tests__/fulfillment/index.spec.ts index f4be4dc929..f03e44a298 100644 --- a/integration-tests/modules/__tests__/fulfillment/index.spec.ts +++ b/integration-tests/modules/__tests__/fulfillment/index.spec.ts @@ -1,7 +1,7 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { IFulfillmentModuleService } from "@medusajs/types" import { medusaIntegrationTestRunner } from "medusa-test-utils/dist" import { setupFullDataFulfillmentStructure } from "../fixtures" -import { IFulfillmentModuleService } from "@medusajs/types" -import { ModuleRegistrationName } from "@medusajs/modules-sdk" jest.setTimeout(100000) diff --git a/integration-tests/modules/__tests__/link-modules/shipping-option-price-set.spec.ts b/integration-tests/modules/__tests__/link-modules/shipping-option-price-set.spec.ts new file mode 100644 index 0000000000..f868bcb174 --- /dev/null +++ b/integration-tests/modules/__tests__/link-modules/shipping-option-price-set.spec.ts @@ -0,0 +1,112 @@ +import { ModuleRegistrationName, Modules } from "@medusajs/modules-sdk" +import { + IFulfillmentModuleService, + IPricingModuleService, +} from "@medusajs/types" +import { ContainerRegistrationKeys } from "@medusajs/utils" +import { medusaIntegrationTestRunner } from "medusa-test-utils" + +jest.setTimeout(50000) + +const env = { MEDUSA_FF_MEDUSA_V2: true } + +medusaIntegrationTestRunner({ + env, + testSuite: ({ getContainer }) => { + describe("Region and Payment Providers", () => { + let appContainer + let fulfillmentModule: IFulfillmentModuleService + let pricingModule: IPricingModuleService + let remoteQuery + let remoteLink + + beforeAll(async () => { + appContainer = getContainer() + fulfillmentModule = appContainer.resolve( + ModuleRegistrationName.FULFILLMENT + ) + pricingModule = appContainer.resolve(ModuleRegistrationName.PRICING) + remoteQuery = appContainer.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) + remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK) + }) + + it("should query shipping option and price set link with remote query", async () => { + const shippingProfile = await fulfillmentModule.createShippingProfiles({ + name: "Test", + type: "default", + }) + const fulfillmentSet = await fulfillmentModule.create({ + name: "Test", + type: "test-type", + }) + const serviceZone = await fulfillmentModule.createServiceZones({ + name: "Test", + fulfillment_set_id: fulfillmentSet.id, + geo_zones: [ + { + type: "country", + country_code: "us", + }, + ], + }) + + const shippingOption = await fulfillmentModule.createShippingOptions({ + name: "Test shipping option", + service_zone_id: serviceZone.id, + shipping_profile_id: shippingProfile.id, + provider_id: "manual_test-provider", + price_type: "flat", + type: { + label: "Test type", + description: "Test description", + code: "test-code", + }, + }) + + const priceSet = await pricingModule.create({ + prices: [ + { + amount: 3000, + currency_code: "usd", + }, + ], + }) + + await remoteLink.create([ + { + [Modules.FULFILLMENT]: { + shipping_option_id: shippingOption.id, + }, + [Modules.PRICING]: { + price_set_id: priceSet.id, + }, + }, + ]) + + const link = await remoteQuery({ + shipping_option: { + fields: ["id"], + price: { + fields: ["id", "price_set_id", "shipping_option_id"], + }, + }, + }) + + expect(link).toHaveLength(1) + expect(link).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: shippingOption.id, + price: expect.objectContaining({ + price_set_id: priceSet.id, + shipping_option_id: shippingOption.id, + }), + }), + ]) + ) + }) + }) + }, +}) diff --git a/packages/core-flows/src/definition/cart/steps/add-shipping-method-to-cart.ts b/packages/core-flows/src/definition/cart/steps/add-shipping-method-to-cart.ts new file mode 100644 index 0000000000..2c28799058 --- /dev/null +++ b/packages/core-flows/src/definition/cart/steps/add-shipping-method-to-cart.ts @@ -0,0 +1,31 @@ +import { CreateShippingMethodDTO, ICartModuleService } from "@medusajs/types" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" +import { ModuleRegistrationName } from "../../../../../modules-sdk/dist" + +interface StepInput { + shipping_methods: CreateShippingMethodDTO[] +} + +export const addShippingMethodToCartStepId = "add-shipping-method-to-cart-step" +export const addShippingMethodToCartStep = createStep( + addShippingMethodToCartStepId, + async (data: StepInput, { container }) => { + const cartService = container.resolve( + ModuleRegistrationName.CART + ) + + const methods = await cartService.addShippingMethods(data.shipping_methods) + + return new StepResponse(methods, methods) + }, + async (methods, { container }) => { + const cartService: ICartModuleService = container.resolve( + ModuleRegistrationName.CART + ) + if (!methods?.length) { + return + } + + await cartService.deleteShippingMethods(methods.map((m) => m.id)) + } +) diff --git a/packages/core-flows/src/definition/cart/steps/add-to-cart.ts b/packages/core-flows/src/definition/cart/steps/add-to-cart.ts index 58a0d3c532..b4363a1532 100644 --- a/packages/core-flows/src/definition/cart/steps/add-to-cart.ts +++ b/packages/core-flows/src/definition/cart/steps/add-to-cart.ts @@ -16,7 +16,7 @@ export const addToCartStep = createStep( const items = await cartService.addLineItems(data.items) - return new StepResponse(items) + return new StepResponse(items, items) }, async (createdLineItems, { container }) => { const cartService: ICartModuleService = container.resolve( diff --git a/packages/core-flows/src/definition/cart/steps/get-shipping-option-price-sets.ts b/packages/core-flows/src/definition/cart/steps/get-shipping-option-price-sets.ts new file mode 100644 index 0000000000..598eca0563 --- /dev/null +++ b/packages/core-flows/src/definition/cart/steps/get-shipping-option-price-sets.ts @@ -0,0 +1,82 @@ +import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { IPricingModuleService, PricingContext } from "@medusajs/types" +import { + ContainerRegistrationKeys, + MedusaError, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { StepResponse, createStep } from "@medusajs/workflows-sdk" + +interface StepInput { + optionIds: string[] + context?: Record +} + +export const getShippingOptionPriceSetsStepId = "get-variant-price-sets" +export const getShippingOptionPriceSetsStep = createStep( + getShippingOptionPriceSetsStepId, + async (data: StepInput, { container }) => { + if (!data.optionIds.length) { + return new StepResponse({}) + } + + const pricingModuleService = container.resolve( + ModuleRegistrationName.PRICING + ) + + const remoteQuery = container.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) + + const query = remoteQueryObjectFromString({ + entryPoint: "shipping_option_price_set", + fields: ["id", "shipping_option_id", "price_set_id"], + variables: { + shipping_option_id: data.optionIds, + }, + }) + + const optionPriceSets = await remoteQuery(query) + + const notFound: string[] = [] + const priceSetIds: string[] = [] + + optionPriceSets.forEach((v) => { + if (v.price_set_id) { + priceSetIds.push(v.price_set_id) + } else { + notFound.push(v.shipping_option_id) + } + }) + + if (notFound.length) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Shipping options with IDs ${notFound.join(", ")} do not have a price` + ) + } + + const calculatedPriceSets = await pricingModuleService.calculatePrices( + { id: priceSetIds }, + { context: data.context as PricingContext["context"] } + ) + + const idToPriceSet = new Map>( + calculatedPriceSets.map((p) => [p.id, p]) + ) + + const optionToCalculatedPriceSets = optionPriceSets.reduce( + (acc, { shipping_option_id, price_set_id }) => { + const calculatedPriceSet = idToPriceSet.get(price_set_id) + if (calculatedPriceSet) { + acc[shipping_option_id] = calculatedPriceSet + } + + return acc + }, + {} + ) + + return new StepResponse(optionToCalculatedPriceSets) + } +) diff --git a/packages/core-flows/src/definition/cart/steps/index.ts b/packages/core-flows/src/definition/cart/steps/index.ts index 8f046c5d67..e81f09ba55 100644 --- a/packages/core-flows/src/definition/cart/steps/index.ts +++ b/packages/core-flows/src/definition/cart/steps/index.ts @@ -1,3 +1,4 @@ +export * from "./add-shipping-method-to-cart" export * from "./add-to-cart" export * from "./create-carts" export * from "./create-line-item-adjustments" @@ -7,6 +8,7 @@ export * from "./find-or-create-customer" export * from "./find-sales-channel" export * from "./get-actions-to-compute-from-promotions" export * from "./get-item-tax-lines" +export * from "./get-shipping-option-price-sets" export * from "./get-variant-price-sets" export * from "./get-variants" export * from "./prepare-adjustments-from-promotion-actions" @@ -18,3 +20,4 @@ export * from "./set-tax-lines-for-items" export * from "./update-cart-promotions" export * from "./update-carts" export * from "./validate-variants-existence" + diff --git a/packages/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts b/packages/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts new file mode 100644 index 0000000000..e64fb965a0 --- /dev/null +++ b/packages/core-flows/src/definition/cart/workflows/add-shipping-method-to-cart.ts @@ -0,0 +1,76 @@ +import { + WorkflowData, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { useRemoteQueryStep } from "../../../common/steps/use-remote-query" +import { addShippingMethodToCartStep } from "../steps" +import { getShippingOptionPriceSetsStep } from "../steps/get-shipping-option-price-sets" +import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions" +import { updateTaxLinesStep } from "../steps/update-tax-lines" + +interface AddShippingMethodToCartWorkflowInput { + cart_id: string + currency_code: string + options: { + id: string + data?: Record + }[] +} + +export const addShippingMethodToCartWorkflowId = "add-shipping-method-to-cart" +export const addShippingMethodToWorkflow = createWorkflow( + addShippingMethodToCartWorkflowId, + ( + input: WorkflowData + ): WorkflowData => { + const optionIds = transform({ input }, (data) => { + return (data.input.options ?? []).map((i) => i.id) + }) + + const priceSets = getShippingOptionPriceSetsStep({ + optionIds: optionIds, + context: { currency_code: input.currency_code }, + }) + + const shippingOptions = useRemoteQueryStep({ + entry_point: "shipping_option", + fields: ["id", "name"], + variables: { + id: optionIds, + }, + }) + + const shippingMethodInput = transform( + { priceSets, input, shippingOptions }, + (data) => { + const options = (data.input.options ?? []).map((option) => { + const shippingOption = data.shippingOptions.find( + (so) => so.id === option.id + )! + + const price = data.priceSets[option.id].calculated_amount + + return { + shipping_option_id: shippingOption.id, + amount: price, + data: option.data ?? {}, + name: shippingOption.name, + cart_id: data.input.cart_id, + } + }) + + return options + } + ) + + const shippingMethods = addShippingMethodToCartStep({ + shipping_methods: shippingMethodInput, + }) + refreshCartPromotionsStep({ id: input.cart_id }) + updateTaxLinesStep({ + cart_or_cart_id: input.cart_id, + shipping_methods: shippingMethods, + }) + } +) diff --git a/packages/core-flows/src/definition/cart/workflows/add-to-cart.ts b/packages/core-flows/src/definition/cart/workflows/add-to-cart.ts index f20c013949..87dda4ff24 100644 --- a/packages/core-flows/src/definition/cart/workflows/add-to-cart.ts +++ b/packages/core-flows/src/definition/cart/workflows/add-to-cart.ts @@ -92,7 +92,6 @@ export const addToCartWorkflow = createWorkflow( updateTaxLinesStep({ cart_or_cart_id: input.cart, items, - // TODO: add shipping methods here when its ready }) refreshCartPromotionsStep({ id: input.cart.id }) diff --git a/packages/core-flows/src/definition/cart/workflows/index.ts b/packages/core-flows/src/definition/cart/workflows/index.ts index 9753920928..27b47c7e5f 100644 --- a/packages/core-flows/src/definition/cart/workflows/index.ts +++ b/packages/core-flows/src/definition/cart/workflows/index.ts @@ -1,3 +1,4 @@ +export * from "./add-shipping-method-to-cart" export * from "./add-to-cart" export * from "./create-carts" export * from "./create-payment-collection-for-cart" diff --git a/packages/fulfillment/src/loaders/providers.ts b/packages/fulfillment/src/loaders/providers.ts index 7e5f3572a0..ab0be93881 100644 --- a/packages/fulfillment/src/loaders/providers.ts +++ b/packages/fulfillment/src/loaders/providers.ts @@ -1,13 +1,13 @@ import { moduleProviderLoader } from "@medusajs/modules-sdk" import { LoaderOptions, ModuleProvider, ModulesSdkTypes } from "@medusajs/types" -import { asFunction, asValue, Lifetime } from "awilix" -import { FulfillmentIdentifiersRegistrationName } from "@types" import { ContainerRegistrationKeys, lowerCaseFirst, promiseAll, } from "@medusajs/utils" import { FulfillmentProviderService } from "@services" +import { FulfillmentIdentifiersRegistrationName } from "@types" +import { Lifetime, asFunction, asValue } from "awilix" const registrationFn = async (klass, container, pluginOptions) => { Object.entries(pluginOptions.config || []).map(([name, config]) => { diff --git a/packages/fulfillment/src/services/fulfillment-module-service.ts b/packages/fulfillment/src/services/fulfillment-module-service.ts index 3c7e681ff1..68bbb2d301 100644 --- a/packages/fulfillment/src/services/fulfillment-module-service.ts +++ b/packages/fulfillment/src/services/fulfillment-module-service.ts @@ -25,7 +25,6 @@ import { promiseAll, } from "@medusajs/utils" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" import { Fulfillment, FulfillmentSet, @@ -37,6 +36,7 @@ import { ShippingProfile, } from "@models" import { isContextValid, validateRules } from "@utils" +import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" import FulfillmentProviderService from "./fulfillment-provider" const generateMethodForModels = [ @@ -435,7 +435,7 @@ export default class FulfillmentModuleService< return [] } - const rules = data_.flatMap((d) => d.rules) + const rules = data_.flatMap((d) => d.rules).filter(Boolean) if (rules.length) { validateRules(rules as Record[]) } diff --git a/packages/link-modules/src/definitions/index.ts b/packages/link-modules/src/definitions/index.ts index cd4feac8bd..c44a853a11 100644 --- a/packages/link-modules/src/definitions/index.ts +++ b/packages/link-modules/src/definitions/index.ts @@ -12,4 +12,5 @@ export * from "./product-variant-price-set" export * from "./publishable-api-key-sales-channel" export * from "./region-payment-provider" export * from "./sales-channel-location" +export * from "./shipping-option-price-set" diff --git a/packages/link-modules/src/definitions/shipping-option-price-set.ts b/packages/link-modules/src/definitions/shipping-option-price-set.ts new file mode 100644 index 0000000000..2e8337be16 --- /dev/null +++ b/packages/link-modules/src/definitions/shipping-option-price-set.ts @@ -0,0 +1,62 @@ +import { Modules } from "@medusajs/modules-sdk" +import { ModuleJoinerConfig } from "@medusajs/types" +import { LINKS } from "../links" + +export const ShippingOptionPriceSet: ModuleJoinerConfig = { + serviceName: LINKS.ShippingOptionPriceSet, + isLink: true, + databaseConfig: { + tableName: "shipping_option_price_set", + idPrefix: "sops", + }, + alias: [ + { + name: ["shipping_option_price_set", "shipping_option_price_sets"], + args: { + entity: "LinkShippingOptionPriceSet", + }, + }, + ], + primaryKeys: ["id", "shipping_option_id", "price_set_id"], + relationships: [ + { + serviceName: Modules.FULFILLMENT, + primaryKey: "id", + foreignKey: "shipping_option_id", + alias: "shipping_option", + args: { + methodSuffix: "ShippingOptions", + }, + }, + { + serviceName: Modules.PRICING, + primaryKey: "id", + foreignKey: "price_set_id", + alias: "price_set", + deleteCascade: true, + }, + ], + extends: [ + { + serviceName: Modules.FULFILLMENT, + relationship: { + serviceName: LINKS.ShippingOptionPriceSet, + primaryKey: "shipping_option_id", + foreignKey: "id", + alias: "price", + }, + }, + { + serviceName: Modules.PRICING, + relationship: { + serviceName: LINKS.ShippingOptionPriceSet, + primaryKey: "price_set_id", + foreignKey: "id", + alias: "shipping_option_link", + }, + fieldAlias: { + shipping_option: "shipping_option_link.shipping_option", + }, + }, + ], +} diff --git a/packages/link-modules/src/links.ts b/packages/link-modules/src/links.ts index e8699e3db8..d0bab47137 100644 --- a/packages/link-modules/src/links.ts +++ b/packages/link-modules/src/links.ts @@ -14,6 +14,12 @@ export const LINKS = { Modules.PRICING, "price_set_id" ), + ShippingOptionPriceSet: composeLinkName( + Modules.FULFILLMENT, + "shipping_option_id", + Modules.PRICING, + "price_set_id" + ), CartPaymentCollection: composeLinkName( Modules.CART, "cart_id", diff --git a/packages/pricing/src/services/pricing-module.ts b/packages/pricing/src/services/pricing-module.ts index 16f949ee3b..a3b15029b3 100644 --- a/packages/pricing/src/services/pricing-module.ts +++ b/packages/pricing/src/services/pricing-module.ts @@ -15,15 +15,15 @@ import { RuleTypeDTO, } from "@medusajs/types" import { - arrayDifference, - deduplicate, - groupBy, InjectManager, InjectTransactionManager, MedusaContext, MedusaError, ModulesSdkUtils, PriceListType, + arrayDifference, + deduplicate, + groupBy, removeNullish, } from "@medusajs/utils" @@ -47,9 +47,9 @@ import { PriceRuleService, RuleTypeService, } from "@services" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" -import { validatePriceListDates } from "@utils" import { ServiceTypes } from "@types" +import { validatePriceListDates } from "@utils" +import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService diff --git a/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts b/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts index a167dcc4cf..66296b3104 100644 --- a/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts +++ b/packages/utils/src/dal/mikro-orm/mikro-orm-repository.ts @@ -21,11 +21,7 @@ import { } from "@mikro-orm/core/typings" import { SqlEntityManager } from "@mikro-orm/postgresql" import { isString } from "../../common" -import { - InjectTransactionManager, - MedusaContext, - buildQuery, -} from "../../modules-sdk" +import { buildQuery } from "../../modules-sdk" import { getSoftDeletedCascadedEntitiesIdsMappedBy, transactionWrapper, @@ -119,10 +115,9 @@ export class MikroOrmBaseRepository throw new Error("Method not implemented.") } - @InjectTransactionManager() async softDelete( idsOrFilter: string[] | InternalFilterQuery, - @MedusaContext() sharedContext: Context = {} + sharedContext: Context = {} ): Promise<[T[], Record]> { const isArray = Array.isArray(idsOrFilter) // TODO handle composite keys @@ -152,10 +147,9 @@ export class MikroOrmBaseRepository return [entities, softDeletedEntitiesMap] } - @InjectTransactionManager() async restore( idsOrFilter: string[] | InternalFilterQuery, - @MedusaContext() sharedContext: Context = {} + sharedContext: Context = {} ): Promise<[T[], Record]> { // TODO handle composite keys const isArray = Array.isArray(idsOrFilter) 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 580304467e..2926f13e21 100644 --- a/packages/utils/src/modules-sdk/decorators/inject-transaction-manager.ts +++ b/packages/utils/src/modules-sdk/decorators/inject-transaction-manager.ts @@ -15,7 +15,9 @@ export function InjectTransactionManager( ): void { if (!target.MedusaContextIndex_) { throw new Error( - `To apply @InjectTransactionManager you have to flag a parameter using @MedusaContext` + `An error occured applying decorator '@InjectTransactionManager' to method ${String( + propertyKey + )}: Missing parameter with flag @MedusaContext` ) }