diff --git a/integration-tests/http/__tests__/returns/returns.spec.ts b/integration-tests/http/__tests__/returns/returns.spec.ts index 6116f791fd..1e347ec5ac 100644 --- a/integration-tests/http/__tests__/returns/returns.spec.ts +++ b/integration-tests/http/__tests__/returns/returns.spec.ts @@ -215,6 +215,14 @@ medusaIntegrationTestRunner({ fulfillment_set_id: fulfillmentSet.id, }, }, + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, { [Modules.SALES_CHANNEL]: { sales_channel_id: "test", diff --git a/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts b/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts index 5b374bf285..88ba47d373 100644 --- a/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts +++ b/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts @@ -1,4 +1,8 @@ -import { RuleOperator } from "@medusajs/utils" +import { + ContainerRegistrationKeys, + Modules, + RuleOperator, +} from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" import { adminHeaders, @@ -14,6 +18,9 @@ medusaIntegrationTestRunner({ let shippingProfile let fulfillmentSet let region + let appContainer + let location + let location2 const shippingOptionRule = { operator: RuleOperator.EQ, @@ -22,7 +29,8 @@ medusaIntegrationTestRunner({ } beforeEach(async () => { - const appContainer = getContainer() + appContainer = getContainer() + await createAdminUser(dbConnection, adminHeaders, appContainer) shippingProfile = ( @@ -36,7 +44,7 @@ medusaIntegrationTestRunner({ ) ).data.shipping_profile - let location = ( + location = ( await api.post( `/admin/stock-locations`, { @@ -103,6 +111,22 @@ medusaIntegrationTestRunner({ }) it("should create a shipping option successfully", async () => { + const remoteLink = appContainer.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + // TODO: move this to an endpoint when available + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + const shippingOptionPayload = { name: "Test shipping option", service_zone_id: fulfillmentSet.service_zones[0].id, @@ -173,6 +197,41 @@ medusaIntegrationTestRunner({ }) ) }) + + it("should throw error when provider does not exist on a location", async () => { + const shippingOptionPayload = { + name: "Test shipping option", + service_zone_id: fulfillmentSet.service_zones[0].id, + shipping_profile_id: shippingProfile.id, + provider_id: "does-not-exist", + price_type: "flat", + type: { + label: "Test type", + description: "Test description", + code: "test-code", + }, + prices: [ + { + currency_code: "usd", + amount: 1000, + }, + ], + rules: [shippingOptionRule], + } + + const error = await api + .post( + `/admin/shipping-options`, + shippingOptionPayload, + adminHeaders + ) + .catch((e) => e) + + expect(error.response.status).toEqual(400) + expect(error.response.data.message).toEqual( + "Providers (does-not-exist) are not enabled for the service location" + ) + }) }) describe("POST /admin/shipping-options/:id", () => { @@ -212,6 +271,22 @@ medusaIntegrationTestRunner({ ], } + const remoteLink = appContainer.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + // TODO: move this to an endpoint when available + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + const response = await api.post( `/admin/shipping-options`, shippingOptionPayload, @@ -331,6 +406,73 @@ medusaIntegrationTestRunner({ }) ) }) + + it("should throw an error when provider does not belong to service location", async () => { + const shippingOptionPayload = { + name: "Test shipping option", + service_zone_id: fulfillmentSet.service_zones[0].id, + shipping_profile_id: shippingProfile.id, + provider_id: "manual_test-provider", + price_type: "flat", + type: { + label: "Test type", + description: "Test description", + code: "test-code", + }, + prices: [ + { + currency_code: "usd", + amount: 1000, + }, + { + region_id: region.id, + amount: 1000, + }, + ], + rules: [shippingOptionRule], + } + + const remoteLink = appContainer.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + // TODO: move this to an endpoint when available + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + + const response = await api.post( + `/admin/shipping-options`, + shippingOptionPayload, + adminHeaders + ) + + const shippingOptionId = response.data.shipping_option.id + + const updateShippingOptionPayload = { + provider_id: "another_test-provider", + } + + const error = await api + .post( + `/admin/shipping-options/${shippingOptionId}`, + updateShippingOptionPayload, + adminHeaders + ) + .catch((e) => e) + + expect(error.response.status).toEqual(400) + expect(error.response.data.message).toEqual( + "Providers (another_test-provider) are not enabled for the service location" + ) + }) }) describe("DELETE /admin/shipping-options/:id", () => { @@ -359,6 +501,22 @@ medusaIntegrationTestRunner({ rules: [shippingOptionRule], } + const remoteLink = appContainer.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + // TODO: move this to an endpoint when available + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + const response = await api.post( `/admin/shipping-options`, shippingOptionPayload, 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 6f2e2e0338..0fd2f1b0e1 100644 --- a/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/cart.workflows.spec.ts @@ -1480,6 +1480,36 @@ medusaIntegrationTestRunner({ }) it("should add shipping method to cart", async () => { + const stockLocation = ( + await api.post( + `/admin/stock-locations`, + { name: "test location" }, + adminHeaders + ) + ).data.stock_location + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: stockLocation.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + ]) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: stockLocation.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + const shippingOption = await fulfillmentModule.createShippingOptions({ name: "Test shipping option", service_zone_id: fulfillmentSet.service_zones[0].id, diff --git a/integration-tests/modules/__tests__/cart/store/carts.spec.ts b/integration-tests/modules/__tests__/cart/store/carts.spec.ts index 219bb27741..65c18a25b4 100644 --- a/integration-tests/modules/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/modules/__tests__/cart/store/carts.spec.ts @@ -1930,6 +1930,28 @@ medusaIntegrationTestRunner({ ) ).data.product + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: stockLocation.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + ]) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: stockLocation.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + shippingOption = ( await api.post( `/admin/shipping-options`, 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 index 84d4555eb7..2ddfd3fce6 100644 --- 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 @@ -8,6 +8,10 @@ import { Modules, } from "@medusajs/utils" import { medusaIntegrationTestRunner } from "medusa-test-utils" +import { + adminHeaders, + createAdminUser, +} from "../../../helpers/create-admin-user" jest.setTimeout(50000) @@ -15,13 +19,12 @@ const env = { MEDUSA_FF_MEDUSA_V2: true } medusaIntegrationTestRunner({ env, - testSuite: ({ getContainer }) => { + testSuite: ({ dbConnection, getContainer, api }) => { describe("Region and Payment Providers", () => { let appContainer let fulfillmentModule: IFulfillmentModuleService let pricingModule: IPricingModuleService let remoteQuery - let remoteLink beforeAll(async () => { appContainer = getContainer() @@ -32,7 +35,7 @@ medusaIntegrationTestRunner({ remoteQuery = appContainer.resolve( ContainerRegistrationKeys.REMOTE_QUERY ) - remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK) + await createAdminUser(dbConnection, adminHeaders, getContainer()) }) it("should query shipping option and price set link with remote query", async () => { @@ -44,6 +47,38 @@ medusaIntegrationTestRunner({ name: "Test", type: "test-type", }) + const remoteLink = appContainer.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + const location = ( + await api.post( + `/admin/stock-locations`, + { name: "Test location" }, + adminHeaders + ) + ).data.stock_location + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + ]) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) const serviceZone = await fulfillmentModule.createServiceZones({ name: "Test", fulfillment_set_id: fulfillmentSet.id, diff --git a/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts b/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts index 2bdbe15dde..c3d69c6609 100644 --- a/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts +++ b/integration-tests/modules/__tests__/order/workflows/__fixtures__/index.ts @@ -130,6 +130,17 @@ export async function prepareDataFixtures({ container }) { }, ]) + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + const shippingOptionData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput = { name: "Shipping option", diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts index 0cdc305595..8914ac9441 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-claim.spec.ts @@ -117,6 +117,17 @@ async function prepareDataFixtures({ container }) { const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + await remoteLink.create([ { [Modules.STOCK_LOCATION]: { diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts index b28de4aec7..9516c828e7 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-exchange.spec.ts @@ -43,6 +43,7 @@ async function prepareDataFixtures({ container }) { const pricingModule = container.resolve(ModuleRegistrationName.PRICING) const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) const customerService = container.resolve(ModuleRegistrationName.CUSTOMER) + const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) const customer = await customerService.createCustomers({ first_name: "joe", @@ -98,6 +99,17 @@ async function prepareDataFixtures({ container }) { }, }) + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + const [product] = await productModule.createProducts([ { title: "Test product", @@ -150,8 +162,6 @@ async function prepareDataFixtures({ container }) { }, ]) - const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) - await remoteLink.create([ { [Modules.STOCK_LOCATION]: { diff --git a/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts b/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts index cc5d006400..a9e1844f57 100644 --- a/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/begin-order-return.spec.ts @@ -115,6 +115,17 @@ async function prepareDataFixtures({ container }) { const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + await remoteLink.create([ { [Modules.STOCK_LOCATION]: { diff --git a/integration-tests/modules/__tests__/order/workflows/create-complete-return.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-complete-return.spec.ts index 2b7295712d..7e7bd8c1cb 100644 --- a/integration-tests/modules/__tests__/order/workflows/create-complete-return.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/create-complete-return.spec.ts @@ -42,6 +42,7 @@ async function prepareDataFixtures({ container }) { const productModule = container.resolve(ModuleRegistrationName.PRODUCT) const pricingModule = container.resolve(ModuleRegistrationName.PRICING) const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY) + const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) const shippingProfile = await fulfillmentService.createShippingProfiles({ name: "test", @@ -92,6 +93,17 @@ async function prepareDataFixtures({ container }) { }, }) + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + const [product] = await productModule.createProducts([ { title: "Test product", @@ -117,8 +129,6 @@ async function prepareDataFixtures({ container }) { }, ]) - const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) - await remoteLink.create([ { [Modules.STOCK_LOCATION]: { diff --git a/integration-tests/modules/__tests__/order/workflows/create-fulfillment.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-fulfillment.spec.ts index 1c974558e4..775f6d361d 100644 --- a/integration-tests/modules/__tests__/order/workflows/create-fulfillment.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/create-fulfillment.spec.ts @@ -124,6 +124,17 @@ async function prepareDataFixtures({ container }) { const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + await remoteLink.create([ { [Modules.STOCK_LOCATION]: { diff --git a/integration-tests/modules/__tests__/order/workflows/create-shipment.spec.ts b/integration-tests/modules/__tests__/order/workflows/create-shipment.spec.ts index 8647d713b3..0073a3fcd1 100644 --- a/integration-tests/modules/__tests__/order/workflows/create-shipment.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/create-shipment.spec.ts @@ -118,6 +118,17 @@ async function prepareDataFixtures({ container }) { const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK) + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) + await remoteLink.create([ { [Modules.STOCK_LOCATION]: { diff --git a/integration-tests/modules/__tests__/order/workflows/return/create-exchange-return-shipping.spec.ts b/integration-tests/modules/__tests__/order/workflows/return/create-exchange-return-shipping.spec.ts index 25d5914b5b..dc3ed435e9 100644 --- a/integration-tests/modules/__tests__/order/workflows/return/create-exchange-return-shipping.spec.ts +++ b/integration-tests/modules/__tests__/order/workflows/return/create-exchange-return-shipping.spec.ts @@ -29,7 +29,6 @@ medusaIntegrationTestRunner({ beforeEach(async () => { fixtures = await prepareDataFixtures({ container }) - order = await createOrderFixture({ container, product: fixtures.product, diff --git a/integration-tests/modules/__tests__/shipping-options/store/shipping-options.spec.ts b/integration-tests/modules/__tests__/shipping-options/store/shipping-options.spec.ts index daed25f245..1bf57af1bd 100644 --- a/integration-tests/modules/__tests__/shipping-options/store/shipping-options.spec.ts +++ b/integration-tests/modules/__tests__/shipping-options/store/shipping-options.spec.ts @@ -126,9 +126,6 @@ medusaIntegrationTestRunner({ stock_location_id: stockLocation.id, }, }, - ]) - - await remoteLinkService.create([ { [Modules.STOCK_LOCATION]: { stock_location_id: stockLocation.id, @@ -137,6 +134,14 @@ medusaIntegrationTestRunner({ fulfillment_set_id: fulfillmentSet.id, }, }, + { + [Modules.STOCK_LOCATION]: { + stock_location_id: stockLocation.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, ]) shippingOption = ( diff --git a/integration-tests/modules/__tests__/shipping-options/workflows/batch-shipping-options-rules.ts b/integration-tests/modules/__tests__/shipping-options/workflows/batch-shipping-options-rules.ts index f0a42478ed..13a691c3e6 100644 --- a/integration-tests/modules/__tests__/shipping-options/workflows/batch-shipping-options-rules.ts +++ b/integration-tests/modules/__tests__/shipping-options/workflows/batch-shipping-options-rules.ts @@ -16,6 +16,7 @@ import { import { ContainerRegistrationKeys, ModuleRegistrationName, + Modules, RuleOperator, remoteQueryObjectFromString, } from "@medusajs/utils" @@ -151,6 +152,37 @@ medusaIntegrationTestRunner({ }, ], }) + + const stockLocationModule = container.resolve( + ModuleRegistrationName.STOCK_LOCATION + ) + + const location = await stockLocationModule.createStockLocations({ + name: "Europe", + }) + + const remoteLink = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) }) it("should create, update and delete rules in batch", async () => { diff --git a/integration-tests/modules/__tests__/shipping-options/workflows/create-shipping-options.ts b/integration-tests/modules/__tests__/shipping-options/workflows/create-shipping-options.ts index 421477c082..1d126866c8 100644 --- a/integration-tests/modules/__tests__/shipping-options/workflows/create-shipping-options.ts +++ b/integration-tests/modules/__tests__/shipping-options/workflows/create-shipping-options.ts @@ -10,6 +10,7 @@ import { import { ContainerRegistrationKeys, ModuleRegistrationName, + Modules, RuleOperator, remoteQueryObjectFromString, } from "@medusajs/utils" @@ -57,6 +58,37 @@ medusaIntegrationTestRunner({ }, ], }) + + const stockLocationModule = container.resolve( + ModuleRegistrationName.STOCK_LOCATION + ) + + const location = await stockLocationModule.createStockLocations({ + name: "Europe", + }) + + const remoteLink = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) }) it("should create shipping options and prices", async () => { diff --git a/integration-tests/modules/__tests__/shipping-options/workflows/delete-shipping-options.ts b/integration-tests/modules/__tests__/shipping-options/workflows/delete-shipping-options.ts index 91b30d2afa..11fbc6da99 100644 --- a/integration-tests/modules/__tests__/shipping-options/workflows/delete-shipping-options.ts +++ b/integration-tests/modules/__tests__/shipping-options/workflows/delete-shipping-options.ts @@ -13,6 +13,7 @@ import { import { ContainerRegistrationKeys, ModuleRegistrationName, + Modules, RuleOperator, remoteQueryObjectFromString, } from "@medusajs/utils" @@ -60,6 +61,37 @@ medusaIntegrationTestRunner({ }, ], }) + + const stockLocationModule = container.resolve( + ModuleRegistrationName.STOCK_LOCATION + ) + + const location = await stockLocationModule.createStockLocations({ + name: "Europe", + }) + + const remoteLink = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) }) it("should delete shipping options", async () => { diff --git a/integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts b/integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts index d4cc85cf9f..951da2c682 100644 --- a/integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts +++ b/integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts @@ -14,6 +14,7 @@ import { import { ContainerRegistrationKeys, ModuleRegistrationName, + Modules, RuleOperator, remoteQueryObjectFromString, } from "@medusajs/utils" @@ -61,6 +62,37 @@ medusaIntegrationTestRunner({ }, ], }) + + const stockLocationModule = container.resolve( + ModuleRegistrationName.STOCK_LOCATION + ) + + const location = await stockLocationModule.createStockLocations({ + name: "Europe", + }) + + const remoteLink = container.resolve( + ContainerRegistrationKeys.REMOTE_LINK + ) + + await remoteLink.create([ + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: fulfillmentSet.id, + }, + }, + { + [Modules.STOCK_LOCATION]: { + stock_location_id: location.id, + }, + [Modules.FULFILLMENT]: { + fulfillment_provider_id: "manual_test-provider", + }, + }, + ]) }) it("should update shipping options", async () => { diff --git a/packages/core/core-flows/src/fulfillment/steps/validate-fulfillment-providers.ts b/packages/core/core-flows/src/fulfillment/steps/validate-fulfillment-providers.ts new file mode 100644 index 0000000000..b59cf815e7 --- /dev/null +++ b/packages/core/core-flows/src/fulfillment/steps/validate-fulfillment-providers.ts @@ -0,0 +1,118 @@ +import { ServiceZoneDTO, ShippingOptionDTO } from "@medusajs/types" +import { + ContainerRegistrationKeys, + MedusaError, + ModuleRegistrationName, + remoteQueryObjectFromString, +} from "@medusajs/utils" +import { createStep, StepResponse } from "@medusajs/workflows-sdk" + +type FulfillmentProviderValidationInput = { + id?: string + service_zone_id?: string + provider_id?: string +} + +export const validateFulfillmentProvidersStepId = + "validate-fulfillment-providers-step" +export const validateFulfillmentProvidersStep = createStep( + validateFulfillmentProvidersStepId, + async (input: FulfillmentProviderValidationInput[], { container }) => { + const dataToValidate: { + service_zone_id: string + provider_id: string + }[] = [] + const fulfillmentService = container.resolve( + ModuleRegistrationName.FULFILLMENT + ) + + const shippingOptions = await fulfillmentService.listShippingOptions({ + id: input.map((d) => d.id).filter(Boolean) as string[], + }) + + const shippingOptionsMap = new Map( + shippingOptions.map((so) => [so.id, so]) + ) + + const remoteQuery = container.resolve( + ContainerRegistrationKeys.REMOTE_QUERY + ) + + for (const data of input) { + if ("id" in data) { + const existingShippingOption = shippingOptionsMap.get(data.id!) + + dataToValidate.push({ + service_zone_id: + data.service_zone_id! || existingShippingOption?.service_zone_id!, + provider_id: + data.provider_id! || existingShippingOption?.provider_id!, + }) + + continue + } + + if (data.service_zone_id && data.provider_id) { + dataToValidate.push({ + service_zone_id: data.service_zone_id, + provider_id: data.provider_id, + }) + + continue + } + + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + `service_zone_id and provider_id are required to create a shipping option` + ) + } + + const serviceZoneQuery = remoteQueryObjectFromString({ + entryPoint: "service_zone", + fields: ["id", "fulfillment_set.locations.fulfillment_providers.id"], + }) + + const serviceZones = await remoteQuery(serviceZoneQuery, { + id: input.map((d) => d.service_zone_id), + }) + + const serviceZonesMap = new Map< + string, + ServiceZoneDTO & { + fulfillment_set: { + locations: { fulfillment_providers: { id: string }[] }[] + } + } + >(serviceZones.map((sz) => [sz.id, sz])) + + const invalidProviders: string[] = [] + + for (const data of dataToValidate) { + const serviceZone = serviceZonesMap.get(data.service_zone_id)! + const stockLocations = serviceZone.fulfillment_set.locations + const fulfillmentProviders: string[] = [] + + for (const stockLocation of stockLocations) { + const providersForStockLocation = + stockLocation.fulfillment_providers.map((fp) => fp.id) + + fulfillmentProviders.push(...providersForStockLocation) + } + + if (!fulfillmentProviders.includes(data.provider_id)) { + invalidProviders.push(data.provider_id) + } + } + + if (invalidProviders.length) { + throw new MedusaError( + MedusaError.Types.NOT_ALLOWED, + `Providers (${invalidProviders.join( + "," + )}) are not enabled for the service location` + ) + } + + return new StepResponse(void 0) + } +) diff --git a/packages/core/core-flows/src/fulfillment/workflows/create-shipping-options.ts b/packages/core/core-flows/src/fulfillment/workflows/create-shipping-options.ts index 3af2c2b7ba..cc331a328c 100644 --- a/packages/core/core-flows/src/fulfillment/workflows/create-shipping-options.ts +++ b/packages/core/core-flows/src/fulfillment/workflows/create-shipping-options.ts @@ -9,6 +9,7 @@ import { upsertShippingOptionsStep, } from "../steps" import { setShippingOptionsPriceSetsStep } from "../steps/set-shipping-options-price-sets" +import { validateFulfillmentProvidersStep } from "../steps/validate-fulfillment-providers" export const createShippingOptionsWorkflowId = "create-shipping-options-workflow" @@ -19,6 +20,8 @@ export const createShippingOptionsWorkflow = createWorkflow( FulfillmentWorkflow.CreateShippingOptionsWorkflowInput[] > ): WorkflowData => { + validateFulfillmentProvidersStep(input) + const data = transform(input, (data) => { const shippingOptionsIndexToPrices = data.map((option, index) => { const prices = option.prices diff --git a/packages/core/core-flows/src/fulfillment/workflows/update-shipping-options.ts b/packages/core/core-flows/src/fulfillment/workflows/update-shipping-options.ts index 5f73a32aef..a91bd00f20 100644 --- a/packages/core/core-flows/src/fulfillment/workflows/update-shipping-options.ts +++ b/packages/core/core-flows/src/fulfillment/workflows/update-shipping-options.ts @@ -8,6 +8,7 @@ import { setShippingOptionsPricesStep, upsertShippingOptionsStep, } from "../steps" +import { validateFulfillmentProvidersStep } from "../steps/validate-fulfillment-providers" export const updateShippingOptionsWorkflowId = "update-shipping-options-workflow" @@ -18,6 +19,8 @@ export const updateShippingOptionsWorkflow = createWorkflow( FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput[] > ): WorkflowData => { + validateFulfillmentProvidersStep(input) + const data = transform(input, (data) => { const shippingOptionsIndexToPrices = data.map((option, index) => { const prices = option.prices diff --git a/packages/core/types/src/fulfillment/common/service-zone.ts b/packages/core/types/src/fulfillment/common/service-zone.ts index 31483e7f88..2884d146af 100644 --- a/packages/core/types/src/fulfillment/common/service-zone.ts +++ b/packages/core/types/src/fulfillment/common/service-zone.ts @@ -1,10 +1,10 @@ +import { BaseFilterable, OperatorMap } from "../../dal" import { FilterableFulfillmentSetProps, FulfillmentSetDTO, } from "./fulfillment-set" import { FilterableGeoZoneProps, GeoZoneDTO } from "./geo-zone" import { ShippingOptionDTO } from "./shipping-option" -import { BaseFilterable, OperatorMap } from "../../dal" /** * The service zone details. @@ -26,9 +26,14 @@ export interface ServiceZoneDTO { metadata: Record | null /** - * The fulfillment sets assoiated with the service zone. + * The fulfillment set assoiated with the service zone. */ - fulfillment_sets: FulfillmentSetDTO[] + fulfillment_set: FulfillmentSetDTO + + /** + * The fulfillment set id of the service zone. + */ + fulfillment_set_id: string /** * The geo zones assoiated with the service zone. diff --git a/packages/modules/link-modules/src/definitions/index.ts b/packages/modules/link-modules/src/definitions/index.ts index 8a4019d318..b5c61e4333 100644 --- a/packages/modules/link-modules/src/definitions/index.ts +++ b/packages/modules/link-modules/src/definitions/index.ts @@ -1,5 +1,6 @@ export * from "./cart-payment-collection" export * from "./cart-promotion" +export * from "./fulfillment-provider-location" export * from "./fulfillment-set-location" export * from "./order-cart" export * from "./order-fulfillment" @@ -13,4 +14,3 @@ export * from "./readonly" export * from "./region-payment-provider" export * from "./sales-channel-location" export * from "./shipping-option-price-set" -