# 📦 ORDER MANAGEMENT SYSTEM

## ✅ Complete Implementation

---

## 📊 Order Flow Diagram

```
Customer Places Order
        ↓
    [PENDING] ← Order created, awaiting restaurant confirmation
        ↓
    [ACCEPTED] ← Restaurant accepts order
        ↓
    [PREPARING] ← Kitchen starts cooking
        ↓
    [READY] ← Food is ready for pickup
        ↓
    [PICKED_UP] ← Delivery boy picks up order
        ↓
    [ON_THE_WAY] ← Delivery in progress
        ↓
    [DELIVERED] ← Order delivered to customer
        ↓
    [COMPLETED] ← Order completed successfully

Alternative Flows:
    [CANCELLED] ← Customer/Admin cancels (only from pending/accepted)
    [REFUNDED] ← Payment refunded
```

---

## 📦 1. Database Migrations

### 1.1 Orders Table
**File:** `database/migrations/2024_01_01_000007_create_orders_table.php`

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->id();
            $table->string('order_number')->unique();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->foreignId('restaurant_id')->constrained()->onDelete('cascade');
            $table->enum('status', [
                'pending',
                'accepted',
                'preparing',
                'ready',
                'picked_up',
                'on_the_way',
                'delivered',
                'completed',
                'cancelled',
                'refunded'
            ])->default('pending');
            
            // Delivery Address
            $table->text('delivery_address');
            $table->decimal('delivery_latitude', 10, 7);
            $table->decimal('delivery_longitude', 10, 7);
            $table->string('delivery_phone');
            $table->text('delivery_instructions')->nullable();
            
            // Pricing
            $table->decimal('subtotal', 10, 2);
            $table->decimal('delivery_fee', 10, 2)->default(0);
            $table->decimal('service_charge', 10, 2)->default(0);
            $table->decimal('tax', 10, 2)->default(0);
            $table->decimal('discount', 10, 2)->default(0);
            $table->decimal('total', 10, 2);
            
            // Distance & Time
            $table->decimal('distance_km', 8, 2)->nullable();
            $table->integer('estimated_delivery_time')->nullable();
            
            // Payment
            $table->enum('payment_method', ['cash', 'card', 'wallet'])->default('cash');
            $table->enum('payment_status', ['pending', 'paid', 'failed', 'refunded'])->default('pending');
            
            // Notes
            $table->text('customer_notes')->nullable();
            $table->text('kitchen_notes')->nullable();
            $table->text('delivery_notes')->nullable();
            $table->text('cancellation_reason')->nullable();
            
            // Timestamps
            $table->timestamp('accepted_at')->nullable();
            $table->timestamp('preparing_at')->nullable();
            $table->timestamp('ready_at')->nullable();
            $table->timestamp('picked_up_at')->nullable();
            $table->timestamp('delivered_at')->nullable();
            $table->timestamp('cancelled_at')->nullable();
            
            $table->timestamps();
            $table->softDeletes();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('orders');
    }
};
```

---

### 1.2 Order Items Table
**File:** `database/migrations/2024_01_01_000008_create_order_items_table.php`

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('order_items', function (Blueprint $table) {
            $table->id();
            $table->foreignId('order_id')->constrained()->onDelete('cascade');
            $table->foreignId('product_id')->constrained()->onDelete('cascade');
            $table->string('product_name');
            $table->integer('quantity');
            $table->decimal('unit_price', 10, 2);
            $table->json('variations')->nullable();
            $table->json('addons')->nullable();
            $table->text('special_notes')->nullable();
            $table->decimal('total_price', 10, 2);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('order_items');
    }
};
```

**Run Migrations:**
```bash
php artisan migrate
```

---

## 🎯 2. Models

### 2.1 Order Model
**File:** `app/Models/Order.php`

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Order extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'order_number', 'user_id', 'restaurant_id', 'status', 'delivery_address',
        'delivery_latitude', 'delivery_longitude', 'delivery_phone', 'delivery_instructions',
        'subtotal', 'delivery_fee', 'service_charge', 'tax', 'discount', 'total',
        'distance_km', 'estimated_delivery_time', 'payment_method', 'payment_status',
        'customer_notes', 'kitchen_notes', 'delivery_notes', 'cancellation_reason',
        'accepted_at', 'preparing_at', 'ready_at', 'picked_up_at', 'delivered_at', 'cancelled_at'
    ];

    protected $casts = [
        'delivery_latitude' => 'decimal:7',
        'delivery_longitude' => 'decimal:7',
        'subtotal' => 'decimal:2',
        'delivery_fee' => 'decimal:2',
        'service_charge' => 'decimal:2',
        'tax' => 'decimal:2',
        'discount' => 'decimal:2',
        'total' => 'decimal:2',
        'distance_km' => 'decimal:2',
        'accepted_at' => 'datetime',
        'preparing_at' => 'datetime',
        'ready_at' => 'datetime',
        'picked_up_at' => 'datetime',
        'delivered_at' => 'datetime',
        'cancelled_at' => 'datetime',
    ];

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    public function restaurant(): BelongsTo
    {
        return $this->belongsTo(Restaurant::class);
    }

    public function items(): HasMany
    {
        return $this->hasMany(OrderItem::class);
    }

    public function delivery(): HasOne
    {
        return $this->hasOne(Delivery::class);
    }

    public function payment(): HasOne
    {
        return $this->hasOne(Payment::class);
    }

    public function reviews(): HasMany
    {
        return $this->hasMany(Review::class);
    }

    // Check if order can be cancelled
    public function canBeCancelled(): bool
    {
        return in_array($this->status, ['pending', 'accepted']);
    }

    // Check if status can be updated
    public function canUpdateStatus($newStatus): bool
    {
        $allowedTransitions = [
            'pending' => ['accepted', 'cancelled'],
            'accepted' => ['preparing', 'cancelled'],
            'preparing' => ['ready'],
            'ready' => ['picked_up'],
            'picked_up' => ['on_the_way'],
            'on_the_way' => ['delivered'],
            'delivered' => ['completed'],
        ];

        return in_array($newStatus, $allowedTransitions[$this->status] ?? []);
    }
}
```

---

### 2.2 OrderItem Model
**File:** `app/Models/OrderItem.php`

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class OrderItem extends Model
{
    protected $fillable = [
        'order_id', 'product_id', 'product_name', 'quantity', 'unit_price',
        'variations', 'addons', 'special_notes', 'total_price'
    ];

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

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

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

---

## 🎮 3. Order Controller

**File:** `app/Http/Controllers/API/OrderController.php`

```php
<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Cart;
use App\Models\Restaurant;
use App\Services\MapService;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class OrderController extends Controller
{
    // List orders
    public function index(Request $request)
    {
        $query = Order::with(['restaurant', 'items.product', 'delivery.deliveryBoy']);

        // Filter by user role
        if ($request->user()->hasRole('customer')) {
            $query->where('user_id', $request->user()->id);
        } elseif ($request->user()->hasRole('manager')) {
            $restaurantIds = $request->user()->restaurants->pluck('id');
            $query->whereIn('restaurant_id', $restaurantIds);
        }
        // Admin sees all orders

        $orders = $query->latest()->paginate(15);

        return response()->json($orders);
    }

    // Create order from cart
    public function store(Request $request)
    {
        $validated = $request->validate([
            'restaurant_id' => 'required|exists:restaurants,id',
            'delivery_address' => 'required|string',
            'delivery_latitude' => 'required|numeric',
            'delivery_longitude' => 'required|numeric',
            'delivery_phone' => 'required|string',
            'delivery_instructions' => 'nullable|string',
            'payment_method' => 'required|in:cash,card,wallet',
            'customer_notes' => 'nullable|string',
        ]);

        // Get cart items
        $carts = Cart::where('user_id', $request->user()->id)
            ->where('restaurant_id', $validated['restaurant_id'])
            ->get();

        if ($carts->isEmpty()) {
            return response()->json(['message' => 'Cart is empty'], 400);
        }

        // Get restaurant
        $restaurant = Restaurant::findOrFail($validated['restaurant_id']);

        // Calculate distance and fees
        $mapService = new MapService();
        $distance = $mapService->calculateDistance(
            $restaurant->latitude,
            $restaurant->longitude,
            $validated['delivery_latitude'],
            $validated['delivery_longitude']
        );

        $subtotal = $carts->sum('total_price');
        $deliveryFee = $mapService->calculateDeliveryFee($distance);
        $serviceCharge = $subtotal * ($restaurant->service_charge_percentage / 100);
        $tax = $subtotal * ($restaurant->tax_percentage / 100);
        $total = $subtotal + $deliveryFee + $serviceCharge + $tax;
        $estimatedTime = $mapService->estimateDeliveryTime($distance);

        // Create order
        $order = Order::create([
            'order_number' => 'ORD-' . strtoupper(Str::random(8)),
            'user_id' => $request->user()->id,
            'restaurant_id' => $validated['restaurant_id'],
            'delivery_address' => $validated['delivery_address'],
            'delivery_latitude' => $validated['delivery_latitude'],
            'delivery_longitude' => $validated['delivery_longitude'],
            'delivery_phone' => $validated['delivery_phone'],
            'delivery_instructions' => $validated['delivery_instructions'],
            'subtotal' => $subtotal,
            'delivery_fee' => $deliveryFee,
            'service_charge' => $serviceCharge,
            'tax' => $tax,
            'total' => $total,
            'distance_km' => $distance,
            'estimated_delivery_time' => $estimatedTime,
            'payment_method' => $validated['payment_method'],
            'customer_notes' => $validated['customer_notes'],
        ]);

        // Create order items
        foreach ($carts as $cart) {
            OrderItem::create([
                'order_id' => $order->id,
                'product_id' => $cart->product_id,
                'product_name' => $cart->product->name,
                'quantity' => $cart->quantity,
                'unit_price' => $cart->unit_price,
                'variations' => $cart->variations,
                'addons' => $cart->addons,
                'special_notes' => $cart->special_notes,
                'total_price' => $cart->total_price,
            ]);
        }

        // Clear cart
        Cart::where('user_id', $request->user()->id)
            ->where('restaurant_id', $validated['restaurant_id'])
            ->delete();

        return response()->json([
            'message' => 'Order placed successfully',
            'order' => $order->load('items')
        ], 201);
    }

    // Get order details
    public function show($id)
    {
        $order = Order::with(['restaurant', 'items.product', 'delivery.deliveryBoy', 'payment'])
            ->findOrFail($id);

        return response()->json($order);
    }

    // Update order status
    public function updateStatus(Request $request, $id)
    {
        $validated = $request->validate([
            'status' => 'required|in:accepted,preparing,ready,picked_up,on_the_way,delivered,completed',
        ]);

        $order = Order::findOrFail($id);

        // Check if status transition is allowed
        if (!$order->canUpdateStatus($validated['status'])) {
            return response()->json([
                'message' => "Cannot change status from {$order->status} to {$validated['status']}"
            ], 400);
        }

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

        // Update timestamps
        $timestampMap = [
            'accepted' => 'accepted_at',
            'preparing' => 'preparing_at',
            'ready' => 'ready_at',
            'picked_up' => 'picked_up_at',
            'delivered' => 'delivered_at',
        ];

        if (isset($timestampMap[$validated['status']])) {
            $order->update([$timestampMap[$validated['status']] => now()]);
        }

        return response()->json([
            'message' => 'Order status updated',
            'order' => $order
        ]);
    }

    // Cancel order
    public function cancel(Request $request, $id)
    {
        $order = Order::findOrFail($id);

        if (!$order->canBeCancelled()) {
            return response()->json([
                'message' => 'Order cannot be cancelled at this stage'
            ], 400);
        }

        $validated = $request->validate([
            'reason' => 'nullable|string|max:500',
        ]);

        $order->update([
            'status' => 'cancelled',
            'cancelled_at' => now(),
            'cancellation_reason' => $validated['reason'] ?? 'Cancelled by user',
        ]);

        return response()->json([
            'message' => 'Order cancelled successfully',
            'order' => $order
        ]);
    }
}
```

---

## 🛣️ 4. API Routes

**File:** `routes/api.php`

```php
Route::middleware('auth:sanctum')->group(function () {
    
    // Orders
    Route::get('/orders', [OrderController::class, 'index']);
    Route::post('/orders', [OrderController::class, 'store']);
    Route::get('/orders/{id}', [OrderController::class, 'show']);
    Route::put('/orders/{id}/status', [OrderController::class, 'updateStatus']);
    Route::post('/orders/{id}/cancel', [OrderController::class, 'cancel']);
});
```

---

## 📡 5. API Examples

### 5.1 Place Order
```http
POST /api/orders
Authorization: Bearer {token}
Content-Type: application/json

{
  "restaurant_id": 1,
  "delivery_address": "123 Main St, Colombo",
  "delivery_latitude": 6.9271,
  "delivery_longitude": 79.8612,
  "delivery_phone": "0771234567",
  "delivery_instructions": "Call when you arrive",
  "payment_method": "cash",
  "customer_notes": "Extra napkins please"
}
```

**Response (201):**
```json
{
  "message": "Order placed successfully",
  "order": {
    "id": 1,
    "order_number": "ORD-ABC12345",
    "status": "pending",
    "subtotal": 2900,
    "delivery_fee": 200,
    "service_charge": 145,
    "tax": 290,
    "total": 3535,
    "distance_km": 5.2,
    "estimated_delivery_time": 35,
    "items": [...]
  }
}
```

---

### 5.2 Get My Orders
```http
GET /api/orders
Authorization: Bearer {token}
```

**Response:**
```json
{
  "data": [
    {
      "id": 1,
      "order_number": "ORD-ABC12345",
      "status": "pending",
      "total": 3535,
      "restaurant": {
        "name": "Pizza Palace"
      },
      "created_at": "2024-01-15T10:30:00Z"
    }
  ]
}
```

---

### 5.3 Get Order Details
```http
GET /api/orders/1
Authorization: Bearer {token}
```

**Response:**
```json
{
  "id": 1,
  "order_number": "ORD-ABC12345",
  "status": "preparing",
  "delivery_address": "123 Main St",
  "subtotal": 2900,
  "delivery_fee": 200,
  "service_charge": 145,
  "tax": 290,
  "total": 3535,
  "items": [
    {
      "product_name": "Margherita Pizza",
      "quantity": 2,
      "unit_price": 1000,
      "total_price": 2900,
      "variations": [...],
      "addons": [...]
    }
  ],
  "restaurant": {...},
  "delivery": {...}
}
```

---

### 5.4 Update Order Status (Manager/Cashier)
```http
PUT /api/orders/1/status
Authorization: Bearer {manager_token}
Content-Type: application/json

{
  "status": "accepted"
}
```

**Response:**
```json
{
  "message": "Order status updated",
  "order": {
    "id": 1,
    "status": "accepted",
    "accepted_at": "2024-01-15T10:35:00Z"
  }
}
```

---

### 5.5 Cancel Order
```http
POST /api/orders/1/cancel
Authorization: Bearer {token}
Content-Type: application/json

{
  "reason": "Changed my mind"
}
```

**Response:**
```json
{
  "message": "Order cancelled successfully",
  "order": {
    "id": 1,
    "status": "cancelled",
    "cancelled_at": "2024-01-15T10:40:00Z",
    "cancellation_reason": "Changed my mind"
  }
}
```

---

## 🔄 Order Status Flow

### Status Transitions

```php
'pending' → ['accepted', 'cancelled']
'accepted' → ['preparing', 'cancelled']
'preparing' → ['ready']
'ready' → ['picked_up']
'picked_up' → ['on_the_way']
'on_the_way' → ['delivered']
'delivered' → ['completed']
```

### Cancellation Rules

- ✅ Can cancel: `pending`, `accepted`
- ❌ Cannot cancel: `preparing`, `ready`, `picked_up`, `on_the_way`, `delivered`

---

## 👥 Role-Based Actions

| Action | Customer | Cashier | Manager | Admin |
|--------|----------|---------|---------|-------|
| Place order | ✅ | ❌ | ❌ | ✅ |
| View own orders | ✅ | ❌ | ❌ | ✅ |
| View restaurant orders | ❌ | ✅ | ✅ | ✅ |
| Update status | ❌ | ✅ | ✅ | ✅ |
| Cancel order | ✅ (own) | ✅ | ✅ | ✅ |
| View all orders | ❌ | ❌ | ❌ | ✅ |

---

## 📄 Invoice Data Structure

```json
{
  "order_number": "ORD-ABC12345",
  "date": "2024-01-15",
  "customer": {
    "name": "John Doe",
    "phone": "0771234567",
    "address": "123 Main St"
  },
  "restaurant": {
    "name": "Pizza Palace",
    "phone": "0112345678"
  },
  "items": [
    {
      "name": "Margherita Pizza (Large)",
      "quantity": 2,
      "unit_price": 1300,
      "total": 2600
    }
  ],
  "subtotal": 2900,
  "delivery_fee": 200,
  "service_charge": 145,
  "tax": 290,
  "total": 3535,
  "payment_method": "Cash on Delivery"
}
```

---

## ✅ Features Implemented

- ✅ Place order from cart
- ✅ Auto price calculation
- ✅ Distance-based delivery fee
- ✅ Estimated delivery time
- ✅ Order status tracking
- ✅ Status transition validation
- ✅ Cancellation rules
- ✅ Role-based access
- ✅ Order history
- ✅ Order details
- ✅ Customer/Kitchen/Delivery notes
- ✅ Timestamps for each status
- ✅ Invoice data support

---

## 🧪 Test Flow

```bash
# 1. Login as customer
POST /api/login

# 2. Add items to cart
POST /api/cart

# 3. Place order
POST /api/orders

# 4. View order
GET /api/orders/1

# 5. Login as manager
POST /api/login

# 6. Accept order
PUT /api/orders/1/status
{"status": "accepted"}

# 7. Update to preparing
PUT /api/orders/1/status
{"status": "preparing"}

# 8. Mark as ready
PUT /api/orders/1/status
{"status": "ready"}

# 9. Delivery boy picks up
PUT /api/orders/1/status
{"status": "picked_up"}

# 10. On the way
PUT /api/orders/1/status
{"status": "on_the_way"}

# 11. Delivered
PUT /api/orders/1/status
{"status": "delivered"}

# 12. Complete
PUT /api/orders/1/status
{"status": "completed"}
```

---

**ORDER MANAGEMENT SYSTEM COMPLETE!** 📦✅
