Documentation

Ecommerce Orders

Ecommerce Orders

UltraViolet Pro includes a comprehensive order management system that handles the complete order lifecycle from cart to fulfillment.

Order Management

Order Lifecycle

The order system follows a structured workflow:

  1. Cart → Customer adds items to cart
  2. Checkout → Customer provides shipping and payment info
  3. Pending → Order is created and awaiting payment
  4. Processing → Payment confirmed, order being prepared
  5. Shipped → Order has been shipped to customer
  6. Delivered → Order has been delivered
  7. Cancelled → Order was cancelled (if applicable)

Order Status Management

// Order status constants
class OrderStatus
{
    const PENDING = 'pending';
    const PROCESSING = 'processing';
    const SHIPPED = 'shipped';
    const DELIVERED = 'delivered';
    const CANCELLED = 'cancelled';
    const REFUNDED = 'refunded';
}

// Payment status constants
class PaymentStatus
{
    const PENDING = 'pending';
    const PAID = 'paid';
    const FAILED = 'failed';
    const REFUNDED = 'refunded';
    const PARTIALLY_REFUNDED = 'partially_refunded';
}

Creating Orders

Cart to Order Conversion

// Convert cart to order
public function createOrderFromCart($cart, $user, $shippingAddress, $billingAddress, $paymentMethod)
{
    DB::beginTransaction();

    try {
        // Create order
        $order = Order::create([
            'order_number' => $this->generateOrderNumber(),
            'user_id' => $user->id,
            'status' => OrderStatus::PENDING,
            'subtotal' => $cart->subtotal,
            'tax_amount' => $cart->tax_amount,
            'shipping_amount' => $cart->shipping_amount,
            'discount_amount' => $cart->discount_amount,
            'total_amount' => $cart->total,
            'currency' => 'USD',
            'shipping_address' => $shippingAddress,
            'billing_address' => $billingAddress,
            'payment_method' => $paymentMethod,
            'payment_status' => PaymentStatus::PENDING,
        ]);

        // Create order items
        foreach ($cart->items as $cartItem) {
            $order->items()->create([
                'product_id' => $cartItem->product_id,
                'product_variant_id' => $cartItem->product_variant_id,
                'product_name' => $cartItem->product->name,
                'product_sku' => $cartItem->product->sku,
                'quantity' => $cartItem->quantity,
                'unit_price' => $cartItem->unit_price,
                'total_price' => $cartItem->total_price,
                'product_data' => $cartItem->product->toArray(), // Store product snapshot
            ]);
        }

        // Update inventory
        $this->updateInventory($order);

        // Clear cart
        $cart->items()->delete();
        $cart->delete();

        DB::commit();

        // Send order confirmation email
        $user->notify(new OrderConfirmation($order));

        return $order;

    } catch (Exception $e) {
        DB::rollback();
        throw $e;
    }
}

// Generate unique order number
private function generateOrderNumber()
{
    do {
        $orderNumber = 'ORD-' . date('Y') . '-' . strtoupper(Str::random(6));
    } while (Order::where('order_number', $orderNumber)->exists());

    return $orderNumber;
}

Order Items Model

class OrderItem extends Model
{
    protected $fillable = [
        'order_id', 'product_id', 'product_variant_id',
        'product_name', 'product_sku', 'quantity',
        'unit_price', 'total_price', 'product_data'
    ];

    protected $casts = [
        'product_data' => 'array',
        'unit_price' => 'decimal:2',
        'total_price' => 'decimal:2',
    ];

    public function order()
    {
        return $this->belongsTo(Order::class);
    }

    public function product()
    {
        return $this->belongsTo(Product::class);
    }

    public function variant()
    {
        return $this->belongsTo(ProductVariant::class, 'product_variant_id');
    }
}

Order Display and Management

Order List View

<div class="orders-list">
    <div class="d-flex justify-content-between align-items-center mb-4">
        <h2>Orders</h2>
        <div class="order-filters">
            <select class="form-select d-inline-block w-auto" id="statusFilter" onchange="filterOrders()">
                <option value="">All Orders</option>
                <option value="pending">Pending</option>
                <option value="processing">Processing</option>
                <option value="shipped">Shipped</option>
                <option value="delivered">Delivered</option>
                <option value="cancelled">Cancelled</option>
            </select>
        </div>
    </div>

    <div class="table-responsive">
        <table class="table table-hover">
            <thead>
                <tr>
                    <th>Order #</th>
                    <th>Customer</th>
                    <th>Date</th>
                    <th>Status</th>
                    <th>Payment</th>
                    <th>Total</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody>
                @foreach($orders as $order)
                <tr>
                    <td>
                        <a href="route-admin.orders.show-order.html" class="text-decoration-none">
                            {{ $order->order_number }}
                        </a>
                    </td>
                    <td>
                        <div>
                            <div class="fw-bold">{{ $order->user->name }}</div>
                            <small class="text-muted">{{ $order->user->email }}</small>
                        </div>
                    </td>
                    <td>{{ $order->created_at->format('M d, Y') }}</td>
                    <td>
                        <span class="badge bg-{{ $order->status_color }}">
                            {{ ucfirst($order->status) }}
                        </span>
                    </td>
                    <td>
                        <span class="badge bg-{{ $order->payment_status_color }}">
                            {{ ucfirst($order->payment_status) }}
                        </span>
                    </td>
                    <td class="fw-bold">${{ $order->total_amount }}</td>
                    <td>
                        <div class="btn-group btn-group-sm">
                            <a href="route-admin.orders.show-order.html" 
                               class="btn btn-outline-primary" title="View">
                                <i class="bi bi-eye"></i>
                            </a>
                            <button class="btn btn-outline-secondary" 
                                    onclick="updateOrderStatus({{ $order->id }})" title="Update Status">
                                <i class="bi bi-pencil"></i>
                            </button>
                            <a href="route-admin.orders.invoice-order.html" 
                               class="btn btn-outline-info" title="Invoice">
                                <i class="bi bi-file-text"></i>
                            </a>
                        </div>
                    </td>
                </tr>
                @endforeach
            </tbody>
        </table>
    </div>

    {{ $orders->links() }}
</div>

Order Detail View

<div class="order-details">
    <div class="row">
        <div class="col-md-8">
            <!-- Order Information -->
            <div class="card dashboard-margin">
                <div class="card-header d-flex justify-content-between align-items-center">
                    <h5 class="mb-0">Order #{{ $order->order_number }}</h5>
                    <div class="order-status">
                        <span class="badge bg-{{ $order->status_color }} fs-6">
                            {{ ucfirst($order->status) }}
                        </span>
                    </div>
                </div>
                <div class="card-body">
                    <div class="row">
                        <div class="col-md-6">
                            <h6>Order Information</h6>
                            <table class="table table-sm">
                                <tr>
                                    <td><strong>Order Date:</strong></td>
                                    <td>{{ $order->created_at->format('M d, Y H:i') }}</td>
                                </tr>
                                <tr>
                                    <td><strong>Order Number:</strong></td>
                                    <td>{{ $order->order_number }}</td>
                                </tr>
                                <tr>
                                    <td><strong>Payment Method:</strong></td>
                                    <td>{{ ucfirst($order->payment_method) }}</td>
                                </tr>
                                <tr>
                                    <td><strong>Payment Status:</strong></td>
                                    <td>
                                        <span class="badge bg-{{ $order->payment_status_color }}">
                                            {{ ucfirst($order->payment_status) }}
                                        </span>
                                    </td>
                                </tr>
                            </table>
                        </div>
                        <div class="col-md-6">
                            <h6>Customer Information</h6>
                            <table class="table table-sm">
                                <tr>
                                    <td><strong>Name:</strong></td>
                                    <td>{{ $order->user->name }}</td>
                                </tr>
                                <tr>
                                    <td><strong>Email:</strong></td>
                                    <td>{{ $order->user->email }}</td>
                                </tr>
                                <tr>
                                    <td><strong>Phone:</strong></td>
                                    <td>{{ $order->user->phone ?? 'N/A' }}</td>
                                </tr>
                            </table>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Order Items -->
            <div class="card dashboard-margin">
                <div class="card-header">
                    <h5 class="mb-0">Order Items</h5>
                </div>
                <div class="card-body">
                    <div class="table-responsive">
                        <table class="table">
                            <thead>
                                <tr>
                                    <th>Product</th>
                                    <th>SKU</th>
                                    <th>Quantity</th>
                                    <th>Unit Price</th>
                                    <th>Total</th>
                                </tr>
                            </thead>
                            <tbody>
                                @foreach($order->items as $item)
                                <tr>
                                    <td>
                                        <div class="d-flex align-items-center">
                                            <img src="{{ $item->product_data['primary_image'] ?? '/images/placeholder.jpg' }}" 
                                                 class="me-3" style="width: 50px; height: 50px; object-fit: cover;">
                                            <div>
                                                <div class="fw-bold">{{ $item->product_name }}</div>
                                                @if($item->variant)
                                                <small class="text-muted">{{ $item->variant->attributes }}</small>
                                                @endif
                                            </div>
                                        </div>
                                    </td>
                                    <td>{{ $item->product_sku }}</td>
                                    <td>{{ $item->quantity }}</td>
                                    <td>${{ $item->unit_price }}</td>
                                    <td class="fw-bold">${{ $item->total_price }}</td>
                                </tr>
                                @endforeach
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>

        <div class="col-md-4">
            <!-- Order Summary -->
            <div class="card dashboard-margin">
                <div class="card-header">
                    <h5 class="mb-0">Order Summary</h5>
                </div>
                <div class="card-body">
                    <table class="table table-sm">
                        <tr>
                            <td>Subtotal:</td>
                            <td class="text-end">${{ $order->subtotal }}</td>
                        </tr>
                        <tr>
                            <td>Shipping:</td>
                            <td class="text-end">${{ $order->shipping_amount }}</td>
                        </tr>
                        <tr>
                            <td>Tax:</td>
                            <td class="text-end">${{ $order->tax_amount }}</td>
                        </tr>
                        @if($order->discount_amount > 0)
                        <tr>
                            <td>Discount:</td>
                            <td class="text-end text-success">-${{ $order->discount_amount }}</td>
                        </tr>
                        @endif
                        <tr class="table-active">
                            <td><strong>Total:</strong></td>
                            <td class="text-end"><strong>${{ $order->total_amount }}</strong></td>
                        </tr>
                    </table>
                </div>
            </div>

            <!-- Shipping Address -->
            <div class="card dashboard-margin">
                <div class="card-header">
                    <h5 class="mb-0">Shipping Address</h5>
                </div>
                <div class="card-body">
                    <address>
                        {{ $order->shipping_address['name'] }}<br>
                        {{ $order->shipping_address['address_line_1'] }}<br>
                        @if($order->shipping_address['address_line_2'])
                        {{ $order->shipping_address['address_line_2'] }}<br>
                        @endif
                        {{ $order->shipping_address['city'] }}, {{ $order->shipping_address['state'] }} {{ $order->shipping_address['postal_code'] }}<br>
                        {{ $order->shipping_address['country'] }}
                    </address>
                </div>
            </div>

            <!-- Order Actions -->
            <div class="card">
                <div class="card-header">
                    <h5 class="mb-0">Order Actions</h5>
                </div>
                <div class="card-body">
                    <div class="d-grid gap-2">
                        <button class="btn btn-primary" onclick="updateOrderStatus({{ $order->id }})">
                            Update Status
                        </button>
                        <a href="route-admin.orders.invoice-order.html" class="btn btn-outline-primary">
                            Generate Invoice
                        </a>
                        <button class="btn btn-outline-info" onclick="sendOrderUpdate({{ $order->id }})">
                            Send Update Email
                        </button>
                        @if($order->status !== 'cancelled')
                        <button class="btn btn-outline-danger" onclick="cancelOrder({{ $order->id }})">
                            Cancel Order
                        </button>
                        @endif
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

Order Status Management

Status Update Interface

<!-- Order Status Update Modal -->
<div class="modal fade" id="statusUpdateModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Update Order Status</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <div class="modal-body">
                <form id="statusUpdateForm">
                    @csrf
                    <div class="mb-3">
                        <label for="orderStatus" class="form-label">Order Status</label>
                        <select class="form-select" id="orderStatus" name="status">
                            <option value="pending">Pending</option>
                            <option value="processing">Processing</option>
                            <option value="shipped">Shipped</option>
                            <option value="delivered">Delivered</option>
                            <option value="cancelled">Cancelled</option>
                        </select>
                    </div>

                    <div class="mb-3">
                        <label for="paymentStatus" class="form-label">Payment Status</label>
                        <select class="form-select" id="paymentStatus" name="payment_status">
                            <option value="pending">Pending</option>
                            <option value="paid">Paid</option>
                            <option value="failed">Failed</option>
                            <option value="refunded">Refunded</option>
                        </select>
                    </div>

                    <div class="mb-3">
                        <label for="trackingNumber" class="form-label">Tracking Number</label>
                        <input type="text" class="form-control" id="trackingNumber" name="tracking_number">
                    </div>

                    <div class="mb-3">
                        <label for="statusNotes" class="form-label">Notes</label>
                        <textarea class="form-control" id="statusNotes" name="notes" rows="3"></textarea>
                    </div>

                    <div class="form-check">
                        <input class="form-check-input" type="checkbox" id="sendEmail" name="send_email" checked>
                        <label class="form-check-label" for="sendEmail">
                            Send email notification to customer
                        </label>
                    </div>
                </form>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                <button type="button" class="btn btn-primary" onclick="saveStatusUpdate()">Update Status</button>
            </div>
        </div>
    </div>
</div>

<script>
function updateOrderStatus(orderId) {
    // Fetch current order data
    fetch(`/api/orders/${orderId}`)
        .then(response => response.json())
        .then(data => {
            document.getElementById('orderStatus').value = data.status;
            document.getElementById('paymentStatus').value = data.payment_status;
            document.getElementById('trackingNumber').value = data.tracking_number || '';

            // Show modal
            const modal = new bootstrap.Modal(document.getElementById('statusUpdateModal'));
            modal.show();

            // Store order ID for form submission
            document.getElementById('statusUpdateForm').dataset.orderId = orderId;
        });
}

function saveStatusUpdate() {
    const form = document.getElementById('statusUpdateForm');
    const orderId = form.dataset.orderId;
    const formData = new FormData(form);

    fetch(`/api/orders/${orderId}/status`, {
        method: 'POST',
        headers: {
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
        },
        body: formData
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            // Close modal
            const modal = bootstrap.Modal.getInstance(document.getElementById('statusUpdateModal'));
            modal.hide();

            // Reload page or update UI
            location.reload();
        } else {
            alert('Error updating order status: ' + data.message);
        }
    });
}
</script>

Order Status Controller

// Order status update controller
public function updateStatus(Request $request, Order $order)
{
    $validated = $request->validate([
        'status' => 'required|in:pending,processing,shipped,delivered,cancelled',
        'payment_status' => 'required|in:pending,paid,failed,refunded',
        'tracking_number' => 'nullable|string|max:255',
        'notes' => 'nullable|string',
        'send_email' => 'boolean'
    ]);

    $oldStatus = $order->status;

    // Update order
    $order->update([
        'status' => $validated['status'],
        'payment_status' => $validated['payment_status'],
        'tracking_number' => $validated['tracking_number'],
        'notes' => $validated['notes']
    ]);

    // Create status history entry
    $order->statusHistory()->create([
        'status' => $validated['status'],
        'payment_status' => $validated['payment_status'],
        'notes' => $validated['notes'],
        'changed_by' => auth()->id(),
        'changed_at' => now()
    ]);

    // Send email notification if requested
    if ($request->boolean('send_email') && $oldStatus !== $validated['status']) {
        $order->user->notify(new OrderStatusUpdate($order, $oldStatus));
    }

    // Update inventory if order is cancelled
    if ($validated['status'] === 'cancelled' && $oldStatus !== 'cancelled') {
        $this->restoreInventory($order);
    }

    return response()->json([
        'success' => true,
        'message' => 'Order status updated successfully'
    ]);
}

Order Analytics and Reporting

Order Statistics

// Order analytics
class OrderAnalytics
{
    public function getOrderStats($period = '30_days')
    {
        $dateRange = $this->getDateRange($period);

        return [
            'total_orders' => Order::whereBetween('created_at', $dateRange)->count(),
            'total_revenue' => Order::whereBetween('created_at', $dateRange)
                ->where('payment_status', 'paid')
                ->sum('total_amount'),
            'average_order_value' => Order::whereBetween('created_at', $dateRange)
                ->where('payment_status', 'paid')
                ->avg('total_amount'),
            'orders_by_status' => Order::whereBetween('created_at', $dateRange)
                ->groupBy('status')
                ->selectRaw('status, count(*) as count')
                ->pluck('count', 'status'),
            'revenue_by_day' => Order::whereBetween('created_at', $dateRange)
                ->where('payment_status', 'paid')
                ->groupBy(DB::raw('DATE(created_at)'))
                ->selectRaw('DATE(created_at) as date, SUM(total_amount) as revenue')
                ->orderBy('date')
                ->get()
        ];
    }

    private function getDateRange($period)
    {
        switch ($period) {
            case '7_days':
                return [now()->subDays(7), now()];
            case '30_days':
                return [now()->subDays(30), now()];
            case '90_days':
                return [now()->subDays(90), now()];
            case '1_year':
                return [now()->subYear(), now()];
            default:
                return [now()->subDays(30), now()];
        }
    }
}

Order Dashboard

<div class="order-dashboard">
    <div class="row mb-4">
        <div class="col-md-3">
            <div class="card bg-primary text-bg-primary">
                <div class="card-body">
                    <div class="d-flex justify-content-between">
                        <div>
                            <h4>{{ $stats['total_orders'] }}</h4>
                            <p class="mb-0">Total Orders</p>
                        </div>
                        <div class="align-self-center">
                            <i class="bi bi-cart-check fs-1"></i>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card bg-success text-bg-success">
                <div class="card-body">
                    <div class="d-flex justify-content-between">
                        <div>
                            <h4>${{ number_format($stats['total_revenue'], 2) }}</h4>
                            <p class="mb-0">Total Revenue</p>
                        </div>
                        <div class="align-self-center">
                            <i class="bi bi-currency-dollar fs-1"></i>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card bg-info text-white">
                <div class="card-body">
                    <div class="d-flex justify-content-between">
                        <div>
                            <h4>${{ number_format($stats['average_order_value'], 2) }}</h4>
                            <p class="mb-0">Average Order Value</p>
                        </div>
                        <div class="align-self-center">
                            <i class="bi bi-graph-up fs-1"></i>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card bg-warning text-white">
                <div class="card-body">
                    <div class="d-flex justify-content-between">
                        <div>
                            <h4>{{ $stats['orders_by_status']['pending'] ?? 0 }}</h4>
                            <p class="mb-0">Pending Orders</p>
                        </div>
                        <div class="align-self-center">
                            <i class="bi bi-clock fs-1"></i>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- Revenue Chart -->
    <div class="row">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">
                    <h5>Revenue Trend</h5>
                </div>
                <div class="card-body">
                    <canvas id="revenueChart" height="100"></canvas>
                </div>
            </div>
        </div>
        <div class="col-md-4">
            <div class="card">
                <div class="card-header">
                    <h5>Orders by Status</h5>
                </div>
                <div class="card-body">
                    <canvas id="statusChart"></canvas>
                </div>
            </div>
        </div>
    </div>
</div>

This comprehensive order management system provides everything needed to handle the complete order lifecycle in an ecommerce application, from creation to fulfillment and analytics.