All files / src/app/api/admin/pricing/preview route.ts

0% Statements 0/108
100% Branches 0/0
0% Functions 0/1
0% Lines 0/108

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109                                                                                                                                                                                                                         
export const dynamic = "force-dynamic";

import { NextRequest, NextResponse } from 'next/server';
import { } from "next-auth";
import { prisma } from "@/lib/prisma";
import { logger } from "@/lib/logging";
import { z } from "zod";
import { pricingEngine, PricingContext } from "@/lib/ecommerce";
import {
  withAdmin,
  withErrorHandling,
  successResponse,
  ApiError,
  ApiSuccessResponse,
  ApiErrorResponse } from "@/lib/api";
import { } from "@/lib/api/middleware";

const LOG_CATEGORY = "ADMIN_PRICING_PREVIEW_API";

const previewSchema = z.object({
  productId: z.number().int().positive(),
  quantity: z.number().int().positive().default(1),
  userId: z.number().int().positive().optional(),
  cartTotal: z.number().min(0).optional(),
  isFirstPurchase: z.boolean().optional(),
  customerSegmentIds: z.array(z.number()).optional() });

/**
 * POST /api/admin/pricing/preview
 * Preview dynamic pricing for a product
 */
async function handlePost(request: NextRequest): Promise<NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>> {
  const body = await request.json();

  // Validate input
  const validationResult = previewSchema.safeParse(body);
  if (!validationResult.success) {
    throw ApiError.validation("Validation failed", validationResult.error.issues);
  }

  const validatedData = validationResult.data;

  // Get product info
  const product = await prisma.product.findUnique({
    where: { id: validatedData.productId },
    select: {
      id: true,
      title: true,
      price: true,
      discountedPrice: true,
      categoryId: true,
      stock: true } });

  if (!product) {
    throw ApiError.notFound("Product");
  }

  // Build pricing context
  const context: PricingContext = {
    productId: product.id,
    quantity: validatedData.quantity,
    categoryId: product.categoryId,
    stockLevel: product.stock,
    currentTime: new Date(),
    dayOfWeek: new Date().getDay(),
    hourOfDay: new Date().getHours(),
    userId: validatedData.userId,
    cartTotal: validatedData.cartTotal,
    isFirstPurchase: validatedData.isFirstPurchase,
    customerSegmentIds: validatedData.customerSegmentIds };

  // Calculate dynamic price
  const result = await pricingEngine.previewPrice(
    product.id,
    product.price,
    context
  );

  // Get volume tiers for this product
  const volumeTiers = await pricingEngine.getVolumeTiers(product.id);

  logger.info("Pricing preview calculated", {
    category: LOG_CATEGORY,
    productId: product.id,
    originalPrice: result.originalPrice,
    finalPrice: result.finalPrice });

  return successResponse({
    product: {
      id: product.id,
      title: product.title,
      basePrice: product.price,
      currentDiscountedPrice: product.discountedPrice },
    pricing: {
      originalPrice: result.originalPrice,
      finalPrice: result.finalPrice,
      discountAmount: result.discountAmount,
      discountPercent: result.discountPercent,
      appliedRules: result.appliedRules },
    volumeTiers,
    context: {
      quantity: context.quantity,
      stockLevel: context.stockLevel,
      dayOfWeek: context.dayOfWeek,
      hourOfDay: context.hourOfDay } });
}

export const POST = withErrorHandling(withAdmin(handlePost));