feat(core-flows, fulfillment): validate shipping options to stock locations (#8291)
what: - validate shipping options to stock locations RESOLVES CC-232
This commit is contained in:
@@ -215,6 +215,14 @@ medusaIntegrationTestRunner({
|
|||||||
fulfillment_set_id: fulfillmentSet.id,
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
stock_location_id: location.id,
|
||||||
|
},
|
||||||
|
[Modules.FULFILLMENT]: {
|
||||||
|
fulfillment_provider_id: "manual_test-provider",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
[Modules.SALES_CHANNEL]: {
|
[Modules.SALES_CHANNEL]: {
|
||||||
sales_channel_id: "test",
|
sales_channel_id: "test",
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { RuleOperator } from "@medusajs/utils"
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
Modules,
|
||||||
|
RuleOperator,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import {
|
import {
|
||||||
adminHeaders,
|
adminHeaders,
|
||||||
@@ -14,6 +18,9 @@ medusaIntegrationTestRunner({
|
|||||||
let shippingProfile
|
let shippingProfile
|
||||||
let fulfillmentSet
|
let fulfillmentSet
|
||||||
let region
|
let region
|
||||||
|
let appContainer
|
||||||
|
let location
|
||||||
|
let location2
|
||||||
|
|
||||||
const shippingOptionRule = {
|
const shippingOptionRule = {
|
||||||
operator: RuleOperator.EQ,
|
operator: RuleOperator.EQ,
|
||||||
@@ -22,7 +29,8 @@ medusaIntegrationTestRunner({
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const appContainer = getContainer()
|
appContainer = getContainer()
|
||||||
|
|
||||||
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
||||||
|
|
||||||
shippingProfile = (
|
shippingProfile = (
|
||||||
@@ -36,7 +44,7 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
).data.shipping_profile
|
).data.shipping_profile
|
||||||
|
|
||||||
let location = (
|
location = (
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/stock-locations`,
|
`/admin/stock-locations`,
|
||||||
{
|
{
|
||||||
@@ -103,6 +111,22 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should create a shipping option successfully", async () => {
|
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 = {
|
const shippingOptionPayload = {
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
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", () => {
|
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(
|
const response = await api.post(
|
||||||
`/admin/shipping-options`,
|
`/admin/shipping-options`,
|
||||||
shippingOptionPayload,
|
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", () => {
|
describe("DELETE /admin/shipping-options/:id", () => {
|
||||||
@@ -359,6 +501,22 @@ medusaIntegrationTestRunner({
|
|||||||
rules: [shippingOptionRule],
|
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(
|
const response = await api.post(
|
||||||
`/admin/shipping-options`,
|
`/admin/shipping-options`,
|
||||||
shippingOptionPayload,
|
shippingOptionPayload,
|
||||||
|
|||||||
@@ -1480,6 +1480,36 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should add shipping method to cart", async () => {
|
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({
|
const shippingOption = await fulfillmentModule.createShippingOptions({
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
|||||||
@@ -1930,6 +1930,28 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
).data.product
|
).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 = (
|
shippingOption = (
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/shipping-options`,
|
`/admin/shipping-options`,
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import {
|
|||||||
Modules,
|
Modules,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
|
import {
|
||||||
|
adminHeaders,
|
||||||
|
createAdminUser,
|
||||||
|
} from "../../../helpers/create-admin-user"
|
||||||
|
|
||||||
jest.setTimeout(50000)
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
@@ -15,13 +19,12 @@ const env = { MEDUSA_FF_MEDUSA_V2: true }
|
|||||||
|
|
||||||
medusaIntegrationTestRunner({
|
medusaIntegrationTestRunner({
|
||||||
env,
|
env,
|
||||||
testSuite: ({ getContainer }) => {
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
describe("Region and Payment Providers", () => {
|
describe("Region and Payment Providers", () => {
|
||||||
let appContainer
|
let appContainer
|
||||||
let fulfillmentModule: IFulfillmentModuleService
|
let fulfillmentModule: IFulfillmentModuleService
|
||||||
let pricingModule: IPricingModuleService
|
let pricingModule: IPricingModuleService
|
||||||
let remoteQuery
|
let remoteQuery
|
||||||
let remoteLink
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
appContainer = getContainer()
|
appContainer = getContainer()
|
||||||
@@ -32,7 +35,7 @@ medusaIntegrationTestRunner({
|
|||||||
remoteQuery = appContainer.resolve(
|
remoteQuery = appContainer.resolve(
|
||||||
ContainerRegistrationKeys.REMOTE_QUERY
|
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 () => {
|
it("should query shipping option and price set link with remote query", async () => {
|
||||||
@@ -44,6 +47,38 @@ medusaIntegrationTestRunner({
|
|||||||
name: "Test",
|
name: "Test",
|
||||||
type: "test-type",
|
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({
|
const serviceZone = await fulfillmentModule.createServiceZones({
|
||||||
name: "Test",
|
name: "Test",
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
|||||||
@@ -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 =
|
const shippingOptionData: FulfillmentWorkflow.CreateShippingOptionsWorkflowInput =
|
||||||
{
|
{
|
||||||
name: "Shipping option",
|
name: "Shipping option",
|
||||||
|
|||||||
@@ -117,6 +117,17 @@ async function prepareDataFixtures({ container }) {
|
|||||||
|
|
||||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
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([
|
await remoteLink.create([
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ async function prepareDataFixtures({ container }) {
|
|||||||
const pricingModule = container.resolve(ModuleRegistrationName.PRICING)
|
const pricingModule = container.resolve(ModuleRegistrationName.PRICING)
|
||||||
const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY)
|
const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY)
|
||||||
const customerService = container.resolve(ModuleRegistrationName.CUSTOMER)
|
const customerService = container.resolve(ModuleRegistrationName.CUSTOMER)
|
||||||
|
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
|
|
||||||
const customer = await customerService.createCustomers({
|
const customer = await customerService.createCustomers({
|
||||||
first_name: "joe",
|
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([
|
const [product] = await productModule.createProducts([
|
||||||
{
|
{
|
||||||
title: "Test product",
|
title: "Test product",
|
||||||
@@ -150,8 +162,6 @@ async function prepareDataFixtures({ container }) {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
|
||||||
|
|
||||||
await remoteLink.create([
|
await remoteLink.create([
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
|||||||
@@ -115,6 +115,17 @@ async function prepareDataFixtures({ container }) {
|
|||||||
|
|
||||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
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([
|
await remoteLink.create([
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ async function prepareDataFixtures({ container }) {
|
|||||||
const productModule = container.resolve(ModuleRegistrationName.PRODUCT)
|
const productModule = container.resolve(ModuleRegistrationName.PRODUCT)
|
||||||
const pricingModule = container.resolve(ModuleRegistrationName.PRICING)
|
const pricingModule = container.resolve(ModuleRegistrationName.PRICING)
|
||||||
const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY)
|
const inventoryModule = container.resolve(ModuleRegistrationName.INVENTORY)
|
||||||
|
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
|
|
||||||
const shippingProfile = await fulfillmentService.createShippingProfiles({
|
const shippingProfile = await fulfillmentService.createShippingProfiles({
|
||||||
name: "test",
|
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([
|
const [product] = await productModule.createProducts([
|
||||||
{
|
{
|
||||||
title: "Test product",
|
title: "Test product",
|
||||||
@@ -117,8 +129,6 @@ async function prepareDataFixtures({ container }) {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
|
||||||
|
|
||||||
await remoteLink.create([
|
await remoteLink.create([
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
|||||||
@@ -124,6 +124,17 @@ async function prepareDataFixtures({ container }) {
|
|||||||
|
|
||||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
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([
|
await remoteLink.create([
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
|||||||
@@ -118,6 +118,17 @@ async function prepareDataFixtures({ container }) {
|
|||||||
|
|
||||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
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([
|
await remoteLink.create([
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
|||||||
-1
@@ -29,7 +29,6 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
fixtures = await prepareDataFixtures({ container })
|
fixtures = await prepareDataFixtures({ container })
|
||||||
|
|
||||||
order = await createOrderFixture({
|
order = await createOrderFixture({
|
||||||
container,
|
container,
|
||||||
product: fixtures.product,
|
product: fixtures.product,
|
||||||
|
|||||||
@@ -126,9 +126,6 @@ medusaIntegrationTestRunner({
|
|||||||
stock_location_id: stockLocation.id,
|
stock_location_id: stockLocation.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
|
||||||
|
|
||||||
await remoteLinkService.create([
|
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
stock_location_id: stockLocation.id,
|
stock_location_id: stockLocation.id,
|
||||||
@@ -137,6 +134,14 @@ medusaIntegrationTestRunner({
|
|||||||
fulfillment_set_id: fulfillmentSet.id,
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
stock_location_id: stockLocation.id,
|
||||||
|
},
|
||||||
|
[Modules.FULFILLMENT]: {
|
||||||
|
fulfillment_provider_id: "manual_test-provider",
|
||||||
|
},
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
shippingOption = (
|
shippingOption = (
|
||||||
|
|||||||
+32
@@ -16,6 +16,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
ModuleRegistrationName,
|
ModuleRegistrationName,
|
||||||
|
Modules,
|
||||||
RuleOperator,
|
RuleOperator,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
} from "@medusajs/utils"
|
} 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 () => {
|
it("should create, update and delete rules in batch", async () => {
|
||||||
|
|||||||
+32
@@ -10,6 +10,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
ModuleRegistrationName,
|
ModuleRegistrationName,
|
||||||
|
Modules,
|
||||||
RuleOperator,
|
RuleOperator,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
} from "@medusajs/utils"
|
} 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 () => {
|
it("should create shipping options and prices", async () => {
|
||||||
|
|||||||
+32
@@ -13,6 +13,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
ModuleRegistrationName,
|
ModuleRegistrationName,
|
||||||
|
Modules,
|
||||||
RuleOperator,
|
RuleOperator,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
} from "@medusajs/utils"
|
} 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 () => {
|
it("should delete shipping options", async () => {
|
||||||
|
|||||||
+32
@@ -14,6 +14,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
ModuleRegistrationName,
|
ModuleRegistrationName,
|
||||||
|
Modules,
|
||||||
RuleOperator,
|
RuleOperator,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
} from "@medusajs/utils"
|
} 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 () => {
|
it("should update shipping options", async () => {
|
||||||
|
|||||||
@@ -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<string, ShippingOptionDTO>(
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
upsertShippingOptionsStep,
|
upsertShippingOptionsStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
import { setShippingOptionsPriceSetsStep } from "../steps/set-shipping-options-price-sets"
|
import { setShippingOptionsPriceSetsStep } from "../steps/set-shipping-options-price-sets"
|
||||||
|
import { validateFulfillmentProvidersStep } from "../steps/validate-fulfillment-providers"
|
||||||
|
|
||||||
export const createShippingOptionsWorkflowId =
|
export const createShippingOptionsWorkflowId =
|
||||||
"create-shipping-options-workflow"
|
"create-shipping-options-workflow"
|
||||||
@@ -19,6 +20,8 @@ export const createShippingOptionsWorkflow = createWorkflow(
|
|||||||
FulfillmentWorkflow.CreateShippingOptionsWorkflowInput[]
|
FulfillmentWorkflow.CreateShippingOptionsWorkflowInput[]
|
||||||
>
|
>
|
||||||
): WorkflowData<FulfillmentWorkflow.CreateShippingOptionsWorkflowOutput> => {
|
): WorkflowData<FulfillmentWorkflow.CreateShippingOptionsWorkflowOutput> => {
|
||||||
|
validateFulfillmentProvidersStep(input)
|
||||||
|
|
||||||
const data = transform(input, (data) => {
|
const data = transform(input, (data) => {
|
||||||
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
||||||
const prices = option.prices
|
const prices = option.prices
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
setShippingOptionsPricesStep,
|
setShippingOptionsPricesStep,
|
||||||
upsertShippingOptionsStep,
|
upsertShippingOptionsStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
|
import { validateFulfillmentProvidersStep } from "../steps/validate-fulfillment-providers"
|
||||||
|
|
||||||
export const updateShippingOptionsWorkflowId =
|
export const updateShippingOptionsWorkflowId =
|
||||||
"update-shipping-options-workflow"
|
"update-shipping-options-workflow"
|
||||||
@@ -18,6 +19,8 @@ export const updateShippingOptionsWorkflow = createWorkflow(
|
|||||||
FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput[]
|
FulfillmentWorkflow.UpdateShippingOptionsWorkflowInput[]
|
||||||
>
|
>
|
||||||
): WorkflowData<FulfillmentWorkflow.UpdateShippingOptionsWorkflowOutput> => {
|
): WorkflowData<FulfillmentWorkflow.UpdateShippingOptionsWorkflowOutput> => {
|
||||||
|
validateFulfillmentProvidersStep(input)
|
||||||
|
|
||||||
const data = transform(input, (data) => {
|
const data = transform(input, (data) => {
|
||||||
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
const shippingOptionsIndexToPrices = data.map((option, index) => {
|
||||||
const prices = option.prices
|
const prices = option.prices
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import { BaseFilterable, OperatorMap } from "../../dal"
|
||||||
import {
|
import {
|
||||||
FilterableFulfillmentSetProps,
|
FilterableFulfillmentSetProps,
|
||||||
FulfillmentSetDTO,
|
FulfillmentSetDTO,
|
||||||
} from "./fulfillment-set"
|
} from "./fulfillment-set"
|
||||||
import { FilterableGeoZoneProps, GeoZoneDTO } from "./geo-zone"
|
import { FilterableGeoZoneProps, GeoZoneDTO } from "./geo-zone"
|
||||||
import { ShippingOptionDTO } from "./shipping-option"
|
import { ShippingOptionDTO } from "./shipping-option"
|
||||||
import { BaseFilterable, OperatorMap } from "../../dal"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service zone details.
|
* The service zone details.
|
||||||
@@ -26,9 +26,14 @@ export interface ServiceZoneDTO {
|
|||||||
metadata: Record<string, unknown> | null
|
metadata: Record<string, unknown> | 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.
|
* The geo zones assoiated with the service zone.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from "./cart-payment-collection"
|
export * from "./cart-payment-collection"
|
||||||
export * from "./cart-promotion"
|
export * from "./cart-promotion"
|
||||||
|
export * from "./fulfillment-provider-location"
|
||||||
export * from "./fulfillment-set-location"
|
export * from "./fulfillment-set-location"
|
||||||
export * from "./order-cart"
|
export * from "./order-cart"
|
||||||
export * from "./order-fulfillment"
|
export * from "./order-fulfillment"
|
||||||
@@ -13,4 +14,3 @@ export * from "./readonly"
|
|||||||
export * from "./region-payment-provider"
|
export * from "./region-payment-provider"
|
||||||
export * from "./sales-channel-location"
|
export * from "./sales-channel-location"
|
||||||
export * from "./shipping-option-price-set"
|
export * from "./shipping-option-price-set"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user