Orders Utilities
The orders utilities module (src/utils/orders/) is a set of focused helpers that sit on top of the database abstraction layer. It handles pagination/query wrappers, pure UI formatters, business-rule predicates, a security gate for the admin-bypass pattern, and an org-wide service-count aggregator.
Module Structure
Section titled “Module Structure”src/utils/orders.ts was refactored into a folder so each concern lives in its own file. The public import path #utils/orders is unchanged — a barrel index.ts re-exports everything.
src/utils/orders/├── index.ts ← barrel (public API — always import from here)├── data-access.ts ← DB query wrappers + getServiceCountsForDate├── formatters.ts ← pure UI helpers (currency, status labels/badges)├── predicates.ts ← business-rule booleans (editable, deletable)└── privilege.ts ← admin-bypass gate (resolveOrderPrivilege)Rule: Always import from #utils/orders. Only import from submodules directly if you specifically need to reference a single concern in isolation (e.g. #utils/orders/privilege in a security audit).
// ✅ Standard — always use the barrelimport { getAllOrders, formatCurrency, resolveOrderPrivilege } from '#utils/orders'
// ✅ Acceptable for security-sensitive code that wants an explicit audit trailimport { resolveOrderPrivilege } from '#utils/orders/privilege'
// ❌ Avoid — leaks internal structure unnecessarilyimport { getAllOrders } from '#utils/orders/data-access'Data Access
Section titled “Data Access”Constants
Section titled “Constants”ORDER_LIST_FIELDS
Section titled “ORDER_LIST_FIELDS”Pre-defined field list for optimized list/card views. Pass to db.getOrders({ fields }) to avoid fetching columns that are not needed for display.
export const ORDER_LIST_FIELDS: (keyof Order)[] = [ 'id', 'order_number', 'client_name', 'status', 'order_date', 'total_amount', 'created_at',]Functions
Section titled “Functions”getAllOrders(userId, limit?, offset?, fields?)
Section titled “getAllOrders(userId, limit?, offset?, fields?)”Fetches all orders for a user, newest first. Validates parameters and wraps the db.getOrders() call with structured error logging. Pass fields to retrieve a column subset for list/card views; omit it to get full rows.
| Param | Type | Default | Notes |
|---|---|---|---|
userId | string | — | Required, non-empty |
limit | number | 10 | 1–100; logs warning >100 |
offset | number | 0 | Non-negative |
fields | (keyof Order)[] | undefined | Column subset; omit for full rows |
import { getAllOrders } from '#utils/orders'
// First pageconst orders = await getAllOrders(userId)
// Second pageconst page2 = await getAllOrders(userId, 10, 10)List/card views — getAllOrders(..., ORDER_LIST_FIELDS)
Section titled “List/card views — getAllOrders(..., ORDER_LIST_FIELDS)”For list/card views where you do not need the full row, pass ORDER_LIST_FIELDS as the fields argument to getAllOrders. This replaces the former dedicated list helper.
import { getAllOrders, ORDER_LIST_FIELDS } from '#utils/orders'
const orders = await getAllOrders(userId, 25, 0, ORDER_LIST_FIELDS)orders.forEach(o => console.log(`${o.order_number} - ${o.client_name}`))getOrderById(id, userId)
Section titled “getOrderById(id, userId)”Fetches a single order by UUID with ownership enforcement.
import { getOrderById } from '#utils/orders'
const order = await getOrderById(orderId, userId)if (!order) { // Not found or not owned by this user}getOrdersCount(userId)
Section titled “getOrdersCount(userId)”Returns the total number of orders for a user. Use with getAllOrders to build paginated UIs.
import { getOrdersCount } from '#utils/orders'
const total = await getOrdersCount(userId)const totalPages = Math.ceil(total / pageSize)getServiceCountsForDate(date)
Section titled “getServiceCountsForDate(date)”Returns a per-service count map (Record<ServiceId, number>) across all users for a given date. All service IDs from the catalog are present in the result; services with no orders that day have value 0. Cancelled orders are excluded from all counts.
Security: This function intentionally omits the user_id filter (admin-bypass path). Callers must gate usage behind resolveOrderPrivilege(locals) before exposing the result to a user.
| Param | Type | Notes |
|---|---|---|
date | string | YYYY-MM-DD format required |
Implementation details:
- Pages through
db.getOrders()in 200-row pages - Hard cap of 50 pages (10 000 orders) — logs a warning and returns a partial count if exceeded
- Defends against timezone drift: filters by
from_date/to_dateand validates each row’sorder_datestarts with the requested date string
import { getServiceCountsForDate, resolveOrderPrivilege } from '#utils/orders'
// In an Astro page or API route:const isPrivileged = resolveOrderPrivilege(Astro.locals)if (!isPrivileged) { return new Response(null, { status: 403 })}
const counts = await getServiceCountsForDate('2026-04-06')// → { clothing: 12, haircuts: 7, showers: 3, hygiene: 5, other: 1 }---import { getServiceCountsForDate, resolveOrderPrivilege } from '#utils/orders'
if (!resolveOrderPrivilege(Astro.locals)) { return Astro.redirect('/403')}
const today = new Date().toISOString().split('T')[0]const counts = await getServiceCountsForDate(today)---
<ul> {Object.entries(counts).map(([service, count]) => ( <li>{service}: {count}</li> ))}</ul>import { getServiceCountsForDate, resolveOrderPrivilege } from '#utils/orders'
export async function GET({ locals, url }: APIContext) { if (!resolveOrderPrivilege(locals)) { return new Response(JSON.stringify({ error: 'Forbidden' }), { status: 403 }) }
const date = url.searchParams.get('date') ?? new Date().toISOString().split('T')[0] const counts = await getServiceCountsForDate(date)
return new Response(JSON.stringify({ counts }), { status: 200 })}Formatters
Section titled “Formatters”Pure functions — no database or logger imports. Safe to call on the client side.
formatCurrency(amount)
Section titled “formatCurrency(amount)”Formats a number as USD with two decimal places.
import { formatCurrency } from '#utils/orders'
formatCurrency(1234.56) // "$1,234.56"formatCurrency(100) // "$100.00"formatCurrency(0) // "$0.00"formatOrderStatus(status)
Section titled “formatOrderStatus(status)”Returns a human-readable label and a semantic color string for a status badge.
import { formatOrderStatus } from '#utils/orders'
formatOrderStatus('open') // { label: 'Open', color: 'blue' }formatOrderStatus('pending') // { label: 'Pending', color: 'yellow' }formatOrderStatus('ready') // { label: 'Ready', color: 'purple' }formatOrderStatus('complete') // { label: 'Complete', color: 'green' }formatOrderStatus('cancelled') // { label: 'Cancelled', color: 'red' }getOrderStatusBadgeClass(status)
Section titled “getOrderStatusBadgeClass(status)”Returns a @fpkit/acss badge class string. Use when rendering status chips inside React components.
import { getOrderStatusBadgeClass } from '#utils/orders'
getOrderStatusBadgeClass('open') // 'badge-info'getOrderStatusBadgeClass('pending') // 'badge-warning'getOrderStatusBadgeClass('ready') // 'badge-primary'getOrderStatusBadgeClass('complete') // 'badge-success'getOrderStatusBadgeClass('cancelled') // 'badge-error'Predicates
Section titled “Predicates”Pure boolean functions encoding order business rules. No database or logger imports.
isOrderEditable(order)
Section titled “isOrderEditable(order)”Returns true unless the order is complete or cancelled. Use before rendering edit controls or allowing form submissions.
import { isOrderEditable } from '#utils/orders'
if (!isOrderEditable(order)) { return new Response(JSON.stringify({ error: 'Order cannot be edited' }), { status: 422 })}canDeleteOrder(order)
Section titled “canDeleteOrder(order)”Returns true only for open, pending, or cancelled orders. Orders that are ready or complete should be archived, not deleted.
import { canDeleteOrder } from '#utils/orders'
// In the delete API route, after fetching the order:if (!canDeleteOrder(order)) { return new Response(JSON.stringify({ error: 'Order cannot be deleted' }), { status: 422 })}Privilege Gate
Section titled “Privilege Gate”resolveOrderPrivilege(locals)
Section titled “resolveOrderPrivilege(locals)”Security-sensitive. The single authorized decision point for the order admin-bypass pattern.
All order DB methods (getOrderById, deleteOrder, updateOrder, getServiceCountsForDate) accept an optional userId. When userId is omitted, the ownership WHERE clause is skipped — granting unrestricted access to every order in the database. This function is the only code that should decide whether to omit it.
Bypass threshold: team_manager or higher (see ROLE_HIERARCHY in config/roles.config.ts).
import { resolveOrderPrivilege } from '#utils/orders'
// In any order API route:const isPrivileged = resolveOrderPrivilege(Astro.locals)
const order = isPrivileged ? await db.getOrderById(orderId) // admin path — ownership filter skipped : await db.getOrderById(orderId, userDbId) // member path — ownership enforcedRoles that receive the bypass:
| Role | Bypass? |
|---|---|
super_admin | Yes |
admin | Yes |
team_admin | Yes |
team_manager | Yes |
volunteer | No |
member | No |
All Exports at a Glance
Section titled “All Exports at a Glance”| Export | Module | Type |
|---|---|---|
ORDER_LIST_FIELDS | data-access | const |
getAllOrders | data-access | async fn |
getOrderById | data-access | async fn |
getOrdersCount | data-access | async fn |
getServiceCountsForDate | data-access | async fn |
formatCurrency | formatters | fn |
formatOrderStatus | formatters | fn |
getOrderStatusBadgeClass | formatters | fn |
isOrderEditable | predicates | fn |
canDeleteOrder | predicates | fn |
resolveOrderPrivilege | privilege | fn |
Related
Section titled “Related”- Orders System — database layer, schema, API endpoints, React components
- Order Details System — line items
- Database Architecture — provider abstraction
- Role Guard Usage —
hasRoleOrHigher, role hierarchy