/** * POST /api/orders/create * * Create a new order from the user's cart * * Request Body: * { * salutation: 'male' | 'female' | 'other' * firstName: string * lastName: string * dateOfBirth: string (YYYY-MM-DD) * street: string * postCode: string * city: string * countryCode: string * saveAddress: boolean (optional, default: false) * } * * Behavior: * - Creates order with status 'pending' * - Copies cart items to order_items with price snapshot * - Stores billing address snapshot in order * - Generates unique order number (format: EXP-2025-00001) * - Optionally saves address to user profile * - Does NOT clear cart (cart is cleared after order confirmation) * * Response: * { * success: true * orderId: string * orderNumber: string * message: string * } */ import { checkoutSchema } from '../../utils/schemas/checkout' import { orders, orderItems, users } from '../../database/schema' import { eq, desc, sql } from 'drizzle-orm' export default defineEventHandler(async (event) => { // Require authentication const { user } = await requireUserSession(event) // Validate request body const body = await readBody(event) const checkoutData = await checkoutSchema.parseAsync(body) const db = useDatabase() // Get user's cart const cart = await getOrCreateCart(event) const cartSummary = await getCartWithItems(cart.id) // Validate cart has items if (cartSummary.items.length === 0) { throw createError({ statusCode: 400, statusMessage: 'Warenkorb ist leer', }) } // Generate unique order number // Format: EXP-YYYY-NNNNN (e.g., EXP-2025-00001) const year = new Date().getFullYear() // Get the highest order number for this year const lastOrder = await db.query.orders.findFirst({ where: sql`${orders.orderNumber} LIKE ${`EXP-${year}-%`}`, orderBy: desc(orders.createdAt), }) let sequenceNumber = 1 if (lastOrder) { // Extract sequence number from last order number (EXP-2025-00123 -> 123) const match = lastOrder.orderNumber.match(/EXP-\d{4}-(\d{5})/) if (match) { sequenceNumber = Number.parseInt(match[1], 10) + 1 } } const orderNumber = `EXP-${year}-${String(sequenceNumber).padStart(5, '0')}` // Prepare billing address (exclude saveAddress flag) const billingAddress = { salutation: checkoutData.salutation, firstName: checkoutData.firstName, lastName: checkoutData.lastName, dateOfBirth: checkoutData.dateOfBirth, street: checkoutData.street, postCode: checkoutData.postCode, city: checkoutData.city, countryCode: checkoutData.countryCode, } // Create order const [order] = await db .insert(orders) .values({ orderNumber, userId: user.id, totalAmount: cartSummary.total.toFixed(2), status: 'pending', // Order starts as pending (awaiting mock payment) billingAddress, }) .returning() // Create order items with price snapshots const orderItemsData = cartSummary.items.map((item) => ({ orderId: order.id, productId: item.productId, quantity: item.quantity, priceSnapshot: item.product.price, // Snapshot price at time of order productSnapshot: { name: item.product.name, description: item.product.description, navProductId: item.product.navProductId, category: item.product.category, }, })) await db.insert(orderItems).values(orderItemsData) // Optionally save address to user profile if (checkoutData.saveAddress) { await db .update(users) .set({ salutation: checkoutData.salutation, dateOfBirth: new Date(checkoutData.dateOfBirth), street: checkoutData.street, postCode: checkoutData.postCode, city: checkoutData.city, countryCode: checkoutData.countryCode, updatedAt: new Date(), }) .where(eq(users.id, user.id)) } return { success: true, orderId: order.id, orderNumber: order.orderNumber, message: 'Bestellung erfolgreich erstellt', } })