feat(core-flows, medusa): add shipping methods to cart API (#7150)
* feat(core-flows, medusa): add shipping methods to cart API * chore: change id to option_id * chore: use list listShippingOptionsForContext instead of validateShippingOption * chore: remove comment * chore: add refresh shipping methods step * chore: set cart step * chore: update all workflows to refresh shipping methods * chore: add tests + cleanup
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(core-flows, medusa): add shipping methods to cart API
|
||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
ISalesChannelModuleService,
|
ISalesChannelModuleService,
|
||||||
IStockLocationServiceNext,
|
IStockLocationServiceNext,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
import { ContainerRegistrationKeys, RuleOperator } 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"
|
||||||
|
|
||||||
@@ -1397,34 +1397,44 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("AddShippingMethodToCartWorkflow", () => {
|
describe("AddShippingMethodToCartWorkflow", () => {
|
||||||
it("should add shipping method to cart", async () => {
|
let cart
|
||||||
let cart = await cartModuleService.create({
|
let shippingProfile
|
||||||
|
let fulfillmentSet
|
||||||
|
let priceSet
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
cart = await cartModuleService.create({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
|
shipping_address: {
|
||||||
|
country_code: "us",
|
||||||
|
province: "ny",
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const shippingProfile =
|
shippingProfile = await fulfillmentModule.createShippingProfiles({
|
||||||
await fulfillmentModule.createShippingProfiles({
|
name: "Test",
|
||||||
name: "Test",
|
type: "default",
|
||||||
type: "default",
|
})
|
||||||
})
|
|
||||||
|
|
||||||
const fulfillmentSet = await fulfillmentModule.create({
|
fulfillmentSet = await fulfillmentModule.create({
|
||||||
name: "Test",
|
name: "Test",
|
||||||
type: "test-type",
|
type: "test-type",
|
||||||
service_zones: [
|
service_zones: [
|
||||||
{
|
{
|
||||||
name: "Test",
|
name: "Test",
|
||||||
geo_zones: [
|
geo_zones: [{ type: "country", country_code: "us" }],
|
||||||
{
|
|
||||||
type: "country",
|
|
||||||
country_code: "us",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
priceSet = await pricingModule.create({
|
||||||
|
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should add shipping method to cart", async () => {
|
||||||
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,
|
||||||
@@ -1436,41 +1446,26 @@ medusaIntegrationTestRunner({
|
|||||||
description: "Test description",
|
description: "Test description",
|
||||||
code: "test-code",
|
code: "test-code",
|
||||||
},
|
},
|
||||||
})
|
rules: [
|
||||||
|
|
||||||
const priceSet = await pricingModule.create({
|
|
||||||
prices: [
|
|
||||||
{
|
{
|
||||||
amount: 3000,
|
operator: RuleOperator.EQ,
|
||||||
currency_code: "usd",
|
attribute: "shipping_address.province",
|
||||||
|
value: "ny",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
await remoteLink.create([
|
await remoteLink.create([
|
||||||
{
|
{
|
||||||
[Modules.FULFILLMENT]: {
|
[Modules.FULFILLMENT]: { shipping_option_id: shippingOption.id },
|
||||||
shipping_option_id: shippingOption.id,
|
[Modules.PRICING]: { price_set_id: priceSet.id },
|
||||||
},
|
|
||||||
[Modules.PRICING]: {
|
|
||||||
price_set_id: priceSet.id,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
cart = await cartModuleService.retrieve(cart.id, {
|
|
||||||
select: ["id", "region_id", "currency_code"],
|
|
||||||
})
|
|
||||||
|
|
||||||
await addShippingMethodToWorkflow(appContainer).run({
|
await addShippingMethodToWorkflow(appContainer).run({
|
||||||
input: {
|
input: {
|
||||||
options: [
|
options: [{ id: shippingOption.id }],
|
||||||
{
|
|
||||||
id: shippingOption.id,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
cart_id: cart.id,
|
cart_id: cart.id,
|
||||||
currency_code: cart.currency_code,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1491,6 +1486,77 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should throw error when shipping option is not valid", async () => {
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "shipping_address.city",
|
||||||
|
value: "sf",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
[Modules.FULFILLMENT]: { shipping_option_id: shippingOption.id },
|
||||||
|
[Modules.PRICING]: { price_set_id: priceSet.id },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const { errors } = await addShippingMethodToWorkflow(
|
||||||
|
appContainer
|
||||||
|
).run({
|
||||||
|
input: {
|
||||||
|
options: [{ id: shippingOption.id }],
|
||||||
|
cart_id: cart.id,
|
||||||
|
},
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Rules are setup only for Germany, this should throw an error
|
||||||
|
expect(errors).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
error: expect.objectContaining({
|
||||||
|
message: `Shipping Options are invalid for cart.`,
|
||||||
|
type: "invalid_data",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw error when shipping option is not present in the db", async () => {
|
||||||
|
const { errors } = await addShippingMethodToWorkflow(
|
||||||
|
appContainer
|
||||||
|
).run({
|
||||||
|
input: {
|
||||||
|
options: [{ id: "does-not-exist" }],
|
||||||
|
cart_id: cart.id,
|
||||||
|
},
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Rules are setup only for Berlin, this should throw an error
|
||||||
|
expect(errors).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
error: expect.objectContaining({
|
||||||
|
message: "Shipping Options are invalid for cart.",
|
||||||
|
type: "invalid_data",
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("listShippingOptionsForCartWorkflow", () => {
|
describe("listShippingOptionsForCartWorkflow", () => {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ICartModuleService,
|
ICartModuleService,
|
||||||
ICustomerModuleService,
|
ICustomerModuleService,
|
||||||
|
IFulfillmentModuleService,
|
||||||
IPricingModuleService,
|
IPricingModuleService,
|
||||||
IProductModuleService,
|
IProductModuleService,
|
||||||
IPromotionModuleService,
|
IPromotionModuleService,
|
||||||
@@ -14,7 +15,11 @@ import {
|
|||||||
ISalesChannelModuleService,
|
ISalesChannelModuleService,
|
||||||
ITaxModuleService,
|
ITaxModuleService,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { PromotionRuleOperator, PromotionType } from "@medusajs/utils"
|
import {
|
||||||
|
PromotionRuleOperator,
|
||||||
|
PromotionType,
|
||||||
|
RuleOperator,
|
||||||
|
} 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"
|
||||||
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
|
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
|
||||||
@@ -38,6 +43,7 @@ medusaIntegrationTestRunner({
|
|||||||
let remoteLink: RemoteLink
|
let remoteLink: RemoteLink
|
||||||
let promotionModule: IPromotionModuleService
|
let promotionModule: IPromotionModuleService
|
||||||
let taxModule: ITaxModuleService
|
let taxModule: ITaxModuleService
|
||||||
|
let fulfillmentModule: IFulfillmentModuleService
|
||||||
|
|
||||||
let defaultRegion
|
let defaultRegion
|
||||||
|
|
||||||
@@ -52,6 +58,9 @@ medusaIntegrationTestRunner({
|
|||||||
remoteLink = appContainer.resolve(LinkModuleUtils.REMOTE_LINK)
|
remoteLink = appContainer.resolve(LinkModuleUtils.REMOTE_LINK)
|
||||||
promotionModule = appContainer.resolve(ModuleRegistrationName.PROMOTION)
|
promotionModule = appContainer.resolve(ModuleRegistrationName.PROMOTION)
|
||||||
taxModule = appContainer.resolve(ModuleRegistrationName.TAX)
|
taxModule = appContainer.resolve(ModuleRegistrationName.TAX)
|
||||||
|
fulfillmentModule = appContainer.resolve(
|
||||||
|
ModuleRegistrationName.FULFILLMENT
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -548,7 +557,7 @@ medusaIntegrationTestRunner({
|
|||||||
await setupTaxStructure(taxModule)
|
await setupTaxStructure(taxModule)
|
||||||
|
|
||||||
const region = await regionModule.create({
|
const region = await regionModule.create({
|
||||||
name: "US",
|
name: "us",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -562,9 +571,9 @@ medusaIntegrationTestRunner({
|
|||||||
shipping_address: {
|
shipping_address: {
|
||||||
address_1: "test address 1",
|
address_1: "test address 1",
|
||||||
address_2: "test address 2",
|
address_2: "test address 2",
|
||||||
city: "NY",
|
city: "ny",
|
||||||
country_code: "US",
|
country_code: "us",
|
||||||
province: "NY",
|
province: "ny",
|
||||||
postal_code: "94016",
|
postal_code: "94016",
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
@@ -578,11 +587,78 @@ medusaIntegrationTestRunner({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "customer.email",
|
||||||
|
value: "tony@stark.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption2 = 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",
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "customer.email",
|
||||||
|
value: "tony@stark.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Manually inserting shipping methods here since the cart does not
|
// Manually inserting shipping methods here since the cart does not
|
||||||
// currently support it. Move to API when ready.
|
// currently support it. Move to API when ready.
|
||||||
await cartModule.addShippingMethods(cart.id, [
|
await cartModule.addShippingMethods(cart.id, [
|
||||||
{ amount: 500, name: "express" },
|
{
|
||||||
{ amount: 500, name: "standard" },
|
amount: 500,
|
||||||
|
name: "express",
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
amount: 500,
|
||||||
|
name: "standard",
|
||||||
|
shipping_option_id: shippingOption2.id,
|
||||||
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
let updated = await api.post(`/store/carts/${cart.id}`, {
|
let updated = await api.post(`/store/carts/${cart.id}`, {
|
||||||
@@ -590,7 +666,10 @@ medusaIntegrationTestRunner({
|
|||||||
email: "tony@stark.com",
|
email: "tony@stark.com",
|
||||||
sales_channel_id: salesChannel.id,
|
sales_channel_id: salesChannel.id,
|
||||||
})
|
})
|
||||||
|
console.log(
|
||||||
|
"updated.data.cart --- ",
|
||||||
|
JSON.stringify(updated.data.cart, null, 4)
|
||||||
|
)
|
||||||
expect(updated.status).toEqual(200)
|
expect(updated.status).toEqual(200)
|
||||||
expect(updated.data.cart).toEqual(
|
expect(updated.data.cart).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -606,13 +685,13 @@ medusaIntegrationTestRunner({
|
|||||||
}),
|
}),
|
||||||
sales_channel_id: salesChannel.id,
|
sales_channel_id: salesChannel.id,
|
||||||
shipping_address: expect.objectContaining({
|
shipping_address: expect.objectContaining({
|
||||||
city: "NY",
|
city: "ny",
|
||||||
country_code: "US",
|
country_code: "us",
|
||||||
province: "NY",
|
province: "ny",
|
||||||
}),
|
}),
|
||||||
shipping_methods: expect.arrayContaining([
|
shipping_methods: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
shipping_option_id: null,
|
shipping_option_id: shippingOption2.id,
|
||||||
amount: 500,
|
amount: 500,
|
||||||
tax_lines: [
|
tax_lines: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -625,7 +704,7 @@ medusaIntegrationTestRunner({
|
|||||||
adjustments: [],
|
adjustments: [],
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
shipping_option_id: null,
|
shipping_option_id: shippingOption.id,
|
||||||
amount: 500,
|
amount: 500,
|
||||||
tax_lines: [
|
tax_lines: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -685,6 +764,140 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should remove invalid shipping methods", async () => {
|
||||||
|
await setupTaxStructure(taxModule)
|
||||||
|
|
||||||
|
const region = await regionModule.create({
|
||||||
|
name: "US",
|
||||||
|
currency_code: "usd",
|
||||||
|
})
|
||||||
|
|
||||||
|
const cart = await cartModule.create({
|
||||||
|
region_id: region.id,
|
||||||
|
currency_code: "eur",
|
||||||
|
email: "tony@stark.com",
|
||||||
|
shipping_address: {
|
||||||
|
address_1: "test address 1",
|
||||||
|
address_2: "test address 2",
|
||||||
|
city: "ny",
|
||||||
|
country_code: "us",
|
||||||
|
province: "ny",
|
||||||
|
postal_code: "94016",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
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 shippingOptionOldValid =
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "customer.email",
|
||||||
|
value: "tony@stark.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptionNewValid =
|
||||||
|
await fulfillmentModule.createShippingOptions({
|
||||||
|
name: "Test shipping option new",
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "customer.email",
|
||||||
|
value: "jon@stark.com",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await cartModule.addShippingMethods(cart.id, [
|
||||||
|
// should be removed
|
||||||
|
{
|
||||||
|
amount: 500,
|
||||||
|
name: "express",
|
||||||
|
shipping_option_id: shippingOptionOldValid.id,
|
||||||
|
},
|
||||||
|
// should be kept
|
||||||
|
{
|
||||||
|
amount: 500,
|
||||||
|
name: "express-new",
|
||||||
|
shipping_option_id: shippingOptionNewValid.id,
|
||||||
|
},
|
||||||
|
// should be removed
|
||||||
|
{
|
||||||
|
amount: 500,
|
||||||
|
name: "standard",
|
||||||
|
shipping_option_id: "does-not-exist",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
let updated = await api.post(`/store/carts/${cart.id}`, {
|
||||||
|
email: "jon@stark.com",
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(updated.status).toEqual(200)
|
||||||
|
expect(updated.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
email: "jon@stark.com",
|
||||||
|
shipping_methods: [
|
||||||
|
expect.objectContaining({
|
||||||
|
shipping_option_id: shippingOptionNewValid.id,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
updated = await api.post(`/store/carts/${cart.id}`, {
|
||||||
|
email: null,
|
||||||
|
sales_channel_id: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(updated.status).toEqual(200)
|
||||||
|
expect(updated.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
email: null,
|
||||||
|
shipping_methods: [],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /store/carts/:id", () => {
|
describe("POST /store/carts/:id", () => {
|
||||||
@@ -1100,6 +1313,85 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("POST /store/carts/:id/shipping-methods", () => {
|
||||||
|
it("should add a shipping methods to a cart", async () => {
|
||||||
|
const cart = await cartModule.create({
|
||||||
|
currency_code: "usd",
|
||||||
|
shipping_address: { country_code: "us" },
|
||||||
|
items: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
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 priceSet = await pricingModule.create({
|
||||||
|
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||||
|
})
|
||||||
|
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "shipping_address.country_code",
|
||||||
|
value: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
[Modules.FULFILLMENT]: { shipping_option_id: shippingOption.id },
|
||||||
|
[Modules.PRICING]: { price_set_id: priceSet.id },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
let response = await api.post(
|
||||||
|
`/store/carts/${cart.id}/shipping-methods`,
|
||||||
|
{ option_id: shippingOption.id }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
shipping_methods: [
|
||||||
|
{
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
amount: 3000,
|
||||||
|
id: expect.any(String),
|
||||||
|
tax_lines: [],
|
||||||
|
adjustments: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,21 +5,24 @@ interface StepInput {
|
|||||||
entry_point: string
|
entry_point: string
|
||||||
fields: string[]
|
fields: string[]
|
||||||
variables?: Record<string, any>
|
variables?: Record<string, any>
|
||||||
|
list?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useRemoteQueryStepId = "use-remote-query"
|
export const useRemoteQueryStepId = "use-remote-query"
|
||||||
export const useRemoteQueryStep = createStep(
|
export const useRemoteQueryStep = createStep(
|
||||||
useRemoteQueryStepId,
|
useRemoteQueryStepId,
|
||||||
async (data: StepInput, { container }) => {
|
async (data: StepInput, { container }) => {
|
||||||
|
const { list = true, fields, variables, entry_point: entryPoint } = data
|
||||||
const query = container.resolve("remoteQuery")
|
const query = container.resolve("remoteQuery")
|
||||||
|
|
||||||
const queryObject = remoteQueryObjectFromString({
|
const queryObject = remoteQueryObjectFromString({
|
||||||
entryPoint: data.entry_point,
|
entryPoint,
|
||||||
fields: data.fields,
|
fields,
|
||||||
variables: data.variables,
|
variables,
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await query(queryObject)
|
const entities = await query(queryObject)
|
||||||
|
const result = list ? entities : entities[0]
|
||||||
|
|
||||||
return new StepResponse(result)
|
return new StepResponse(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export * from "./get-shipping-option-price-sets"
|
|||||||
export * from "./get-variant-price-sets"
|
export * from "./get-variant-price-sets"
|
||||||
export * from "./get-variants"
|
export * from "./get-variants"
|
||||||
export * from "./prepare-adjustments-from-promotion-actions"
|
export * from "./prepare-adjustments-from-promotion-actions"
|
||||||
|
export * from "./refresh-cart-shipping-methods"
|
||||||
export * from "./remove-line-item-adjustments"
|
export * from "./remove-line-item-adjustments"
|
||||||
export * from "./remove-shipping-method-adjustments"
|
export * from "./remove-shipping-method-adjustments"
|
||||||
export * from "./retrieve-cart"
|
export * from "./retrieve-cart"
|
||||||
@@ -20,5 +21,5 @@ export * from "./retrieve-cart-with-links"
|
|||||||
export * from "./set-tax-lines-for-items"
|
export * from "./set-tax-lines-for-items"
|
||||||
export * from "./update-cart-promotions"
|
export * from "./update-cart-promotions"
|
||||||
export * from "./update-carts"
|
export * from "./update-carts"
|
||||||
|
export * from "./validate-cart-shipping-options"
|
||||||
export * from "./validate-variants-existence"
|
export * from "./validate-variants-existence"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { CartDTO, ICartModuleService } from "@medusajs/types"
|
||||||
|
import { arrayDifference } from "@medusajs/utils"
|
||||||
|
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||||
|
import { IFulfillmentModuleService } from "../../../../../types/dist/fulfillment/service"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
cart: CartDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export const refreshCartShippingMethodsStepId = "refresh-cart-shipping-methods"
|
||||||
|
export const refreshCartShippingMethodsStep = createStep(
|
||||||
|
refreshCartShippingMethodsStepId,
|
||||||
|
async (data: StepInput, { container }) => {
|
||||||
|
const { cart } = data
|
||||||
|
const { shipping_methods: shippingMethods = [] } = cart
|
||||||
|
|
||||||
|
if (!shippingMethods?.length) {
|
||||||
|
return new StepResponse(void 0, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
||||||
|
ModuleRegistrationName.FULFILLMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
const cartModule = container.resolve<ICartModuleService>(
|
||||||
|
ModuleRegistrationName.CART
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingOptionIds: string[] = shippingMethods.map(
|
||||||
|
(sm) => sm.shipping_option_id!
|
||||||
|
)
|
||||||
|
|
||||||
|
const validShippingOptions =
|
||||||
|
await fulfillmentModule.listShippingOptionsForContext(
|
||||||
|
{
|
||||||
|
id: shippingOptionIds,
|
||||||
|
context: { ...cart },
|
||||||
|
address: {
|
||||||
|
country_code: cart.shipping_address?.country_code,
|
||||||
|
province_code: cart.shipping_address?.province,
|
||||||
|
city: cart.shipping_address?.city,
|
||||||
|
postal_expression: cart.shipping_address?.postal_code,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ relations: ["rules"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
const validShippingOptionIds = validShippingOptions.map((o) => o.id)
|
||||||
|
const invalidShippingOptionIds = arrayDifference(
|
||||||
|
shippingOptionIds,
|
||||||
|
validShippingOptionIds
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingMethodsToDelete = shippingMethods
|
||||||
|
.filter((sm) => invalidShippingOptionIds.includes(sm.shipping_option_id!))
|
||||||
|
.map((sm) => sm.id)
|
||||||
|
|
||||||
|
await cartModule.softDeleteShippingMethods(shippingMethodsToDelete)
|
||||||
|
|
||||||
|
return new StepResponse(void 0, shippingMethodsToDelete)
|
||||||
|
},
|
||||||
|
async (shippingMethodsToRestore, { container }) => {
|
||||||
|
if (shippingMethodsToRestore?.length) {
|
||||||
|
const cartModule = container.resolve<ICartModuleService>(
|
||||||
|
ModuleRegistrationName.CART
|
||||||
|
)
|
||||||
|
|
||||||
|
await cartModule.restoreShippingMethods(shippingMethodsToRestore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { CartDTO } from "@medusajs/types"
|
||||||
|
import { arrayDifference, MedusaError } from "@medusajs/utils"
|
||||||
|
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||||
|
import { IFulfillmentModuleService } from "../../../../../types/dist/fulfillment/service"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
cart: CartDTO
|
||||||
|
option_ids: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validateCartShippingOptionsStepId =
|
||||||
|
"validate-cart-shipping-options"
|
||||||
|
export const validateCartShippingOptionsStep = createStep(
|
||||||
|
validateCartShippingOptionsStepId,
|
||||||
|
async (data: StepInput, { container }) => {
|
||||||
|
const { option_ids: optionIds = [], cart } = data
|
||||||
|
|
||||||
|
if (!optionIds.length) {
|
||||||
|
return new StepResponse(void 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
||||||
|
ModuleRegistrationName.FULFILLMENT
|
||||||
|
)
|
||||||
|
|
||||||
|
const validShippingOptions =
|
||||||
|
await fulfillmentModule.listShippingOptionsForContext(
|
||||||
|
{
|
||||||
|
id: optionIds,
|
||||||
|
context: { ...cart },
|
||||||
|
address: {
|
||||||
|
country_code: cart.shipping_address?.country_code,
|
||||||
|
province_code: cart.shipping_address?.province,
|
||||||
|
city: cart.shipping_address?.city,
|
||||||
|
postal_expression: cart.shipping_address?.postal_code,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ relations: ["rules"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
const validShippingOptionIds = validShippingOptions.map((o) => o.id)
|
||||||
|
const invalidOptionIds = arrayDifference(optionIds, validShippingOptionIds)
|
||||||
|
|
||||||
|
if (invalidOptionIds.length) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Shipping Options are invalid for cart.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StepResponse(void 0)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
export const cartFieldsForRefreshSteps = [
|
||||||
|
"region_id",
|
||||||
|
"currency_code",
|
||||||
|
"region.*",
|
||||||
|
"items.*",
|
||||||
|
"items.tax_lines.*",
|
||||||
|
"shipping_address.*",
|
||||||
|
"shipping_methods.*",
|
||||||
|
"shipping_methods.tax_lines*",
|
||||||
|
"customer.*",
|
||||||
|
"customer.groups.*",
|
||||||
|
]
|
||||||
@@ -4,13 +4,16 @@ import {
|
|||||||
transform,
|
transform,
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||||
import { addShippingMethodToCartStep } from "../steps"
|
import {
|
||||||
|
addShippingMethodToCartStep,
|
||||||
|
validateCartShippingOptionsStep,
|
||||||
|
} from "../steps"
|
||||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||||
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||||
|
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
||||||
|
|
||||||
interface AddShippingMethodToCartWorkflowInput {
|
interface AddShippingMethodToCartWorkflowInput {
|
||||||
cart_id: string
|
cart_id: string
|
||||||
currency_code: string
|
|
||||||
options: {
|
options: {
|
||||||
id: string
|
id: string
|
||||||
data?: Record<string, unknown>
|
data?: Record<string, unknown>
|
||||||
@@ -23,22 +26,32 @@ export const addShippingMethodToWorkflow = createWorkflow(
|
|||||||
(
|
(
|
||||||
input: WorkflowData<AddShippingMethodToCartWorkflowInput>
|
input: WorkflowData<AddShippingMethodToCartWorkflowInput>
|
||||||
): WorkflowData<void> => {
|
): WorkflowData<void> => {
|
||||||
|
const cart = useRemoteQueryStep({
|
||||||
|
entry_point: "cart",
|
||||||
|
fields: cartFieldsForRefreshSteps,
|
||||||
|
variables: { id: input.cart_id },
|
||||||
|
list: false,
|
||||||
|
})
|
||||||
|
|
||||||
const optionIds = transform({ input }, (data) => {
|
const optionIds = transform({ input }, (data) => {
|
||||||
return (data.input.options ?? []).map((i) => i.id)
|
return (data.input.options ?? []).map((i) => i.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
validateCartShippingOptionsStep({
|
||||||
|
option_ids: optionIds,
|
||||||
|
cart,
|
||||||
|
})
|
||||||
|
|
||||||
const shippingOptions = useRemoteQueryStep({
|
const shippingOptions = useRemoteQueryStep({
|
||||||
entry_point: "shipping_option",
|
entry_point: "shipping_option",
|
||||||
fields: ["id", "name", "calculated_price.calculated_amount"],
|
fields: ["id", "name", "calculated_price.calculated_amount"],
|
||||||
variables: {
|
variables: {
|
||||||
id: optionIds,
|
id: optionIds,
|
||||||
calculated_price: {
|
calculated_price: {
|
||||||
context: {
|
context: { currency_code: cart.currency_code },
|
||||||
currency_code: input.currency_code,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}).config({ name: "fetch-shipping-option" })
|
||||||
|
|
||||||
const shippingMethodInput = transform(
|
const shippingMethodInput = transform(
|
||||||
{ input, shippingOptions },
|
{ input, shippingOptions },
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ import {
|
|||||||
confirmInventoryStep,
|
confirmInventoryStep,
|
||||||
getVariantPriceSetsStep,
|
getVariantPriceSetsStep,
|
||||||
getVariantsStep,
|
getVariantsStep,
|
||||||
|
refreshCartShippingMethodsStep,
|
||||||
validateVariantsExistStep,
|
validateVariantsExistStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||||
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||||
|
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
||||||
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
||||||
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
||||||
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
||||||
@@ -128,15 +130,19 @@ export const addToCartWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const items = addToCartStep({ items: lineItems })
|
const items = addToCartStep({ items: lineItems })
|
||||||
|
|
||||||
updateTaxLinesStep({
|
const cart = useRemoteQueryStep({
|
||||||
cart_or_cart_id: input.cart,
|
entry_point: "cart",
|
||||||
items,
|
fields: cartFieldsForRefreshSteps,
|
||||||
})
|
variables: { id: input.cart.id },
|
||||||
|
list: false,
|
||||||
|
}).config({ name: "refetch–cart" })
|
||||||
|
|
||||||
|
refreshCartShippingMethodsStep({ cart })
|
||||||
|
// TODO: since refreshCartShippingMethodsStep potentially removes cart shipping methods, we need the updated cart here
|
||||||
|
// for the following 2 steps as they act upon final cart shape
|
||||||
|
updateTaxLinesStep({ cart_or_cart_id: cart, items })
|
||||||
refreshCartPromotionsStep({ id: input.cart.id })
|
refreshCartPromotionsStep({ id: input.cart.id })
|
||||||
refreshPaymentCollectionForCartStep({
|
refreshPaymentCollectionForCartStep({ cart_id: input.cart.id })
|
||||||
cart_id: input.cart.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,17 @@ import {
|
|||||||
parallelize,
|
parallelize,
|
||||||
transform,
|
transform,
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
|
import { useRemoteQueryStep } from "../../../common"
|
||||||
import {
|
import {
|
||||||
findOneOrAnyRegionStep,
|
findOneOrAnyRegionStep,
|
||||||
findOrCreateCustomerStep,
|
findOrCreateCustomerStep,
|
||||||
findSalesChannelStep,
|
findSalesChannelStep,
|
||||||
|
refreshCartShippingMethodsStep,
|
||||||
updateCartsStep,
|
updateCartsStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||||
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||||
|
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
||||||
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
||||||
|
|
||||||
export const updateCartWorkflowId = "update-cart"
|
export const updateCartWorkflowId = "update-cart"
|
||||||
@@ -63,6 +66,14 @@ export const updateCartWorkflow = createWorkflow(
|
|||||||
|
|
||||||
const carts = updateCartsStep([cartInput])
|
const carts = updateCartsStep([cartInput])
|
||||||
|
|
||||||
|
const cart = useRemoteQueryStep({
|
||||||
|
entry_point: "cart",
|
||||||
|
fields: cartFieldsForRefreshSteps,
|
||||||
|
variables: { id: cartInput.id },
|
||||||
|
list: false,
|
||||||
|
}).config({ name: "refetch–cart" })
|
||||||
|
|
||||||
|
refreshCartShippingMethodsStep({ cart })
|
||||||
updateTaxLinesStep({ cart_or_cart_id: carts[0].id })
|
updateTaxLinesStep({ cart_or_cart_id: carts[0].id })
|
||||||
refreshCartPromotionsStep({
|
refreshCartPromotionsStep({
|
||||||
id: input.id,
|
id: input.id,
|
||||||
|
|||||||
@@ -5,14 +5,16 @@ import {
|
|||||||
transform,
|
transform,
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import { MedusaError } from "medusa-core-utils"
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||||
|
import { updateLineItemsStep } from "../../line-item/steps"
|
||||||
import {
|
import {
|
||||||
confirmInventoryStep,
|
confirmInventoryStep,
|
||||||
getVariantPriceSetsStep,
|
getVariantPriceSetsStep,
|
||||||
getVariantsStep,
|
getVariantsStep,
|
||||||
} from ".."
|
refreshCartShippingMethodsStep,
|
||||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
} from "../steps"
|
||||||
import { updateLineItemsStep } from "../../line-item/steps"
|
|
||||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||||
|
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
||||||
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
||||||
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
||||||
|
|
||||||
@@ -106,10 +108,16 @@ export const updateLineItemInCartWorkflow = createWorkflow(
|
|||||||
selector: lineItemUpdate.selector,
|
selector: lineItemUpdate.selector,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const cart = useRemoteQueryStep({
|
||||||
|
entry_point: "cart",
|
||||||
|
fields: cartFieldsForRefreshSteps,
|
||||||
|
variables: { id: input.cart.id },
|
||||||
|
list: false,
|
||||||
|
}).config({ name: "refetch–cart" })
|
||||||
|
|
||||||
|
refreshCartShippingMethodsStep({ cart })
|
||||||
refreshCartPromotionsStep({ id: input.cart.id })
|
refreshCartPromotionsStep({ id: input.cart.id })
|
||||||
refreshPaymentCollectionForCartStep({
|
refreshPaymentCollectionForCartStep({ cart_id: input.cart.id })
|
||||||
cart_id: input.cart.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updatedItem = transform({ result }, (data) => data.result?.[0])
|
const updatedItem = transform({ result }, (data) => data.result?.[0])
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { addShippingMethodToWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
|
||||||
|
import { refetchCart } from "../../helpers"
|
||||||
|
import { StoreAddCartShippingMethodsType } from "../../validators"
|
||||||
|
|
||||||
|
export const POST = async (
|
||||||
|
req: MedusaRequest<StoreAddCartShippingMethodsType>,
|
||||||
|
res: MedusaResponse
|
||||||
|
) => {
|
||||||
|
const workflow = addShippingMethodToWorkflow(req.scope)
|
||||||
|
const payload = req.validatedBody
|
||||||
|
|
||||||
|
const { errors } = await workflow.run({
|
||||||
|
input: {
|
||||||
|
options: [{ id: payload.option_id, data: payload.data }],
|
||||||
|
cart_id: req.params.id,
|
||||||
|
},
|
||||||
|
throwOnError: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (Array.isArray(errors) && errors[0]) {
|
||||||
|
throw errors[0].error
|
||||||
|
}
|
||||||
|
|
||||||
|
const cart = await refetchCart(
|
||||||
|
req.params.id,
|
||||||
|
req.scope,
|
||||||
|
req.remoteQueryConfig.fields
|
||||||
|
)
|
||||||
|
|
||||||
|
res.status(200).json({ cart })
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import * as QueryConfig from "./query-config"
|
|||||||
import {
|
import {
|
||||||
StoreAddCartLineItem,
|
StoreAddCartLineItem,
|
||||||
StoreAddCartPromotions,
|
StoreAddCartPromotions,
|
||||||
|
StoreAddCartShippingMethods,
|
||||||
StoreCalculateCartTaxes,
|
StoreCalculateCartTaxes,
|
||||||
StoreCreateCart,
|
StoreCreateCart,
|
||||||
StoreGetCartsCart,
|
StoreGetCartsCart,
|
||||||
@@ -121,6 +122,17 @@ export const storeCartRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: ["POST"],
|
||||||
|
matcher: "/store/carts/:id/shipping-methods",
|
||||||
|
middlewares: [
|
||||||
|
validateAndTransformBody(StoreAddCartShippingMethods),
|
||||||
|
validateAndTransformQuery(
|
||||||
|
StoreGetCartsCart,
|
||||||
|
QueryConfig.retrieveTransformQueryConfig
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
method: ["DELETE"],
|
method: ["DELETE"],
|
||||||
matcher: "/store/carts/:id/promotions",
|
matcher: "/store/carts/:id/promotions",
|
||||||
|
|||||||
@@ -72,3 +72,13 @@ export const StoreUpdateCartLineItem = z.object({
|
|||||||
quantity: z.number(),
|
quantity: z.number(),
|
||||||
metadata: z.record(z.unknown()).optional(),
|
metadata: z.record(z.unknown()).optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export type StoreAddCartShippingMethodsType = z.infer<
|
||||||
|
typeof StoreAddCartShippingMethods
|
||||||
|
>
|
||||||
|
export const StoreAddCartShippingMethods = z
|
||||||
|
.object({
|
||||||
|
option_id: z.string(),
|
||||||
|
data: z.record(z.unknown()).optional(),
|
||||||
|
})
|
||||||
|
.strict()
|
||||||
|
|||||||
@@ -1145,7 +1145,7 @@ export interface FilterableShippingMethodProps
|
|||||||
/**
|
/**
|
||||||
* Filter the shipping methods by the ID of their associated shipping option.
|
* Filter the shipping methods by the ID of their associated shipping option.
|
||||||
*/
|
*/
|
||||||
shipping_option_id?: string | string[]
|
shipping_option_id?: string | string[] | OperatorMap<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -673,6 +673,11 @@ export interface CreateShippingMethodDTO {
|
|||||||
*/
|
*/
|
||||||
amount: BigNumberInput
|
amount: BigNumberInput
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of the shipping method.
|
||||||
|
*/
|
||||||
|
shipping_option_id?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data of the shipping method.
|
* The data of the shipping method.
|
||||||
*/
|
*/
|
||||||
@@ -703,6 +708,11 @@ export interface CreateShippingMethodForSingleCartDTO {
|
|||||||
*/
|
*/
|
||||||
amount: BigNumberInput
|
amount: BigNumberInput
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of the shipping method.
|
||||||
|
*/
|
||||||
|
shipping_option_id?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data of the shipping method.
|
* The data of the shipping method.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -743,7 +743,7 @@ export interface ICartModuleService extends IModuleService {
|
|||||||
*/
|
*/
|
||||||
listShippingMethods(
|
listShippingMethods(
|
||||||
filters: FilterableShippingMethodProps,
|
filters: FilterableShippingMethodProps,
|
||||||
config: FindConfig<CartShippingMethodDTO>,
|
config?: FindConfig<CartShippingMethodDTO>,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<CartShippingMethodDTO[]>
|
): Promise<CartShippingMethodDTO[]>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
type ArrayDifferenceElement = string | number
|
type ArrayDifferenceElement = string | number
|
||||||
|
|
||||||
export function arrayDifference(
|
export function arrayDifference<TElement = ArrayDifferenceElement>(
|
||||||
mainArray: ArrayDifferenceElement[],
|
mainArray: TElement[],
|
||||||
differingArray: ArrayDifferenceElement[]
|
differingArray: TElement[]
|
||||||
): ArrayDifferenceElement[] {
|
): TElement[] {
|
||||||
const mainArraySet = new Set(mainArray)
|
const mainArraySet = new Set(mainArray)
|
||||||
const differingArraySet = new Set(differingArray)
|
const differingArraySet = new Set(differingArray)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user