diff --git a/src/app/[locale]/order/item/[id]/form.tsx b/src/app/[locale]/order/item/[id]/form.tsx index da49d39..7f48efc 100644 --- a/src/app/[locale]/order/item/[id]/form.tsx +++ b/src/app/[locale]/order/item/[id]/form.tsx @@ -26,7 +26,7 @@ import { Product } from "@/db/schema"; import { MinusIcon, PlusIcon } from "@heroicons/react/24/solid"; import { useMemo, useState } from "react"; import { debounce } from "radash"; -import { calculateItemPrice } from "@/utils/pricing"; +import { calculateItemPricing } from "@/utils/pricing"; const Files = dynamic(() => import("./files/files"), { ssr: false, @@ -83,7 +83,7 @@ const Form = ({ session, initialCart, itemId, product }: Props) => { pieces: quantity, }, }; - const computedPrice = calculateItemPrice(product, previewItem); + const computedPrice = calculateItemPricing(product, previewItem).price; return ( <> diff --git a/src/app/[locale]/order/items.tsx b/src/app/[locale]/order/items.tsx index f1d8cbd..89b4729 100644 --- a/src/app/[locale]/order/items.tsx +++ b/src/app/[locale]/order/items.tsx @@ -16,7 +16,7 @@ import { Tooltip, } from "@radix-ui/themes"; import { useFormatter, useLocale, useTranslations } from "next-intl"; -import { calculateItemPrice } from "@/utils/pricing"; +import { calculateItemPricing } from "@/utils/pricing"; type Props = { cart: ShoppingCart; @@ -46,7 +46,7 @@ const OrderItems = ({ cart: initialCart, products }: Props) => { (product) => product.id === item.productId ); const itemPrice = - item.price ?? (product ? calculateItemPrice(product, item) : 0); + item.price ?? (product ? calculateItemPricing(product, item).price : 0); return ( { return semantics(match).eval(context); }; -export const calculateItemPrice = ( - product: Product, - item: Partial -) => { - const formula = product.pricing?.formula; - if (!formula) return 0; +export type ItemPricingBreakdown = { + baseCost: number; + margin: number; + price: number; +}; - const context = buildPriceContext(product, item); +const evaluateFormulaSafely = (formula: string | undefined, context: unknown) => { + if (!formula) return 0; try { const result = evaluatePriceFormula(formula, context); @@ -200,3 +200,52 @@ export const calculateItemPrice = ( return 0; } }; + +export const calculateItemPricing = ( + product: Product, + item: Partial +): ItemPricingBreakdown => { + const baseCostFormula = product.pricing?.baseCostFormula; + const marginFormula = product.pricing?.marginFormula; + const legacyFormula = product.pricing?.formula; + + const context = buildPriceContext(product, item); + + if (baseCostFormula || marginFormula) { + const baseCost = evaluateFormulaSafely(baseCostFormula, context); + const margin = evaluateFormulaSafely(marginFormula, { + ...context, + pricing: { baseCost }, + }); + + return { + baseCost, + margin, + price: baseCost + margin, + }; + } + + if (legacyFormula) { + const legacyPrice = evaluateFormulaSafely(legacyFormula, context); + return { + // Legacy formulas only define total price; we keep margin at 0 to avoid + // inventing profit data and treat the total as production cost. + baseCost: legacyPrice, + margin: 0, + price: legacyPrice, + }; + } + + return { + baseCost: 0, + margin: 0, + price: 0, + }; +}; + +export const calculateItemPrice = ( + product: Product, + item: Partial +) => { + return calculateItemPricing(product, item).price; +};