feat(core-flows, types): update shipping methods upon cart ops (#10382)
* feat(core-flows,framework,medusa): list shipping options pass in cart as pricing context * chore: add test for shipping options returning free shipping * feat(core-flows, types): update shipping methods upon cart ops * chore: fix specs * chore: fix bugs + specs * Update update-shipping-methods.ts Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> * Update mutations.ts Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> * chore: undo refresh changes * chore: merge with latest * chore: address PR comments * chore: fix conflicts * chore: fix specs * chore: address reviews --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
1 parent
f95c4e240c
commit
9e797dc3d2
21 files changed
+1057
-1132
No files matched your search
@@ -302,7 +302,107 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /store/carts/:id/line-items", () => {
|
describe("POST /store/carts/:id/line-items", () => {
|
||||||
|
let shippingOption
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const stockLocation = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations`,
|
||||||
|
{ name: "test location" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.stock_location
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/sales-channels`,
|
||||||
|
{ add: [salesChannel.id] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingProfile = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-profiles`,
|
||||||
|
{ name: `test-${stockLocation.id}`, type: "default" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_profile
|
||||||
|
|
||||||
|
const fulfillmentSets = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-sets?fields=*fulfillment_sets`,
|
||||||
|
{
|
||||||
|
name: `Test-${shippingProfile.id}`,
|
||||||
|
type: "test-type",
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.stock_location.fulfillment_sets
|
||||||
|
|
||||||
|
const fulfillmentSet = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/fulfillment-sets/${fulfillmentSets[0].id}/service-zones`,
|
||||||
|
{
|
||||||
|
name: `Test-${shippingProfile.id}`,
|
||||||
|
geo_zones: [
|
||||||
|
{ type: "country", country_code: "it" },
|
||||||
|
{ type: "country", country_code: "us" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.fulfillment_set
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
|
||||||
|
{ add: ["manual_test-provider"] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
shippingOption = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
{
|
||||||
|
name: `Shipping`,
|
||||||
|
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 },
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 0,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "item_total",
|
||||||
|
operator: "gt",
|
||||||
|
value: 5000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "enabled_in_store",
|
||||||
|
value: '"true"',
|
||||||
|
operator: "eq",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute: "is_return",
|
||||||
|
value: "false",
|
||||||
|
operator: "eq",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_option
|
||||||
|
|
||||||
cart = (
|
cart = (
|
||||||
await api.post(
|
await api.post(
|
||||||
`/store/carts`,
|
`/store/carts`,
|
||||||
@@ -362,6 +462,101 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("with custom shipping options prices", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
cart = (
|
||||||
|
await api.post(
|
||||||
|
`/store/carts`,
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
region_id: region.id,
|
||||||
|
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
|
||||||
|
promo_codes: [promotion.code],
|
||||||
|
},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
).data.cart
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update shipping method amount when cart totals change", async () => {
|
||||||
|
let response = await api.post(
|
||||||
|
`/store/carts/${cart.id}/shipping-methods`,
|
||||||
|
{ option_id: shippingOption.id },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
shipping_methods: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
amount: 1000,
|
||||||
|
is_tax_inclusive: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await api.post(
|
||||||
|
`/store/carts/${cart.id}/line-items`,
|
||||||
|
{
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
quantity: 100,
|
||||||
|
},
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
shipping_methods: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
amount: 0,
|
||||||
|
is_tax_inclusive: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should remove shipping methods when they are no longer valid for the cart", async () => {
|
||||||
|
let response = await api.post(
|
||||||
|
`/store/carts/${cart.id}/shipping-methods`,
|
||||||
|
{ option_id: shippingOption.id },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
shipping_methods: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
amount: 1000,
|
||||||
|
is_tax_inclusive: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await api.post(
|
||||||
|
`/store/carts/${cart.id}`,
|
||||||
|
{ region_id: noAutomaticRegion.id },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
shipping_methods: expect.arrayContaining([]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it("should add item to cart with tax lines multiple times", async () => {
|
it("should add item to cart with tax lines multiple times", async () => {
|
||||||
let response = await api.post(
|
let response = await api.post(
|
||||||
`/store/carts/${cart.id}/line-items`,
|
`/store/carts/${cart.id}/line-items`,
|
||||||
@@ -1529,6 +1724,222 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("POST /store/carts/:id/shipping-methods", () => {
|
||||||
|
let shippingOption
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const stockLocation = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations`,
|
||||||
|
{ name: "test location" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.stock_location
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/sales-channels`,
|
||||||
|
{ add: [salesChannel.id] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingProfile = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-profiles`,
|
||||||
|
{ name: `test-${stockLocation.id}`, type: "default" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_profile
|
||||||
|
|
||||||
|
const fulfillmentSets = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-sets?fields=*fulfillment_sets`,
|
||||||
|
{
|
||||||
|
name: `Test-${shippingProfile.id}`,
|
||||||
|
type: "test-type",
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.stock_location.fulfillment_sets
|
||||||
|
|
||||||
|
const fulfillmentSet = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/fulfillment-sets/${fulfillmentSets[0].id}/service-zones`,
|
||||||
|
{
|
||||||
|
name: `Test-${shippingProfile.id}`,
|
||||||
|
geo_zones: [
|
||||||
|
{ type: "country", country_code: "it" },
|
||||||
|
{ type: "country", country_code: "us" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.fulfillment_set
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
|
||||||
|
{ add: ["manual_test-provider"] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
shippingOption = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
{
|
||||||
|
name: `Test shipping option ${fulfillmentSet.id}`,
|
||||||
|
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 },
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
amount: 500,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "item_total",
|
||||||
|
operator: "gt",
|
||||||
|
value: 3000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "enabled_in_store",
|
||||||
|
value: '"true"',
|
||||||
|
operator: "eq",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute: "is_return",
|
||||||
|
value: "false",
|
||||||
|
operator: "eq",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_option
|
||||||
|
|
||||||
|
cart = (
|
||||||
|
await api.post(
|
||||||
|
`/store/carts?fields=+total`,
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
region_id: region.id,
|
||||||
|
items: [{ variant_id: product.variants[0].id, quantity: 1 }],
|
||||||
|
},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
).data.cart
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should add shipping method to cart", async () => {
|
||||||
|
let response = await api.post(
|
||||||
|
`/store/carts/${cart.id}/shipping-methods`,
|
||||||
|
{ option_id: shippingOption.id },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
shipping_methods: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
amount: 1000,
|
||||||
|
is_tax_inclusive: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// Total is over the amount 3000 to enable the second pricing rule
|
||||||
|
const cart2 = (
|
||||||
|
await api.post(
|
||||||
|
`/store/carts?fields=+total`,
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
region_id: region.id,
|
||||||
|
items: [{ variant_id: product.variants[0].id, quantity: 5 }],
|
||||||
|
},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
).data.cart
|
||||||
|
|
||||||
|
response = await api.post(
|
||||||
|
`/store/carts/${cart2.id}/shipping-methods`,
|
||||||
|
{ option_id: shippingOption.id },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart2.id,
|
||||||
|
shipping_methods: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
amount: 500,
|
||||||
|
is_tax_inclusive: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw when prices are not setup for shipping option", async () => {
|
||||||
|
cart = (
|
||||||
|
await api.post(
|
||||||
|
`/store/carts?fields=+total`,
|
||||||
|
{
|
||||||
|
currency_code: "eur",
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
region_id: region.id,
|
||||||
|
items: [{ variant_id: product.variants[0].id, quantity: 5 }],
|
||||||
|
},
|
||||||
|
storeHeadersWithCustomer
|
||||||
|
)
|
||||||
|
).data.cart
|
||||||
|
|
||||||
|
let { response } = await api
|
||||||
|
.post(
|
||||||
|
`/store/carts/${cart.id}/shipping-methods`,
|
||||||
|
{ option_id: shippingOption.id },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.data).toEqual({
|
||||||
|
type: "invalid_data",
|
||||||
|
message: `Shipping options with IDs ${shippingOption.id} do not have a price`,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw when shipping option id is not found", async () => {
|
||||||
|
let { response } = await api
|
||||||
|
.post(
|
||||||
|
`/store/carts/${cart.id}/shipping-methods`,
|
||||||
|
{ option_id: "does-not-exist" },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(response.status).toEqual(400)
|
||||||
|
expect(response.data).toEqual({
|
||||||
|
type: "invalid_data",
|
||||||
|
message: "Shipping Options are invalid for cart.",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -152,12 +152,12 @@ medusaIntegrationTestRunner({
|
|||||||
amount: 500,
|
amount: 500,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "gte",
|
operator: "gte",
|
||||||
value: 100,
|
value: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "lte",
|
operator: "lte",
|
||||||
value: 200,
|
value: 200,
|
||||||
},
|
},
|
||||||
@@ -220,12 +220,12 @@ medusaIntegrationTestRunner({
|
|||||||
rules_count: 2,
|
rules_count: 2,
|
||||||
price_rules: expect.arrayContaining([
|
price_rules: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "gte",
|
operator: "gte",
|
||||||
value: "100",
|
value: "100",
|
||||||
}),
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "lte",
|
operator: "lte",
|
||||||
value: "200",
|
value: "200",
|
||||||
}),
|
}),
|
||||||
@@ -329,7 +329,7 @@ medusaIntegrationTestRunner({
|
|||||||
amount: 500,
|
amount: 500,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "gt",
|
operator: "gt",
|
||||||
value: 200,
|
value: 200,
|
||||||
},
|
},
|
||||||
@@ -380,7 +380,7 @@ medusaIntegrationTestRunner({
|
|||||||
rules_count: 2,
|
rules_count: 2,
|
||||||
price_rules: expect.arrayContaining([
|
price_rules: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "gt",
|
operator: "gt",
|
||||||
value: "200",
|
value: "200",
|
||||||
}),
|
}),
|
||||||
@@ -460,7 +460,7 @@ medusaIntegrationTestRunner({
|
|||||||
amount: 500,
|
amount: 500,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "not_whitelisted",
|
operator: "not_whitelisted",
|
||||||
value: 100,
|
value: 100,
|
||||||
},
|
},
|
||||||
@@ -498,7 +498,7 @@ medusaIntegrationTestRunner({
|
|||||||
amount: 500,
|
amount: 500,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "gt",
|
operator: "gt",
|
||||||
value: "string",
|
value: "string",
|
||||||
},
|
},
|
||||||
@@ -628,7 +628,7 @@ medusaIntegrationTestRunner({
|
|||||||
amount: 5,
|
amount: 5,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "gt",
|
operator: "gt",
|
||||||
value: 200,
|
value: 200,
|
||||||
},
|
},
|
||||||
@@ -704,7 +704,7 @@ medusaIntegrationTestRunner({
|
|||||||
amount: 5,
|
amount: 5,
|
||||||
price_rules: [
|
price_rules: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
operator: "gt",
|
operator: "gt",
|
||||||
value: "200",
|
value: "200",
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
|
||||||
import {
|
|
||||||
IFulfillmentModuleService,
|
|
||||||
IRegionModuleService,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
import { ContainerRegistrationKeys, Modules } from "@medusajs/utils"
|
|
||||||
import {
|
import {
|
||||||
createAdminUser,
|
createAdminUser,
|
||||||
generatePublishableKey,
|
generatePublishableKey,
|
||||||
@@ -20,9 +15,6 @@ medusaIntegrationTestRunner({
|
|||||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
describe("Store: Shipping Option API", () => {
|
describe("Store: Shipping Option API", () => {
|
||||||
let appContainer
|
let appContainer
|
||||||
let fulfillmentModule: IFulfillmentModuleService
|
|
||||||
let regionService: IRegionModuleService
|
|
||||||
|
|
||||||
let salesChannel
|
let salesChannel
|
||||||
let region
|
let region
|
||||||
let regionTwo
|
let regionTwo
|
||||||
@@ -36,8 +28,6 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
appContainer = getContainer()
|
appContainer = getContainer()
|
||||||
fulfillmentModule = appContainer.resolve(Modules.FULFILLMENT)
|
|
||||||
regionService = appContainer.resolve(Modules.REGION)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -45,31 +35,27 @@ medusaIntegrationTestRunner({
|
|||||||
storeHeaders = generateStoreHeaders({ publishableKey })
|
storeHeaders = generateStoreHeaders({ publishableKey })
|
||||||
|
|
||||||
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
||||||
const remoteLinkService = appContainer.resolve(
|
|
||||||
ContainerRegistrationKeys.REMOTE_LINK
|
|
||||||
)
|
|
||||||
|
|
||||||
region = await regionService.createRegions({
|
|
||||||
name: "Test region",
|
|
||||||
countries: ["US"],
|
|
||||||
currency_code: "usd",
|
|
||||||
})
|
|
||||||
|
|
||||||
regionTwo = await regionService.createRegions({
|
|
||||||
name: "Test region two",
|
|
||||||
countries: ["DK"],
|
|
||||||
currency_code: "dkk",
|
|
||||||
})
|
|
||||||
|
|
||||||
|
region = (
|
||||||
await api.post(
|
await api.post(
|
||||||
"/admin/price-preferences",
|
"/admin/regions",
|
||||||
|
{ name: "US", currency_code: "usd", countries: ["US"] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.region
|
||||||
|
|
||||||
|
regionTwo = (
|
||||||
|
await api.post(
|
||||||
|
"/admin/regions",
|
||||||
{
|
{
|
||||||
attribute: "region_id",
|
name: "Test region two",
|
||||||
value: regionTwo.id,
|
currency_code: "dkk",
|
||||||
|
countries: ["DK"],
|
||||||
is_tax_inclusive: true,
|
is_tax_inclusive: true,
|
||||||
},
|
},
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
).data.region
|
||||||
|
|
||||||
salesChannel = (
|
salesChannel = (
|
||||||
await api.post(
|
await api.post(
|
||||||
@@ -116,22 +102,39 @@ medusaIntegrationTestRunner({
|
|||||||
stockLocation = (
|
stockLocation = (
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/stock-locations`,
|
`/admin/stock-locations`,
|
||||||
{
|
{ name: "test location" },
|
||||||
name: "test location",
|
|
||||||
},
|
|
||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
).data.stock_location
|
).data.stock_location
|
||||||
|
|
||||||
shippingProfile = await fulfillmentModule.createShippingProfiles({
|
await api.post(
|
||||||
name: "Test",
|
`/admin/stock-locations/${stockLocation.id}/sales-channels`,
|
||||||
type: "default",
|
{ add: [salesChannel.id] },
|
||||||
})
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
fulfillmentSet = await fulfillmentModule.createFulfillmentSets({
|
shippingProfile = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-profiles`,
|
||||||
|
{ name: "Test", type: "default" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_profile
|
||||||
|
|
||||||
|
const fulfillmentSets = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-sets?fields=*fulfillment_sets`,
|
||||||
|
{
|
||||||
name: "Test",
|
name: "Test",
|
||||||
type: "test-type",
|
type: "test-type",
|
||||||
service_zones: [
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.stock_location.fulfillment_sets
|
||||||
|
|
||||||
|
fulfillmentSet = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/fulfillment-sets/${fulfillmentSets[0].id}/service-zones`,
|
||||||
{
|
{
|
||||||
name: "Test",
|
name: "Test",
|
||||||
geo_zones: [
|
geo_zones: [
|
||||||
@@ -139,27 +142,9 @@ medusaIntegrationTestRunner({
|
|||||||
{ type: "country", country_code: "dk" },
|
{ type: "country", country_code: "dk" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
adminHeaders
|
||||||
})
|
)
|
||||||
|
).data.fulfillment_set
|
||||||
await remoteLinkService.create([
|
|
||||||
{
|
|
||||||
[Modules.SALES_CHANNEL]: {
|
|
||||||
sales_channel_id: salesChannel.id,
|
|
||||||
},
|
|
||||||
[Modules.STOCK_LOCATION]: {
|
|
||||||
stock_location_id: stockLocation.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[Modules.STOCK_LOCATION]: {
|
|
||||||
stock_location_id: stockLocation.id,
|
|
||||||
},
|
|
||||||
[Modules.FULFILLMENT]: {
|
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
|
||||||
@@ -196,7 +181,7 @@ medusaIntegrationTestRunner({
|
|||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
operator: "gt",
|
operator: "gt",
|
||||||
attribute: "total",
|
attribute: "item_total",
|
||||||
value: 2000,
|
value: 2000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -246,8 +231,11 @@ medusaIntegrationTestRunner({
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: shippingOption.id,
|
id: shippingOption.id,
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
amount: 1100,
|
|
||||||
price_type: "flat",
|
price_type: "flat",
|
||||||
|
amount: 1100,
|
||||||
|
calculated_price: expect.objectContaining({
|
||||||
|
calculated_amount: 1100,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -272,8 +260,12 @@ medusaIntegrationTestRunner({
|
|||||||
id: shippingOption.id,
|
id: shippingOption.id,
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
amount: 500,
|
amount: 500,
|
||||||
price_type: "flat",
|
|
||||||
is_tax_inclusive: true,
|
is_tax_inclusive: true,
|
||||||
|
calculated_price: expect.objectContaining({
|
||||||
|
calculated_amount: 500,
|
||||||
|
is_calculated_price_tax_inclusive: true,
|
||||||
|
}),
|
||||||
|
price_type: "flat",
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -313,6 +305,9 @@ medusaIntegrationTestRunner({
|
|||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
// Free shipping due to cart total being greater than 2000
|
// Free shipping due to cart total being greater than 2000
|
||||||
amount: 0,
|
amount: 0,
|
||||||
|
calculated_price: expect.objectContaining({
|
||||||
|
calculated_amount: 0,
|
||||||
|
}),
|
||||||
price_type: "flat",
|
price_type: "flat",
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,8 +34,11 @@ import {
|
|||||||
import {
|
import {
|
||||||
adminHeaders,
|
adminHeaders,
|
||||||
createAdminUser,
|
createAdminUser,
|
||||||
|
generatePublishableKey,
|
||||||
|
generateStoreHeaders,
|
||||||
} from "../../../../helpers/create-admin-user"
|
} from "../../../../helpers/create-admin-user"
|
||||||
import { seedStorefrontDefaults } from "../../../../helpers/seed-storefront-defaults"
|
import { seedStorefrontDefaults } from "../../../../helpers/seed-storefront-defaults"
|
||||||
|
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
|
||||||
|
|
||||||
jest.setTimeout(200000)
|
jest.setTimeout(200000)
|
||||||
|
|
||||||
@@ -56,9 +59,10 @@ medusaIntegrationTestRunner({
|
|||||||
let stockLocationModule: IStockLocationService
|
let stockLocationModule: IStockLocationService
|
||||||
let inventoryModule: IInventoryService
|
let inventoryModule: IInventoryService
|
||||||
let fulfillmentModule: IFulfillmentModuleService
|
let fulfillmentModule: IFulfillmentModuleService
|
||||||
let remoteLink, remoteQuery
|
let remoteLink, remoteQuery, storeHeaders
|
||||||
|
let salesChannel
|
||||||
let defaultRegion
|
let defaultRegion
|
||||||
|
let customer, storeHeadersWithCustomer
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
appContainer = getContainer()
|
appContainer = getContainer()
|
||||||
@@ -69,9 +73,9 @@ medusaIntegrationTestRunner({
|
|||||||
productModule = appContainer.resolve(Modules.PRODUCT)
|
productModule = appContainer.resolve(Modules.PRODUCT)
|
||||||
pricingModule = appContainer.resolve(Modules.PRICING)
|
pricingModule = appContainer.resolve(Modules.PRICING)
|
||||||
paymentModule = appContainer.resolve(Modules.PAYMENT)
|
paymentModule = appContainer.resolve(Modules.PAYMENT)
|
||||||
|
fulfillmentModule = appContainer.resolve(Modules.FULFILLMENT)
|
||||||
inventoryModule = appContainer.resolve(Modules.INVENTORY)
|
inventoryModule = appContainer.resolve(Modules.INVENTORY)
|
||||||
stockLocationModule = appContainer.resolve(Modules.STOCK_LOCATION)
|
stockLocationModule = appContainer.resolve(Modules.STOCK_LOCATION)
|
||||||
fulfillmentModule = appContainer.resolve(Modules.FULFILLMENT)
|
|
||||||
remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
remoteLink = appContainer.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||||
remoteQuery = appContainer.resolve(
|
remoteQuery = appContainer.resolve(
|
||||||
ContainerRegistrationKeys.REMOTE_QUERY
|
ContainerRegistrationKeys.REMOTE_QUERY
|
||||||
@@ -79,11 +83,35 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
const publishableKey = await generatePublishableKey(appContainer)
|
||||||
|
storeHeaders = generateStoreHeaders({ publishableKey })
|
||||||
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
await createAdminUser(dbConnection, adminHeaders, appContainer)
|
||||||
|
|
||||||
|
const result = await createAuthenticatedCustomer(api, storeHeaders, {
|
||||||
|
first_name: "tony",
|
||||||
|
last_name: "stark",
|
||||||
|
email: "tony@test-industries.com",
|
||||||
|
})
|
||||||
|
|
||||||
|
customer = result.customer
|
||||||
|
storeHeadersWithCustomer = {
|
||||||
|
headers: {
|
||||||
|
...storeHeaders.headers,
|
||||||
|
authorization: `Bearer ${result.jwt}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
const { region } = await seedStorefrontDefaults(appContainer, "dkk")
|
const { region } = await seedStorefrontDefaults(appContainer, "dkk")
|
||||||
|
|
||||||
defaultRegion = region
|
defaultRegion = region
|
||||||
|
|
||||||
|
salesChannel = (
|
||||||
|
await api.post(
|
||||||
|
"/admin/sales-channels",
|
||||||
|
{ name: "test sales channel", description: "channel" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.sales_channel
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("CreateCartWorkflow", () => {
|
describe("CreateCartWorkflow", () => {
|
||||||
@@ -896,123 +924,6 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("updateLineItemInCartWorkflow", () => {
|
describe("updateLineItemInCartWorkflow", () => {
|
||||||
it("should update item in cart", async () => {
|
|
||||||
const salesChannel = await scModuleService.createSalesChannels({
|
|
||||||
name: "Webshop",
|
|
||||||
})
|
|
||||||
|
|
||||||
const location = await stockLocationModule.createStockLocations({
|
|
||||||
name: "Warehouse",
|
|
||||||
})
|
|
||||||
|
|
||||||
const [product] = await productModule.createProducts([
|
|
||||||
{
|
|
||||||
title: "Test product",
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
title: "Test variant",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const inventoryItem = await inventoryModule.createInventoryItems({
|
|
||||||
sku: "inv-1234",
|
|
||||||
})
|
|
||||||
|
|
||||||
await inventoryModule.createInventoryLevels([
|
|
||||||
{
|
|
||||||
inventory_item_id: inventoryItem.id,
|
|
||||||
location_id: location.id,
|
|
||||||
stocked_quantity: 2,
|
|
||||||
reserved_quantity: 0,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const priceSet = await pricingModule.createPriceSets({
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 3000,
|
|
||||||
currency_code: "usd",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
await remoteLink.create([
|
|
||||||
{
|
|
||||||
[Modules.PRODUCT]: {
|
|
||||||
variant_id: product.variants[0].id,
|
|
||||||
},
|
|
||||||
[Modules.PRICING]: {
|
|
||||||
price_set_id: priceSet.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[Modules.SALES_CHANNEL]: {
|
|
||||||
sales_channel_id: salesChannel.id,
|
|
||||||
},
|
|
||||||
[Modules.STOCK_LOCATION]: {
|
|
||||||
stock_location_id: location.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[Modules.PRODUCT]: {
|
|
||||||
variant_id: product.variants[0].id,
|
|
||||||
},
|
|
||||||
[Modules.INVENTORY]: {
|
|
||||||
inventory_item_id: inventoryItem.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
let cart = await cartModuleService.createCarts({
|
|
||||||
currency_code: "usd",
|
|
||||||
sales_channel_id: salesChannel.id,
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
variant_id: product.variants[0].id,
|
|
||||||
quantity: 1,
|
|
||||||
unit_price: 5000,
|
|
||||||
title: "Test item",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
cart = await cartModuleService.retrieveCart(cart.id, {
|
|
||||||
select: ["id", "region_id", "currency_code"],
|
|
||||||
relations: ["items", "items.variant_id", "items.metadata"],
|
|
||||||
})
|
|
||||||
|
|
||||||
const item = cart.items?.[0]!
|
|
||||||
|
|
||||||
const { errors } = await updateLineItemInCartWorkflow(
|
|
||||||
appContainer
|
|
||||||
).run({
|
|
||||||
input: {
|
|
||||||
cart,
|
|
||||||
item,
|
|
||||||
update: {
|
|
||||||
metadata: {
|
|
||||||
foo: "bar",
|
|
||||||
},
|
|
||||||
quantity: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
throwOnError: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updatedItem = await cartModuleService.retrieveLineItem(item.id)
|
|
||||||
|
|
||||||
expect(updatedItem).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: item.id,
|
|
||||||
unit_price: 3000,
|
|
||||||
quantity: 2,
|
|
||||||
title: "Test item",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("compensation", () => {
|
describe("compensation", () => {
|
||||||
it("should revert line item update to original state", async () => {
|
it("should revert line item update to original state", async () => {
|
||||||
expect.assertions(2)
|
expect.assertions(2)
|
||||||
@@ -1544,45 +1455,23 @@ medusaIntegrationTestRunner({
|
|||||||
let shippingProfile
|
let shippingProfile
|
||||||
let fulfillmentSet
|
let fulfillmentSet
|
||||||
let priceSet
|
let priceSet
|
||||||
|
let region
|
||||||
|
let stockLocation
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
cart = await cartModuleService.createCarts({
|
region = (
|
||||||
currency_code: "usd",
|
await api.post(
|
||||||
shipping_address: {
|
"/admin/regions",
|
||||||
country_code: "us",
|
|
||||||
province: "ny",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
shippingProfile = await fulfillmentModule.createShippingProfiles({
|
|
||||||
name: "Test",
|
|
||||||
type: "default",
|
|
||||||
})
|
|
||||||
|
|
||||||
fulfillmentSet = await fulfillmentModule.createFulfillmentSets({
|
|
||||||
name: "Test",
|
|
||||||
type: "test-type",
|
|
||||||
service_zones: [
|
|
||||||
{
|
{
|
||||||
name: "Test",
|
name: "test-region",
|
||||||
geo_zones: [{ type: "country", country_code: "us" }],
|
currency_code: "usd",
|
||||||
|
countries: ["us"],
|
||||||
},
|
},
|
||||||
],
|
adminHeaders
|
||||||
})
|
)
|
||||||
|
).data.region
|
||||||
|
|
||||||
priceSet = await pricingModule.createPriceSets({
|
stockLocation = (
|
||||||
prices: [{ amount: 3000, currency_code: "usd" }],
|
|
||||||
})
|
|
||||||
|
|
||||||
await pricingModule.createPricePreferences({
|
|
||||||
attribute: "currency_code",
|
|
||||||
value: "usd",
|
|
||||||
is_tax_inclusive: true,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should add shipping method to cart", async () => {
|
|
||||||
const stockLocation = (
|
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/stock-locations`,
|
`/admin/stock-locations`,
|
||||||
{ name: "test location" },
|
{ name: "test location" },
|
||||||
@@ -1590,16 +1479,41 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
).data.stock_location
|
).data.stock_location
|
||||||
|
|
||||||
await remoteLink.create([
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/sales-channels`,
|
||||||
|
{ add: [salesChannel.id] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
shippingProfile = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-profiles`,
|
||||||
|
{ name: "Test", type: "default" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_profile
|
||||||
|
|
||||||
|
const fulfillmentSets = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-sets?fields=*fulfillment_sets`,
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
name: "Test",
|
||||||
stock_location_id: stockLocation.id,
|
type: "test-type",
|
||||||
},
|
},
|
||||||
[Modules.FULFILLMENT]: {
|
adminHeaders
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
)
|
||||||
|
).data.stock_location.fulfillment_sets
|
||||||
|
|
||||||
|
fulfillmentSet = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/fulfillment-sets/${fulfillmentSets[0].id}/service-zones`,
|
||||||
|
{
|
||||||
|
name: "Test",
|
||||||
|
geo_zones: [{ type: "country", country_code: "us" }],
|
||||||
},
|
},
|
||||||
},
|
adminHeaders
|
||||||
])
|
)
|
||||||
|
).data.fulfillment_set
|
||||||
|
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
|
||||||
@@ -1607,7 +1521,34 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
const shippingOption = await fulfillmentModule.createShippingOptions({
|
cart = (
|
||||||
|
await api.post(
|
||||||
|
`/store/carts`,
|
||||||
|
{
|
||||||
|
currency_code: "usd",
|
||||||
|
region_id: region.id,
|
||||||
|
sales_channel_id: salesChannel.id,
|
||||||
|
},
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
).data.cart
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
"/admin/price-preferences",
|
||||||
|
{
|
||||||
|
attribute: "currency_code",
|
||||||
|
value: "usd",
|
||||||
|
is_tax_inclusive: true,
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should add shipping method to cart", async () => {
|
||||||
|
const shippingOption = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
{
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
shipping_profile_id: shippingProfile.id,
|
shipping_profile_id: shippingProfile.id,
|
||||||
@@ -1618,6 +1559,7 @@ medusaIntegrationTestRunner({
|
|||||||
description: "Test description",
|
description: "Test description",
|
||||||
code: "test-code",
|
code: "test-code",
|
||||||
},
|
},
|
||||||
|
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
operator: RuleOperator.EQ,
|
operator: RuleOperator.EQ,
|
||||||
@@ -1630,14 +1572,10 @@ medusaIntegrationTestRunner({
|
|||||||
value: "true",
|
value: "true",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
|
||||||
|
|
||||||
await remoteLink.create([
|
|
||||||
{
|
|
||||||
[Modules.FULFILLMENT]: { shipping_option_id: shippingOption.id },
|
|
||||||
[Modules.PRICING]: { price_set_id: priceSet.id },
|
|
||||||
},
|
},
|
||||||
])
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_option
|
||||||
|
|
||||||
await addShippingMethodToCartWorkflow(appContainer).run({
|
await addShippingMethodToCartWorkflow(appContainer).run({
|
||||||
input: {
|
input: {
|
||||||
@@ -1646,9 +1584,8 @@ medusaIntegrationTestRunner({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
cart = await cartModuleService.retrieveCart(cart.id, {
|
cart = (await api.get(`/store/carts/${cart.id}`, storeHeaders)).data
|
||||||
relations: ["shipping_methods"],
|
.cart
|
||||||
})
|
|
||||||
|
|
||||||
expect(cart).toEqual(
|
expect(cart).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -1658,7 +1595,6 @@ medusaIntegrationTestRunner({
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
amount: 3000,
|
amount: 3000,
|
||||||
is_tax_inclusive: true,
|
is_tax_inclusive: true,
|
||||||
name: "Test shipping option",
|
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@@ -1666,7 +1602,10 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should throw error when shipping option is not valid", async () => {
|
it("should throw error when shipping option is not valid", async () => {
|
||||||
const shippingOption = await fulfillmentModule.createShippingOptions({
|
const shippingOption = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
{
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
shipping_profile_id: shippingProfile.id,
|
shipping_profile_id: shippingProfile.id,
|
||||||
@@ -1684,22 +1623,16 @@ medusaIntegrationTestRunner({
|
|||||||
value: "sf",
|
value: "sf",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||||
|
|
||||||
await remoteLink.create([
|
|
||||||
{
|
|
||||||
[Modules.FULFILLMENT]: { shipping_option_id: shippingOption.id },
|
|
||||||
[Modules.PRICING]: { price_set_id: priceSet.id },
|
|
||||||
},
|
},
|
||||||
])
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_option
|
||||||
|
|
||||||
const { errors } = await addShippingMethodToCartWorkflow(
|
const { errors } = await addShippingMethodToCartWorkflow(
|
||||||
appContainer
|
appContainer
|
||||||
).run({
|
).run({
|
||||||
input: {
|
input: { options: [{ id: shippingOption.id }], cart_id: cart.id },
|
||||||
options: [{ id: shippingOption.id }],
|
|
||||||
cart_id: cart.id,
|
|
||||||
},
|
|
||||||
throwOnError: false,
|
throwOnError: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1737,32 +1670,10 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should add shipping method with custom data", async () => {
|
it("should add shipping method with custom data", async () => {
|
||||||
const stockLocation = (
|
const shippingOption = (
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/stock-locations`,
|
`/admin/shipping-options`,
|
||||||
{ 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 api.post(
|
|
||||||
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
|
|
||||||
{ add: ["manual_test-provider"] },
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
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,
|
||||||
shipping_profile_id: shippingProfile.id,
|
shipping_profile_id: shippingProfile.id,
|
||||||
@@ -1785,14 +1696,11 @@ medusaIntegrationTestRunner({
|
|||||||
value: "true",
|
value: "true",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||||
|
|
||||||
await remoteLink.create([
|
|
||||||
{
|
|
||||||
[Modules.FULFILLMENT]: { shipping_option_id: shippingOption.id },
|
|
||||||
[Modules.PRICING]: { price_set_id: priceSet.id },
|
|
||||||
},
|
},
|
||||||
])
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_option
|
||||||
|
|
||||||
await addShippingMethodToCartWorkflow(appContainer).run({
|
await addShippingMethodToCartWorkflow(appContainer).run({
|
||||||
input: {
|
input: {
|
||||||
@@ -1801,33 +1709,23 @@ medusaIntegrationTestRunner({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
cart = await cartModuleService.retrieveCart(cart.id, {
|
cart = (
|
||||||
select: ["id"],
|
await api.get(
|
||||||
relations: ["shipping_methods"],
|
`/store/carts/${cart.id}?fields=+shipping_methods.data`,
|
||||||
})
|
storeHeaders
|
||||||
|
)
|
||||||
|
).data.cart
|
||||||
|
|
||||||
expect(cart).toEqual(
|
expect(cart).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: cart.id,
|
id: cart.id,
|
||||||
shipping_methods: [
|
shipping_methods: [
|
||||||
{
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
|
||||||
cart_id: cart.id,
|
|
||||||
description: null,
|
|
||||||
amount: 3000,
|
amount: 3000,
|
||||||
raw_amount: {
|
|
||||||
value: "3000",
|
|
||||||
precision: 20,
|
|
||||||
},
|
|
||||||
metadata: null,
|
|
||||||
is_tax_inclusive: true,
|
is_tax_inclusive: true,
|
||||||
name: "Test shipping option",
|
|
||||||
data: { test: "test" },
|
data: { test: "test" },
|
||||||
shipping_option_id: shippingOption.id,
|
shipping_option_id: shippingOption.id,
|
||||||
deleted_at: null,
|
}),
|
||||||
updated_at: expect.any(Date),
|
|
||||||
created_at: expect.any(Date),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -1835,58 +1733,103 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("listShippingOptionsForCartWorkflow", () => {
|
describe("listShippingOptionsForCartWorkflow", () => {
|
||||||
|
let cart
|
||||||
|
let shippingProfile
|
||||||
|
let fulfillmentSet
|
||||||
let region
|
let region
|
||||||
|
let stockLocation
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
region = await regionModuleService.createRegions({
|
region = (
|
||||||
name: "US",
|
await api.post(
|
||||||
|
"/admin/regions",
|
||||||
|
{
|
||||||
|
name: "test-region",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
countries: ["us"],
|
||||||
})
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.region
|
||||||
|
|
||||||
it("should list shipping options for cart", async () => {
|
stockLocation = (
|
||||||
const salesChannel = await scModuleService.createSalesChannels({
|
await api.post(
|
||||||
name: "Webshop",
|
`/admin/stock-locations`,
|
||||||
})
|
{ name: "test location" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.stock_location
|
||||||
|
|
||||||
const location = await stockLocationModule.createStockLocations({
|
await api.post(
|
||||||
name: "Europe",
|
`/admin/stock-locations/${stockLocation.id}/sales-channels`,
|
||||||
})
|
{ add: [salesChannel.id] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
let cart = await cartModuleService.createCarts({
|
shippingProfile = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-profiles`,
|
||||||
|
{ name: "Test", type: "default" },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_profile
|
||||||
|
|
||||||
|
const fulfillmentSets = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-sets?fields=*fulfillment_sets`,
|
||||||
|
{
|
||||||
|
name: "Test",
|
||||||
|
type: "test-type",
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.stock_location.fulfillment_sets
|
||||||
|
|
||||||
|
fulfillmentSet = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/fulfillment-sets/${fulfillmentSets[0].id}/service-zones`,
|
||||||
|
{
|
||||||
|
name: "Test",
|
||||||
|
geo_zones: [{ type: "country", country_code: "us" }],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.fulfillment_set
|
||||||
|
|
||||||
|
await api.post(
|
||||||
|
`/admin/stock-locations/${stockLocation.id}/fulfillment-providers`,
|
||||||
|
{ add: ["manual_test-provider"] },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
|
||||||
|
cart = (
|
||||||
|
await api.post(
|
||||||
|
`/store/carts`,
|
||||||
|
{
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
region_id: region.id,
|
region_id: region.id,
|
||||||
sales_channel_id: salesChannel.id,
|
sales_channel_id: salesChannel.id,
|
||||||
shipping_address: {
|
|
||||||
city: "CPH",
|
|
||||||
province: "Sjaelland",
|
|
||||||
country_code: "dk",
|
|
||||||
},
|
},
|
||||||
})
|
storeHeaders
|
||||||
|
)
|
||||||
|
).data.cart
|
||||||
|
|
||||||
const shippingProfile =
|
await api.post(
|
||||||
await fulfillmentModule.createShippingProfiles({
|
"/admin/price-preferences",
|
||||||
name: "Test",
|
|
||||||
type: "default",
|
|
||||||
})
|
|
||||||
|
|
||||||
const fulfillmentSet = await fulfillmentModule.createFulfillmentSets({
|
|
||||||
name: "Test",
|
|
||||||
type: "test-type",
|
|
||||||
service_zones: [
|
|
||||||
{
|
{
|
||||||
name: "Test",
|
attribute: "currency_code",
|
||||||
geo_zones: [
|
value: "usd",
|
||||||
{
|
is_tax_inclusive: true,
|
||||||
type: "country",
|
|
||||||
country_code: "dk",
|
|
||||||
},
|
},
|
||||||
],
|
adminHeaders
|
||||||
},
|
)
|
||||||
],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const shippingOption = await fulfillmentModule.createShippingOptions({
|
it("should list shipping options for cart", async () => {
|
||||||
|
const shippingOption = (
|
||||||
|
await api.post(
|
||||||
|
`/admin/shipping-options`,
|
||||||
|
{
|
||||||
name: "Test shipping option",
|
name: "Test shipping option",
|
||||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
shipping_profile_id: shippingProfile.id,
|
shipping_profile_id: shippingProfile.id,
|
||||||
@@ -1897,61 +1840,39 @@ medusaIntegrationTestRunner({
|
|||||||
description: "Test description",
|
description: "Test description",
|
||||||
code: "test-code",
|
code: "test-code",
|
||||||
},
|
},
|
||||||
})
|
|
||||||
|
|
||||||
const priceSet = await pricingModule.createPriceSets({
|
|
||||||
prices: [
|
prices: [
|
||||||
{
|
{
|
||||||
amount: 3000,
|
amount: 3000,
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
rules: [
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "is_return",
|
||||||
|
value: "false",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
operator: RuleOperator.EQ,
|
||||||
|
attribute: "enabled_in_store",
|
||||||
|
value: "true",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
).data.shipping_option
|
||||||
|
|
||||||
await remoteLink.create([
|
cart = (await api.get(`/store/carts/${cart.id}`, storeHeaders)).data
|
||||||
{
|
.cart
|
||||||
[Modules.SALES_CHANNEL]: {
|
|
||||||
sales_channel_id: salesChannel.id,
|
|
||||||
},
|
|
||||||
[Modules.STOCK_LOCATION]: {
|
|
||||||
stock_location_id: location.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[Modules.STOCK_LOCATION]: {
|
|
||||||
stock_location_id: location.id,
|
|
||||||
},
|
|
||||||
[Modules.FULFILLMENT]: {
|
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[Modules.FULFILLMENT]: {
|
|
||||||
shipping_option_id: shippingOption.id,
|
|
||||||
},
|
|
||||||
[Modules.PRICING]: {
|
|
||||||
price_set_id: priceSet.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
cart = await cartModuleService.retrieveCart(cart.id, {
|
|
||||||
select: ["id"],
|
|
||||||
relations: ["shipping_address"],
|
|
||||||
})
|
|
||||||
|
|
||||||
const { result } = await listShippingOptionsForCartWorkflow(
|
const { result } = await listShippingOptionsForCartWorkflow(
|
||||||
appContainer
|
appContainer
|
||||||
).run({
|
).run({ input: { cart_id: cart.id } })
|
||||||
input: {
|
|
||||||
cart_id: cart.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
amount: 3000,
|
amount: 3000,
|
||||||
name: "Test shipping option",
|
|
||||||
id: shippingOption.id,
|
id: shippingOption.id,
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
@@ -1962,43 +1883,15 @@ medusaIntegrationTestRunner({
|
|||||||
name: "Webshop",
|
name: "Webshop",
|
||||||
})
|
})
|
||||||
|
|
||||||
const location = await stockLocationModule.createStockLocations({
|
|
||||||
name: "Europe",
|
|
||||||
})
|
|
||||||
|
|
||||||
let cart = await cartModuleService.createCarts({
|
let cart = await cartModuleService.createCarts({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
region_id: region.id,
|
region_id: region.id,
|
||||||
sales_channel_id: salesChannel.id,
|
sales_channel_id: salesChannel.id,
|
||||||
shipping_address: {
|
shipping_address: {
|
||||||
city: "CPH",
|
country_code: "us",
|
||||||
province: "Sjaelland",
|
|
||||||
country_code: "dk",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const shippingProfile =
|
|
||||||
await fulfillmentModule.createShippingProfiles({
|
|
||||||
name: "Test",
|
|
||||||
type: "default",
|
|
||||||
})
|
|
||||||
|
|
||||||
const fulfillmentSet = await fulfillmentModule.createFulfillmentSets({
|
|
||||||
name: "Test",
|
|
||||||
type: "test-type",
|
|
||||||
service_zones: [
|
|
||||||
{
|
|
||||||
name: "Test",
|
|
||||||
geo_zones: [
|
|
||||||
{
|
|
||||||
type: "country",
|
|
||||||
country_code: "dk",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const shippingOption = await fulfillmentModule.createShippingOptions([
|
const shippingOption = await fulfillmentModule.createShippingOptions([
|
||||||
{
|
{
|
||||||
name: "Return shipping option",
|
name: "Return shipping option",
|
||||||
@@ -2057,12 +1950,12 @@ medusaIntegrationTestRunner({
|
|||||||
sales_channel_id: salesChannel.id,
|
sales_channel_id: salesChannel.id,
|
||||||
},
|
},
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
stock_location_id: location.id,
|
stock_location_id: stockLocation.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.STOCK_LOCATION]: {
|
||||||
stock_location_id: location.id,
|
stock_location_id: stockLocation.id,
|
||||||
},
|
},
|
||||||
[Modules.FULFILLMENT]: {
|
[Modules.FULFILLMENT]: {
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
@@ -2116,44 +2009,15 @@ medusaIntegrationTestRunner({
|
|||||||
name: "Webshop",
|
name: "Webshop",
|
||||||
})
|
})
|
||||||
|
|
||||||
const location = await stockLocationModule.createStockLocations({
|
await api.post(
|
||||||
name: "Europe",
|
`/store/carts/${cart.id}`,
|
||||||
})
|
{ sales_channel_id: salesChannel.id },
|
||||||
|
storeHeaders
|
||||||
|
)
|
||||||
|
|
||||||
let cart = await cartModuleService.createCarts({
|
await api.post(
|
||||||
currency_code: "usd",
|
`/admin/shipping-options`,
|
||||||
region_id: region.id,
|
|
||||||
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.createFulfillmentSets({
|
|
||||||
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",
|
name: "Test shipping option",
|
||||||
service_zone_id: fulfillmentSet.service_zones[0].id,
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
shipping_profile_id: shippingProfile.id,
|
shipping_profile_id: shippingProfile.id,
|
||||||
@@ -2164,40 +2028,30 @@ medusaIntegrationTestRunner({
|
|||||||
description: "Test description",
|
description: "Test description",
|
||||||
code: "test-code",
|
code: "test-code",
|
||||||
},
|
},
|
||||||
})
|
|
||||||
|
|
||||||
const priceSet = await pricingModule.createPriceSets({
|
|
||||||
prices: [
|
prices: [
|
||||||
{
|
{
|
||||||
amount: 3000,
|
amount: 3000,
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
rules: [
|
||||||
|
|
||||||
await remoteLink.create([
|
|
||||||
{
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
operator: RuleOperator.EQ,
|
||||||
stock_location_id: location.id,
|
attribute: "is_return",
|
||||||
},
|
value: "false",
|
||||||
[Modules.FULFILLMENT]: {
|
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[Modules.FULFILLMENT]: {
|
operator: RuleOperator.EQ,
|
||||||
shipping_option_id: shippingOption.id,
|
attribute: "enabled_in_store",
|
||||||
|
value: "true",
|
||||||
},
|
},
|
||||||
[Modules.PRICING]: {
|
],
|
||||||
price_set_id: priceSet.id,
|
|
||||||
},
|
},
|
||||||
},
|
adminHeaders
|
||||||
])
|
)
|
||||||
|
|
||||||
cart = await cartModuleService.retrieveCart(cart.id, {
|
cart = (await api.get(`/store/carts/${cart.id}`, storeHeaders)).data
|
||||||
select: ["id"],
|
.cart
|
||||||
relations: ["shipping_address"],
|
|
||||||
})
|
|
||||||
|
|
||||||
const { result } = await listShippingOptionsForCartWorkflow(
|
const { result } = await listShippingOptionsForCartWorkflow(
|
||||||
appContainer
|
appContainer
|
||||||
@@ -2207,99 +2061,6 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
expect(result).toEqual([])
|
expect(result).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should throw when shipping options are missing prices", async () => {
|
|
||||||
const salesChannel = await scModuleService.createSalesChannels({
|
|
||||||
name: "Webshop",
|
|
||||||
})
|
|
||||||
|
|
||||||
const location = await stockLocationModule.createStockLocations({
|
|
||||||
name: "Europe",
|
|
||||||
})
|
|
||||||
|
|
||||||
let cart = await cartModuleService.createCarts({
|
|
||||||
currency_code: "usd",
|
|
||||||
region_id: region.id,
|
|
||||||
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.createFulfillmentSets({
|
|
||||||
name: "Test",
|
|
||||||
type: "test-type",
|
|
||||||
service_zones: [
|
|
||||||
{
|
|
||||||
name: "Test",
|
|
||||||
geo_zones: [
|
|
||||||
{
|
|
||||||
type: "country",
|
|
||||||
country_code: "dk",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
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.STOCK_LOCATION]: {
|
|
||||||
stock_location_id: location.id,
|
|
||||||
},
|
|
||||||
[Modules.FULFILLMENT]: {
|
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
cart = await cartModuleService.retrieveCart(cart.id, {
|
|
||||||
select: ["id"],
|
|
||||||
relations: ["shipping_address"],
|
|
||||||
})
|
|
||||||
|
|
||||||
const { errors } = await listShippingOptionsForCartWorkflow(
|
|
||||||
appContainer
|
|
||||||
).run({
|
|
||||||
input: { cart_id: cart.id },
|
|
||||||
throwOnError: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(errors).toEqual([
|
|
||||||
expect.objectContaining({
|
|
||||||
message: `Shipping options with IDs ${shippingOption.id} do not have a price`,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("updateTaxLinesWorkflow", () => {
|
describe("updateTaxLinesWorkflow", () => {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import {
|
|||||||
Modules,
|
Modules,
|
||||||
ProductStatus,
|
ProductStatus,
|
||||||
PromotionType,
|
PromotionType,
|
||||||
RuleOperator,
|
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
createAdminUser,
|
createAdminUser,
|
||||||
@@ -715,221 +714,6 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should add item to cart", async () => {
|
|
||||||
const customer = await customerModule.createCustomers({
|
|
||||||
email: "tony@stark-industries.com",
|
|
||||||
})
|
|
||||||
|
|
||||||
const salesChannel = await scModule.createSalesChannels({
|
|
||||||
name: "Webshop",
|
|
||||||
})
|
|
||||||
|
|
||||||
const [productWithSpecialTax] = await productModule.createProducts([
|
|
||||||
{
|
|
||||||
// This product ID is setup in the tax structure fixture (setupTaxStructure)
|
|
||||||
id: "product_id_1",
|
|
||||||
title: "Test product",
|
|
||||||
variants: [{ title: "Test variant", manage_inventory: false }],
|
|
||||||
} as any,
|
|
||||||
])
|
|
||||||
|
|
||||||
const [productWithDefaultTax] = await productModule.createProducts([
|
|
||||||
{
|
|
||||||
title: "Test product default tax",
|
|
||||||
variants: [
|
|
||||||
{ title: "Test variant default tax", manage_inventory: false },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
await api.post(
|
|
||||||
"/admin/price-preferences",
|
|
||||||
{
|
|
||||||
attribute: "currency_code",
|
|
||||||
value: "usd",
|
|
||||||
is_tax_inclusive: true,
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
const cart = await cartModule.createCarts({
|
|
||||||
currency_code: "usd",
|
|
||||||
customer_id: customer.id,
|
|
||||||
sales_channel_id: salesChannel.id,
|
|
||||||
region_id: region.id,
|
|
||||||
shipping_address: {
|
|
||||||
customer_id: customer.id,
|
|
||||||
address_1: "test address 1",
|
|
||||||
address_2: "test address 2",
|
|
||||||
city: "SF",
|
|
||||||
country_code: "US",
|
|
||||||
province: "CA",
|
|
||||||
postal_code: "94016",
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: "item-1",
|
|
||||||
unit_price: 2000,
|
|
||||||
quantity: 1,
|
|
||||||
title: "Test item",
|
|
||||||
product_id: "prod_mat",
|
|
||||||
} as any,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const appliedPromotion = await promotionModule.createPromotions({
|
|
||||||
code: "PROMOTION_APPLIED",
|
|
||||||
type: PromotionType.STANDARD,
|
|
||||||
application_method: {
|
|
||||||
type: "fixed",
|
|
||||||
target_type: "items",
|
|
||||||
allocation: "across",
|
|
||||||
value: 300,
|
|
||||||
apply_to_quantity: 2,
|
|
||||||
currency_code: "usd",
|
|
||||||
target_rules: [
|
|
||||||
{
|
|
||||||
attribute: "product_id",
|
|
||||||
operator: "in",
|
|
||||||
values: ["prod_mat", productWithSpecialTax.id],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const [lineItemAdjustment] = await cartModule.addLineItemAdjustments([
|
|
||||||
{
|
|
||||||
code: appliedPromotion.code!,
|
|
||||||
amount: 300,
|
|
||||||
item_id: "item-1",
|
|
||||||
promotion_id: appliedPromotion.id,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
const [priceSet, priceSetDefaultTax] =
|
|
||||||
await pricingModule.createPriceSets([
|
|
||||||
{
|
|
||||||
prices: [{ amount: 3000, currency_code: "usd" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
prices: [{ amount: 2000, currency_code: "usd" }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
await remoteLink.create([
|
|
||||||
{
|
|
||||||
[Modules.PRODUCT]: {
|
|
||||||
variant_id: productWithSpecialTax.variants[0].id,
|
|
||||||
},
|
|
||||||
[Modules.PRICING]: { price_set_id: priceSet.id },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[Modules.PRODUCT]: {
|
|
||||||
variant_id: productWithDefaultTax.variants[0].id,
|
|
||||||
},
|
|
||||||
[Modules.PRICING]: { price_set_id: priceSetDefaultTax.id },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[Modules.CART]: { cart_id: cart.id },
|
|
||||||
[Modules.PROMOTION]: { promotion_id: appliedPromotion.id },
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
let response = await api.post(
|
|
||||||
`/store/carts/${cart.id}/line-items`,
|
|
||||||
{
|
|
||||||
variant_id: productWithSpecialTax.variants[0].id,
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
storeHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
|
||||||
|
|
||||||
expect(response.data.cart).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: cart.id,
|
|
||||||
currency_code: "usd",
|
|
||||||
items: expect.arrayContaining([
|
|
||||||
expect.objectContaining({
|
|
||||||
unit_price: 3000,
|
|
||||||
is_tax_inclusive: true,
|
|
||||||
quantity: 1,
|
|
||||||
title: "Test variant",
|
|
||||||
tax_lines: [
|
|
||||||
expect.objectContaining({
|
|
||||||
description: "CA Reduced Rate for Products",
|
|
||||||
code: "CAREDUCE_PROD",
|
|
||||||
rate: 3,
|
|
||||||
provider_id: "system",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
adjustments: [
|
|
||||||
expect.objectContaining({
|
|
||||||
code: "PROMOTION_APPLIED",
|
|
||||||
amount: 177.86561264822134,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
unit_price: 2000,
|
|
||||||
is_tax_inclusive: false,
|
|
||||||
quantity: 1,
|
|
||||||
title: "Test item",
|
|
||||||
tax_lines: [
|
|
||||||
expect.objectContaining({
|
|
||||||
code: "CADEFAULT",
|
|
||||||
description: "CA Default Rate",
|
|
||||||
provider_id: "system",
|
|
||||||
rate: 5,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
adjustments: [
|
|
||||||
expect.objectContaining({
|
|
||||||
id: expect.not.stringContaining(lineItemAdjustment.id),
|
|
||||||
code: "PROMOTION_APPLIED",
|
|
||||||
amount: 122.13438735177866,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
response = await api.post(
|
|
||||||
`/store/carts/${cart.id}/line-items`,
|
|
||||||
{
|
|
||||||
variant_id: productWithDefaultTax.variants[0].id,
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
storeHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(response.data.cart).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: cart.id,
|
|
||||||
currency_code: "usd",
|
|
||||||
items: expect.arrayContaining([
|
|
||||||
expect.objectContaining({
|
|
||||||
unit_price: 2000,
|
|
||||||
is_tax_inclusive: true,
|
|
||||||
quantity: 1,
|
|
||||||
title: "Test variant default tax",
|
|
||||||
tax_lines: [
|
|
||||||
// Uses the california default rate
|
|
||||||
expect.objectContaining({
|
|
||||||
description: "CA Default Rate",
|
|
||||||
code: "CADEFAULT",
|
|
||||||
rate: 5,
|
|
||||||
provider_id: "system",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("adding an existing variant should update or create line item depending on metadata", async () => {
|
it("adding an existing variant should update or create line item depending on metadata", async () => {
|
||||||
const product = (
|
const product = (
|
||||||
await api.post(`/admin/products`, productData, adminHeaders)
|
await api.post(`/admin/products`, productData, adminHeaders)
|
||||||
@@ -1304,102 +1088,6 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /store/carts/:id/shipping-methods", () => {
|
|
||||||
it("should add a shipping methods to a cart", async () => {
|
|
||||||
const cart = await cartModule.createCarts({
|
|
||||||
currency_code: "usd",
|
|
||||||
shipping_address: { country_code: "us" },
|
|
||||||
items: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
const shippingProfile =
|
|
||||||
await fulfillmentModule.createShippingProfiles({
|
|
||||||
name: "Test",
|
|
||||||
type: "default",
|
|
||||||
})
|
|
||||||
|
|
||||||
const fulfillmentSet = await fulfillmentModule.createFulfillmentSets({
|
|
||||||
name: "Test",
|
|
||||||
type: "test-type",
|
|
||||||
service_zones: [
|
|
||||||
{
|
|
||||||
name: "Test",
|
|
||||||
geo_zones: [{ type: "country", country_code: "us" }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
await api.post(
|
|
||||||
"/admin/price-preferences",
|
|
||||||
{
|
|
||||||
attribute: "currency_code",
|
|
||||||
value: "usd",
|
|
||||||
is_tax_inclusive: true,
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSet = await pricingModule.createPriceSets({
|
|
||||||
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: "is_return",
|
|
||||||
value: "false",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
operator: RuleOperator.EQ,
|
|
||||||
attribute: "enabled_in_store",
|
|
||||||
value: "true",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
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 },
|
|
||||||
storeHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
|
||||||
expect(response.data.cart).toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
id: cart.id,
|
|
||||||
shipping_methods: [
|
|
||||||
{
|
|
||||||
shipping_option_id: shippingOption.id,
|
|
||||||
amount: 3000,
|
|
||||||
is_tax_inclusive: true,
|
|
||||||
id: expect.any(String),
|
|
||||||
tax_lines: [],
|
|
||||||
adjustments: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("POST /store/carts/:id/complete", () => {
|
describe("POST /store/carts/:id/complete", () => {
|
||||||
let salesChannel
|
let salesChannel
|
||||||
let product
|
let product
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ export * from "./get-promotion-codes-to-apply"
|
|||||||
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 "./remove-shipping-method-from-cart"
|
export * from "./remove-shipping-method-from-cart"
|
||||||
@@ -27,4 +26,3 @@ export * from "./update-line-items"
|
|||||||
export * from "./validate-cart-payments"
|
export * from "./validate-cart-payments"
|
||||||
export * from "./validate-cart-shipping-options"
|
export * from "./validate-cart-shipping-options"
|
||||||
export * from "./validate-variant-prices"
|
export * from "./validate-variant-prices"
|
||||||
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import {
|
|
||||||
CartDTO,
|
|
||||||
ICartModuleService,
|
|
||||||
IFulfillmentModuleService,
|
|
||||||
} from "@medusajs/framework/types"
|
|
||||||
import { Modules, arrayDifference } from "@medusajs/framework/utils"
|
|
||||||
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
|
||||||
|
|
||||||
export interface RefreshCartShippingMethodsStepInput {
|
|
||||||
cart: CartDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
export const refreshCartShippingMethodsStepId = "refresh-cart-shipping-methods"
|
|
||||||
/**
|
|
||||||
* This step refreshes the shipping methods of a cart.
|
|
||||||
*/
|
|
||||||
export const refreshCartShippingMethodsStep = createStep(
|
|
||||||
refreshCartShippingMethodsStepId,
|
|
||||||
async (data: RefreshCartShippingMethodsStepInput, { container }) => {
|
|
||||||
const { cart } = data
|
|
||||||
const { shipping_methods: shippingMethods = [] } = cart
|
|
||||||
|
|
||||||
if (!shippingMethods?.length) {
|
|
||||||
return new StepResponse(void 0, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
|
||||||
Modules.FULFILLMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const cartModule = container.resolve<ICartModuleService>(Modules.CART)
|
|
||||||
|
|
||||||
const shippingOptionIds: string[] = shippingMethods.map(
|
|
||||||
(sm) => sm.shipping_option_id!
|
|
||||||
)
|
|
||||||
|
|
||||||
const validShippingOptions =
|
|
||||||
await fulfillmentModule.listShippingOptionsForContext(
|
|
||||||
{
|
|
||||||
id: shippingOptionIds,
|
|
||||||
context: { ...cart, is_return: "false", enabled_in_store: "true" },
|
|
||||||
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>(Modules.CART)
|
|
||||||
|
|
||||||
await cartModule.restoreShippingMethods(shippingMethodsToRestore)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@@ -16,6 +16,10 @@ export const removeShippingMethodFromCartStep = createStep(
|
|||||||
async (data: RemoveShippingMethodFromCartStepInput, { container }) => {
|
async (data: RemoveShippingMethodFromCartStepInput, { container }) => {
|
||||||
const cartService = container.resolve<ICartModuleService>(Modules.CART)
|
const cartService = container.resolve<ICartModuleService>(Modules.CART)
|
||||||
|
|
||||||
|
if (!data?.shipping_method_ids?.length) {
|
||||||
|
return new StepResponse(null, [])
|
||||||
|
}
|
||||||
|
|
||||||
const methods = await cartService.softDeleteShippingMethods(
|
const methods = await cartService.softDeleteShippingMethods(
|
||||||
data.shipping_method_ids
|
data.shipping_method_ids
|
||||||
)
|
)
|
||||||
@@ -23,7 +27,7 @@ export const removeShippingMethodFromCartStep = createStep(
|
|||||||
return new StepResponse(methods, data.shipping_method_ids)
|
return new StepResponse(methods, data.shipping_method_ids)
|
||||||
},
|
},
|
||||||
async (ids, { container }) => {
|
async (ids, { container }) => {
|
||||||
if (!ids) {
|
if (!ids?.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import {
|
||||||
|
ICartModuleService,
|
||||||
|
UpdateShippingMethodDTO,
|
||||||
|
} from "@medusajs/framework/types"
|
||||||
|
import {
|
||||||
|
Modules,
|
||||||
|
getSelectsAndRelationsFromObjectArray,
|
||||||
|
} from "@medusajs/framework/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
|
||||||
|
|
||||||
|
export const updateShippingMethodsStepId = "update-shipping-methods-step"
|
||||||
|
/**
|
||||||
|
* This step updates a cart's shipping methods.
|
||||||
|
*/
|
||||||
|
export const updateShippingMethodsStep = createStep(
|
||||||
|
updateShippingMethodsStepId,
|
||||||
|
async (data: UpdateShippingMethodDTO[], { container }) => {
|
||||||
|
if (!data?.length) {
|
||||||
|
return new StepResponse([], [])
|
||||||
|
}
|
||||||
|
|
||||||
|
const cartModule = container.resolve<ICartModuleService>(Modules.CART)
|
||||||
|
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data)
|
||||||
|
|
||||||
|
const dataBeforeUpdate = await cartModule.listShippingMethods(
|
||||||
|
{ id: data.map((d) => d.id!) },
|
||||||
|
{ select: selects, relations }
|
||||||
|
)
|
||||||
|
|
||||||
|
const updatedItems = await cartModule.updateShippingMethods(data)
|
||||||
|
|
||||||
|
return new StepResponse(updatedItems, dataBeforeUpdate)
|
||||||
|
},
|
||||||
|
async (dataBeforeUpdate, { container }) => {
|
||||||
|
if (!dataBeforeUpdate?.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const cartModule: ICartModuleService = container.resolve(Modules.CART)
|
||||||
|
|
||||||
|
await cartModule.updateShippingMethods(dataBeforeUpdate)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { isDefined, MedusaError } from "@medusajs/framework/utils"
|
||||||
|
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
|
||||||
|
|
||||||
|
export const validateCartShippingOptionsStepId =
|
||||||
|
"validate-cart-shipping-options"
|
||||||
|
/**
|
||||||
|
* This step validates shipping options to ensure they have a price.
|
||||||
|
*/
|
||||||
|
export const validateCartShippingOptionsPriceStep = createStep(
|
||||||
|
"validate-cart-shipping-options-price",
|
||||||
|
async (data: { shippingOptions: any[] }, { container }) => {
|
||||||
|
const { shippingOptions = [] } = data
|
||||||
|
const optionsMissingPrices: string[] = []
|
||||||
|
|
||||||
|
for (const shippingOption of shippingOptions) {
|
||||||
|
const { calculated_price, ...options } = shippingOption
|
||||||
|
|
||||||
|
if (
|
||||||
|
shippingOption?.id &&
|
||||||
|
!isDefined(calculated_price?.calculated_amount)
|
||||||
|
) {
|
||||||
|
optionsMissingPrices.push(options.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optionsMissingPrices.length) {
|
||||||
|
const ids = optionsMissingPrices.join(", ")
|
||||||
|
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Shipping options with IDs ${ids} do not have a price`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StepResponse(void 0)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -3,6 +3,7 @@ export const cartFieldsForRefreshSteps = [
|
|||||||
"currency_code",
|
"currency_code",
|
||||||
"quantity",
|
"quantity",
|
||||||
"subtotal",
|
"subtotal",
|
||||||
|
"total",
|
||||||
"item_subtotal",
|
"item_subtotal",
|
||||||
"shipping_subtotal",
|
"shipping_subtotal",
|
||||||
"region_id",
|
"region_id",
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import {
|
|||||||
} from "../steps"
|
} from "../steps"
|
||||||
import { validateCartStep } from "../steps/validate-cart"
|
import { validateCartStep } from "../steps/validate-cart"
|
||||||
import { validateAndReturnShippingMethodsDataStep } from "../steps/validate-shipping-methods-data"
|
import { validateAndReturnShippingMethodsDataStep } from "../steps/validate-shipping-methods-data"
|
||||||
|
import { validateCartShippingOptionsPriceStep } from "../steps/validate-shipping-options-price"
|
||||||
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
||||||
|
import { listShippingOptionsForCartWorkflow } from "./list-shipping-options-for-cart"
|
||||||
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
|
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
|
||||||
import { updateTaxLinesWorkflow } from "./update-tax-lines"
|
import { updateTaxLinesWorkflow } from "./update-tax-lines"
|
||||||
|
|
||||||
@@ -54,30 +56,24 @@ export const addShippingMethodToCartWorkflow = createWorkflow(
|
|||||||
shippingOptionsContext: { is_return: "false", enabled_in_store: "true" },
|
shippingOptionsContext: { is_return: "false", enabled_in_store: "true" },
|
||||||
})
|
})
|
||||||
|
|
||||||
const shippingOptions = useRemoteQueryStep({
|
const shippingOptions = listShippingOptionsForCartWorkflow.runAsStep({
|
||||||
entry_point: "shipping_option",
|
input: {
|
||||||
fields: [
|
option_ids: optionIds,
|
||||||
"id",
|
cart_id: cart.id,
|
||||||
"name",
|
is_return: false,
|
||||||
"calculated_price.calculated_amount",
|
|
||||||
"calculated_price.is_calculated_price_tax_inclusive",
|
|
||||||
"provider_id",
|
|
||||||
],
|
|
||||||
variables: {
|
|
||||||
id: optionIds,
|
|
||||||
calculated_price: {
|
|
||||||
context: { currency_code: cart.currency_code },
|
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
}).config({ name: "fetch-shipping-option" })
|
|
||||||
|
validateCartShippingOptionsPriceStep({ shippingOptions })
|
||||||
|
|
||||||
const validateShippingMethodsDataInput = transform(
|
const validateShippingMethodsDataInput = transform(
|
||||||
{ input, shippingOptions },
|
{ input, shippingOptions },
|
||||||
(data) => {
|
({ input, shippingOptions }) => {
|
||||||
return data.input.options.map((inputOption) => {
|
return input.options.map((inputOption) => {
|
||||||
const shippingOption = data.shippingOptions.find(
|
const shippingOption = shippingOptions.find(
|
||||||
(so) => so.id === inputOption.id
|
(so) => so.id === inputOption.id
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: inputOption.id,
|
id: inputOption.id,
|
||||||
provider_id: shippingOption?.provider_id,
|
provider_id: shippingOption?.provider_id,
|
||||||
|
|||||||
@@ -8,27 +8,20 @@ import {
|
|||||||
parallelize,
|
parallelize,
|
||||||
transform,
|
transform,
|
||||||
WorkflowData,
|
WorkflowData,
|
||||||
WorkflowResponse,
|
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { emitEventStep } from "../../common/steps/emit-event"
|
import { emitEventStep } from "../../common/steps/emit-event"
|
||||||
import { useRemoteQueryStep } from "../../common/steps/use-remote-query"
|
import { useRemoteQueryStep } from "../../common/steps/use-remote-query"
|
||||||
import {
|
import {
|
||||||
createLineItemsStep,
|
createLineItemsStep,
|
||||||
getLineItemActionsStep,
|
getLineItemActionsStep,
|
||||||
refreshCartShippingMethodsStep,
|
|
||||||
updateLineItemsStep,
|
updateLineItemsStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
import { validateCartStep } from "../steps/validate-cart"
|
import { validateCartStep } from "../steps/validate-cart"
|
||||||
import { validateVariantPricesStep } from "../steps/validate-variant-prices"
|
import { validateVariantPricesStep } from "../steps/validate-variant-prices"
|
||||||
import {
|
import { productVariantsFields } from "../utils/fields"
|
||||||
cartFieldsForRefreshSteps,
|
|
||||||
productVariantsFields,
|
|
||||||
} from "../utils/fields"
|
|
||||||
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
||||||
import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory"
|
import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory"
|
||||||
import { refreshPaymentCollectionForCartWorkflow } from "./refresh-payment-collection"
|
import { refreshCartItemsWorkflow } from "./refresh-cart-items"
|
||||||
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
|
|
||||||
import { updateTaxLinesWorkflow } from "./update-tax-lines"
|
|
||||||
|
|
||||||
export const addToCartWorkflowId = "add-to-cart"
|
export const addToCartWorkflowId = "add-to-cart"
|
||||||
/**
|
/**
|
||||||
@@ -44,6 +37,7 @@ export const addToCartWorkflow = createWorkflow(
|
|||||||
})
|
})
|
||||||
|
|
||||||
// TODO: This is on par with the context used in v1.*, but we can be more flexible.
|
// TODO: This is on par with the context used in v1.*, but we can be more flexible.
|
||||||
|
// TODO: create a common workflow to fetch variants and its prices
|
||||||
const pricingContext = transform({ cart: input.cart }, (data) => {
|
const pricingContext = transform({ cart: input.cart }, (data) => {
|
||||||
return {
|
return {
|
||||||
currency_code: data.cart.currency_code,
|
currency_code: data.cart.currency_code,
|
||||||
@@ -100,7 +94,7 @@ export const addToCartWorkflow = createWorkflow(
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const [createdItems, updatedItems] = parallelize(
|
parallelize(
|
||||||
createLineItemsStep({
|
createLineItemsStep({
|
||||||
id: input.cart.id,
|
id: input.cart.id,
|
||||||
items: itemsToCreate,
|
items: itemsToCreate,
|
||||||
@@ -111,43 +105,13 @@ export const addToCartWorkflow = createWorkflow(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const items = transform({ createdItems, updatedItems }, (data) => {
|
refreshCartItemsWorkflow.runAsStep({
|
||||||
return [...(data.createdItems || []), ...(data.updatedItems || [])]
|
input: { cart_id: input.cart.id },
|
||||||
})
|
})
|
||||||
|
|
||||||
const cart = useRemoteQueryStep({
|
|
||||||
entry_point: "cart",
|
|
||||||
fields: cartFieldsForRefreshSteps,
|
|
||||||
variables: { id: input.cart.id },
|
|
||||||
list: false,
|
|
||||||
}).config({ name: "refetch–cart" })
|
|
||||||
|
|
||||||
parallelize(
|
|
||||||
refreshCartShippingMethodsStep({ cart }),
|
|
||||||
emitEventStep({
|
emitEventStep({
|
||||||
eventName: CartWorkflowEvents.UPDATED,
|
eventName: CartWorkflowEvents.UPDATED,
|
||||||
data: { id: input.cart.id },
|
data: { id: input.cart.id },
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
|
||||||
updateTaxLinesWorkflow.runAsStep({
|
|
||||||
input: {
|
|
||||||
cart_id: input.cart.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
updateCartPromotionsWorkflow.runAsStep({
|
|
||||||
input: {
|
|
||||||
cart_id: input.cart.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
refreshPaymentCollectionForCartWorkflow.runAsStep({
|
|
||||||
input: {
|
|
||||||
cart_id: input.cart.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return new WorkflowResponse(items)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import { deepFlatMap, isPresent, MedusaError } from "@medusajs/framework/utils"
|
import { deepFlatMap } from "@medusajs/framework/utils"
|
||||||
import {
|
import {
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
transform,
|
transform,
|
||||||
when,
|
|
||||||
WorkflowData,
|
WorkflowData,
|
||||||
WorkflowResponse,
|
WorkflowResponse,
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
@@ -16,7 +15,14 @@ export const listShippingOptionsForCartWorkflowId =
|
|||||||
*/
|
*/
|
||||||
export const listShippingOptionsForCartWorkflow = createWorkflow(
|
export const listShippingOptionsForCartWorkflow = createWorkflow(
|
||||||
listShippingOptionsForCartWorkflowId,
|
listShippingOptionsForCartWorkflowId,
|
||||||
(input: WorkflowData<{ cart_id: string; is_return?: boolean }>) => {
|
(
|
||||||
|
input: WorkflowData<{
|
||||||
|
cart_id: string
|
||||||
|
option_ids?: string[]
|
||||||
|
is_return?: boolean
|
||||||
|
enabled_in_store?: boolean
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
const cartQuery = useQueryGraphStep({
|
const cartQuery = useQueryGraphStep({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
filters: { id: input.cart_id },
|
filters: { id: input.cart_id },
|
||||||
@@ -28,6 +34,8 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
|||||||
"shipping_address.city",
|
"shipping_address.city",
|
||||||
"shipping_address.country_code",
|
"shipping_address.country_code",
|
||||||
"shipping_address.province",
|
"shipping_address.province",
|
||||||
|
"shipping_address.postal_code",
|
||||||
|
"item_total",
|
||||||
"total",
|
"total",
|
||||||
],
|
],
|
||||||
options: { throwIfKeyNotFound: true },
|
options: { throwIfKeyNotFound: true },
|
||||||
@@ -70,42 +78,31 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const customerGroupIds = when(
|
const queryVariables = transform(
|
||||||
"get-customer-group",
|
{ input, fulfillmentSetIds, cart },
|
||||||
{ cart },
|
({ input, fulfillmentSetIds, cart }) => ({
|
||||||
({ cart }) => {
|
id: input.option_ids,
|
||||||
return !!cart.id
|
|
||||||
}
|
|
||||||
).then(() => {
|
|
||||||
const customerQuery = useQueryGraphStep({
|
|
||||||
entity: "customer",
|
|
||||||
filters: { id: cart.customer_id },
|
|
||||||
fields: ["groups.id"],
|
|
||||||
}).config({ name: "get-customer" })
|
|
||||||
|
|
||||||
return transform({ customerQuery }, ({ customerQuery }) => {
|
context: {
|
||||||
const customer = customerQuery.data[0]
|
is_return: input.is_return ?? false,
|
||||||
|
enabled_in_store: input.enabled_in_store ?? true,
|
||||||
|
},
|
||||||
|
|
||||||
if (!isPresent(customer)) {
|
filters: {
|
||||||
return []
|
fulfillment_set_id: fulfillmentSetIds,
|
||||||
}
|
|
||||||
|
|
||||||
const { groups = [] } = customer
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
return groups.map((group) => group.id)
|
calculated_price: { context: cart },
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const pricingContext = transform(
|
|
||||||
{ cart, customerGroupIds },
|
|
||||||
({ cart, customerGroupIds }) => ({
|
|
||||||
...cart,
|
|
||||||
customer_group_id: customerGroupIds,
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const isReturn = transform({ input }, ({ input }) => !!input.is_return)
|
|
||||||
|
|
||||||
const shippingOptions = useRemoteQueryStep({
|
const shippingOptions = useRemoteQueryStep({
|
||||||
entry_point: "shipping_options",
|
entry_point: "shipping_options",
|
||||||
fields: [
|
fields: [
|
||||||
@@ -116,7 +113,6 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
|||||||
"shipping_profile_id",
|
"shipping_profile_id",
|
||||||
"provider_id",
|
"provider_id",
|
||||||
"data",
|
"data",
|
||||||
"amount",
|
|
||||||
|
|
||||||
"type.id",
|
"type.id",
|
||||||
"type.label",
|
"type.label",
|
||||||
@@ -132,55 +128,22 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
|||||||
|
|
||||||
"calculated_price.*",
|
"calculated_price.*",
|
||||||
],
|
],
|
||||||
variables: {
|
variables: queryVariables,
|
||||||
context: {
|
|
||||||
is_return: isReturn,
|
|
||||||
enabled_in_store: "true",
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
fulfillment_set_id: fulfillmentSetIds,
|
|
||||||
address: {
|
|
||||||
city: cart.shipping_address?.city,
|
|
||||||
country_code: cart.shipping_address?.country_code,
|
|
||||||
province_code: cart.shipping_address?.province,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
calculated_price: {
|
|
||||||
context: pricingContext,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}).config({ name: "shipping-options-query" })
|
}).config({ name: "shipping-options-query" })
|
||||||
|
|
||||||
const shippingOptionsWithPrice = transform({ shippingOptions }, (data) => {
|
const shippingOptionsWithPrice = transform(
|
||||||
const optionsMissingPrices: string[] = []
|
{ shippingOptions },
|
||||||
|
({ shippingOptions }) =>
|
||||||
const options = data.shippingOptions.map((shippingOption) => {
|
shippingOptions.map((shippingOption) => {
|
||||||
const { calculated_price, ...options } = shippingOption ?? {}
|
const price = shippingOption.calculated_price
|
||||||
|
|
||||||
if (options?.id && !isPresent(calculated_price?.calculated_amount)) {
|
|
||||||
optionsMissingPrices.push(options.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...options,
|
...shippingOption,
|
||||||
amount: calculated_price?.calculated_amount,
|
amount: price?.calculated_amount,
|
||||||
is_tax_inclusive:
|
is_tax_inclusive: !!price?.is_calculated_price_tax_inclusive,
|
||||||
!!calculated_price?.is_calculated_price_tax_inclusive,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (optionsMissingPrices.length) {
|
|
||||||
const ids = optionsMissingPrices.join(", ")
|
|
||||||
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.INVALID_DATA,
|
|
||||||
`Shipping options with IDs ${ids} do not have a price`
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
|
||||||
})
|
|
||||||
|
|
||||||
return new WorkflowResponse(shippingOptionsWithPrice)
|
return new WorkflowResponse(shippingOptionsWithPrice)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ import {
|
|||||||
WorkflowResponse,
|
WorkflowResponse,
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { useRemoteQueryStep } from "../../common/steps/use-remote-query"
|
import { useRemoteQueryStep } from "../../common/steps/use-remote-query"
|
||||||
import { refreshCartShippingMethodsStep, updateLineItemsStep } from "../steps"
|
import { updateLineItemsStep } from "../steps"
|
||||||
import { validateVariantPricesStep } from "../steps/validate-variant-prices"
|
import { validateVariantPricesStep } from "../steps/validate-variant-prices"
|
||||||
import {
|
import {
|
||||||
cartFieldsForRefreshSteps,
|
cartFieldsForRefreshSteps,
|
||||||
productVariantsFields,
|
productVariantsFields,
|
||||||
} from "../utils/fields"
|
} from "../utils/fields"
|
||||||
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
||||||
|
import { refreshCartShippingMethodsWorkflow } from "./refresh-cart-shipping-methods"
|
||||||
import { refreshPaymentCollectionForCartWorkflow } from "./refresh-payment-collection"
|
import { refreshPaymentCollectionForCartWorkflow } from "./refresh-payment-collection"
|
||||||
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
|
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
|
||||||
import { updateTaxLinesWorkflow } from "./update-tax-lines"
|
import { updateTaxLinesWorkflow } from "./update-tax-lines"
|
||||||
@@ -100,7 +101,9 @@ export const refreshCartItemsWorkflow = createWorkflow(
|
|||||||
list: false,
|
list: false,
|
||||||
}).config({ name: "refetch–cart" })
|
}).config({ name: "refetch–cart" })
|
||||||
|
|
||||||
refreshCartShippingMethodsStep({ cart: refetchedCart })
|
refreshCartShippingMethodsWorkflow.runAsStep({
|
||||||
|
input: { cart_id: cart.id },
|
||||||
|
})
|
||||||
|
|
||||||
updateTaxLinesWorkflow.runAsStep({
|
updateTaxLinesWorkflow.runAsStep({
|
||||||
input: { cart_id: cart.id },
|
input: { cart_id: cart.id },
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import { isDefined, isPresent } from "@medusajs/framework/utils"
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
parallelize,
|
||||||
|
transform,
|
||||||
|
when,
|
||||||
|
WorkflowData,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { useQueryGraphStep } from "../../common"
|
||||||
|
import { removeShippingMethodFromCartStep } from "../steps"
|
||||||
|
import { updateShippingMethodsStep } from "../steps/update-shipping-methods"
|
||||||
|
import { listShippingOptionsForCartWorkflow } from "./list-shipping-options-for-cart"
|
||||||
|
|
||||||
|
export const refreshCartShippingMethodsWorkflowId =
|
||||||
|
"refresh-cart-shipping-methods"
|
||||||
|
/**
|
||||||
|
* This workflow refreshes a cart's shipping methods
|
||||||
|
*/
|
||||||
|
export const refreshCartShippingMethodsWorkflow = createWorkflow(
|
||||||
|
refreshCartShippingMethodsWorkflowId,
|
||||||
|
(input: WorkflowData<{ cart_id: string }>) => {
|
||||||
|
const cartQuery = useQueryGraphStep({
|
||||||
|
entity: "cart",
|
||||||
|
filters: { id: input.cart_id },
|
||||||
|
fields: [
|
||||||
|
"id",
|
||||||
|
"sales_channel_id",
|
||||||
|
"currency_code",
|
||||||
|
"region_id",
|
||||||
|
"shipping_methods.*",
|
||||||
|
"shipping_address.city",
|
||||||
|
"shipping_address.country_code",
|
||||||
|
"shipping_address.province",
|
||||||
|
"shipping_methods.shipping_option_id",
|
||||||
|
"total",
|
||||||
|
],
|
||||||
|
options: { throwIfKeyNotFound: true },
|
||||||
|
}).config({ name: "get-cart" })
|
||||||
|
|
||||||
|
const cart = transform({ cartQuery }, ({ cartQuery }) => cartQuery.data[0])
|
||||||
|
const shippingOptionIds: string[] = transform({ cart }, ({ cart }) =>
|
||||||
|
(cart.shipping_methods || [])
|
||||||
|
.map((shippingMethod) => shippingMethod.shipping_option_id)
|
||||||
|
.filter(Boolean)
|
||||||
|
)
|
||||||
|
|
||||||
|
when({ shippingOptionIds }, ({ shippingOptionIds }) => {
|
||||||
|
return !!shippingOptionIds?.length
|
||||||
|
}).then(() => {
|
||||||
|
const shippingOptions = listShippingOptionsForCartWorkflow.runAsStep({
|
||||||
|
input: {
|
||||||
|
option_ids: shippingOptionIds,
|
||||||
|
cart_id: cart.id,
|
||||||
|
is_return: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Creates an object on which shipping methods to remove or update depending
|
||||||
|
// on the validity of the shipping options for the cart
|
||||||
|
const shippingMethodsData = transform(
|
||||||
|
{ cart, shippingOptions },
|
||||||
|
({ cart, shippingOptions }) => {
|
||||||
|
const { shipping_methods: shippingMethods = [] } = cart
|
||||||
|
|
||||||
|
const validShippingMethods = shippingMethods.filter(
|
||||||
|
(shippingMethod) => {
|
||||||
|
// Fetch the available shipping options for the cart context and find the one associated
|
||||||
|
// with the current shipping method
|
||||||
|
const shippingOption = shippingOptions.find(
|
||||||
|
(shippingOption) =>
|
||||||
|
shippingOption.id === shippingMethod.shipping_option_id
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingOptionPrice =
|
||||||
|
shippingOption?.calculated_price?.calculated_amount
|
||||||
|
|
||||||
|
// The shipping method is only valid if both the shipping option and the price is found
|
||||||
|
// for the context of the cart. The invalid options will lead to a deleted shipping method
|
||||||
|
if (isPresent(shippingOption) && isDefined(shippingOptionPrice)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingMethodIds = shippingMethods.map((sm) => sm.id)
|
||||||
|
const validShippingMethodIds = validShippingMethods.map((sm) => sm.id)
|
||||||
|
const invalidShippingMethodIds = shippingMethodIds.filter(
|
||||||
|
(id) => !validShippingMethodIds.includes(id)
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingMethodsToUpdate = validShippingMethods.map(
|
||||||
|
(shippingMethod) => {
|
||||||
|
const shippingOption = shippingOptions.find(
|
||||||
|
(s) => s.id === shippingMethod.shipping_option_id
|
||||||
|
)!
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: shippingMethod.id,
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
amount: shippingOption.calculated_price.calculated_amount,
|
||||||
|
is_tax_inclusive:
|
||||||
|
shippingOption.calculated_price
|
||||||
|
.is_calculated_price_tax_inclusive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
shippingMethodsToRemove: invalidShippingMethodIds,
|
||||||
|
shippingMethodsToUpdate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
parallelize(
|
||||||
|
removeShippingMethodFromCartStep({
|
||||||
|
shipping_method_ids: shippingMethodsData.shippingMethodsToRemove,
|
||||||
|
}),
|
||||||
|
updateShippingMethodsStep(shippingMethodsData.shippingMethodsToUpdate)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,28 +1,16 @@
|
|||||||
import { UpdateLineItemInCartWorkflowInputDTO } from "@medusajs/framework/types"
|
import { UpdateLineItemInCartWorkflowInputDTO } from "@medusajs/framework/types"
|
||||||
import { CartWorkflowEvents } from "@medusajs/framework/utils"
|
|
||||||
import {
|
import {
|
||||||
WorkflowData,
|
WorkflowData,
|
||||||
WorkflowResponse,
|
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
parallelize,
|
|
||||||
transform,
|
transform,
|
||||||
} from "@medusajs/framework/workflows-sdk"
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { emitEventStep } from "../../common/steps/emit-event"
|
|
||||||
import { useRemoteQueryStep } from "../../common/steps/use-remote-query"
|
import { useRemoteQueryStep } from "../../common/steps/use-remote-query"
|
||||||
import { updateLineItemsStepWithSelector } from "../../line-item/steps"
|
import { updateLineItemsStepWithSelector } from "../../line-item/steps"
|
||||||
import { refreshCartShippingMethodsStep } from "../steps"
|
|
||||||
import { validateCartStep } from "../steps/validate-cart"
|
import { validateCartStep } from "../steps/validate-cart"
|
||||||
import { validateVariantPricesStep } from "../steps/validate-variant-prices"
|
import { validateVariantPricesStep } from "../steps/validate-variant-prices"
|
||||||
import {
|
import { productVariantsFields } from "../utils/fields"
|
||||||
cartFieldsForRefreshSteps,
|
|
||||||
productVariantsFields,
|
|
||||||
} from "../utils/fields"
|
|
||||||
import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory"
|
import { confirmVariantInventoryWorkflow } from "./confirm-variant-inventory"
|
||||||
import { refreshPaymentCollectionForCartWorkflow } from "./refresh-payment-collection"
|
import { refreshCartItemsWorkflow } from "./refresh-cart-items"
|
||||||
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
|
|
||||||
|
|
||||||
// TODO: The UpdateLineItemsWorkflow are missing the following steps:
|
|
||||||
// - Validate shipping methods for new items (fulfillment module)
|
|
||||||
|
|
||||||
export const updateLineItemInCartWorkflowId = "update-line-item-in-cart"
|
export const updateLineItemInCartWorkflowId = "update-line-item-in-cart"
|
||||||
/**
|
/**
|
||||||
@@ -89,35 +77,10 @@ export const updateLineItemInCartWorkflow = createWorkflow(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = updateLineItemsStepWithSelector(lineItemUpdate)
|
updateLineItemsStepWithSelector(lineItemUpdate)
|
||||||
|
|
||||||
const cart = useRemoteQueryStep({
|
refreshCartItemsWorkflow.runAsStep({
|
||||||
entry_point: "cart",
|
|
||||||
fields: cartFieldsForRefreshSteps,
|
|
||||||
variables: { id: input.cart.id },
|
|
||||||
list: false,
|
|
||||||
}).config({ name: "refetch–cart" })
|
|
||||||
|
|
||||||
refreshCartShippingMethodsStep({ cart })
|
|
||||||
|
|
||||||
updateCartPromotionsWorkflow.runAsStep({
|
|
||||||
input: {
|
|
||||||
cart_id: input.cart.id,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
parallelize(
|
|
||||||
refreshPaymentCollectionForCartWorkflow.runAsStep({
|
|
||||||
input: { cart_id: input.cart.id },
|
input: { cart_id: input.cart.id },
|
||||||
}),
|
|
||||||
emitEventStep({
|
|
||||||
eventName: CartWorkflowEvents.UPDATED,
|
|
||||||
data: { id: input.cart.id },
|
|
||||||
})
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const updatedItem = transform({ result }, (data) => data.result?.[0])
|
|
||||||
|
|
||||||
return new WorkflowResponse(updatedItem)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -767,6 +767,11 @@ export interface UpdateShippingMethodDTO {
|
|||||||
*/
|
*/
|
||||||
amount?: BigNumberInput
|
amount?: BigNumberInput
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tax inclusivity setting of the shipping method.
|
||||||
|
*/
|
||||||
|
is_tax_inclusive?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data of the shipping method.
|
* The data of the shipping method.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
UpdateLineItemTaxLineDTO,
|
UpdateLineItemTaxLineDTO,
|
||||||
UpdateLineItemWithSelectorDTO,
|
UpdateLineItemWithSelectorDTO,
|
||||||
UpdateShippingMethodAdjustmentDTO,
|
UpdateShippingMethodAdjustmentDTO,
|
||||||
|
UpdateShippingMethodDTO,
|
||||||
UpdateShippingMethodTaxLineDTO,
|
UpdateShippingMethodTaxLineDTO,
|
||||||
UpsertLineItemAdjustmentDTO,
|
UpsertLineItemAdjustmentDTO,
|
||||||
} from "./mutations"
|
} from "./mutations"
|
||||||
@@ -822,6 +823,46 @@ export interface ICartModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<CartShippingMethodDTO[]>
|
): Promise<CartShippingMethodDTO[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method updates existing shipping methods.
|
||||||
|
*
|
||||||
|
* @param {UpdateShippingMethodDTO[]} data - A list of shipping methods to update
|
||||||
|
* @returns {Promise<CartShippingMethodDTO[]>} The updated shipping methods.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const shippingMethods = await cartModuleService.updateShippingMethods([
|
||||||
|
* {
|
||||||
|
* id: "casm_123",
|
||||||
|
* amount: 2,
|
||||||
|
* },
|
||||||
|
* ])
|
||||||
|
*/
|
||||||
|
updateShippingMethods(
|
||||||
|
data: UpdateShippingMethodDTO[]
|
||||||
|
): Promise<CartShippingMethodDTO[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method updates an existing shipping method.
|
||||||
|
*
|
||||||
|
* @param {string} shippingMethodId - The shipping methods's ID.
|
||||||
|
* @param {Partial<UpdateShippingMethodDTO>} data - The attributes to update in the shipping method.
|
||||||
|
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
|
* @returns {Promise<CartShippingMethodDTO>} The updated shipping method.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const lineItem = await cartModuleService.updateShippingMethods(
|
||||||
|
* "casm_123",
|
||||||
|
* {
|
||||||
|
* amount: 3000,
|
||||||
|
* }
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
updateShippingMethods(
|
||||||
|
shippingMethodId: string,
|
||||||
|
data: Partial<UpdateShippingMethodDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<CartShippingMethodDTO>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method retrieves a paginated list of line item adjustments based on optional filters and configuration.
|
* This method retrieves a paginated list of line item adjustments based on optional filters and configuration.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export const AdminCreateShippingOptionTypeObject = z
|
|||||||
|
|
||||||
const AdminPriceRules = z.array(
|
const AdminPriceRules = z.array(
|
||||||
z.object({
|
z.object({
|
||||||
attribute: z.literal("total"),
|
attribute: z.literal("item_total"),
|
||||||
operator: z.nativeEnum(PricingRuleOperator),
|
operator: z.nativeEnum(PricingRuleOperator),
|
||||||
value: z.number(),
|
value: z.number(),
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -8,11 +8,11 @@ import {
|
|||||||
GeoZoneType,
|
GeoZoneType,
|
||||||
Modules,
|
Modules,
|
||||||
} from "@medusajs/framework/utils"
|
} from "@medusajs/framework/utils"
|
||||||
import { FulfillmentProviderService } from "@services"
|
|
||||||
import {
|
import {
|
||||||
MockEventBusService,
|
MockEventBusService,
|
||||||
moduleIntegrationTestRunner,
|
moduleIntegrationTestRunner,
|
||||||
} from "@medusajs/test-utils"
|
} from "@medusajs/test-utils"
|
||||||
|
import { FulfillmentProviderService } from "@services"
|
||||||
import { resolve } from "path"
|
import { resolve } from "path"
|
||||||
import {
|
import {
|
||||||
buildExpectedEventMessageShape,
|
buildExpectedEventMessageShape,
|
||||||
|
|||||||
Reference in new issue
Block a user