import { QueryCtx } from "../_generated/server"; import { Id, Doc } from "../_generated/dataModel"; export async function getOrderWithItems( ctx: QueryCtx, orderId: Id<"orders">, ) { const order = await ctx.db.get(orderId); if (!order) return null; const items = await ctx.db .query("orderItems") .withIndex("by_order", (q) => q.eq("orderId", orderId)) .collect(); return { ...order, items }; } /** * Determines whether a customer is allowed to cancel a given order. * * NOTE: Cancellation only updates order status and restores stock. * Stripe refund processing is a separate concern handled via the admin * dashboard or a future automated flow. This helper does NOT trigger a refund. */ export function canCustomerCancel(order: Doc<"orders">): { allowed: boolean; reason?: string; } { switch (order.status) { case "confirmed": return { allowed: true }; case "pending": return { allowed: false, reason: "Order is still awaiting payment confirmation.", }; case "cancelled": return { allowed: false, reason: "Order is already cancelled." }; case "refunded": return { allowed: false, reason: "Order has already been refunded." }; default: return { allowed: false, reason: "Order has progressed past the cancellation window. Please contact support.", }; } } export interface OutOfStockItem { variantId: Id<"productVariants">; requested: number; available: number; } /** * Check each cart item for sufficient stock. Returns list of out-of-stock entries. */ export async function validateCartItems( ctx: Pick, items: { variantId?: Id<"productVariants">; quantity: number }[] ): Promise { const outOfStock: OutOfStockItem[] = []; for (const item of items) { if (!item.variantId) continue; const variant = await ctx.db.get(item.variantId); if (!variant) { outOfStock.push({ variantId: item.variantId, requested: item.quantity, available: 0, }); continue; } if (variant.stockQuantity < item.quantity) { outOfStock.push({ variantId: item.variantId, requested: item.quantity, available: variant.stockQuantity, }); } } return outOfStock; }