📋 Order Records
Manage orders with automatic order number generation, client linking, and status tracking. Links to existing client records.
The orders system provides comprehensive order management capabilities including creation, querying, updates, and deletion with built-in client linking, automatic order number generation, and row-level security.
The orders system supports:
📋 Order Records
Manage orders with automatic order number generation, client linking, and status tracking. Links to existing client records.
🔒 Secure by Default
Row-level security ensures users can only access their own orders. Stricter isolation than clients table.
⚡ Fast Queries
Optimized indexes provide fast queries for status filtering, date ranges, and client order history.
🔢 Smart Order Numbers
Automatic daily-scoped order numbers (ORD-YYYYMMDD-NNN) with retry logic to prevent collisions during concurrent creation.
import { getDatabase } from '#libs/database'import { generateOrderNumber } from '#utils/order-number'import { logger } from '#utils/logger'import type { OrderData } from '#libs/database'
const db = getDatabase()const correlationId = logger.createCorrelationId()
// Step 1: Generate unique order numberconst orderNumberResult = await generateOrderNumber(db, correlationId)
if (!orderNumberResult.success) { throw new Error(orderNumberResult.error)}
// Step 2: Create order with client linkingconst orderData: OrderData = { order_number: orderNumberResult.orderNumber, // "ORD-20260119-001" user_id: userClerkId, // From Clerk authentication client_id: 'client-uuid-456', client_name: 'John Doe', // Denormalized for fast display status: 'open', order_date: '2026-01-19', // YYYY-MM-DD format total_amount: 150.00, notes: 'Rush order for wedding',}
const orderId = await db.insertOrder(orderData)console.log(`Order created: ${orderId}`)// Get all orders for user (most recent first)const myOrders = await db.getOrders({ user_id: userClerkId, limit: 10, order_by: 'created_at', order_direction: 'desc',})
// Get orders by statusconst openOrders = await db.getOrders({ user_id: userClerkId, status: 'open', limit: 20,})
// Get orders for specific clientconst clientOrders = await db.getOrders({ user_id: userClerkId, client_id: 'client-uuid-456', limit: 50,})
// Date range queryconst januaryOrders = await db.getOrders({ user_id: userClerkId, start_date: '2026-01-01', end_date: '2026-01-31', order_by: 'order_date',})
// Optimized list view (selective fields)const orderList = await db.getOrders({ user_id: userClerkId, fields: ['id', 'order_number', 'client_name', 'status', 'total_amount'], limit: 100,})| Field | Type | Description | Example | Validation |
|---|---|---|---|---|
| order_number | string | Unique order identifier | ”ORD-20260119-001” | Auto-generated |
| user_id | string | Clerk user ID (owner) | “user-clerk-123” | Auto-set from auth |
| client_id | string | Reference to client record | ”client-uuid-456” | Valid UUID, FK |
| client_name | string | Client display name | ”John Doe” | Denormalized |
| status | OrderStatus | Order workflow state | ”open” | Enum (see below) |
| order_date | string | Order placement date | ”2026-01-19” | YYYY-MM-DD format |
| total_amount | number | Order total | 150.00 | >= 0, 2 decimals |
Order Status Values:
open - Order created, not yet processedpending - Order processing in progressready - Order ready for pickup/deliverycomplete - Order fulfilled and closedcancelled - Order cancelled| Field | Type | Description | Example | Validation |
|---|---|---|---|---|
| notes | string | Internal notes | ”Rush order” | Max 1000 chars |
| Field | Type | Description |
|---|---|---|
| id | string | Unique UUID |
| created_at | string | ISO 8601 timestamp |
| updated_at | string | ISO 8601 timestamp |
Format: ORD-YYYYMMDD-NNN
Components:
ORD- (constant identifier)YYYYMMDD (ISO 8601 basic date format)NNN (3-digit zero-padded daily sequence)Examples:
ORD-20260119-001 - First order on January 19, 2026ORD-20260119-042 - 42nd order on the same dayORD-20260120-001 - First order on next day (sequence resets)The system automatically generates unique order numbers with retry logic:
import { generateOrderNumber } from '#utils/order-number'import { logger } from '#utils/logger'
const correlationId = logger.createCorrelationId()const result = await generateOrderNumber(db, correlationId)
if (result.success) { console.log(result.orderNumber) // "ORD-20260119-001"} else { console.error(result.error) // "Failed to generate order number after 3 attempts"}Race Condition Prevention:
Link a new order to an existing client:
import { getDatabase } from '#libs/database'import { generateOrderNumber } from '#utils/order-number'import { logger } from '#utils/logger'
async function createOrder(userClerkId: string, clientId: string, clientName: string, amount: number) { const db = getDatabase() const correlationId = logger.createCorrelationId()
// Generate order number const orderNumberResult = await generateOrderNumber(db, correlationId)
if (!orderNumberResult.success) { throw new Error(orderNumberResult.error) }
// Create order const orderData: OrderData = { order_number: orderNumberResult.orderNumber, user_id: userClerkId, client_id: clientId, client_name: clientName, status: 'open', order_date: new Date().toISOString().split('T')[0], total_amount: amount, }
const orderId = await db.insertOrder(orderData) return orderId}Display paginated order list:
import { getDatabase } from '#libs/database'
async function getOrdersPage(userClerkId: string, page: number, pageSize: number = 10) { const db = getDatabase() const offset = (page - 1) * pageSize
const [orders, totalCount] = await Promise.all([ db.getOrders({ user_id: userClerkId, limit: pageSize, offset, order_by: 'created_at', order_direction: 'desc', }), db.getOrdersCount({ user_id: userClerkId }), ])
const totalPages = Math.ceil(totalCount / pageSize)
return { orders, pagination: { currentPage: page, pageSize, totalCount, totalPages, hasNextPage: page < totalPages, hasPrevPage: page > 1, }, }}Move order through workflow stages:
async function updateOrderStatus(orderId: string, newStatus: OrderStatus) { const db = getDatabase()
const success = await db.updateOrder(orderId, { status: newStatus, })
if (!success) { throw new Error('Failed to update order status') }}
// Usageawait updateOrderStatus('order-uuid-123', 'pending')await updateOrderStatus('order-uuid-123', 'ready')await updateOrderStatus('order-uuid-123', 'complete')Show all orders for a specific client:
async function getClientOrderHistory(userClerkId: string, clientId: string) { const db = getDatabase()
const orders = await db.getOrders({ user_id: userClerkId, client_id: clientId, order_by: 'order_date', order_direction: 'desc', limit: 100, })
const totalRevenue = orders.reduce((sum, order) => sum + order.total_amount, 0) const completedOrders = orders.filter(o => o.status === 'complete')
return { orders, stats: { totalOrders: orders.length, completedOrders: completedOrders.length, totalRevenue, }, }}Show orders grouped by status:
async function getOrdersDashboard(userClerkId: string) { const db = getDatabase()
const statuses: OrderStatus[] = ['open', 'pending', 'ready', 'complete', 'cancelled']
const ordersByStatus = await Promise.all( statuses.map(async status => ({ status, orders: await db.getOrders({ user_id: userClerkId, status, limit: 50, }), count: await db.getOrdersCount({ user_id: userClerkId, status, }), })) )
return ordersByStatus}Remove an order (only owner can delete):
async function deleteOrder(orderId: string) { const db = getDatabase()
const success = await db.deleteOrder(orderId)
if (!success) { throw new Error('Order not found or no permission') }}Filter orders by various criteria:
const myOrders = await db.getOrders({ user_id: userClerkId,})const openOrders = await db.getOrders({ user_id: userClerkId, status: 'open',})const clientOrders = await db.getOrders({ user_id: userClerkId, client_id: 'client-uuid-456',})const januaryOrders = await db.getOrders({ user_id: userClerkId, start_date: '2026-01-01', end_date: '2026-01-31',})const page2Orders = await db.getOrders({ user_id: userClerkId, limit: 10, offset: 10, // Skip first 10 results})Control result order:
// Most recent first (default)const recentOrders = await db.getOrders({ user_id: userClerkId, order_by: 'created_at', order_direction: 'desc',})
// By order date (chronological)const byDate = await db.getOrders({ user_id: userClerkId, order_by: 'order_date', order_direction: 'asc',})
// By order numberconst byOrderNumber = await db.getOrders({ user_id: userClerkId, order_by: 'order_number', order_direction: 'asc',})Select only needed fields for better performance:
// Minimal data for list viewsconst orderList = await db.getOrders({ user_id: userClerkId, fields: ['id', 'order_number', 'client_name', 'status', 'total_amount'], limit: 100,})
// With datesconst withDates = await db.getOrders({ user_id: userClerkId, fields: ['id', 'order_number', 'client_name', 'status', 'order_date'], limit: 100,})Implement paginated order lists:
async function getOrdersPage(userClerkId: string, page: number, pageSize: number = 10) { const db = getDatabase() const offset = (page - 1) * pageSize
const [orders, totalCount] = await Promise.all([ db.getOrders({ user_id: userClerkId, limit: pageSize, offset, order_by: 'created_at', order_direction: 'desc', }), db.getOrdersCount({ user_id: userClerkId }), ])
const totalPages = Math.ceil(totalCount / pageSize)
return { orders, pagination: { currentPage: page, pageSize, totalCount, totalPages, hasNextPage: page < totalPages, hasPrevPage: page > 1, }, }}Example Usage:
const result = await getOrdersPage(userClerkId, 1, 10)console.log(`Page ${result.pagination.currentPage} of ${result.pagination.totalPages}`)console.log(`Total orders: ${result.pagination.totalCount}`)result.orders.forEach(order => { console.log(`${order.order_number}: ${order.client_name} - $${order.total_amount}`)})Form component for creating/editing orders using React Hook Form + Zod validation:
File: src/components/react/OrderFormRHF.tsx
Props:
type Props = { csrfToken: string // CSRF token for form submission order?: Order | null // For edit mode (optional) apiEndpoint?: string // Custom API endpoint (optional) clients: Array<{ id: string; name: string }> // Client dropdown options}Usage in Astro:
---import OrderFormRHF from '#components/react/OrderFormRHF.tsx'import { generateCSRFToken } from '#utils/csrf'import { getDatabase } from '#libs/database'
const csrfToken = await generateCSRFToken(Astro.locals.userId)
// Fetch clients for dropdownconst db = getDatabase()const clientsData = await db.getClients({ created_by: Astro.locals.userId, fields: ['id', 'first_name', 'last_name'], limit: 100,})
const clients = clientsData.map(c => ({ id: c.id, name: `${c.first_name} ${c.last_name}`,}))---
<OrderFormRHF client:load csrfToken={csrfToken} clients={clients} />---import OrderFormRHF from '#components/react/OrderFormRHF.tsx'import { generateCSRFToken } from '#utils/csrf'import { getDatabase } from '#libs/database'
const orderId = Astro.params.idconst db = getDatabase()
const order = await db.getOrderById(orderId)const clientsData = await db.getClients({ created_by: Astro.locals.userId, fields: ['id', 'first_name', 'last_name'], limit: 100,})
const clients = clientsData.map(c => ({ id: c.id, name: `${c.first_name} ${c.last_name}`,}))
const csrfToken = await generateCSRFToken(Astro.locals.userId)---
<OrderFormRHF client:load order={order} csrfToken={csrfToken} clients={clients} apiEndpoint={`/api/orders/edit/${orderId}`}/>Features:
The /dashboard/orders roster, sharing the products and events tables’ markup and prd-* styles:
File: src/components/react/orders/OrdersTable.tsx
Usage:
---import { OrdersTable } from '#components/react/orders'---
<OrdersTable orders={orders} currentPage={currentPage} totalPages={totalPages} totalCount={totalCount} pageSize={10} basePath="/dashboard/orders"/>Features:
pending reads “In progress”); cancelled rows are dimmedorder_date as a calendar date, so a date never shifts with the viewer’s timezoneclient:* directiveDeleting an order happens on /dashboard/orders/edit/[id]: the sidebar mounts DeleteOrderButton
(src/components/react/orders/DeleteOrderButton.tsx) for orders canDeleteOrder allows (open, in
progress, cancelled). It confirms, sends DELETE /api/orders/delete/[id] with the CSRF token from
Astro.locals.csrfToken, and returns to the orders list.
Presentation component for displaying order lists. Used by the service-day order desk
(/service-day/order-desk); the dashboard uses OrdersTable above.
File: src/components/react/OrdersListView.tsx
Props:
type OrdersListViewProps = { orders: Order[] isLoading?: boolean showActions?: boolean emptyMessage?: string currentPage?: number totalPages?: number onPageChange?: (page: number) => void}Usage:
---import OrdersListView from '#components/react/OrdersListView.tsx'import { getDatabase } from '#libs/database'
const db = getDatabase()const orders = await db.getOrders({ user_id: Astro.locals.userId, limit: 10, order_by: 'created_at', order_direction: 'desc',})---
<OrdersListView orders={orders} showActions={true} />Features:
Complete validation reference:
| Field | Required | Min/Max | Format | Special Rules |
|---|---|---|---|---|
| client_id | Yes | N/A | UUID | Must be valid client ID (foreign key) |
| status | Yes | N/A | Enum | open, pending, ready, complete, cancelled |
| order_date | Yes | N/A | YYYY-MM-DD | Valid date format |
| total_amount | Yes | >= 0 | Number | Non-negative, 2 decimal places |
| notes | No | 0-1000 chars | Text | Trimmed |
| order_number | Yes | N/A | Text | Auto-generated (ORD-YYYYMMDD-NNN) |
| user_id | Yes | N/A | Text | Auto-set from Clerk authentication |
| client_name | Yes | N/A | Text | Fetched from client record during order creation |
All order operations require:
User-Scoped Access:
Privileged Access (team_manager and above):
team_manager, team_admin, admin, and super_admin roles can bypass ownership enforcementresolveOrderPrivilege(locals) from #utils/ordersresolveOrderPrivilege and branch on the result — never skip the gateImmutable Ownership:
user_id cannot be changed after creationBefore creating an order, the API endpoint verifies client ownership:
// Verify client exists and user has accessconst client = await db.getClientById(clientId)
if (!client || client.created_by !== internalUserId) { return new Response( JSON.stringify({ error: 'Client not found or access denied' }), { status: 403 } )}Purpose:
Create a new order record.
Security: Auth + Rate Limit + CSRF + Validation + Client Ownership Check
Request (JSON):
{ "client_id": "client-uuid-456", "status": "open", "order_date": "2026-01-19", "total_amount": 150.00, "notes": "Rush order", "csrfToken": "token-here"}Response (Success):
{ "success": true, "orderId": "uuid-string", "orderNumber": "ORD-20260119-001"}Retrieve a single order by ID.
Security: Auth
Response (Success):
{ "success": true, "order": { "id": "uuid", "order_number": "ORD-20260119-001", "user_id": "user-clerk-123", "client_id": "client-uuid-456", "client_name": "John Doe", "status": "open", "order_date": "2026-01-19", "total_amount": 150.00, "notes": "Rush order", "created_at": "2026-01-19T10:30:00Z", "updated_at": "2026-01-19T10:30:00Z" }}Update an existing order record.
Security: Auth + Rate Limit + CSRF + Validation + Ownership Check
Request (JSON):
{ "status": "complete", "notes": "Delivered on time", "csrfToken": "token-here"}Response (Success):
{ "success": true, "orderId": "uuid-string"}For large result sets, select only needed fields:
// ✅ FAST - Minimal data transferconst orders = await db.getOrders({ user_id: userClerkId, fields: ['id', 'order_number', 'client_name', 'status', 'total_amount'], limit: 100,})
// ⚠️ SLOWER - All fields returnedconst orders = await db.getOrders({ user_id: userClerkId, limit: 100,})Always use pagination for lists:
// ✅ GOOD - Limited result setconst orders = await db.getOrders({ user_id: userClerkId, limit: 10, offset: 0,})
// ❌ BAD - Fetches all recordsconst allOrders = await db.getOrders({ user_id: userClerkId, limit: 999999,})Use indexed fields for filtering and sorting:
// ✅ FAST - Uses idx_orders_user_idconst myOrders = await db.getOrders({ user_id: userClerkId,})
// ✅ FAST - Uses idx_orders_statusconst openOrders = await db.getOrders({ user_id: userClerkId, status: 'open',})
// ✅ FAST - Uses idx_orders_order_dateconst recentOrders = await db.getOrders({ user_id: userClerkId, start_date: '2026-01-01', end_date: '2026-01-31',})Symptom: Failed to generate order number after 3 attempts error
Cause: High concurrent order creation causing repeated collisions
Solution:
generateOrderNumber() if neededSymptom: Order created successfully but not visible in queries
Cause: RLS policy requires authentication and user_id match
Solution: Ensure JWT token is present and user_id matches authenticated user
Symptom: You do not have permission to update this order error
Cause: Attempting to update order owned by another user
Solution: Verify ownership (orders have strict user-scoped access, no admin override)
Symptom: Client not found or access denied during order creation
Cause: Client doesn’t exist or user doesn’t have access
Solution:
getClientById()client.created_by)Symptom: Invalid input error on form submission
Cause: Field doesn’t meet validation requirements
Solution: Check validation rules table above and error details
Technical Reference:
Database Documentation:
Migration History:
Each order can have multiple product line items tracked in the order_details table. Line items capture a price snapshot at the time of order so that order history is preserved accurately if products are later renamed or repriced.