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:
Riqwan Thamir
2024-10-15 13:05:14 +02:00
committed by GitHub
co-authored by Adrien de Peretti
parent 827b32cffd
commit 537567b679
31 changed files with 2161 additions and 1772 deletions
File diff suppressed because it is too large Load Diff
@@ -369,22 +369,56 @@ medusaIntegrationTestRunner({
expect(result.summary.current_order_total).toEqual(84) expect(result.summary.current_order_total).toEqual(84)
expect(result.summary.original_order_total).toEqual(60) expect(result.summary.original_order_total).toEqual(60)
// Update item quantity // Update item quantity and unit_price with the same amount as we have originally should not change totals
result = ( result = (
await api.post( await api.post(
`/admin/order-edits/${orderId}/items/item/${item.id}`, `/admin/order-edits/${orderId}/items/item/${item.id}`,
{ {
quantity: 4, quantity: 2,
unit_price: 25,
}, },
adminHeaders adminHeaders
) )
).data.order_preview ).data.order_preview
expect(result.summary.current_order_total).toEqual(134) expect(result.summary.current_order_total).toEqual(84)
expect(result.summary.original_order_total).toEqual(60)
// Update item quantity, but keep the price as it was originally, should add + 25 to previous amount
result = (
await api.post(
`/admin/order-edits/${orderId}/items/item/${item.id}`,
{
quantity: 3,
unit_price: 25,
},
adminHeaders
)
).data.order_preview
expect(result.summary.current_order_total).toEqual(109)
expect(result.summary.original_order_total).toEqual(60)
// Update item quantity, with a new price
// 30 * 3 = 90 (new item)
// 12 * 2 = 24 (custom item)
// 10 * 1 = 10 (shipping item)
// total = 124
result = (
await api.post(
`/admin/order-edits/${orderId}/items/item/${item.id}`,
{
quantity: 3,
unit_price: 30,
},
adminHeaders
)
).data.order_preview
expect(result.summary.current_order_total).toEqual(124)
expect(result.summary.original_order_total).toEqual(60) expect(result.summary.original_order_total).toEqual(60)
// Remove the item by setting the quantity to 0 // Remove the item by setting the quantity to 0
result = ( result = (
await api.post( await api.post(
`/admin/order-edits/${orderId}/items/item/${item.id}`, `/admin/order-edits/${orderId}/items/item/${item.id}`,
@@ -439,7 +473,7 @@ medusaIntegrationTestRunner({
) )
).data.order_changes ).data.order_changes
expect(result[0].actions).toHaveLength(3) expect(result[0].actions).toHaveLength(5)
expect(result[0].status).toEqual("confirmed") expect(result[0].status).toEqual("confirmed")
expect(result[0].confirmed_by).toEqual(expect.stringContaining("user_")) expect(result[0].confirmed_by).toEqual(expect.stringContaining("user_"))
}) })
File diff suppressed because it is too large Load Diff
@@ -48,8 +48,8 @@ export const getLineItemActionsStep = createStep(
if (existingItem && metadataMatches) { if (existingItem && metadataMatches) {
const quantity = MathBN.sum( const quantity = MathBN.sum(
existingItem.quantity as number, existingItem.quantity as number,
item.quantity || 1 item.quantity ?? 1
).toNumber() )
itemsToUpdate.push({ itemsToUpdate.push({
selector: { id: existingItem.id }, selector: { id: existingItem.id },
@@ -57,6 +57,9 @@ export const getLineItemActionsStep = createStep(
id: existingItem.id, id: existingItem.id,
quantity: quantity, quantity: quantity,
variant_id: item.variant_id!, 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 { } else {
@@ -1,10 +1,13 @@
export const cartFieldsForRefreshSteps = [ export const cartFieldsForRefreshSteps = [
"id", "id",
"currency_code",
"quantity",
"subtotal", "subtotal",
"item_subtotal", "item_subtotal",
"shipping_subtotal", "shipping_subtotal",
"region_id", "region_id",
"currency_code", "currency_code",
"metadata",
"completed_at", "completed_at",
"region.*", "region.*",
"items.*", "items.*",
@@ -20,6 +23,7 @@ export const cartFieldsForRefreshSteps = [
"shipping_methods.tax_lines.*", "shipping_methods.tax_lines.*",
"customer.*", "customer.*",
"customer.groups.*", "customer.groups.*",
"promotions.code",
] ]
export const completeCartFields = [ export const completeCartFields = [
@@ -113,8 +117,7 @@ export const productVariantsFields = [
"product.collection.title", "product.collection.title",
"product.handle", "product.handle",
"product.discountable", "product.discountable",
"calculated_price.calculated_amount", "calculated_price.*",
"calculated_price.is_calculated_price_tax_inclusive",
"inventory_items.inventory_item_id", "inventory_items.inventory_item_id",
"inventory_items.required_quantity", "inventory_items.required_quantity",
"inventory_items.inventory.requires_shipping", "inventory_items.inventory.requires_shipping",
@@ -6,16 +6,24 @@ import {
InventoryItemDTO, InventoryItemDTO,
ProductVariantDTO, ProductVariantDTO,
} from "@medusajs/framework/types" } from "@medusajs/framework/types"
import { isDefined } from "@medusajs/framework/utils" import { isDefined, MathBN, PriceListType } from "@medusajs/framework/utils"
interface Input { interface Input {
item?: CartLineItemDTO item?: CartLineItemDTO
quantity: BigNumberInput quantity: BigNumberInput
metadata?: Record<string, any> metadata?: Record<string, any>
unitPrice: BigNumberInput unitPrice: BigNumberInput
compareAtUnitPrice?: BigNumberInput | null
isTaxInclusive?: boolean isTaxInclusive?: boolean
variant: ProductVariantDTO & { variant: ProductVariantDTO & {
inventory_items: { inventory: InventoryItemDTO }[] inventory_items: { inventory: InventoryItemDTO }[]
calculated_price: {
calculated_price: {
price_list_type: string
}
original_amount: BigNumberInput
calculated_amount: BigNumberInput
}
} }
taxLines?: CreateOrderLineItemTaxLineDTO[] taxLines?: CreateOrderLineItemTaxLineDTO[]
adjustments?: CreateOrderAdjustmentDTO[] adjustments?: CreateOrderAdjustmentDTO[]
@@ -39,6 +47,20 @@ export function prepareLineItemData(data: Input) {
throw new Error("Variant does not have a product") 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 // Note: If any of the items require shipping, we enable fulfillment
// unless explicitly set to not require shipping by the item in the request // unless explicitly set to not require shipping by the item in the request
const { inventory_items: inventoryItems } = variant const { inventory_items: inventoryItems } = variant
@@ -78,7 +100,9 @@ export function prepareLineItemData(data: Input) {
requires_shipping: requiresShipping, requires_shipping: requiresShipping,
unit_price: unitPrice, unit_price: unitPrice,
compare_at_unit_price: compareAtUnitPrice,
is_tax_inclusive: !!isTaxInclusive, is_tax_inclusive: !!isTaxInclusive,
metadata, metadata,
} }
@@ -1,6 +1,6 @@
import { import {
CartWorkflowDTO, CartWorkflowDTO,
UsageComputedActions UsageComputedActions,
} from "@medusajs/framework/types" } from "@medusajs/framework/types"
import { import {
Modules, Modules,
@@ -85,20 +85,19 @@ export const completeCartWorkflow = createWorkflow(
}) })
const { variants, sales_channel_id } = transform({ cart }, (data) => { const { variants, sales_channel_id } = transform({ cart }, (data) => {
const allItems: any[] = [] const variantsMap: Record<string, any> = {}
const allVariants: any[] = [] const allItems = data.cart?.items?.map((item) => {
variantsMap[item.variant_id] = item.variant
data.cart?.items?.forEach((item) => { return {
allItems.push({ id: item.id,
id: item.id, variant_id: item.variant_id,
variant_id: item.variant_id, quantity: item.quantity,
quantity: item.quantity, }
}) })
allVariants.push(item.variant)
})
return { return {
variants: allVariants, variants: Object.values(variantsMap),
items: allItems, items: allItems,
sales_channel_id: data.cart.sales_channel_id, sales_channel_id: data.cart.sales_channel_id,
} }
@@ -110,6 +109,8 @@ export const completeCartWorkflow = createWorkflow(
item, item,
variant: item.variant, variant: item.variant,
unitPrice: item.raw_unit_price ?? item.unit_price, 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, isTaxInclusive: item.is_tax_inclusive,
quantity: item.raw_quantity ?? item.quantity, quantity: item.raw_quantity ?? item.quantity,
metadata: item?.metadata, 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: "refetchcart" })
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, AdditionalData,
UpdateCartWorkflowInputDTO, UpdateCartWorkflowInputDTO,
} from "@medusajs/framework/types" } from "@medusajs/framework/types"
import { MedusaError, PromotionActions } from "@medusajs/framework/utils" import { MedusaError } from "@medusajs/framework/utils"
import { import {
createHook, createHook,
createWorkflow, createWorkflow,
@@ -16,13 +16,9 @@ import { useRemoteQueryStep } from "../../common"
import { import {
findOrCreateCustomerStep, findOrCreateCustomerStep,
findSalesChannelStep, findSalesChannelStep,
refreshCartShippingMethodsStep,
updateCartsStep, updateCartsStep,
} from "../steps" } from "../steps"
import { cartFieldsForRefreshSteps } from "../utils/fields" import { refreshCartItemsWorkflow } from "./refresh-cart-items"
import { refreshPaymentCollectionForCartWorkflow } from "./refresh-payment-collection"
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
import { updateTaxLinesWorkflow } from "./update-tax-lines"
export const updateCartWorkflowId = "update-cart" export const updateCartWorkflowId = "update-cart"
/** /**
@@ -133,35 +129,10 @@ export const updateCartWorkflow = createWorkflow(
} }
) )
const carts = updateCartsStep([cartInput]) updateCartsStep([cartInput])
const cart = useRemoteQueryStep({ const cart = refreshCartItemsWorkflow.runAsStep({
entry_point: "cart", input: { cart_id: cartInput.id, promo_codes: input.promo_codes },
fields: cartFieldsForRefreshSteps,
variables: { id: cartInput.id },
list: false,
}).config({ name: "refetchcart" })
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 cartUpdated = createHook("cartUpdated", { const cartUpdated = createHook("cartUpdated", {
@@ -13,7 +13,7 @@ export const productVariantsFields = [
"product.type.value", "product.type.value",
"product.collection.title", "product.collection.title",
"product.handle", "product.handle",
"calculated_price.calculated_amount", "calculated_price.*",
"inventory_items.inventory_item_id", "inventory_items.inventory_item_id",
"inventory_items.required_quantity", "inventory_items.required_quantity",
"inventory_items.inventory.requires_shipping", "inventory_items.inventory.requires_shipping",
@@ -171,6 +171,10 @@ export const confirmOrderEditRequestWorkflow = createWorkflow(
const unitPrice: BigNumberInput = const unitPrice: BigNumberInput =
itemAction.raw_unit_price ?? itemAction.unit_price 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( const updateAction = itemAction.actions!.find(
(a) => a.action === ChangeActionType.ITEM_UPDATE (a) => a.action === ChangeActionType.ITEM_UPDATE
) )
@@ -196,6 +200,7 @@ export const confirmOrderEditRequestWorkflow = createWorkflow(
variant_id: ordItem.variant_id, variant_id: ordItem.variant_id,
quantity: reservationQuantity, quantity: reservationQuantity,
unit_price: unitPrice, unit_price: unitPrice,
compare_at_unit_price: compareAtUnitPrice,
}) })
allVariants.push(ordItem.variant) allVariants.push(ordItem.variant)
}) })
@@ -104,6 +104,9 @@ export const orderEditAddNewItemWorkflow = createWorkflow(
reference_id: lineItems[index].id, reference_id: lineItems[index].id,
quantity: item.quantity, quantity: item.quantity,
unit_price: item.unit_price ?? lineItems[index].unit_price, 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, metadata: item.metadata,
}, },
})) }))
@@ -85,6 +85,7 @@ export const orderEditUpdateItemQuantityWorkflow = createWorkflow(
reference_id: item.id, reference_id: item.id,
quantity: item.quantity, quantity: item.quantity,
unit_price: item.unit_price, unit_price: item.unit_price,
compare_at_unit_price: item.compare_at_unit_price,
}, },
})) }))
} }
@@ -105,6 +105,9 @@ export const updateOrderEditAddItemWorkflow = createWorkflow(
details: { details: {
quantity: data.quantity ?? originalAction.details?.quantity, quantity: data.quantity ?? originalAction.details?.quantity,
unit_price: data.unit_price ?? originalAction.details?.unit_price, 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, internal_note: data.internal_note,
} }
@@ -6,6 +6,7 @@ export interface StoreCreateCart {
currency_code?: string currency_code?: string
items?: StoreAddCartLineItem[] items?: StoreAddCartLineItem[]
sales_channel_id?: string sales_channel_id?: string
promo_codes?: string[]
metadata?: Record<string, unknown> metadata?: Record<string, unknown>
} }
@@ -4,7 +4,8 @@ import { BigNumberInput } from "../../totals"
interface NewItem { interface NewItem {
variant_id: string variant_id: string
quantity: BigNumberInput quantity: BigNumberInput
unit_price?: BigNumberInput unit_price?: BigNumberInput | null
compare_at_unit_price?: BigNumberInput | null
internal_note?: string | null internal_note?: string | null
metadata?: Record<string, any> | null metadata?: Record<string, any> | null
} }
@@ -12,7 +13,8 @@ interface NewItem {
interface ExistingItem { interface ExistingItem {
id: string id: string
quantity: BigNumberInput quantity: BigNumberInput
unit_price?: BigNumberInput unit_price?: BigNumberInput | null
compare_at_unit_price?: BigNumberInput | null
internal_note?: string | null internal_note?: string | null
} }
@@ -60,7 +62,8 @@ export interface UpdateOrderEditAddNewItemWorkflowInput {
action_id: string action_id: string
data: { data: {
quantity?: BigNumberInput quantity?: BigNumberInput
unit_price?: BigNumberInput unit_price?: BigNumberInput | null
compare_at_unit_price?: BigNumberInput | null
internal_note?: string | null internal_note?: string | null
} }
} }
@@ -37,8 +37,9 @@ export const AdminPostOrderEditsAddItemsReqSchema = z.object({
z.object({ z.object({
variant_id: z.string(), variant_id: z.string(),
quantity: z.number(), quantity: z.number(),
unit_price: z.number().optional(), unit_price: z.number().nullish(),
internal_note: z.string().optional(), compare_at_unit_price: z.number().nullish(),
internal_note: z.string().nullish(),
allow_backorder: z.boolean().optional(), allow_backorder: z.boolean().optional(),
metadata: z.record(z.unknown()).optional(), metadata: z.record(z.unknown()).optional(),
}) })
@@ -51,7 +52,8 @@ export type AdminPostOrderEditsAddItemsReqSchemaType = z.infer<
export const AdminPostOrderEditsItemsActionReqSchema = z.object({ export const AdminPostOrderEditsItemsActionReqSchema = z.object({
quantity: z.number().optional(), quantity: z.number().optional(),
unit_price: z.number().optional(), unit_price: z.number().nullish(),
compare_at_unit_price: z.number().nullish(),
internal_note: z.string().nullish().optional(), internal_note: z.string().nullish().optional(),
}) })
@@ -61,7 +63,8 @@ export type AdminPostOrderEditsItemsActionReqSchemaType = z.infer<
export const AdminPostOrderEditsUpdateItemQuantityReqSchema = z.object({ export const AdminPostOrderEditsUpdateItemQuantityReqSchema = z.object({
quantity: z.number(), quantity: z.number(),
unit_price: z.number().optional(), unit_price: z.number().nullish(),
compare_at_unit_price: z.number().nullish(),
internal_note: z.string().nullish().optional(), internal_note: z.string().nullish().optional(),
}) })
@@ -57,6 +57,7 @@ export const defaultStoreCartFields = [
"items.title", "items.title",
"items.quantity", "items.quantity",
"items.unit_price", "items.unit_price",
"items.compare_at_unit_price",
"items.is_tax_inclusive", "items.is_tax_inclusive",
"items.tax_lines.id", "items.tax_lines.id",
"items.tax_lines.description", "items.tax_lines.description",
@@ -21,6 +21,7 @@ export const CreateCart = z
currency_code: z.string().nullish(), currency_code: z.string().nullish(),
items: z.array(ItemSchema).optional(), items: z.array(ItemSchema).optional(),
sales_channel_id: z.string().nullish(), sales_channel_id: z.string().nullish(),
promo_codes: z.array(z.string()).optional(),
metadata: z.record(z.unknown()).nullish(), metadata: z.record(z.unknown()).nullish(),
}) })
.strict() .strict()
@@ -45,7 +46,7 @@ export const StoreRemoveCartPromotions = z
export type StoreUpdateCartType = z.infer<typeof UpdateCart> export type StoreUpdateCartType = z.infer<typeof UpdateCart>
export const UpdateCart = z export const UpdateCart = z
.object({ .object({
region_id: z.string().nullish(), region_id: z.string().optional(),
email: z.string().email().nullish(), email: z.string().email().nullish(),
billing_address: z.union([AddressPayload, z.string()]).optional(), billing_address: z.union([AddressPayload, z.string()]).optional(),
shipping_address: z.union([AddressPayload, z.string()]).optional(), shipping_address: z.union([AddressPayload, z.string()]).optional(),
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20241014142943 extends Migration {
async up(): Promise<void> {
this.addSql(
'alter table if exists "order_item" add column if not exists "compare_at_unit_price" numeric null, add column if not exists "raw_compare_at_unit_price" jsonb null;'
)
}
async down(): Promise<void> {
this.addSql(
'alter table if exists "order_item" drop column if exists "compare_at_unit_price";'
)
this.addSql(
'alter table if exists "order_item" drop column if exists "raw_compare_at_unit_price";'
)
}
}
@@ -90,6 +90,12 @@ export default class OrderItem {
@Property({ columnType: "jsonb", nullable: true }) @Property({ columnType: "jsonb", nullable: true })
raw_unit_price: BigNumberRawValue | null = null raw_unit_price: BigNumberRawValue | null = null
@MikroOrmBigNumberProperty({ nullable: true })
compare_at_unit_price: BigNumber | number | null = null
@Property({ columnType: "jsonb", nullable: true })
raw_compare_at_unit_price: BigNumberRawValue | null = null
@MikroOrmBigNumberProperty() @MikroOrmBigNumberProperty()
quantity: BigNumber | number quantity: BigNumber | number
@@ -10,6 +10,7 @@ describe("Order Exchange - Actions", function () {
id: "1", id: "1",
quantity: 1, quantity: 1,
unit_price: 10, unit_price: 10,
compare_at_unit_price: null,
order_id: "1", order_id: "1",
detail: { detail: {
@@ -28,6 +29,7 @@ describe("Order Exchange - Actions", function () {
id: "2", id: "2",
quantity: 2, quantity: 2,
unit_price: 100, unit_price: 100,
compare_at_unit_price: null,
order_id: "1", order_id: "1",
detail: { detail: {
@@ -46,6 +48,7 @@ describe("Order Exchange - Actions", function () {
id: "3", id: "3",
quantity: 3, quantity: 3,
unit_price: 20, unit_price: 20,
compare_at_unit_price: null,
order_id: "1", order_id: "1",
detail: { detail: {
@@ -127,6 +130,7 @@ describe("Order Exchange - Actions", function () {
order_id: "1", order_id: "1",
quantity: 1, quantity: 1,
unit_price: 10, unit_price: 10,
compare_at_unit_price: null,
detail: { detail: {
quantity: 1, quantity: 1,
order_id: "1", order_id: "1",
@@ -144,6 +148,7 @@ describe("Order Exchange - Actions", function () {
order_id: "1", order_id: "1",
quantity: 2, quantity: 2,
unit_price: 100, unit_price: 100,
compare_at_unit_price: null,
detail: { detail: {
quantity: 2, quantity: 2,
order_id: "1", order_id: "1",
@@ -161,6 +166,7 @@ describe("Order Exchange - Actions", function () {
order_id: "1", order_id: "1",
quantity: 3, quantity: 3,
unit_price: 20, unit_price: 20,
compare_at_unit_price: null,
detail: { detail: {
quantity: 3, quantity: 3,
order_id: "1", order_id: "1",
@@ -10,6 +10,7 @@ describe("Order Return - Actions", function () {
id: "1", id: "1",
quantity: 1, quantity: 1,
unit_price: 10, unit_price: 10,
compare_at_unit_price: null,
order_id: "1", order_id: "1",
detail: { detail: {
@@ -28,6 +29,7 @@ describe("Order Return - Actions", function () {
id: "2", id: "2",
quantity: 2, quantity: 2,
unit_price: 100, unit_price: 100,
compare_at_unit_price: null,
order_id: "1", order_id: "1",
detail: { detail: {
@@ -46,6 +48,7 @@ describe("Order Return - Actions", function () {
id: "3", id: "3",
quantity: 3, quantity: 3,
unit_price: 20, unit_price: 20,
compare_at_unit_price: null,
order_id: "1", order_id: "1",
detail: { detail: {
@@ -144,6 +147,7 @@ describe("Order Return - Actions", function () {
order_id: "1", order_id: "1",
quantity: 1, quantity: 1,
unit_price: 10, unit_price: 10,
compare_at_unit_price: null,
detail: { detail: {
order_id: "1", order_id: "1",
quantity: 1, quantity: 1,
@@ -161,6 +165,7 @@ describe("Order Return - Actions", function () {
order_id: "1", order_id: "1",
quantity: 2, quantity: 2,
unit_price: 100, unit_price: 100,
compare_at_unit_price: null,
detail: { detail: {
order_id: "1", order_id: "1",
quantity: 2, quantity: 2,
@@ -178,6 +183,7 @@ describe("Order Return - Actions", function () {
order_id: "1", order_id: "1",
quantity: 3, quantity: 3,
unit_price: 20, unit_price: 20,
compare_at_unit_price: null,
detail: { detail: {
quantity: 3, quantity: 3,
order_id: "1", order_id: "1",
@@ -284,6 +290,7 @@ describe("Order Return - Actions", function () {
order_id: "1", order_id: "1",
quantity: 1, quantity: 1,
unit_price: 10, unit_price: 10,
compare_at_unit_price: null,
detail: { detail: {
quantity: 1, quantity: 1,
order_id: "1", order_id: "1",
@@ -301,6 +308,7 @@ describe("Order Return - Actions", function () {
order_id: "1", order_id: "1",
quantity: 2, quantity: 2,
unit_price: 100, unit_price: 100,
compare_at_unit_price: null,
detail: { detail: {
quantity: 2, quantity: 2,
order_id: "1", order_id: "1",
@@ -318,6 +326,7 @@ describe("Order Return - Actions", function () {
order_id: "1", order_id: "1",
quantity: 3, quantity: 3,
unit_price: 20, unit_price: 20,
compare_at_unit_price: null,
detail: { detail: {
quantity: 3, quantity: 3,
order_id: "1", order_id: "1",
@@ -1994,7 +1994,10 @@ export default class OrderModuleService<
if (!isExistingItem) { if (!isExistingItem) {
addedItems[item.id] = { addedItems[item.id] = {
...item, ...item,
unit_price: item.detail?.unit_price ?? item.unit_price, quantity: item.detail?.quantity ?? item.quantity,
unit_price: item.detail?.unit_price || item.unit_price,
compare_at_unit_price:
item.detail?.compare_at_unit_price || item.compare_at_unit_price,
} }
} }
} }
@@ -2026,13 +2029,15 @@ export default class OrderModuleService<
const newItem = itemsToUpsert.find((d) => d.item_id === item.id)! const newItem = itemsToUpsert.find((d) => d.item_id === item.id)!
const unitPrice = newItem?.unit_price ?? item.unit_price const unitPrice = newItem?.unit_price ?? item.unit_price
const compareAtUnitPrice =
newItem?.compare_at_unit_price ?? item.compare_at_unit_price
calculated.order.items[idx] = { calculated.order.items[idx] = {
...lineItem, ...lineItem,
actions, actions,
quantity: newItem.quantity, quantity: newItem.quantity,
unit_price: unitPrice, unit_price: unitPrice,
raw_unit_price: new BigNumber(unitPrice), compare_at_unit_price: compareAtUnitPrice,
detail: { detail: {
...newItem, ...newItem,
...item, ...item,
@@ -11,6 +11,7 @@ export type VirtualOrder = {
exchange_id?: string exchange_id?: string
unit_price: BigNumberInput unit_price: BigNumberInput
compare_at_unit_price: BigNumberInput | null
quantity: BigNumberInput quantity: BigNumberInput
detail: { detail: {
@@ -22,6 +23,7 @@ export type VirtualOrder = {
item_id?: string item_id?: string
unit_price?: BigNumberInput unit_price?: BigNumberInput
compare_at_unit_price?: BigNumberInput | null
quantity: BigNumberInput quantity: BigNumberInput
shipped_quantity: BigNumberInput shipped_quantity: BigNumberInput
fulfilled_quantity: BigNumberInput fulfilled_quantity: BigNumberInput
@@ -31,6 +31,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
exchange_id: action.exchange_id, exchange_id: action.exchange_id,
unit_price: action.details.unit_price, unit_price: action.details.unit_price,
compare_at_unit_price: action.details.compare_at_unit_price,
quantity: action.details.quantity, quantity: action.details.quantity,
} as VirtualOrder["items"][0] } as VirtualOrder["items"][0]
@@ -1,5 +1,4 @@
import { import {
BigNumber,
ChangeActionType, ChangeActionType,
MathBN, MathBN,
MedusaError, MedusaError,
@@ -13,36 +12,28 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_UPDATE, {
(item) => item.id === action.details.reference_id (item) => item.id === action.details.reference_id
) )
const unitPrice = action.details.unit_price
const existing = currentOrder.items[existingIndex] const existing = currentOrder.items[existingIndex]
existing.detail.quantity ??= 0 const originalQuantity = MathBN.convert(
existing.detail.quantity ?? existing.quantity
let quantityDiff = MathBN.sub( )
action.details.quantity, const originalUnitPrice = MathBN.convert(
existing.detail.quantity existing.detail.unit_price ?? existing.unit_price
) )
const quant = new BigNumber(action.details.quantity) const currentQuantity = MathBN.convert(action.details.quantity)
existing.quantity = quant const quantityDiff = MathBN.sub(currentQuantity, originalQuantity)
existing.detail.quantity = quant
if (unitPrice) { existing.quantity = currentQuantity
const currentUnitPriceBN = MathBN.convert(unitPrice) existing.detail.quantity = currentQuantity
const originalUnitPriceBn = MathBN.convert(
existing.detail.unit_price ?? existing.unit_price
)
const currentQuantityBn = MathBN.convert(action.details.quantity) if (action.details.unit_price) {
const originalQuantityBn = MathBN.convert( const currentUnitPrice = MathBN.convert(action.details.unit_price)
existing.detail.quantity ?? existing.quantity const originalTotal = MathBN.mult(originalUnitPrice, originalQuantity)
) const currentTotal = MathBN.mult(currentUnitPrice, currentQuantity)
const originalTotal = MathBN.mult(originalUnitPriceBn, originalQuantityBn) existing.unit_price = currentUnitPrice
const currentTotal = MathBN.mult(currentUnitPriceBN, currentQuantityBn) existing.detail.unit_price = currentUnitPrice
existing.unit_price = currentUnitPriceBN
existing.detail.unit_price = currentUnitPriceBN
setActionReference(existing, action, options) setActionReference(existing, action, options)
@@ -50,6 +41,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_UPDATE, {
} }
setActionReference(existing, action, options) setActionReference(existing, action, options)
return MathBN.mult(existing.unit_price, quantityDiff) return MathBN.mult(existing.unit_price, quantityDiff)
}, },
validate({ action, currentOrder }) { validate({ action, currentOrder }) {
@@ -58,6 +58,8 @@ export function applyChangesToOrder(
version, version,
quantity: orderItem.quantity, quantity: orderItem.quantity,
unit_price: item.unit_price ?? orderItem.unit_price, unit_price: item.unit_price ?? orderItem.unit_price,
compare_at_unit_price:
item.compare_at_unit_price ?? orderItem.compare_at_unit_price,
fulfilled_quantity: orderItem.fulfilled_quantity ?? 0, fulfilled_quantity: orderItem.fulfilled_quantity ?? 0,
delivered_quantity: orderItem.delivered_quantity ?? 0, delivered_quantity: orderItem.delivered_quantity ?? 0,
shipped_quantity: orderItem.shipped_quantity ?? 0, shipped_quantity: orderItem.shipped_quantity ?? 0,
@@ -48,6 +48,11 @@ export function formatOrder<T = any>(
raw_quantity: detail.raw_quantity, raw_quantity: detail.raw_quantity,
unit_price: detail.unit_price ?? orderItem.item.unit_price, unit_price: detail.unit_price ?? orderItem.item.unit_price,
raw_unit_price: detail.raw_unit_price ?? orderItem.item.raw_unit_price, raw_unit_price: detail.raw_unit_price ?? orderItem.item.raw_unit_price,
compare_at_unit_price:
detail.compare_at_unit_price ?? orderItem.item.compare_at_unit_price,
raw_compare_at_unit_price:
detail.raw_compare_at_unit_price ??
orderItem.item.raw_compare_at_unit_price,
detail, detail,
} }
}) })
@@ -254,6 +259,11 @@ export function mapRepositoryToOrderModel(config, isRelatedEntity = false) {
delete conf.where.items.item.unit_price delete conf.where.items.item.unit_price
} }
if (original.compare_at_unit_price) {
conf.where.items.compare_at_unit_price = original.compare_at_unit_price
delete conf.where.items.item.compare_at_unit_price
}
if (original.detail) { if (original.detail) {
conf.where.items = { conf.where.items = {
...original.detail, ...original.detail,
@@ -47,8 +47,8 @@ import {
PriceSet, PriceSet,
} from "@models" } from "@models"
import { eventBuilders, validatePriceListDates } from "@utils"
import { ServiceTypes } from "@types" import { ServiceTypes } from "@types"
import { eventBuilders, validatePriceListDates } from "@utils"
import { joinerConfig } from "../joiner-config" import { joinerConfig } from "../joiner-config"
type InjectedDependencies = { type InjectedDependencies = {