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:
Carlos R. L. Rodrigues
2024-09-27 09:32:32 -03:00
committed by GitHub
parent 74b3385a65
commit 19bc8d7f61
8 changed files with 136 additions and 106 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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: {

View File

@@ -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,

View File

@@ -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(),
})

View File

@@ -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[]