chore(core-utils): avoid overfetching to refresh cart (#11602)
What: * Not all Cart operations need a full refresh updating items. This PR introduces a flag to force the refresh for special ocasions, like updating the Cart's region, or transfering the Cart to another customer. For all other flows it will update only promotions, taxes and payment collection if needed.
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
chore(core-flows): avoid overfetching to refresh cart
|
||||||
@@ -2068,7 +2068,7 @@ medusaIntegrationTestRunner({
|
|||||||
unit_price: 4000,
|
unit_price: 4000,
|
||||||
is_custom_price: true,
|
is_custom_price: true,
|
||||||
quantity: 2,
|
quantity: 2,
|
||||||
title: "Test variant",
|
title: "Test item",
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ export const cartFieldsForRefreshSteps = [
|
|||||||
"customer.*",
|
"customer.*",
|
||||||
"customer.groups.*",
|
"customer.groups.*",
|
||||||
"promotions.code",
|
"promotions.code",
|
||||||
|
"payment_collection.id",
|
||||||
|
"payment_collection.raw_amount",
|
||||||
|
"payment_collection.amount",
|
||||||
|
"payment_collection.currency_code",
|
||||||
|
"payment_collection.payment_sessions.id",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const completeCartFields = [
|
export const completeCartFields = [
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
PromotionActions,
|
PromotionActions,
|
||||||
} from "@medusajs/framework/utils"
|
} from "@medusajs/framework/utils"
|
||||||
import {
|
import {
|
||||||
createHook,
|
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
transform,
|
transform,
|
||||||
when,
|
when,
|
||||||
@@ -41,6 +40,10 @@ export type RefreshCartItemsWorkflowInput = {
|
|||||||
* These promotion codes will replace previously applied codes.
|
* These promotion codes will replace previously applied codes.
|
||||||
*/
|
*/
|
||||||
promo_codes?: string[]
|
promo_codes?: string[]
|
||||||
|
/**
|
||||||
|
* Force refresh the cart items
|
||||||
|
*/
|
||||||
|
force_refresh?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const refreshCartItemsWorkflowId = "refresh-cart-items"
|
export const refreshCartItemsWorkflowId = "refresh-cart-items"
|
||||||
@@ -66,32 +69,29 @@ export const refreshCartItemsWorkflowId = "refresh-cart-items"
|
|||||||
*
|
*
|
||||||
* Refresh a cart's details after an update.
|
* Refresh a cart's details after an update.
|
||||||
*
|
*
|
||||||
* @property hooks.validate - This hook is executed before all operations. You can consume this hook to perform any custom validation. If validation fails, you can throw an error to stop the workflow execution.
|
|
||||||
*/
|
*/
|
||||||
export const refreshCartItemsWorkflow = createWorkflow(
|
export const refreshCartItemsWorkflow = createWorkflow(
|
||||||
refreshCartItemsWorkflowId,
|
refreshCartItemsWorkflowId,
|
||||||
(
|
(input: WorkflowData<RefreshCartItemsWorkflowInput>) => {
|
||||||
input: WorkflowData<RefreshCartItemsWorkflowInput>
|
when({ input }, ({ input }) => {
|
||||||
) => {
|
return !!input.force_refresh
|
||||||
const cart = useRemoteQueryStep({
|
|
||||||
entry_point: "cart",
|
|
||||||
fields: cartFieldsForRefreshSteps,
|
|
||||||
variables: { id: input.cart_id },
|
|
||||||
list: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const variantIds = transform({ cart }, (data) => {
|
|
||||||
return (data.cart.items ?? []).map((i) => i.variant_id).filter(Boolean)
|
|
||||||
})
|
|
||||||
|
|
||||||
const cartPricingContext = transform({ cart }, ({ cart }) => {
|
|
||||||
return filterObjectByKeys(cart, cartFieldsForPricingContext)
|
|
||||||
})
|
|
||||||
|
|
||||||
const variants = when({ variantIds }, ({ variantIds }) => {
|
|
||||||
return !!variantIds.length
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
return useRemoteQueryStep({
|
const cart = useRemoteQueryStep({
|
||||||
|
entry_point: "cart",
|
||||||
|
fields: cartFieldsForRefreshSteps,
|
||||||
|
variables: { id: input.cart_id },
|
||||||
|
list: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const variantIds = transform({ cart }, (data) => {
|
||||||
|
return (data.cart.items ?? []).map((i) => i.variant_id).filter(Boolean)
|
||||||
|
})
|
||||||
|
|
||||||
|
const cartPricingContext = transform({ cart }, ({ cart }) => {
|
||||||
|
return filterObjectByKeys(cart, cartFieldsForPricingContext)
|
||||||
|
})
|
||||||
|
|
||||||
|
const variants = useRemoteQueryStep({
|
||||||
entry_point: "variants",
|
entry_point: "variants",
|
||||||
fields: productVariantsFields,
|
fields: productVariantsFields,
|
||||||
variables: {
|
variables: {
|
||||||
@@ -101,62 +101,59 @@ export const refreshCartItemsWorkflow = createWorkflow(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}).config({ name: "fetch-variants" })
|
}).config({ name: "fetch-variants" })
|
||||||
})
|
|
||||||
|
|
||||||
validateVariantPricesStep({ variants })
|
validateVariantPricesStep({ variants })
|
||||||
|
|
||||||
const validate = createHook("validate", {
|
const lineItems = transform({ cart, variants }, ({ cart, variants }) => {
|
||||||
input,
|
const items = cart.items.map((item) => {
|
||||||
cart,
|
const variant = (variants ?? []).find(
|
||||||
})
|
(v) => v.id === item.variant_id
|
||||||
|
)!
|
||||||
|
|
||||||
const lineItems = transform({ cart, variants }, ({ cart, variants }) => {
|
const input: PrepareLineItemDataInput = {
|
||||||
const items = cart.items.map((item) => {
|
item,
|
||||||
const variant = (variants ?? []).find((v) => v.id === item.variant_id)!
|
variant: variant,
|
||||||
|
cartId: cart.id,
|
||||||
|
unitPrice: item.unit_price,
|
||||||
|
isTaxInclusive: item.is_tax_inclusive,
|
||||||
|
}
|
||||||
|
|
||||||
const input: PrepareLineItemDataInput = {
|
if (variant && !item.is_custom_price) {
|
||||||
item,
|
input.unitPrice = variant.calculated_price?.calculated_amount
|
||||||
variant: variant,
|
input.isTaxInclusive =
|
||||||
cartId: cart.id,
|
variant.calculated_price?.is_calculated_price_tax_inclusive
|
||||||
unitPrice: item.unit_price,
|
}
|
||||||
isTaxInclusive: item.is_tax_inclusive,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (variant && !item.is_custom_price) {
|
const preparedItem = prepareLineItemData(input)
|
||||||
input.unitPrice = variant.calculated_price?.calculated_amount
|
|
||||||
input.isTaxInclusive =
|
|
||||||
variant.calculated_price?.is_calculated_price_tax_inclusive
|
|
||||||
}
|
|
||||||
|
|
||||||
const preparedItem = prepareLineItemData(input)
|
return {
|
||||||
|
selector: { id: item.id },
|
||||||
|
data: preparedItem,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return items
|
||||||
selector: { id: item.id },
|
|
||||||
data: preparedItem,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return items
|
updateLineItemsStep({
|
||||||
})
|
id: cart.id,
|
||||||
|
items: lineItems,
|
||||||
updateLineItemsStep({
|
})
|
||||||
id: cart.id,
|
|
||||||
items: lineItems,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const refetchedCart = useRemoteQueryStep({
|
const refetchedCart = useRemoteQueryStep({
|
||||||
entry_point: "cart",
|
entry_point: "cart",
|
||||||
fields: cartFieldsForRefreshSteps,
|
fields: cartFieldsForRefreshSteps,
|
||||||
variables: { id: cart.id },
|
variables: { id: input.cart_id },
|
||||||
list: false,
|
list: false,
|
||||||
}).config({ name: "refetch–cart" })
|
}).config({ name: "refetch–cart" })
|
||||||
|
|
||||||
refreshCartShippingMethodsWorkflow.runAsStep({
|
refreshCartShippingMethodsWorkflow.runAsStep({
|
||||||
input: { cart_id: cart.id },
|
input: { cart_id: input.cart_id },
|
||||||
})
|
})
|
||||||
|
|
||||||
updateTaxLinesWorkflow.runAsStep({
|
updateTaxLinesWorkflow.runAsStep({
|
||||||
input: { cart_id: cart.id },
|
input: { cart_id: input.cart_id },
|
||||||
})
|
})
|
||||||
|
|
||||||
const cartPromoCodes = transform(
|
const cartPromoCodes = transform(
|
||||||
@@ -172,18 +169,16 @@ export const refreshCartItemsWorkflow = createWorkflow(
|
|||||||
|
|
||||||
updateCartPromotionsWorkflow.runAsStep({
|
updateCartPromotionsWorkflow.runAsStep({
|
||||||
input: {
|
input: {
|
||||||
cart_id: cart.id,
|
cart_id: input.cart_id,
|
||||||
promo_codes: cartPromoCodes,
|
promo_codes: cartPromoCodes,
|
||||||
action: PromotionActions.REPLACE,
|
action: PromotionActions.REPLACE,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
refreshPaymentCollectionForCartWorkflow.runAsStep({
|
refreshPaymentCollectionForCartWorkflow.runAsStep({
|
||||||
input: { cart_id: cart.id },
|
input: { cart: refetchedCart },
|
||||||
})
|
})
|
||||||
|
|
||||||
return new WorkflowResponse(refetchedCart, {
|
return new WorkflowResponse(refetchedCart)
|
||||||
hooks: [validate],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ export type RefreshPaymentCollectionForCartWorklowInput = {
|
|||||||
/**
|
/**
|
||||||
* The cart's ID.
|
* The cart's ID.
|
||||||
*/
|
*/
|
||||||
cart_id: string
|
cart_id?: string
|
||||||
|
/**
|
||||||
|
* The Cart reference.
|
||||||
|
*/
|
||||||
|
cart?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export const refreshPaymentCollectionForCartWorkflowId =
|
export const refreshPaymentCollectionForCartWorkflowId =
|
||||||
@@ -52,23 +56,31 @@ export const refreshPaymentCollectionForCartWorkflowId =
|
|||||||
export const refreshPaymentCollectionForCartWorkflow = createWorkflow(
|
export const refreshPaymentCollectionForCartWorkflow = createWorkflow(
|
||||||
refreshPaymentCollectionForCartWorkflowId,
|
refreshPaymentCollectionForCartWorkflowId,
|
||||||
(input: WorkflowData<RefreshPaymentCollectionForCartWorklowInput>) => {
|
(input: WorkflowData<RefreshPaymentCollectionForCartWorklowInput>) => {
|
||||||
const cart = useRemoteQueryStep({
|
const fetchCart = when({ input }, ({ input }) => {
|
||||||
entry_point: "cart",
|
return !input.cart
|
||||||
fields: [
|
}).then(() => {
|
||||||
"id",
|
return useRemoteQueryStep({
|
||||||
"region_id",
|
entry_point: "cart",
|
||||||
"currency_code",
|
fields: [
|
||||||
"total",
|
"id",
|
||||||
"raw_total",
|
"region_id",
|
||||||
"payment_collection.id",
|
"currency_code",
|
||||||
"payment_collection.raw_amount",
|
"total",
|
||||||
"payment_collection.amount",
|
"raw_total",
|
||||||
"payment_collection.currency_code",
|
"payment_collection.id",
|
||||||
"payment_collection.payment_sessions.id",
|
"payment_collection.raw_amount",
|
||||||
],
|
"payment_collection.amount",
|
||||||
variables: { id: input.cart_id },
|
"payment_collection.currency_code",
|
||||||
throw_if_key_not_found: true,
|
"payment_collection.payment_sessions.id",
|
||||||
list: false,
|
],
|
||||||
|
variables: { id: input.cart_id },
|
||||||
|
throw_if_key_not_found: true,
|
||||||
|
list: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const cart = transform({ fetchCart, input }, ({ fetchCart, input }) => {
|
||||||
|
return input.cart ?? fetchCart
|
||||||
})
|
})
|
||||||
|
|
||||||
const validate = createHook("validate", {
|
const validate = createHook("validate", {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export type TransferCartCustomerWorkflowInput = {
|
|||||||
/**
|
/**
|
||||||
* The cart's ID.
|
* The cart's ID.
|
||||||
*/
|
*/
|
||||||
id: string;
|
id: string
|
||||||
/**
|
/**
|
||||||
* The ID of the customer to transfer the cart to.
|
* The ID of the customer to transfer the cart to.
|
||||||
*/
|
*/
|
||||||
@@ -108,7 +108,7 @@ export const transferCartCustomerWorkflow = createWorkflow(
|
|||||||
updateCartsStep(cartInput)
|
updateCartsStep(cartInput)
|
||||||
|
|
||||||
refreshCartItemsWorkflow.runAsStep({
|
refreshCartItemsWorkflow.runAsStep({
|
||||||
input: { cart_id: input.id },
|
input: { cart_id: input.id, force_refresh: true },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import {
|
|||||||
findSalesChannelStep,
|
findSalesChannelStep,
|
||||||
updateCartsStep,
|
updateCartsStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
import { refreshCartItemsWorkflow } from "./refresh-cart-items"
|
|
||||||
import { validateSalesChannelStep } from "../steps/validate-sales-channel"
|
import { validateSalesChannelStep } from "../steps/validate-sales-channel"
|
||||||
|
import { refreshCartItemsWorkflow } from "./refresh-cart-items"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data to update the cart, along with custom data that's passed to the workflow's hooks.
|
* The data to update the cart, along with custom data that's passed to the workflow's hooks.
|
||||||
@@ -278,7 +278,11 @@ export const updateCartWorkflow = createWorkflow(
|
|||||||
})
|
})
|
||||||
|
|
||||||
const cart = refreshCartItemsWorkflow.runAsStep({
|
const cart = refreshCartItemsWorkflow.runAsStep({
|
||||||
input: { cart_id: cartInput.id, promo_codes: input.promo_codes },
|
input: {
|
||||||
|
cart_id: cartInput.id,
|
||||||
|
promo_codes: input.promo_codes,
|
||||||
|
force_refresh: !!newRegion,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const cartUpdated = createHook("cartUpdated", {
|
const cartUpdated = createHook("cartUpdated", {
|
||||||
|
|||||||
Reference in New Issue
Block a user