chore: add compare_at_unit_price when price list price is retrieved (#9564)
* chore: add compare_at_unit_price when price list price is retrieved * chore: add test for update item + more fixes along the way * chore: fix tests * chore: add refresh spec * Apply suggestions from code review Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com> * chore: use undefined checker * chore: switch to map --------- Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com>
This commit is contained in:
@@ -48,8 +48,8 @@ export const getLineItemActionsStep = createStep(
|
||||
if (existingItem && metadataMatches) {
|
||||
const quantity = MathBN.sum(
|
||||
existingItem.quantity as number,
|
||||
item.quantity || 1
|
||||
).toNumber()
|
||||
item.quantity ?? 1
|
||||
)
|
||||
|
||||
itemsToUpdate.push({
|
||||
selector: { id: existingItem.id },
|
||||
@@ -57,6 +57,9 @@ export const getLineItemActionsStep = createStep(
|
||||
id: existingItem.id,
|
||||
quantity: quantity,
|
||||
variant_id: item.variant_id!,
|
||||
unit_price: item.unit_price ?? existingItem.unit_price,
|
||||
compare_at_unit_price:
|
||||
item.compare_at_unit_price ?? existingItem.compare_at_unit_price,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
export const cartFieldsForRefreshSteps = [
|
||||
"id",
|
||||
"currency_code",
|
||||
"quantity",
|
||||
"subtotal",
|
||||
"item_subtotal",
|
||||
"shipping_subtotal",
|
||||
"region_id",
|
||||
"currency_code",
|
||||
"metadata",
|
||||
"completed_at",
|
||||
"region.*",
|
||||
"items.*",
|
||||
@@ -20,6 +23,7 @@ export const cartFieldsForRefreshSteps = [
|
||||
"shipping_methods.tax_lines.*",
|
||||
"customer.*",
|
||||
"customer.groups.*",
|
||||
"promotions.code",
|
||||
]
|
||||
|
||||
export const completeCartFields = [
|
||||
@@ -113,8 +117,7 @@ export const productVariantsFields = [
|
||||
"product.collection.title",
|
||||
"product.handle",
|
||||
"product.discountable",
|
||||
"calculated_price.calculated_amount",
|
||||
"calculated_price.is_calculated_price_tax_inclusive",
|
||||
"calculated_price.*",
|
||||
"inventory_items.inventory_item_id",
|
||||
"inventory_items.required_quantity",
|
||||
"inventory_items.inventory.requires_shipping",
|
||||
|
||||
@@ -6,16 +6,24 @@ import {
|
||||
InventoryItemDTO,
|
||||
ProductVariantDTO,
|
||||
} from "@medusajs/framework/types"
|
||||
import { isDefined } from "@medusajs/framework/utils"
|
||||
import { isDefined, MathBN, PriceListType } from "@medusajs/framework/utils"
|
||||
|
||||
interface Input {
|
||||
item?: CartLineItemDTO
|
||||
quantity: BigNumberInput
|
||||
metadata?: Record<string, any>
|
||||
unitPrice: BigNumberInput
|
||||
compareAtUnitPrice?: BigNumberInput | null
|
||||
isTaxInclusive?: boolean
|
||||
variant: ProductVariantDTO & {
|
||||
inventory_items: { inventory: InventoryItemDTO }[]
|
||||
calculated_price: {
|
||||
calculated_price: {
|
||||
price_list_type: string
|
||||
}
|
||||
original_amount: BigNumberInput
|
||||
calculated_amount: BigNumberInput
|
||||
}
|
||||
}
|
||||
taxLines?: CreateOrderLineItemTaxLineDTO[]
|
||||
adjustments?: CreateOrderAdjustmentDTO[]
|
||||
@@ -39,6 +47,20 @@ export function prepareLineItemData(data: Input) {
|
||||
throw new Error("Variant does not have a product")
|
||||
}
|
||||
|
||||
let compareAtUnitPrice = data.compareAtUnitPrice
|
||||
|
||||
if (
|
||||
!isDefined(compareAtUnitPrice) &&
|
||||
variant.calculated_price.calculated_price.price_list_type ===
|
||||
PriceListType.SALE &&
|
||||
!MathBN.eq(
|
||||
variant.calculated_price.original_amount,
|
||||
variant.calculated_price.calculated_amount
|
||||
)
|
||||
) {
|
||||
compareAtUnitPrice = variant.calculated_price.original_amount
|
||||
}
|
||||
|
||||
// Note: If any of the items require shipping, we enable fulfillment
|
||||
// unless explicitly set to not require shipping by the item in the request
|
||||
const { inventory_items: inventoryItems } = variant
|
||||
@@ -78,7 +100,9 @@ export function prepareLineItemData(data: Input) {
|
||||
requires_shipping: requiresShipping,
|
||||
|
||||
unit_price: unitPrice,
|
||||
compare_at_unit_price: compareAtUnitPrice,
|
||||
is_tax_inclusive: !!isTaxInclusive,
|
||||
|
||||
metadata,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
CartWorkflowDTO,
|
||||
UsageComputedActions
|
||||
UsageComputedActions,
|
||||
} from "@medusajs/framework/types"
|
||||
import {
|
||||
Modules,
|
||||
@@ -85,20 +85,19 @@ export const completeCartWorkflow = createWorkflow(
|
||||
})
|
||||
|
||||
const { variants, sales_channel_id } = transform({ cart }, (data) => {
|
||||
const allItems: any[] = []
|
||||
const allVariants: any[] = []
|
||||
const variantsMap: Record<string, any> = {}
|
||||
const allItems = data.cart?.items?.map((item) => {
|
||||
variantsMap[item.variant_id] = item.variant
|
||||
|
||||
data.cart?.items?.forEach((item) => {
|
||||
allItems.push({
|
||||
id: item.id,
|
||||
variant_id: item.variant_id,
|
||||
quantity: item.quantity,
|
||||
return {
|
||||
id: item.id,
|
||||
variant_id: item.variant_id,
|
||||
quantity: item.quantity,
|
||||
}
|
||||
})
|
||||
allVariants.push(item.variant)
|
||||
})
|
||||
|
||||
return {
|
||||
variants: allVariants,
|
||||
variants: Object.values(variantsMap),
|
||||
items: allItems,
|
||||
sales_channel_id: data.cart.sales_channel_id,
|
||||
}
|
||||
@@ -110,6 +109,8 @@ export const completeCartWorkflow = createWorkflow(
|
||||
item,
|
||||
variant: item.variant,
|
||||
unitPrice: item.raw_unit_price ?? item.unit_price,
|
||||
compareAtUnitPrice:
|
||||
item.raw_compare_at_unit_price ?? item.compare_at_unit_price,
|
||||
isTaxInclusive: item.is_tax_inclusive,
|
||||
quantity: item.raw_quantity ?? item.quantity,
|
||||
metadata: item?.metadata,
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import { isDefined, PromotionActions } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../common/steps/use-remote-query"
|
||||
import { refreshCartShippingMethodsStep, updateLineItemsStep } from "../steps"
|
||||
import { validateVariantPricesStep } from "../steps/validate-variant-prices"
|
||||
import {
|
||||
cartFieldsForRefreshSteps,
|
||||
productVariantsFields,
|
||||
} from "../utils/fields"
|
||||
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
||||
import { refreshPaymentCollectionForCartWorkflow } from "./refresh-payment-collection"
|
||||
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
|
||||
import { updateTaxLinesWorkflow } from "./update-tax-lines"
|
||||
|
||||
export const refreshCartItemsWorkflowId = "refresh-cart-items"
|
||||
/**
|
||||
* This workflow refreshes a cart's items
|
||||
*/
|
||||
export const refreshCartItemsWorkflow = createWorkflow(
|
||||
refreshCartItemsWorkflowId,
|
||||
(
|
||||
input: WorkflowData<{
|
||||
cart_id: string
|
||||
promo_codes?: string[]
|
||||
}>
|
||||
) => {
|
||||
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)
|
||||
})
|
||||
|
||||
const pricingContext = transform(
|
||||
{ cart },
|
||||
({ cart: { currency_code, region_id, customer_id } }) => {
|
||||
return {
|
||||
currency_code,
|
||||
region_id,
|
||||
customer_id,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const variants = useRemoteQueryStep({
|
||||
entry_point: "variants",
|
||||
fields: productVariantsFields,
|
||||
variables: {
|
||||
id: variantIds,
|
||||
calculated_price: {
|
||||
context: pricingContext,
|
||||
},
|
||||
},
|
||||
throw_if_key_not_found: true,
|
||||
}).config({ name: "fetch-variants" })
|
||||
|
||||
validateVariantPricesStep({ variants })
|
||||
|
||||
const lineItems = transform({ cart, variants }, ({ cart, variants }) => {
|
||||
const items = cart.items.map((item) => {
|
||||
const variant = variants.find((v) => v.id === item.variant_id)!
|
||||
|
||||
const preparedItem = prepareLineItemData({
|
||||
variant: variant,
|
||||
unitPrice: variant.calculated_price.calculated_amount,
|
||||
isTaxInclusive:
|
||||
variant.calculated_price.is_calculated_price_tax_inclusive,
|
||||
quantity: item.quantity,
|
||||
metadata: item.metadata,
|
||||
cartId: cart.id,
|
||||
})
|
||||
|
||||
return {
|
||||
selector: { id: item.id },
|
||||
data: preparedItem,
|
||||
}
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
const items = updateLineItemsStep({
|
||||
id: cart.id,
|
||||
items: lineItems,
|
||||
})
|
||||
|
||||
const refetchedCart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: cartFieldsForRefreshSteps,
|
||||
variables: { id: cart.id },
|
||||
list: false,
|
||||
}).config({ name: "refetch–cart" })
|
||||
|
||||
refreshCartShippingMethodsStep({ cart: refetchedCart })
|
||||
|
||||
updateTaxLinesWorkflow.runAsStep({
|
||||
input: { cart_id: cart.id, items },
|
||||
})
|
||||
|
||||
const cartPromoCodes = transform({ cart, input }, ({ cart, input }) => {
|
||||
if (isDefined(input.promo_codes)) {
|
||||
return input.promo_codes
|
||||
} else {
|
||||
return cart.promotions.map((p) => p.code)
|
||||
}
|
||||
})
|
||||
|
||||
updateCartPromotionsWorkflow.runAsStep({
|
||||
input: {
|
||||
cart_id: cart.id,
|
||||
promo_codes: cartPromoCodes,
|
||||
action: PromotionActions.REPLACE,
|
||||
},
|
||||
})
|
||||
|
||||
refreshPaymentCollectionForCartWorkflow.runAsStep({
|
||||
input: { cart_id: cart.id },
|
||||
})
|
||||
|
||||
return new WorkflowResponse(refetchedCart)
|
||||
}
|
||||
)
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
AdditionalData,
|
||||
UpdateCartWorkflowInputDTO,
|
||||
} from "@medusajs/framework/types"
|
||||
import { MedusaError, PromotionActions } from "@medusajs/framework/utils"
|
||||
import { MedusaError } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createHook,
|
||||
createWorkflow,
|
||||
@@ -16,13 +16,9 @@ import { useRemoteQueryStep } from "../../common"
|
||||
import {
|
||||
findOrCreateCustomerStep,
|
||||
findSalesChannelStep,
|
||||
refreshCartShippingMethodsStep,
|
||||
updateCartsStep,
|
||||
} from "../steps"
|
||||
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
||||
import { refreshPaymentCollectionForCartWorkflow } from "./refresh-payment-collection"
|
||||
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
|
||||
import { updateTaxLinesWorkflow } from "./update-tax-lines"
|
||||
import { refreshCartItemsWorkflow } from "./refresh-cart-items"
|
||||
|
||||
export const updateCartWorkflowId = "update-cart"
|
||||
/**
|
||||
@@ -133,35 +129,10 @@ export const updateCartWorkflow = createWorkflow(
|
||||
}
|
||||
)
|
||||
|
||||
const carts = updateCartsStep([cartInput])
|
||||
updateCartsStep([cartInput])
|
||||
|
||||
const cart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: cartFieldsForRefreshSteps,
|
||||
variables: { id: cartInput.id },
|
||||
list: false,
|
||||
}).config({ name: "refetch–cart" })
|
||||
|
||||
refreshCartShippingMethodsStep({ cart })
|
||||
|
||||
updateTaxLinesWorkflow.runAsStep({
|
||||
input: {
|
||||
cart_id: carts[0].id,
|
||||
},
|
||||
})
|
||||
|
||||
updateCartPromotionsWorkflow.runAsStep({
|
||||
input: {
|
||||
cart_id: input.id,
|
||||
promo_codes: input.promo_codes,
|
||||
action: PromotionActions.REPLACE,
|
||||
},
|
||||
})
|
||||
|
||||
refreshPaymentCollectionForCartWorkflow.runAsStep({
|
||||
input: {
|
||||
cart_id: input.id,
|
||||
},
|
||||
const cart = refreshCartItemsWorkflow.runAsStep({
|
||||
input: { cart_id: cartInput.id, promo_codes: input.promo_codes },
|
||||
})
|
||||
|
||||
const cartUpdated = createHook("cartUpdated", {
|
||||
|
||||
@@ -13,7 +13,7 @@ export const productVariantsFields = [
|
||||
"product.type.value",
|
||||
"product.collection.title",
|
||||
"product.handle",
|
||||
"calculated_price.calculated_amount",
|
||||
"calculated_price.*",
|
||||
"inventory_items.inventory_item_id",
|
||||
"inventory_items.required_quantity",
|
||||
"inventory_items.inventory.requires_shipping",
|
||||
|
||||
@@ -171,6 +171,10 @@ export const confirmOrderEditRequestWorkflow = createWorkflow(
|
||||
const unitPrice: BigNumberInput =
|
||||
itemAction.raw_unit_price ?? itemAction.unit_price
|
||||
|
||||
const compareAtUnitPrice: BigNumberInput | undefined =
|
||||
itemAction.raw_compare_at_unit_price ??
|
||||
itemAction.compare_at_unit_price
|
||||
|
||||
const updateAction = itemAction.actions!.find(
|
||||
(a) => a.action === ChangeActionType.ITEM_UPDATE
|
||||
)
|
||||
@@ -196,6 +200,7 @@ export const confirmOrderEditRequestWorkflow = createWorkflow(
|
||||
variant_id: ordItem.variant_id,
|
||||
quantity: reservationQuantity,
|
||||
unit_price: unitPrice,
|
||||
compare_at_unit_price: compareAtUnitPrice,
|
||||
})
|
||||
allVariants.push(ordItem.variant)
|
||||
})
|
||||
|
||||
@@ -104,6 +104,9 @@ export const orderEditAddNewItemWorkflow = createWorkflow(
|
||||
reference_id: lineItems[index].id,
|
||||
quantity: item.quantity,
|
||||
unit_price: item.unit_price ?? lineItems[index].unit_price,
|
||||
compare_at_unit_price:
|
||||
item.compare_at_unit_price ??
|
||||
lineItems[index].compare_at_unit_price,
|
||||
metadata: item.metadata,
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -85,6 +85,7 @@ export const orderEditUpdateItemQuantityWorkflow = createWorkflow(
|
||||
reference_id: item.id,
|
||||
quantity: item.quantity,
|
||||
unit_price: item.unit_price,
|
||||
compare_at_unit_price: item.compare_at_unit_price,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -105,6 +105,9 @@ export const updateOrderEditAddItemWorkflow = createWorkflow(
|
||||
details: {
|
||||
quantity: data.quantity ?? originalAction.details?.quantity,
|
||||
unit_price: data.unit_price ?? originalAction.details?.unit_price,
|
||||
compare_at_unit_price:
|
||||
data.compare_at_unit_price ??
|
||||
originalAction.details?.compare_at_unit_price,
|
||||
},
|
||||
internal_note: data.internal_note,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user