feat: List shipping options for cart (#6677)
This commit is contained in:
1 parent
db26dafa85
commit
25d176b851
23 files changed
+855
-142
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/orchestration": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: List shipping options for cart
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
deleteLineItemsWorkflow,
|
deleteLineItemsWorkflow,
|
||||||
findOrCreateCustomerStepId,
|
findOrCreateCustomerStepId,
|
||||||
linkCartAndPaymentCollectionsStepId,
|
linkCartAndPaymentCollectionsStepId,
|
||||||
|
listShippingOptionsForCartWorkflow,
|
||||||
refreshPaymentCollectionForCartWorkflow,
|
refreshPaymentCollectionForCartWorkflow,
|
||||||
updateLineItemInCartWorkflow,
|
updateLineItemInCartWorkflow,
|
||||||
updateLineItemsStepId,
|
updateLineItemsStepId,
|
||||||
@@ -22,7 +23,9 @@ import {
|
|||||||
IProductModuleService,
|
IProductModuleService,
|
||||||
IRegionModuleService,
|
IRegionModuleService,
|
||||||
ISalesChannelModuleService,
|
ISalesChannelModuleService,
|
||||||
|
IStockLocationServiceNext,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
|
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
|
|
||||||
@@ -43,6 +46,7 @@ medusaIntegrationTestRunner({
|
|||||||
let pricingModule: IPricingModuleService
|
let pricingModule: IPricingModuleService
|
||||||
let paymentModule: IPaymentModuleService
|
let paymentModule: IPaymentModuleService
|
||||||
let fulfillmentModule: IFulfillmentModuleService
|
let fulfillmentModule: IFulfillmentModuleService
|
||||||
|
let locationModule: IStockLocationServiceNext
|
||||||
let remoteLink, remoteQuery
|
let remoteLink, remoteQuery
|
||||||
|
|
||||||
let defaultRegion
|
let defaultRegion
|
||||||
@@ -63,8 +67,13 @@ medusaIntegrationTestRunner({
|
|||||||
fulfillmentModule = appContainer.resolve(
|
fulfillmentModule = appContainer.resolve(
|
||||||
ModuleRegistrationName.FULFILLMENT
|
ModuleRegistrationName.FULFILLMENT
|
||||||
)
|
)
|
||||||
remoteLink = appContainer.resolve("remoteLink")
|
locationModule = appContainer.resolve(
|
||||||
remoteQuery = appContainer.resolve("remoteQuery")
|
ModuleRegistrationName.STOCK_LOCATION
|
||||||
|
)
|
||||||
|
remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
|
remoteQuery = appContainer.resolve(
|
||||||
|
ContainerRegistrationKeys.REMOTE_QUERY
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -672,12 +681,84 @@ medusaIntegrationTestRunner({
|
|||||||
expect(updatedItem).not.toBeUndefined()
|
expect(updatedItem).not.toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("createPaymentCollectionForCart", () => {
|
describe("createPaymentCollectionForCart", () => {
|
||||||
it("should create a payment collection and link it to cart", async () => {
|
it("should create a payment collection and link it to cart", async () => {
|
||||||
const cart = await cartModuleService.create({
|
const cart = await cartModuleService.create({
|
||||||
currency_code: "dkk",
|
currency_code: "dkk",
|
||||||
|
region_id: defaultRegion.id,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
quantity: 1,
|
||||||
|
unit_price: 5000,
|
||||||
|
title: "Test item",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await createPaymentCollectionForCartWorkflow(appContainer).run({
|
||||||
|
input: {
|
||||||
|
cart_id: cart.id,
|
||||||
region_id: defaultRegion.id,
|
region_id: defaultRegion.id,
|
||||||
|
currency_code: "dkk",
|
||||||
|
amount: 5000,
|
||||||
|
},
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await remoteQuery(
|
||||||
|
{
|
||||||
|
cart: {
|
||||||
|
fields: ["id"],
|
||||||
|
payment_collection: {
|
||||||
|
fields: ["id", "amount", "currency_code"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cart: {
|
||||||
|
id: cart.id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
payment_collection: expect.objectContaining({
|
||||||
|
amount: 5000,
|
||||||
|
currency_code: "dkk",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("compensation", () => {
|
||||||
|
it("should dismiss cart <> payment collection link and delete created payment collection", async () => {
|
||||||
|
const workflow =
|
||||||
|
createPaymentCollectionForCartWorkflow(appContainer)
|
||||||
|
|
||||||
|
workflow.appendAction(
|
||||||
|
"throw",
|
||||||
|
linkCartAndPaymentCollectionsStepId,
|
||||||
|
{
|
||||||
|
invoke: async function failStep() {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to do something after linking cart and payment collection`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const region = await regionModuleService.create({
|
||||||
|
name: "US",
|
||||||
|
currency_code: "usd",
|
||||||
|
})
|
||||||
|
|
||||||
|
const cart = await cartModuleService.create({
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: region.id,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
@@ -687,17 +768,27 @@ medusaIntegrationTestRunner({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
await createPaymentCollectionForCartWorkflow(appContainer).run({
|
const { errors } = await workflow.run({
|
||||||
input: {
|
input: {
|
||||||
cart_id: cart.id,
|
cart_id: cart.id,
|
||||||
region_id: defaultRegion.id,
|
region_id: region.id,
|
||||||
currency_code: "dkk",
|
currency_code: "usd",
|
||||||
amount: 5000,
|
amount: 5000,
|
||||||
},
|
},
|
||||||
throwOnError: false,
|
throwOnError: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await remoteQuery(
|
expect(errors).toEqual([
|
||||||
|
{
|
||||||
|
action: "throw",
|
||||||
|
handlerType: "invoke",
|
||||||
|
error: new Error(
|
||||||
|
`Failed to do something after linking cart and payment collection`
|
||||||
|
),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const carts = await remoteQuery(
|
||||||
{
|
{
|
||||||
cart: {
|
cart: {
|
||||||
fields: ["id"],
|
fields: ["id"],
|
||||||
@@ -713,101 +804,19 @@ medusaIntegrationTestRunner({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(result).toEqual([
|
const payCols = await remoteQuery({
|
||||||
|
payment_collection: {
|
||||||
|
fields: ["id"],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(carts).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: cart.id,
|
id: cart.id,
|
||||||
payment_collection: expect.objectContaining({
|
payment_collection: undefined,
|
||||||
amount: 5000,
|
|
||||||
currency_code: "dkk",
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
})
|
expect(payCols.length).toEqual(0)
|
||||||
|
|
||||||
describe("compensation", () => {
|
|
||||||
it("should dismiss cart <> payment collection link and delete created payment collection", async () => {
|
|
||||||
const workflow =
|
|
||||||
createPaymentCollectionForCartWorkflow(appContainer)
|
|
||||||
|
|
||||||
workflow.appendAction(
|
|
||||||
"throw",
|
|
||||||
linkCartAndPaymentCollectionsStepId,
|
|
||||||
{
|
|
||||||
invoke: async function failStep() {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to do something after linking cart and payment collection`
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const region = await regionModuleService.create({
|
|
||||||
name: "US",
|
|
||||||
currency_code: "usd",
|
|
||||||
})
|
|
||||||
|
|
||||||
const cart = await cartModuleService.create({
|
|
||||||
currency_code: "usd",
|
|
||||||
region_id: region.id,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
quantity: 1,
|
|
||||||
unit_price: 5000,
|
|
||||||
title: "Test item",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const { errors } = await workflow.run({
|
|
||||||
input: {
|
|
||||||
cart_id: cart.id,
|
|
||||||
region_id: region.id,
|
|
||||||
currency_code: "usd",
|
|
||||||
amount: 5000,
|
|
||||||
},
|
|
||||||
throwOnError: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(errors).toEqual([
|
|
||||||
{
|
|
||||||
action: "throw",
|
|
||||||
handlerType: "invoke",
|
|
||||||
error: new Error(
|
|
||||||
`Failed to do something after linking cart and payment collection`
|
|
||||||
),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const carts = await remoteQuery(
|
|
||||||
{
|
|
||||||
cart: {
|
|
||||||
fields: ["id"],
|
|
||||||
payment_collection: {
|
|
||||||
fields: ["id", "amount", "currency_code"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cart: {
|
|
||||||
id: cart.id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const payCols = await remoteQuery({
|
|
||||||
payment_collection: {
|
|
||||||
fields: ["id"],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(carts).toEqual([
|
|
||||||
expect.objectContaining({
|
|
||||||
id: cart.id,
|
|
||||||
payment_collection: undefined,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
expect(payCols.length).toEqual(0)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -995,24 +1004,26 @@ medusaIntegrationTestRunner({
|
|||||||
name: "Test",
|
name: "Test",
|
||||||
type: "default",
|
type: "default",
|
||||||
})
|
})
|
||||||
|
|
||||||
const fulfillmentSet = await fulfillmentModule.create({
|
const fulfillmentSet = await fulfillmentModule.create({
|
||||||
name: "Test",
|
name: "Test",
|
||||||
type: "test-type",
|
type: "test-type",
|
||||||
})
|
service_zones: [
|
||||||
const serviceZone = await fulfillmentModule.createServiceZones({
|
|
||||||
name: "Test",
|
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
|
||||||
geo_zones: [
|
|
||||||
{
|
{
|
||||||
type: "country",
|
name: "Test",
|
||||||
country_code: "us",
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: "country",
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
const shippingOption = await fulfillmentModule.createShippingOptions({
|
const shippingOption = await fulfillmentModule.createShippingOptions({
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
service_zone_id: serviceZone.id,
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
shipping_profile_id: shippingProfile.id,
|
shipping_profile_id: shippingProfile.id,
|
||||||
provider_id: "manual_test-provider",
|
provider_id: "manual_test-provider",
|
||||||
price_type: "flat",
|
price_type: "flat",
|
||||||
@@ -1077,6 +1088,337 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("listShippingOptionsForCartWorkflow", () => {
|
||||||
|
it("should list shipping options for cart", async () => {
|
||||||
|
const salesChannel = await scModuleService.create({
|
||||||
|
name: "Webshop",
|
||||||
|
})
|
||||||
|
|
||||||
|
const location = await locationModule.create({
|
||||||
|
name: "Europe",
|
||||||
|
})
|
||||||
|
|
||||||
|
let cart = await cartModuleService.create({
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
shipping_address: {
|
||||||
|
city: "CPH",
|
||||||
|
province: "Sjaelland",
|
||||||
|
country_code: "dk",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile =
|
||||||
|
await fulfillmentModule.createShippingProfiles({
|
||||||
|
name: "Test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const fulfillmentSet = await fulfillmentModule.create({
|
||||||
|
name: "Test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "Test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: "country",
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await fulfillmentModule.createShippingOptions({
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const priceSet = await pricingModule.create({
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
[Modules.SALES_CHANNEL]: {
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
},
|
||||||
|
[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]: {
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
},
|
||||||
|
[Modules.PRICING]: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
cart = await cartModuleService.retrieve(cart.id, {
|
||||||
|
select: ["id"],
|
||||||
|
relations: ["shipping_address"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const { result } = await listShippingOptionsForCartWorkflow(
|
||||||
|
appContainer
|
||||||
|
).run({
|
||||||
|
input: {
|
||||||
|
cart_id: cart.id,
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
shipping_address: {
|
||||||
|
city: cart.shipping_address?.city,
|
||||||
|
province: cart.shipping_address?.province,
|
||||||
|
country_code: cart.shipping_address?.country_code,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
amount: 3000,
|
||||||
|
name: "Test shipping option",
|
||||||
|
id: shippingOption.id,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should list no shipping options for cart, if sales channel is not associated with location", async () => {
|
||||||
|
const salesChannel = await scModuleService.create({
|
||||||
|
name: "Webshop",
|
||||||
|
})
|
||||||
|
|
||||||
|
const location = await locationModule.create({
|
||||||
|
name: "Europe",
|
||||||
|
})
|
||||||
|
|
||||||
|
let cart = await cartModuleService.create({
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
shipping_address: {
|
||||||
|
city: "CPH",
|
||||||
|
province: "Sjaelland",
|
||||||
|
country_code: "dk",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile =
|
||||||
|
await fulfillmentModule.createShippingProfiles({
|
||||||
|
name: "Test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const fulfillmentSet = await fulfillmentModule.create({
|
||||||
|
name: "Test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "Test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: "country",
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await fulfillmentModule.createShippingOptions({
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const priceSet = await pricingModule.create({
|
||||||
|
prices: [
|
||||||
|
{
|
||||||
|
amount: 3000,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
[Modules.FULFILLMENT]: {
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
},
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
stock_location_id: location.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[Modules.FULFILLMENT]: {
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
},
|
||||||
|
[Modules.PRICING]: {
|
||||||
|
price_set_id: priceSet.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
cart = await cartModuleService.retrieve(cart.id, {
|
||||||
|
select: ["id"],
|
||||||
|
relations: ["shipping_address"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const { result } = await listShippingOptionsForCartWorkflow(
|
||||||
|
appContainer
|
||||||
|
).run({
|
||||||
|
input: {
|
||||||
|
cart_id: cart.id,
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
shipping_address: {
|
||||||
|
city: cart.shipping_address?.city,
|
||||||
|
province: cart.shipping_address?.province,
|
||||||
|
country_code: cart.shipping_address?.country_code,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw when shipping options are missing prices", async () => {
|
||||||
|
const salesChannel = await scModuleService.create({
|
||||||
|
name: "Webshop",
|
||||||
|
})
|
||||||
|
|
||||||
|
const location = await locationModule.create({
|
||||||
|
name: "Europe",
|
||||||
|
})
|
||||||
|
|
||||||
|
let cart = await cartModuleService.create({
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
shipping_address: {
|
||||||
|
city: "CPH",
|
||||||
|
province: "Sjaelland",
|
||||||
|
country_code: "dk",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile =
|
||||||
|
await fulfillmentModule.createShippingProfiles({
|
||||||
|
name: "Test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const fulfillmentSet = await fulfillmentModule.create({
|
||||||
|
name: "Test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "Test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: "country",
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await fulfillmentModule.createShippingOptions({
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
[Modules.SALES_CHANNEL]: {
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
},
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
stock_location_id: location.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[Modules.FULFILLMENT]: {
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
},
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
stock_location_id: location.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
cart = await cartModuleService.retrieve(cart.id, {
|
||||||
|
select: ["id"],
|
||||||
|
relations: ["shipping_address"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const { errors } = await listShippingOptionsForCartWorkflow(
|
||||||
|
appContainer
|
||||||
|
).run({
|
||||||
|
input: {
|
||||||
|
cart_id: cart.id,
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
shipping_address: {
|
||||||
|
city: cart.shipping_address?.city,
|
||||||
|
province: cart.shipping_address?.province,
|
||||||
|
country_code: cart.shipping_address?.country_code,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(errors).toEqual([
|
||||||
|
{
|
||||||
|
action: "get-shipping-option-price-sets",
|
||||||
|
error: new Error(
|
||||||
|
`Shipping options with IDs ${shippingOption.id} do not have a price`
|
||||||
|
),
|
||||||
|
handlerType: "invoke",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { ModuleRegistrationName, Modules } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
ISalesChannelModuleService,
|
||||||
|
IStockLocationServiceNext,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
|
|
||||||
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
|
const env = { MEDUSA_FF_MEDUSA_V2: true }
|
||||||
|
|
||||||
|
medusaIntegrationTestRunner({
|
||||||
|
env,
|
||||||
|
testSuite: ({ getContainer }) => {
|
||||||
|
describe("FulfillmentSet and Location", () => {
|
||||||
|
let appContainer
|
||||||
|
let fulfillmentModule: IFulfillmentModuleService
|
||||||
|
let locationModule: IStockLocationServiceNext
|
||||||
|
let scService: ISalesChannelModuleService
|
||||||
|
let remoteQuery
|
||||||
|
let remoteLink
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
appContainer = getContainer()
|
||||||
|
fulfillmentModule = appContainer.resolve(
|
||||||
|
ModuleRegistrationName.FULFILLMENT
|
||||||
|
)
|
||||||
|
locationModule = appContainer.resolve(
|
||||||
|
ModuleRegistrationName.STOCK_LOCATION
|
||||||
|
)
|
||||||
|
scService = appContainer.resolve(ModuleRegistrationName.SALES_CHANNEL)
|
||||||
|
remoteQuery = appContainer.resolve(
|
||||||
|
ContainerRegistrationKeys.REMOTE_QUERY
|
||||||
|
)
|
||||||
|
remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should query fulfillment set and location link with remote query", async () => {
|
||||||
|
const fulfillmentSet = await fulfillmentModule.create({
|
||||||
|
name: "Test fulfillment set",
|
||||||
|
type: "delivery",
|
||||||
|
})
|
||||||
|
|
||||||
|
const euWarehouse = await locationModule.create({
|
||||||
|
name: "EU Warehouse",
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
[Modules.FULFILLMENT]: {
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
},
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
stock_location_id: euWarehouse.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const linkQuery = remoteQueryObjectFromString({
|
||||||
|
entryPoint: "fulfillment_sets",
|
||||||
|
fields: ["id", "stock_locations.id"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const link = await remoteQuery(linkQuery)
|
||||||
|
|
||||||
|
expect(link).toHaveLength(1)
|
||||||
|
expect(link).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: fulfillmentSet.id,
|
||||||
|
stock_locations: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: euWarehouse.id,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -56,7 +56,7 @@ medusaIntegrationTestRunner({
|
|||||||
sales_channel_id: scWebshop.id,
|
sales_channel_id: scWebshop.id,
|
||||||
},
|
},
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
location_id: euWarehouse.id,
|
stock_location_id: euWarehouse.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -64,7 +64,7 @@ medusaIntegrationTestRunner({
|
|||||||
sales_channel_id: scCphStore.id,
|
sales_channel_id: scCphStore.id,
|
||||||
},
|
},
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
location_id: euWarehouse.id,
|
stock_location_id: euWarehouse.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -72,7 +72,7 @@ medusaIntegrationTestRunner({
|
|||||||
sales_channel_id: scNycStore.id,
|
sales_channel_id: scNycStore.id,
|
||||||
},
|
},
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
location_id: usWarehouse.id,
|
stock_location_id: usWarehouse.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { IPricingModuleService, PricingContext } from "@medusajs/types"
|
|||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
|
arrayDifference,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
@@ -12,7 +13,7 @@ interface StepInput {
|
|||||||
context?: Record<string, unknown>
|
context?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getShippingOptionPriceSetsStepId = "get-variant-price-sets"
|
export const getShippingOptionPriceSetsStepId = "get-shipping-option-price-sets"
|
||||||
export const getShippingOptionPriceSetsStep = createStep(
|
export const getShippingOptionPriceSetsStep = createStep(
|
||||||
getShippingOptionPriceSetsStepId,
|
getShippingOptionPriceSetsStepId,
|
||||||
async (data: StepInput, { container }) => {
|
async (data: StepInput, { container }) => {
|
||||||
@@ -38,26 +39,22 @@ export const getShippingOptionPriceSetsStep = createStep(
|
|||||||
|
|
||||||
const optionPriceSets = await remoteQuery(query)
|
const optionPriceSets = await remoteQuery(query)
|
||||||
|
|
||||||
const notFound: string[] = []
|
const optionsMissingPrices = arrayDifference(
|
||||||
const priceSetIds: string[] = []
|
data.optionIds,
|
||||||
|
optionPriceSets.map((v) => v.shipping_option_id)
|
||||||
|
)
|
||||||
|
|
||||||
optionPriceSets.forEach((v) => {
|
if (optionsMissingPrices.length) {
|
||||||
if (v.price_set_id) {
|
|
||||||
priceSetIds.push(v.price_set_id)
|
|
||||||
} else {
|
|
||||||
notFound.push(v.shipping_option_id)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (notFound.length) {
|
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
`Shipping options with IDs ${notFound.join(", ")} do not have a price`
|
`Shipping options with IDs ${optionsMissingPrices.join(
|
||||||
|
", "
|
||||||
|
)} do not have a price`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const calculatedPriceSets = await pricingModuleService.calculatePrices(
|
const calculatedPriceSets = await pricingModuleService.calculatePrices(
|
||||||
{ id: priceSetIds },
|
{ id: optionPriceSets.map((v) => v.price_set_id) },
|
||||||
{ context: data.context as PricingContext["context"] }
|
{ context: data.context as PricingContext["context"] }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export * from "./add-shipping-method-to-cart"
|
|||||||
export * from "./add-to-cart"
|
export * from "./add-to-cart"
|
||||||
export * from "./create-carts"
|
export * from "./create-carts"
|
||||||
export * from "./create-payment-collection-for-cart"
|
export * from "./create-payment-collection-for-cart"
|
||||||
|
export * from "./list-shipping-options-for-cart"
|
||||||
export * from "./refresh-payment-collection"
|
export * from "./refresh-payment-collection"
|
||||||
export * from "./update-cart"
|
export * from "./update-cart"
|
||||||
export * from "./update-cart-promotions"
|
export * from "./update-cart-promotions"
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import { ListShippingOptionsForCartWorkflowInputDTO } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
WorkflowData,
|
||||||
|
createWorkflow,
|
||||||
|
transform,
|
||||||
|
} from "@medusajs/workflows-sdk"
|
||||||
|
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||||
|
import { listShippingOptionsForContextStep } from "../../../shipping-options"
|
||||||
|
import { getShippingOptionPriceSetsStep } from "../steps"
|
||||||
|
|
||||||
|
export const listShippingOptionsForCartWorkflowId =
|
||||||
|
"list-shipping-options-for-cart"
|
||||||
|
export const listShippingOptionsForCartWorkflow = createWorkflow(
|
||||||
|
listShippingOptionsForCartWorkflowId,
|
||||||
|
(input: WorkflowData<ListShippingOptionsForCartWorkflowInputDTO>) => {
|
||||||
|
const scLocationFulfillmentSets = useRemoteQueryStep({
|
||||||
|
entry_point: "sales_channels",
|
||||||
|
fields: ["stock_locations.fulfillment_sets.id"],
|
||||||
|
variables: { id: input.sales_channel_id },
|
||||||
|
})
|
||||||
|
|
||||||
|
const listOptionsInput = transform(
|
||||||
|
{ scLocationFulfillmentSets, input },
|
||||||
|
(data) => {
|
||||||
|
const fulfillmentSetIds = data.scLocationFulfillmentSets
|
||||||
|
.map((sc) =>
|
||||||
|
sc.stock_locations.map((loc) =>
|
||||||
|
loc.fulfillment_sets.map(({ id }) => id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.flat(2)
|
||||||
|
|
||||||
|
return {
|
||||||
|
context: {
|
||||||
|
fulfillment_set_id: fulfillmentSetIds,
|
||||||
|
service_zone: {
|
||||||
|
geo_zones: {
|
||||||
|
city: data.input.shipping_address?.city,
|
||||||
|
country_code: data.input.shipping_address?.country_code,
|
||||||
|
province_code: data.input.shipping_address?.province,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
select: [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"price_type",
|
||||||
|
"service_zone_id",
|
||||||
|
"shipping_profile_id",
|
||||||
|
"provider_id",
|
||||||
|
"data",
|
||||||
|
"amount",
|
||||||
|
],
|
||||||
|
relations: ["type", "provider"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const options = listShippingOptionsForContextStep(listOptionsInput)
|
||||||
|
|
||||||
|
const optionIds = transform({ options }, (data) =>
|
||||||
|
data.options.map((option) => option.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: Separate shipping options based on price_type, flat_rate vs calculated
|
||||||
|
const priceSets = getShippingOptionPriceSetsStep({
|
||||||
|
optionIds,
|
||||||
|
context: {
|
||||||
|
currency_code: input.currency_code,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptionsWithPrice = transform(
|
||||||
|
{ priceSets, options },
|
||||||
|
(data) => {
|
||||||
|
const options = data.options.map((option) => {
|
||||||
|
const price = data.priceSets?.[option.id].calculated_amount
|
||||||
|
|
||||||
|
return {
|
||||||
|
...option,
|
||||||
|
amount: price,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return shippingOptionsWithPrice
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -9,6 +9,7 @@ export * from "./payment"
|
|||||||
export * from "./product"
|
export * from "./product"
|
||||||
export * from "./promotion"
|
export * from "./promotion"
|
||||||
export * from "./region"
|
export * from "./region"
|
||||||
|
export * from "./shipping-options"
|
||||||
export * from "./store"
|
export * from "./store"
|
||||||
export * from "./tax"
|
export * from "./tax"
|
||||||
export * from "./user"
|
export * from "./user"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./steps"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./list-shipping-options-for-context"
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
FindConfig,
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
ShippingOptionDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
context: Record<string, unknown>
|
||||||
|
config?: FindConfig<ShippingOptionDTO>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listShippingOptionsForContextStepId =
|
||||||
|
"list-shipping-options-for-context"
|
||||||
|
export const listShippingOptionsForContextStep = createStep(
|
||||||
|
listShippingOptionsForContextStepId,
|
||||||
|
async (data: StepInput, { container }) => {
|
||||||
|
const fulfillmentService = container.resolve<IFulfillmentModuleService>(
|
||||||
|
ModuleRegistrationName.FULFILLMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingOptions = await fulfillmentService.listShippingOptions(
|
||||||
|
data.context,
|
||||||
|
data.config
|
||||||
|
)
|
||||||
|
|
||||||
|
return new StepResponse(shippingOptions)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -10,3 +10,4 @@ export { default as ShippingOption } from "./shipping-option"
|
|||||||
export { default as ShippingOptionRule } from "./shipping-option-rule"
|
export { default as ShippingOptionRule } from "./shipping-option-rule"
|
||||||
export { default as ShippingOptionType } from "./shipping-option-type"
|
export { default as ShippingOptionType } from "./shipping-option-type"
|
||||||
export { default as ShippingProfile } from "./shipping-profile"
|
export { default as ShippingProfile } from "./shipping-profile"
|
||||||
|
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
|
import { LINKS } from "../links"
|
||||||
|
|
||||||
|
export const FulfillmentSetLocation: ModuleJoinerConfig = {
|
||||||
|
serviceName: LINKS.FulfillmentSetLocation,
|
||||||
|
isLink: true,
|
||||||
|
databaseConfig: {
|
||||||
|
tableName: "fulfillment_set_location",
|
||||||
|
idPrefix: "fsloc",
|
||||||
|
},
|
||||||
|
alias: [
|
||||||
|
{
|
||||||
|
name: ["fulfillment_set_location", "fulfillment_set_locations"],
|
||||||
|
args: {
|
||||||
|
entity: "LinkFulfillmentSetLocation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primaryKeys: ["id", "fulfillment_set_id", "stock_location_id"],
|
||||||
|
relationships: [
|
||||||
|
{
|
||||||
|
serviceName: Modules.FULFILLMENT,
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "fulfillment_set_id",
|
||||||
|
alias: "fulfillment_set",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: Modules.STOCK_LOCATION,
|
||||||
|
primaryKey: "id",
|
||||||
|
foreignKey: "stock_location_id",
|
||||||
|
alias: "location",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
{
|
||||||
|
serviceName: Modules.FULFILLMENT,
|
||||||
|
fieldAlias: {
|
||||||
|
stock_locations: "locations_link.location",
|
||||||
|
},
|
||||||
|
relationship: {
|
||||||
|
serviceName: LINKS.FulfillmentSetLocation,
|
||||||
|
primaryKey: "fulfillment_set_id",
|
||||||
|
foreignKey: "id",
|
||||||
|
alias: "locations_link",
|
||||||
|
isList: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serviceName: Modules.STOCK_LOCATION,
|
||||||
|
relationship: {
|
||||||
|
serviceName: LINKS.FulfillmentSetLocation,
|
||||||
|
primaryKey: "stock_location_id",
|
||||||
|
foreignKey: "id",
|
||||||
|
alias: "fulfillment_set_link",
|
||||||
|
isList: true,
|
||||||
|
},
|
||||||
|
fieldAlias: {
|
||||||
|
fulfillment_sets: "fulfillment_set_link.fulfillment_set",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ export * from "./cart-payment-collection"
|
|||||||
export * from "./cart-promotion"
|
export * from "./cart-promotion"
|
||||||
export * from "./cart-region"
|
export * from "./cart-region"
|
||||||
export * from "./cart-sales-channel"
|
export * from "./cart-sales-channel"
|
||||||
|
export * from "./fulfillment-set-location"
|
||||||
export * from "./inventory-level-stock-location"
|
export * from "./inventory-level-stock-location"
|
||||||
export * from "./order-sales-channel"
|
export * from "./order-sales-channel"
|
||||||
export * from "./product-sales-channel"
|
export * from "./product-sales-channel"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const SalesChannelLocation: ModuleJoinerConfig = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
primaryKeys: ["id", "sales_channel_id", "location_id"],
|
primaryKeys: ["id", "sales_channel_id", "stock_location_id"],
|
||||||
relationships: [
|
relationships: [
|
||||||
{
|
{
|
||||||
serviceName: Modules.SALES_CHANNEL,
|
serviceName: Modules.SALES_CHANNEL,
|
||||||
@@ -28,7 +28,7 @@ export const SalesChannelLocation: ModuleJoinerConfig = {
|
|||||||
{
|
{
|
||||||
serviceName: Modules.STOCK_LOCATION,
|
serviceName: Modules.STOCK_LOCATION,
|
||||||
primaryKey: "id",
|
primaryKey: "id",
|
||||||
foreignKey: "location_id",
|
foreignKey: "stock_location_id",
|
||||||
alias: "location",
|
alias: "location",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -36,7 +36,7 @@ export const SalesChannelLocation: ModuleJoinerConfig = {
|
|||||||
{
|
{
|
||||||
serviceName: Modules.SALES_CHANNEL,
|
serviceName: Modules.SALES_CHANNEL,
|
||||||
fieldAlias: {
|
fieldAlias: {
|
||||||
locations: "locations_link.location",
|
stock_locations: "locations_link.location",
|
||||||
},
|
},
|
||||||
relationship: {
|
relationship: {
|
||||||
serviceName: LINKS.SalesChannelLocation,
|
serviceName: LINKS.SalesChannelLocation,
|
||||||
@@ -53,7 +53,7 @@ export const SalesChannelLocation: ModuleJoinerConfig = {
|
|||||||
},
|
},
|
||||||
relationship: {
|
relationship: {
|
||||||
serviceName: LINKS.SalesChannelLocation,
|
serviceName: LINKS.SalesChannelLocation,
|
||||||
primaryKey: "location_id",
|
primaryKey: "stock_location_id",
|
||||||
foreignKey: "id",
|
foreignKey: "id",
|
||||||
alias: "sales_channels_link",
|
alias: "sales_channels_link",
|
||||||
isList: true,
|
isList: true,
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ export const LINKS = {
|
|||||||
Modules.STOCK_LOCATION,
|
Modules.STOCK_LOCATION,
|
||||||
"location_id"
|
"location_id"
|
||||||
),
|
),
|
||||||
|
FulfillmentSetLocation: composeLinkName(
|
||||||
|
Modules.FULFILLMENT,
|
||||||
|
"fulfillment_set_id",
|
||||||
|
Modules.STOCK_LOCATION,
|
||||||
|
"location_id"
|
||||||
|
),
|
||||||
|
|
||||||
// Internal services
|
// Internal services
|
||||||
ProductShippingProfile: composeLinkName(
|
ProductShippingProfile: composeLinkName(
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { listShippingOptionsForCartWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { ICartModuleService } from "@medusajs/types"
|
||||||
|
import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
||||||
|
|
||||||
|
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||||
|
const { cart_id } = req.params
|
||||||
|
|
||||||
|
const cartService = req.scope.resolve<ICartModuleService>(
|
||||||
|
ModuleRegistrationName.CART
|
||||||
|
)
|
||||||
|
|
||||||
|
const cart = await cartService.retrieve(cart_id, {
|
||||||
|
select: [
|
||||||
|
"id",
|
||||||
|
"sales_channel_id",
|
||||||
|
"subtotal",
|
||||||
|
"currency_code",
|
||||||
|
"shipping_address.city",
|
||||||
|
"shipping_address.country_code",
|
||||||
|
"shipping_address.province",
|
||||||
|
],
|
||||||
|
relations: ["shipping_address"],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptions = await listShippingOptionsForCartWorkflow(
|
||||||
|
req.scope
|
||||||
|
).run({
|
||||||
|
input: {
|
||||||
|
cart_id: cart.id,
|
||||||
|
sales_channel_id: cart.sales_channel_id,
|
||||||
|
currency_code: cart.currency_code,
|
||||||
|
shipping_address: {
|
||||||
|
city: cart.shipping_address?.city,
|
||||||
|
country_code: cart.shipping_address?.country_code,
|
||||||
|
province: cart.shipping_address?.province,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
res.json({ shipping_options: shippingOptions })
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { MiddlewareRoute } from "../../../loaders/helpers/routing/types"
|
||||||
|
|
||||||
|
export const storeShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [
|
||||||
|
{
|
||||||
|
method: ["GET"],
|
||||||
|
matcher: "/store/shipping-options/:cart_id",
|
||||||
|
middlewares: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export const defaultStoreShippingOptionsFields = [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
"price_type",
|
||||||
|
"service_zone_id",
|
||||||
|
"shipping_profile_id",
|
||||||
|
"fulfillment_provider_id",
|
||||||
|
"shipping_option_type_id",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const listTransformQueryConfig = {
|
||||||
|
defaultLimit: 20,
|
||||||
|
isList: true,
|
||||||
|
}
|
||||||
@@ -777,7 +777,7 @@ export class RemoteJoiner {
|
|||||||
// remove alias from fields
|
// remove alias from fields
|
||||||
const parentPath = [BASE_PATH, ...currentPath].join(".")
|
const parentPath = [BASE_PATH, ...currentPath].join(".")
|
||||||
const parentExpands = parsedExpands.get(parentPath)
|
const parentExpands = parsedExpands.get(parentPath)
|
||||||
parentExpands.fields = parentExpands.fields.filter(
|
parentExpands.fields = parentExpands.fields?.filter(
|
||||||
(field) => field !== property
|
(field) => field !== property
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -787,12 +787,13 @@ export class RemoteJoiner {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const parentFieldAlias = fullPath[Math.max(fullPath.length - 2, 0)]
|
||||||
implodeMapping.push({
|
implodeMapping.push({
|
||||||
location: [...currentPath],
|
location: [...currentPath],
|
||||||
property,
|
property,
|
||||||
path: fullPath,
|
path: fullPath,
|
||||||
isList: !!serviceConfig.relationships?.find(
|
isList: !!serviceConfig.relationships?.find(
|
||||||
(relationship) => relationship.alias === fullPath[0]
|
(relationship) => relationship.alias === parentFieldAlias
|
||||||
)?.isList,
|
)?.isList,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { MapToConfig } from "@medusajs/utils"
|
|
||||||
import { ModuleJoinerConfig } from "@medusajs/types"
|
|
||||||
import { Modules } from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { ModuleJoinerConfig } from "@medusajs/types"
|
||||||
|
import { MapToConfig } from "@medusajs/utils"
|
||||||
import { StockLocation } from "./models"
|
import { StockLocation } from "./models"
|
||||||
import moduleSchema from "./schema"
|
import moduleSchema from "./schema"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CustomerDTO } from "../customer"
|
import { CustomerDTO } from "../customer"
|
||||||
|
import { ShippingOptionDTO } from "../fulfillment"
|
||||||
import { ProductDTO } from "../product"
|
import { ProductDTO } from "../product"
|
||||||
import { RegionDTO } from "../region"
|
import { RegionDTO } from "../region"
|
||||||
import { CartDTO, CartLineItemDTO } from "./common"
|
import { CartDTO, CartLineItemDTO } from "./common"
|
||||||
@@ -101,3 +102,18 @@ export interface CartWorkflowDTO extends CartDTO {
|
|||||||
product?: ProductDTO
|
product?: ProductDTO
|
||||||
region?: RegionDTO
|
region?: RegionDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ListShippingOptionsForCartWorkflowInputDTO {
|
||||||
|
cart_id: string
|
||||||
|
sales_channel_id?: string
|
||||||
|
currency_code: string
|
||||||
|
shipping_address: {
|
||||||
|
city?: string
|
||||||
|
country_code?: string
|
||||||
|
province?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PricedShippingOptionDTO extends ShippingOptionDTO {
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
import { FilterableServiceZoneProps, ServiceZoneDTO } from "./service-zone"
|
import { BaseFilterable, OperatorMap } from "../../dal"
|
||||||
import { ShippingProfileDTO } from "./shipping-profile"
|
import { FulfillmentDTO } from "./fulfillment"
|
||||||
import { FulfillmentProviderDTO } from "./fulfillment-provider"
|
import { FulfillmentProviderDTO } from "./fulfillment-provider"
|
||||||
import {
|
import { FilterableServiceZoneProps, ServiceZoneDTO } from "./service-zone"
|
||||||
FilterableShippingOptionTypeProps,
|
|
||||||
ShippingOptionTypeDTO,
|
|
||||||
} from "./shipping-option-type"
|
|
||||||
import {
|
import {
|
||||||
FilterableShippingOptionRuleProps,
|
FilterableShippingOptionRuleProps,
|
||||||
ShippingOptionRuleDTO,
|
ShippingOptionRuleDTO,
|
||||||
} from "./shipping-option-rule"
|
} from "./shipping-option-rule"
|
||||||
import { BaseFilterable, OperatorMap } from "../../dal"
|
import {
|
||||||
import { FulfillmentDTO } from "./fulfillment"
|
FilterableShippingOptionTypeProps,
|
||||||
|
ShippingOptionTypeDTO,
|
||||||
|
} from "./shipping-option-type"
|
||||||
|
import { ShippingProfileDTO } from "./shipping-profile"
|
||||||
|
|
||||||
export type ShippingOptionPriceType = "calculated" | "flat"
|
export type ShippingOptionPriceType = "calculated" | "flat"
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ export interface FilterableShippingOptionProps
|
|||||||
id?: string | string[] | OperatorMap<string | string[]>
|
id?: string | string[] | OperatorMap<string | string[]>
|
||||||
name?: string | string[] | OperatorMap<string | string[]>
|
name?: string | string[] | OperatorMap<string | string[]>
|
||||||
fulfillment_set_id?: string | string[] | OperatorMap<string | string[]>
|
fulfillment_set_id?: string | string[] | OperatorMap<string | string[]>
|
||||||
|
shipping_profile_id?: string | string[] | OperatorMap<string | string[]>
|
||||||
fulfillment_set_type?: string | string[] | OperatorMap<string | string[]>
|
fulfillment_set_type?: string | string[] | OperatorMap<string | string[]>
|
||||||
price_type?:
|
price_type?:
|
||||||
| ShippingOptionPriceType
|
| ShippingOptionPriceType
|
||||||
|
|||||||
Reference in new issue
Block a user