fix(core-flows): shipping options for cart (#9343)
FIXES: CC-536 Co-authored-by: Frane Polić <16856471+fPolic@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
74b3385a65
commit
19bc8d7f61
@@ -200,7 +200,7 @@ medusaIntegrationTestRunner({
|
||||
thumbnail: "test-image.png",
|
||||
status: "draft",
|
||||
description: "test-product-description\ntest line 2",
|
||||
options: [
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "size",
|
||||
values: expect.arrayContaining([
|
||||
@@ -220,7 +220,7 @@ medusaIntegrationTestRunner({
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
@@ -243,12 +243,12 @@ medusaIntegrationTestRunner({
|
||||
collection: expect.objectContaining({
|
||||
id: baseCollection.id,
|
||||
}),
|
||||
variants: [
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
@@ -261,21 +261,21 @@ medusaIntegrationTestRunner({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
]),
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "Test variant 2",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 50,
|
||||
@@ -288,17 +288,17 @@ medusaIntegrationTestRunner({
|
||||
currency_code: "usd",
|
||||
amount: 200,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "small",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
@@ -309,7 +309,7 @@ medusaIntegrationTestRunner({
|
||||
thumbnail: "test-image.png",
|
||||
status: "proposed",
|
||||
description: "test-product-description",
|
||||
options: [
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "size",
|
||||
values: expect.arrayContaining([
|
||||
@@ -326,7 +326,7 @@ medusaIntegrationTestRunner({
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
@@ -344,12 +344,12 @@ medusaIntegrationTestRunner({
|
||||
id: baseType.id,
|
||||
}),
|
||||
collection: null,
|
||||
variants: [
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
@@ -362,17 +362,17 @@ medusaIntegrationTestRunner({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
@@ -613,7 +613,7 @@ medusaIntegrationTestRunner({
|
||||
thumbnail: "test-image.png",
|
||||
status: "draft",
|
||||
description: "test-product-description",
|
||||
options: [
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Size",
|
||||
values: expect.arrayContaining([
|
||||
@@ -628,7 +628,7 @@ medusaIntegrationTestRunner({
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
@@ -645,7 +645,7 @@ medusaIntegrationTestRunner({
|
||||
collection: expect.objectContaining({
|
||||
id: baseCollection.id,
|
||||
}),
|
||||
variants: [
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
sku: "test-sku-2",
|
||||
@@ -689,7 +689,7 @@ medusaIntegrationTestRunner({
|
||||
barcode: "test-barcode-4",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
@@ -703,14 +703,14 @@ medusaIntegrationTestRunner({
|
||||
currency_code: "dkk",
|
||||
amount: 30,
|
||||
}),
|
||||
],
|
||||
]),
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "Large",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
@@ -723,7 +723,7 @@ medusaIntegrationTestRunner({
|
||||
status: "draft",
|
||||
description:
|
||||
"Hopper Stripes Bedding, available as duvet cover, pillow sham and sheet.\\n100% organic cotton, soft and crisp to the touch. Made in Portugal.",
|
||||
options: [
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "test-option-1",
|
||||
values: expect.arrayContaining([
|
||||
@@ -740,7 +740,7 @@ medusaIntegrationTestRunner({
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
images: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
url: "test-image.png",
|
||||
@@ -748,14 +748,14 @@ medusaIntegrationTestRunner({
|
||||
]),
|
||||
tags: [],
|
||||
type: null,
|
||||
variants: [
|
||||
variants: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
sku: "test-sku-1-1",
|
||||
barcode: "test-barcode-1-1",
|
||||
allow_backorder: false,
|
||||
manage_inventory: true,
|
||||
prices: [
|
||||
prices: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
rules: {
|
||||
@@ -767,17 +767,17 @@ medusaIntegrationTestRunner({
|
||||
currency_code: "usd",
|
||||
amount: 1.1,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
]),
|
||||
options: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
value: "option 1 value red",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "option 2 value 1",
|
||||
}),
|
||||
],
|
||||
]),
|
||||
}),
|
||||
],
|
||||
]),
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
|
||||
@@ -200,6 +200,7 @@ medusaIntegrationTestRunner({
|
||||
)
|
||||
|
||||
const shippingOptions = resp.data.shipping_options
|
||||
|
||||
expect(shippingOptions).toHaveLength(1)
|
||||
expect(shippingOptions[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MedusaError } from "@medusajs/framework/utils"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
@@ -74,6 +75,13 @@ export const addShippingMethodToWorkflow = createWorkflow(
|
||||
(so) => so.id === option.id
|
||||
)!
|
||||
|
||||
if (!shippingOption?.calculated_price) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Shipping option with ID ${shippingOption.id} do not have a price`
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
shipping_option_id: shippingOption.id,
|
||||
amount: shippingOption.calculated_price.calculated_amount,
|
||||
|
||||
@@ -18,87 +18,101 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
||||
(input: WorkflowData<ListShippingOptionsForCartWorkflowInputDTO>) => {
|
||||
const scLocationFulfillmentSets = useRemoteQueryStep({
|
||||
entry_point: "sales_channels",
|
||||
fields: [
|
||||
"stock_locations.fulfillment_sets.id",
|
||||
"stock_locations.fulfillment_sets.name",
|
||||
"stock_locations.fulfillment_sets.price_type",
|
||||
"stock_locations.fulfillment_sets.service_zone_id",
|
||||
"stock_locations.fulfillment_sets.shipping_profile_id",
|
||||
"stock_locations.fulfillment_sets.provider_id",
|
||||
"stock_locations.fulfillment_sets.data",
|
||||
"stock_locations.fulfillment_sets.amount",
|
||||
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.name",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.price_type",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.service_zone_id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.shipping_profile_id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.provider_id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.data",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.amount",
|
||||
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.type.id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.type.label",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.type.description",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.type.code",
|
||||
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.provider.id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.provider.is_enabled",
|
||||
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price.calculated_amount",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price.is_calculated_price_tax_inclusive",
|
||||
],
|
||||
fields: ["stock_locations.fulfillment_sets.id"],
|
||||
variables: {
|
||||
id: input.sales_channel_id,
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options": {
|
||||
context: {
|
||||
is_return: "false",
|
||||
enabled_in_store: "true",
|
||||
},
|
||||
filters: {
|
||||
address: {
|
||||
city: input.shipping_address?.city,
|
||||
country_code: input.shipping_address?.country_code,
|
||||
province_code: input.shipping_address?.province,
|
||||
},
|
||||
},
|
||||
},
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price":
|
||||
{
|
||||
context: {
|
||||
currency_code: input.currency_code,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}).config({ name: "sales_channels-fulfillment-query" })
|
||||
|
||||
const shippingOptionsWithPrice = transform(
|
||||
const fulfillmentSetIds = transform(
|
||||
{ options: scLocationFulfillmentSets },
|
||||
(data) => {
|
||||
const optionsMissingPrices: string[] = []
|
||||
const fulfillmentSetIds = new Set<string>()
|
||||
|
||||
const options = deepFlatMap(
|
||||
deepFlatMap(
|
||||
data.options,
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price",
|
||||
({ shipping_options }) => {
|
||||
const { calculated_price, ...options } = shipping_options ?? {}
|
||||
|
||||
if (
|
||||
options?.id &&
|
||||
!isPresent(calculated_price?.calculated_amount)
|
||||
) {
|
||||
optionsMissingPrices.push(options.id)
|
||||
}
|
||||
|
||||
return {
|
||||
...options,
|
||||
amount: calculated_price?.calculated_amount,
|
||||
is_tax_inclusive:
|
||||
!!calculated_price?.is_calculated_price_tax_inclusive,
|
||||
"stock_locations.fulfillment_sets",
|
||||
({ fulfillment_sets }) => {
|
||||
if (fulfillment_sets?.id) {
|
||||
fulfillmentSetIds.add(fulfillment_sets.id)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return Array.from(fulfillmentSetIds)
|
||||
}
|
||||
)
|
||||
|
||||
const shippingOptions = useRemoteQueryStep({
|
||||
entry_point: "shipping_options",
|
||||
fields: [
|
||||
"id",
|
||||
"name",
|
||||
"price_type",
|
||||
"service_zone_id",
|
||||
"shipping_profile_id",
|
||||
"provider_id",
|
||||
"data",
|
||||
"amount",
|
||||
|
||||
"type.id",
|
||||
"type.label",
|
||||
"type.description",
|
||||
"type.code",
|
||||
|
||||
"provider.id",
|
||||
"provider.is_enabled",
|
||||
|
||||
"rules.attribute",
|
||||
"rules.value",
|
||||
"rules.operator",
|
||||
|
||||
"calculated_price.*",
|
||||
],
|
||||
variables: {
|
||||
context: {
|
||||
is_return: input.is_return,
|
||||
enabled_in_store: "true",
|
||||
},
|
||||
filters: {
|
||||
fulfillment_set_id: fulfillmentSetIds,
|
||||
address: {
|
||||
city: input.shipping_address?.city,
|
||||
country_code: input.shipping_address?.country_code,
|
||||
province_code: input.shipping_address?.province,
|
||||
},
|
||||
},
|
||||
|
||||
calculated_price: {
|
||||
context: {
|
||||
currency_code: input.currency_code,
|
||||
},
|
||||
},
|
||||
},
|
||||
}).config({ name: "shipping-options-query" })
|
||||
|
||||
const shippingOptionsWithPrice = transform(
|
||||
{
|
||||
shippingOptions,
|
||||
},
|
||||
(data) => {
|
||||
const optionsMissingPrices: string[] = []
|
||||
|
||||
const options = data.shippingOptions.map((shippingOption) => {
|
||||
const { calculated_price, ...options } = shippingOption ?? {}
|
||||
|
||||
if (options?.id && !isPresent(calculated_price?.calculated_amount)) {
|
||||
optionsMissingPrices.push(options.id)
|
||||
}
|
||||
|
||||
return {
|
||||
...options,
|
||||
amount: calculated_price?.calculated_amount,
|
||||
is_tax_inclusive:
|
||||
!!calculated_price?.is_calculated_price_tax_inclusive,
|
||||
}
|
||||
})
|
||||
|
||||
if (optionsMissingPrices.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
|
||||
@@ -104,6 +104,7 @@ export interface CartWorkflowDTO extends CartDTO {
|
||||
|
||||
export interface ListShippingOptionsForCartWorkflowInputDTO {
|
||||
cart_id: string
|
||||
is_return?: boolean
|
||||
sales_channel_id?: string
|
||||
currency_code: string
|
||||
shipping_address: {
|
||||
|
||||
@@ -2,12 +2,15 @@ import { listShippingOptionsForCartWorkflow } from "@medusajs/core-flows"
|
||||
import { HttpTypes, ICartModuleService } from "@medusajs/framework/types"
|
||||
import { MedusaError, Modules } from "@medusajs/framework/utils"
|
||||
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
|
||||
import { StoreGetShippingOptionsType } from "./validators"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest<HttpTypes.StoreGetShippingOptionList>,
|
||||
res: MedusaResponse<HttpTypes.StoreShippingOptionListResponse>
|
||||
) => {
|
||||
const { cart_id } = req.filterableFields as { cart_id: string }
|
||||
const { cart_id, is_return } =
|
||||
req.filterableFields as StoreGetShippingOptionsType
|
||||
|
||||
if (!cart_id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
@@ -34,6 +37,7 @@ export const GET = async (
|
||||
cart_id: cart.id,
|
||||
sales_channel_id: cart.sales_channel_id,
|
||||
currency_code: cart.currency_code,
|
||||
is_return: !!is_return,
|
||||
shipping_address: {
|
||||
city: cart.shipping_address?.city,
|
||||
country_code: cart.shipping_address?.country_code,
|
||||
|
||||
@@ -10,6 +10,7 @@ export const StoreGetShippingOptions = createFindParams({
|
||||
}).merge(
|
||||
z.object({
|
||||
cart_id: z.string(),
|
||||
is_return: z.boolean().optional(),
|
||||
$and: z.lazy(() => StoreGetShippingOptions.array()).optional(),
|
||||
$or: z.lazy(() => StoreGetShippingOptions.array()).optional(),
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
MedusaError,
|
||||
RuleOperator,
|
||||
isObject,
|
||||
isString,
|
||||
MedusaError,
|
||||
pickValueFromObject,
|
||||
RuleOperator,
|
||||
} from "@medusajs/framework/utils"
|
||||
|
||||
/**
|
||||
@@ -81,6 +81,7 @@ export function isContextValid(
|
||||
const predicate = (rule) => {
|
||||
const { attribute, operator, value } = rule
|
||||
const contextValue = pickValueFromObject(attribute, context)
|
||||
|
||||
return operatorsPredicate[operator](
|
||||
contextValue,
|
||||
value as string & string[]
|
||||
|
||||
Reference in New Issue
Block a user