Skip to main content
Functions are in src/services/orders.service.ts. The service imports loyalty logic from src/lib/domain/loyalty and src/services/loyalty.service.ts.

Types

OrderItem

interface OrderItem {
    product_id: string;
    variant_id?: string | null;
    variant_name?: string | null;
    name: string;
    price: number;
    quantity: number;
    image?: string;
    section?: string;
}

OrderRecord

interface OrderRecord {
    id: string;
    order_number: string;       // Auto-generated by DB trigger
    customer_id: string;
    items: OrderItem[];
    subtotal: number;
    shipping_cost: number;
    discount: number;
    total: number;
    status: string;
    payment_method: string;
    payment_status: string;
    shipping_address_id: string | null;
    billing_address_id: string | null;
    tracking_notes: string | null;
    whatsapp_sent: boolean;
    whatsapp_sent_at: string | null;
    created_at: string;
    updated_at: string;
}

CreateOrderData

interface CreateOrderData {
    customer_id: string;
    items: OrderItem[];
    subtotal: number;
    shipping_cost?: number;     // defaults to 0
    discount?: number;          // defaults to 0
    total: number;
    payment_method: 'cash' | 'transfer' | 'card' | 'mercadopago' | 'whatsapp';
    shipping_address_id?: string;
    billing_address_id?: string;
    tracking_notes?: string;
    earned_points?: number;     // override auto-calculated loyalty points
}

createOrder()

export async function createOrder(
    data: CreateOrderData
): Promise<OrderRecord>
Creates a new order and automatically triggers loyalty point accrual. After the DB insert, calls calculateLoyaltyPoints(data.total) (unless earned_points is provided) and then addLoyaltyPoints(). Loyalty failures are logged but do not throw — the order is always returned.
data
CreateOrderData
required
Full order payload.
OrderRecord
OrderRecord
The complete order record as stored in the database.
import { createOrder } from '@/services';

const order = await createOrder({
    customer_id: 'user-uuid',
    items: [
        {
            product_id: 'prod-uuid',
            name: 'Aegis Legend 2',
            price: 1299,
            quantity: 1,
        }
    ],
    subtotal: 1299,
    total: 1299,
    payment_method: 'whatsapp',
});

console.log(order.order_number); // e.g. "ORD-0042"

getCustomerOrders()

export async function getCustomerOrders(
    customerId: string
): Promise<OrderRecord[]>
Returns all orders for a customer, ordered by created_at DESC.
customerId
string
required
UUID of the customer.
const orders = await getCustomerOrders('user-uuid');

getOrderById()

export async function getOrderById(
    id: string
): Promise<OrderRecord | null>
Fetches a single order by primary key. Returns null for not-found (PGRST116) instead of throwing.
const order = await getOrderById('order-uuid');

getOrderNotificationDetails()

export async function getOrderNotificationDetails(
    orderId: string
): Promise<RealtimeOrderEvent | null>
Fetches enriched data for Social Proof toast notifications. Joins customer_profiles and addresses to produce a RealtimeOrderEvent containing the customer name, city, first product name, and product image.
interface RealtimeOrderEvent {
    id: string;
    customer_name: string;
    city: string;
    product_name: string;
    product_image: string;
}

markWhatsAppSent()

export async function markWhatsAppSent(
    orderId: string
): Promise<void>
Sets whatsapp_sent = true and records whatsapp_sent_at as the current ISO timestamp on the order.
await markWhatsAppSent('order-uuid');

calculateLoyaltyPoints()

Imported from src/lib/domain/loyalty. The rate is 10 points per $100 MXN spent.
import { calculateLoyaltyPoints } from '@/lib/domain/loyalty';

calculateLoyaltyPoints(1299); // → 129 points

addLoyaltyPoints() — from loyalty.service.ts

export async function addLoyaltyPoints(
    customerId: string,
    points: number,
    orderId: string,
    description: string
): Promise<void>
Calls the process_loyalty_points Supabase RPC function. Errors are logged but not re-thrown to avoid blocking the order flow.
customerId
string
required
UUID of the customer.
points
number
required
Points to add (positive integer).
orderId
string
required
Associated order UUID.
description
string
required
Human-readable description, e.g. "Compra #ORD-0042".

getPointsBalance() — from loyalty.service.ts

export async function getPointsBalance(
    customerId: string
): Promise<number>
Calls the get_customer_points_balance Supabase RPC function. Returns 0 on error rather than throwing.
customerId
string
required
UUID of the customer.
number
number
Current V-Coins balance. Returns 0 if the RPC fails.
// Direct service call
const balance = await getPointsBalance('user-uuid');

// Via hook (preferred in components)
import { usePointsBalance } from '@/hooks/useOrders';
const { data: points } = usePointsBalance(user?.id);
The underlying RPC call:
-- Supabase RPC
SELECT get_customer_points_balance('user-uuid');

Build docs developers (and LLMs) love