You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
4.0 KiB
145 lines
4.0 KiB
/**
|
|
* 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',
|
|
}
|
|
})
|
|
|