Feature: Implement context-aware pricing for product types

Updates pricing logic to use correct price based on product type:
- Updated getEffectivePrice to accept priceType parameter ('base', 'plain', 'personalised')
- Updated toProductDTO to accept and use priceType when computing effective price
- Updated made-to-order pages to use 'personalised' price type
- Updated products catalog pages to use 'plain' price type
- Updated checkout logic to determine price type based on design

Pricing behavior:
- Personalised orders (with design): use personalisedPrice if set, else basePrice
- Plain/blank orders (no design): use plainPrice if set, else basePrice
- Ready-made orders (no design, no plainPrice): use basePrice

All prices now correctly reflect the product variant being purchased.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Andymick
2026-07-20 18:16:17 +01:00
co-authored by Claude Haiku 4.5
parent e5fb22aab1
commit 5bd6f3de39
9 changed files with 31 additions and 12 deletions
+11 -1
View File
@@ -110,9 +110,19 @@ export async function POST(req: Request) {
const pricedItems = items.map((i) => {
const design = typeof i.designJson === 'string' ? JSON.parse(i.designJson) : i.designJson;
const isPersonalised = (design.front?.length ?? 0) > 0 || (design.back?.length ?? 0) > 0;
const product = productsById.get(i.productId)!;
// Determine which price to use
let priceType: 'base' | 'plain' | 'personalised' = 'base';
if (isPersonalised) {
priceType = 'personalised';
} else if (product.plainPrice != null) {
priceType = 'plain';
}
return {
...i,
unitPrice: getEffectivePrice(productsById.get(i.productId)!, activePromotions, new Date(), isPersonalised).price,
unitPrice: getEffectivePrice(product, activePromotions, new Date(), priceType).price,
};
});
+2 -2
View File
@@ -19,13 +19,13 @@ export default async function ProductDetailPage({
const row = await prisma.product.findUnique({ where: { slug: params.slug } });
if (!row || !row.showOnPersonalised) notFound();
const product = toProductDTO(row, activePromotions);
const product = toProductDTO(row, activePromotions, 'personalised');
const relatedRows = await prisma.product.findMany({
where: { category: product.category, showOnPersonalised: true, NOT: { id: product.id } },
take: 4,
});
const related = relatedRows.map((p) => toProductDTO(p, activePromotions));
const related = relatedRows.map((p) => toProductDTO(p, activePromotions, 'personalised'));
// Fetch custom photo from request if customRequestId is provided
let customPhoto: string | undefined;
+1 -1
View File
@@ -70,7 +70,7 @@ export default async function ProductsPage({ searchParams }: { searchParams: Sea
const activePromotions = await fetchActivePromotions();
const rows = await prisma.product.findMany({ where, orderBy: { createdAt: 'asc' } });
const products = rows.map((p) => toProductDTO(p, activePromotions));
const products = rows.map((p) => toProductDTO(p, activePromotions, 'personalised'));
const hasFilters =
selectedCategories.length > 0 ||
+2 -2
View File
@@ -11,13 +11,13 @@ export default async function StandardProductPage({ params }: { params: { slug:
const row = await prisma.product.findUnique({ where: { slug: params.slug } });
if (!row || !row.showOnProducts) notFound();
const product = toProductDTO(row, activePromotions);
const product = toProductDTO(row, activePromotions, 'plain');
const relatedRows = await prisma.product.findMany({
where: { category: product.category, showOnProducts: true, NOT: { id: product.id } },
take: 4,
});
const related = relatedRows.map((p) => toProductDTO(p, activePromotions));
const related = relatedRows.map((p) => toProductDTO(p, activePromotions, 'plain'));
return (
<div className="mx-auto max-w-6xl px-6 py-12">
+1 -1
View File
@@ -69,7 +69,7 @@ export default async function ReadyMadeProductsPage({ searchParams }: { searchPa
const activePromotions = await fetchActivePromotions();
const rows = await prisma.product.findMany({ where, orderBy: { createdAt: 'asc' } });
const products = rows.map((p) => toProductDTO(p, activePromotions));
const products = rows.map((p) => toProductDTO(p, activePromotions, 'plain'));
const hasFilters =
selectedCategories.length > 0 ||