Below is a proposed developer‐focused documentation (in Markdown) for the complete delivery subsystem you now have in place. It covers:

1. High‐level overview
2. Schema & table descriptions (with columns, indexes, and relationships)
3. Eloquent model sketches (showing key relationships)
4. Typical usage flows / code examples
5. Notes on slot reservation, capacity, and event tracking

Feel free to adapt any section to your own style or fill in additional details (e.g., API routes, front-end snippets) as needed.

---

## Delivery Subsystem Documentation

### 1 Overview

This delivery subsystem is designed to handle:

* **Slot‐based scheduling** (weekly time slots + date exceptions, with capacity enforcement)
* **Order‐level deliveries** (one or more items per delivery)
* **Carrier/provider support**, including both in‐house fleet and third‐party couriers
* **Address snapshots** for each order (in case the user later edits their stored address)
* **Slot reservations** (15‐minute holds to prevent over-booking)
* **Event tracking** (immutable timeline of status changes + carrier webhooks)
* **Daily capacity caching** for quick “remaining slots” lookups in high-traffic flows

Below is a breakdown of every table, its purpose, and how it links into the larger workflow. Wherever you see `→`, it denotes a foreign‐key relationship.

---

### 2 Schema & Table Descriptions

#### 2.1 `delivery_providers`

* **Purpose:** Stores all possible carriers or internal fleet names.  Used by the `deliveries` table to tie a specific shipment to a provider.

* **Structure:**

  | Column          | Type          | Attributes                             |
  | --------------- | ------------- | -------------------------------------- |
  | `id`            | `bigint` (PK) | Auto‐increment                         |
  | `name`          | `string`      | `unique` (e.g. `"DHL"`, `"InHouse"`)   |
  | `code`          | `string`      | Nullable (e.g. `"DHL_DE"`, `"DPD_UK"`) |
  | `contact_phone` | `string`      | Nullable                               |
  | `contact_email` | `string`      | Nullable                               |
  | `notes`         | `text`        | Nullable                               |
  | `created_at`    | `timestamp`   | Laravel default                        |
  | `updated_at`    | `timestamp`   | Laravel default                        |

* **Relationships:**

  * One‐to‐many → `deliveries.provider_id` (nullable).

    * When a delivery is created, you can optionally assign a `provider_id`.
    * If the provider is deleted, `provider_id` on existing deliveries is set to `null` (via `nullOnDelete()`).

---

#### 2.2 `delivery_times`

* **Purpose:** Defines the *recurring* weekly delivery “slots.”  Each record is a time window on a given weekday (0=Sunday, 1=Monday, …, 6=Saturday) with a max capacity.

* **Structure:**

  | Column       | Type                  | Attributes                                       |
  | ------------ | --------------------- | ------------------------------------------------ |
  | `id`         | `bigint` (PK)         | Auto‐increment                                   |
  | `day`        | `unsignedTinyInteger` | Values 0..6 (0 = Sunday, … 6 = Saturday)         |
  | `from`       | `time`                | Start of time window (e.g. `08:00:00`)           |
  | `until`      | `time`                | End of time window (e.g. `12:00:00`)             |
  | `capacity`   | `unsignedInteger`     | Maximum deliveries allowed in this slot per date |
  | `created_at` | `timestamp`           | Laravel default                                  |
  | `updated_at` | `timestamp`           | Laravel default                                  |

* **Indexes & Constraints:**

  * `unique(['day', 'from', 'until'])`

    * Prevents duplicate or exact overlapping slots for the same weekday.  (If you need to guard *partially* overlapping time ranges, enforce it in application logic or a database CHECK/trigger.)

* **Relationships:**

  * One‐to‐many → `delivery_time_exceptions.delivery_time_id`
  * One‐to‐many → `delivery_day_capacities.delivery_time_id`
  * One‐to‐many → `delivery_time_reservations.delivery_time_id`
  * One‐to‐many → `deliveries.delivery_time_id`

---

#### 2.3 `delivery_time_exceptions`

* **Purpose:** Lists specific calendar dates on which a given weekly slot is *not available*.  For example, if “Monday 08:00–12:00” should not run on Christmas, add `(delivery_time_id = X, date = ’2025-12-25’)`.

* **Structure:**

  | Column             | Type          | Attributes                                        |
  | ------------------ | ------------- | ------------------------------------------------- |
  | `id`               | `bigint` (PK) | Auto‐increment                                    |
  | `delivery_time_id` | `bigint` (FK) | References `delivery_times.id`, cascade on delete |
  | `date`             | `date`        | e.g. `2025-12-25`                                 |
  | `created_at`       | `timestamp`   | Laravel default                                   |
  | `updated_at`       | `timestamp`   | Laravel default                                   |

* **Indexes & Constraints:**

  * `unique(['delivery_time_id', 'date'])`

    * Prevents duplicate exception entries.

* **Relationships:**

  * Belongs to → `delivery_times`

---

#### 2.4 `delivery_day_capacities` (optional but highly recommended)

* **Purpose:** Caches the number of *already booked* deliveries for each `(date, delivery_time_id)` pair.  This makes “remaining capacity” queries O(1) instead of scanning all `deliveries` each time.

* **Structure:**

  | Column             | Type              | Attributes                                        |
  | ------------------ | ----------------- | ------------------------------------------------- |
  | `id`               | `bigint` (PK)     | Auto‐increment                                    |
  | `date`             | `date`            | The actual calendar date (e.g. `2025-06-10`)      |
  | `delivery_time_id` | `bigint` (FK)     | References `delivery_times.id`, cascade on delete |
  | `taken`            | `unsignedInteger` | Number of deliveries already scheduled            |
  | `created_at`       | `timestamp`       | Laravel default                                   |
  | `updated_at`       | `timestamp`       | Laravel default                                   |

* **Indexes & Constraints:**

  * `unique(['date', 'delivery_time_id'])`

    * Ensures exactly one “cache row” per date/slot.

* **Relationships:**

  * Belongs to → `delivery_times`
  * Used in code to increment/decrement when a delivery is created/cancelled.

---

#### 2.5 `delivery_time_reservations`

* **Purpose:** Temporarily “locks” a slot for a user (e.g., at the moment they click “Select this slot” in checkout), preventing another user from taking the last spot in that window while they complete payment/checkout.

* **Structure:**

  | Column             | Type          | Attributes                                                  |
  | ------------------ | ------------- | ----------------------------------------------------------- |
  | `id`               | `bigint` (PK) | Auto‐increment                                              |
  | `user_id`          | `bigint` (FK) | References `users.id`, cascade on delete                    |
  | `delivery_time_id` | `bigint` (FK) | References `delivery_times.id`, cascade on delete           |
  | `date`             | `date`        | The date the user wants (e.g. `2025-06-10`)                 |
  | `expires_at`       | `dateTime`    | When the hold automatically expires (e.g. now()+15 minutes) |
  | `created_at`       | `timestamp`   | Laravel default                                             |
  | `updated_at`       | `timestamp`   | Laravel default                                             |

* **Indexes & Constraints:**

  * `unique(['user_id', 'delivery_time_id', 'date'], 'res_user_time_date_unique')`

    * Ensures a given user can’t reserve the exact same slot/date twice.
  * `index('expires_at')`

    * For fast cleanup of expired reservations via a scheduled job.

* **Relationships:**

  * Belongs to → `users`
  * Belongs to → `delivery_times`

---

#### 2.6 `deliveries`

* **Purpose:** Each record represents one physical delivery attempt for an order.  An order can map to multiple deliveries (if you batch multiple orders into one route, or split a big order into multiple shipments).

* **Structure:**

  | Column             | Type            | Attributes                                                       |
  | ------------------ | --------------- | ---------------------------------------------------------------- |
  | `id`               | `bigint` (PK)   | Auto‐increment                                                   |
  | `order_id`         | `bigint` (FK)   | References `orders.id`, cascade on delete                        |
  | `delivery_time_id` | `bigint` (FK)   | References `delivery_times.id`, cascade on delete                |
  | `user_id`          | `bigint` (FK)   | References `users.id`, nullable, nullOnDelete                    |
  | `address_id`       | `bigint` (FK)   | References `order_delivery_addresses.id`, cascadeOnDelete        |
  | `tracking_code`    | `string`        | Unique; external tracking code                                   |
  | `type`             | `enum`          | Values from `DeliveryType` enum (e.g. `standard`, `express`)     |
  | `date`             | `date`          | Actual chosen calendar date                                      |
  | `time`             | `time`          | Optional ETA or window (if known)                                |
  | `status`           | `enum`          | Values from `DeliveryStatus` enum (e.g. `pending`, `in_transit`) |
  | `price`            | `decimal(10,2)` | Delivery charge                                                  |
  | `provider_id`      | `bigint` (FK)   | References `delivery_providers.id`, nullable, nullOnDelete       |
  | `carrier`          | `string`        | Optional: carrier name override (e.g. `"DHL"`)                   |
  | `carrier_code`     | `string`        | Optional: carrier’s internal shipment code                       |
  | `origin_lat`       | `decimal(10,8)` | Coordinates (if using in‐house routing)                          |
  | `origin_lon`       | `decimal(11,8)` | Coordinates                                                      |
  | `dest_lat`         | `decimal(10,8)` | Coordinates of final dropoff                                     |
  | `dest_lon`         | `decimal(11,8)` | Coordinates                                                      |
  | `picked_up_at`     | `dateTime`      | When the driver picked up the package                            |
  | `delivered_at`     | `dateTime`      | Actual delivery timestamp                                        |
  | `created_at`       | `timestamp`     | Laravel default                                                  |
  | `updated_at`       | `timestamp`     | Laravel default                                                  |

* **Indexes & Constraints:**

  * `index(['status', 'date'])`  — to quickly find “pending” or “in\_transit” deliveries by date.
  * `unique(['tracking_code'])`
  * Foreign keys all use `constrained()->cascadeOnDelete()` (except `user_id` & `provider_id` which use `nullOnDelete()`).

* **Relationships:**

  * Belongs to → `orders`
  * Belongs to → `delivery_times`
  * Belongs to → `users` (nullable; the user who requested it)
  * Belongs to → `order_delivery_addresses`
  * Belongs to → `delivery_providers` (nullable)
  * Has many → `delivery_items`
  * Has many → `delivery_events`

---

#### 2.7 `delivery_items`

* **Purpose:** Pivot table linking each delivery to the specific `order_items` being shipped.  Allows partial shipments or grouping multiple `order_items` under one `delivery_id`.

* **Structure:**

  | Column          | Type            | Attributes                                     |
  | --------------- | --------------- | ---------------------------------------------- |
  | `id`            | `bigint` (PK)   | Auto‐increment                                 |
  | `delivery_id`   | `bigint` (FK)   | References `deliveries.id`, cascade on delete  |
  | `order_item_id` | `bigint` (FK)   | References `order_items.id`, cascade on delete |
  | `weight`        | `decimal(10,2)` | Optional: store weight for packaging           |
  | `barcode`       | `string`        | Optional: printed barcode/label ID             |
  | `created_at`    | `timestamp`     | Laravel default                                |
  | `updated_at`    | `timestamp`     | Laravel default                                |

* **Relationships:**

  * Belongs to → `deliveries`
  * Belongs to → `order_items`

---

#### 2.8 `delivery_events`

* **Purpose:** Immutable log of every status change or carrier webhook call for a given `delivery_id`.  Powers the “Tracking Timeline” that you can show customers.

* **Structure:**

  | Column        | Type          | Attributes                                                                                              |
  | ------------- | ------------- | ------------------------------------------------------------------------------------------------------- |
  | `id`          | `bigint` (PK) | Auto‐increment                                                                                          |
  | `delivery_id` | `bigint` (FK) | References `deliveries.id`, cascade on delete                                                           |
  | `status`      | `enum`        | E.g. `pending`, `ready_for_pickup`, `in_transit`, `out_for_delivery`, `delivered`, `failed`, `returned` |
  | `payload`     | `json`        | Arbitrary carrier data or extra metadata (nullable)                                                     |
  | `happened_at` | `dateTime`    | Timestamp when this event occurred                                                                      |
  | `created_at`  | `timestamp`   | Laravel default                                                                                         |
  | `updated_at`  | `timestamp`   | Laravel default                                                                                         |

* **Indexes & Constraints:**

  * `index(['status'])` & `index(['happened_at'])`
  * `index(['delivery_id', 'happened_at'])` — for fast timeline retrieval per delivery.

* **Relationships:**

  * Belongs to → `deliveries`

---

#### 2.9 `order_delivery_addresses`

* **Purpose:** Snapshots the address details exactly as the customer entered them at checkout, so that if the user later edits their saved address, the historical “Ship To” on an order remains correct.

* **Structure:**

  | Column           | Type            | Attributes                                |
  | ---------------- | --------------- | ----------------------------------------- |
  | `id`             | `bigint` (PK)   | Auto‐increment                            |
  | `order_id`       | `bigint` (FK)   | References `orders.id`, cascade on delete |
  | `recipient_name` | `string`        | Name printed on shipping label            |
  | `address_line1`  | `string`        | Street + number                           |
  | `address_line2`  | `string`        | Nullable                                  |
  | `city`           | `string`        |                                           |
  | `state`          | `string`        | Nullable                                  |
  | `postal_code`    | `string`        |                                           |
  | `country`        | `string`        |                                           |
  | `phone`          | `string`        | Nullable                                  |
  | `latitude`       | `decimal(10,8)` | Optional geocoded latitude                |
  | `longitude`      | `decimal(11,8)` | Optional geocoded longitude               |
  | `created_at`     | `timestamp`     | Laravel default                           |
  | `updated_at`     | `timestamp`     | Laravel default                           |

* **Indexes & Constraints:**

  * `unique(['order_id'])` — one snapshot per order.

* **Relationships:**

  * Belongs to → `orders`

---

### 3 Eloquent Model Sketches

Below are minimal examples showing key relationships.  Adjust namespace paths and fill in fillable/casts as desired.

```php
<?php
// app/Models/DeliveryProvider.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeliveryProvider extends Model
{
    protected $fillable = [
        'name', 'code', 'contact_phone', 'contact_email', 'notes',
    ];

    public function deliveries()
    {
        return $this->hasMany(Delivery::class);
    }
}
```

```php
<?php
// app/Models/DeliveryTime.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeliveryTime extends Model
{
    protected $fillable = [
        'day', 'from', 'until', 'capacity',
    ];

    public function exceptions()
    {
        return $this->hasMany(DeliveryTimeException::class);
    }

    public function dayCapacities()
    {
        return $this->hasMany(DeliveryDayCapacity::class);
    }

    public function reservations()
    {
        return $this->hasMany(DeliveryTimeReservation::class);
    }

    public function deliveries()
    {
        return $this->hasMany(Delivery::class);
    }
}
```

```php
<?php
// app/Models/DeliveryTimeException.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeliveryTimeException extends Model
{
    protected $fillable = ['delivery_time_id', 'date'];

    public function deliveryTime()
    {
        return $this->belongsTo(DeliveryTime::class);
    }
}
```

```php
<?php
// app/Models/DeliveryDayCapacity.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeliveryDayCapacity extends Model
{
    protected $fillable = ['date', 'delivery_time_id', 'taken'];

    public function deliveryTime()
    {
        return $this->belongsTo(DeliveryTime::class);
    }
}
```

```php
<?php
// app/Models/DeliveryTimeReservation.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeliveryTimeReservation extends Model
{
    protected $fillable = [
        'user_id', 'delivery_time_id', 'date', 'expires_at',
    ];

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

    public function deliveryTime()
    {
        return $this->belongsTo(DeliveryTime::class);
    }
}
```

```php
<?php
// app/Models/Delivery.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Delivery extends Model
{
    protected $fillable = [
        'order_id',
        'delivery_time_id',
        'user_id',
        'address_id',
        'tracking_code',
        'type',
        'date',
        'time',
        'status',
        'price',
        'provider_id',
        'carrier',
        'carrier_code',
        'origin_lat',
        'origin_lon',
        'dest_lat',
        'dest_lon',
        'picked_up_at',
        'delivered_at',
    ];

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

    public function deliveryTime()
    {
        return $this->belongsTo(DeliveryTime::class);
    }

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

    public function address()
    {
        return $this->belongsTo(OrderDeliveryAddress::class, 'address_id');
    }

    public function provider()
    {
        return $this->belongsTo(DeliveryProvider::class);
    }

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

    public function events()
    {
        return $this->hasMany(DeliveryEvent::class)
                    ->orderBy('happened_at', 'asc');
    }
}
```

```php
<?php
// app/Models/DeliveryItem.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeliveryItem extends Model
{
    protected $fillable = ['delivery_id', 'order_item_id', 'weight', 'barcode'];

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

    public function orderItem()
    {
        return $this->belongsTo(OrderItem::class);
    }
}
```

```php
<?php
// app/Models/DeliveryEvent.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class DeliveryEvent extends Model
{
    protected $fillable = ['delivery_id', 'status', 'payload', 'happened_at'];

    protected $casts = [
        'payload'     => 'array',
        'happened_at' => 'datetime',
    ];

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

```php
<?php
// app/Models/OrderDeliveryAddress.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class OrderDeliveryAddress extends Model
{
    protected $fillable = [
        'order_id',
        'recipient_name',
        'address_line1',
        'address_line2',
        'city',
        'state',
        'postal_code',
        'country',
        'phone',
        'latitude',
        'longitude',
    ];

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

---

### 4 Typical Usage Flows & Code Examples

Below are a few common scenarios, with example code snippets, showing how you might leverage the above tables and models in practice.

#### 4.1 Fetching Available Slots for a Given Date

**Goal:** Show user all weekly slots for “2025-06-10 (Wednesday)” that aren’t fully booked and aren’t in the exception table.

```php
use App\Models\DeliveryTime;
use App\Models\DeliveryTimeException;
use App\Models\DeliveryDayCapacity;
use Illuminate\Support\Carbon;

$requestedDate = Carbon::parse('2025-06-10'); // Wednesday

// 1) Find which weekday index that is:
$weekday = $requestedDate->dayOfWeek; // 0 (Sun) … 6 (Sat) => here, $weekday = 3 (Wed)

// 2) Load all DeliveryTime rows where day = $weekday
$query = DeliveryTime::where('day', $weekday);

// 3) Exclude any that have an exception on this date:
$query->whereDoesntHave('exceptions', function($q) use ($requestedDate) {
    $q->where('date', $requestedDate->toDateString());
});

// 4) Join (or eager‐load) the “cache” table to see how many have been taken:
$slots = $query->with(['dayCapacities' => function($q) use ($requestedDate) {
    $q->where('date', $requestedDate->toDateString());
}])->get();

// 5) Build a “remaining capacity” value
$slots = $slots->map(function(DeliveryTime $slot) use ($requestedDate) {
    $cache = $slot->dayCapacities->first(); 
    $taken = $cache ? $cache->taken : 0;
    $remaining = max(0, $slot->capacity - $taken);

    return [
        'id'            => $slot->id,
        'from'          => $slot->from,
        'until'         => $slot->until,
        'capacity'      => $slot->capacity,
        'remaining'     => $remaining,
        'is_full'       => $remaining === 0,
    ];
});

// 6) Return or pass $slots to the view, greying out any with 'is_full' = true
return view('checkout.select_slot', compact('slots', 'requestedDate'));
```

> **Note:** If you did not create the `delivery_day_capacities` cache table, you can instead do a subquery or `selectRaw('count(*) as taken…')` on the `deliveries` table—but that will be slower under heavy load.

---

#### 4.2 Reserving a Slot for 15 Minutes (Cart Hold)

**Goal:** When the user clicks “Reserve this slot,” create a row in `delivery_time_reservations`, setting `expires_at = now()->addMinutes(15)`.  You may also simultaneously increment `delivery_day_capacities.taken`, or defer that until actual order placement.  Below shows a pattern where you reserve first (holding the spot), then decrement/clear if they abandon.

```php
use App\Models\DeliveryTimeReservation;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

// Controller method: user presses “Reserve Slot” on checkout
public function reserveSlot(Request $request)
{
    $request->validate([
        'delivery_time_id' => 'required|exists:delivery_times,id',
        'date'             => 'required|date|after_or_equal:today',
    ]);

    $user       = Auth::user();
    $slotId     = $request->input('delivery_time_id');
    $slotDate   = Carbon::parse($request->input('date'))->startOfDay();

    // 1) Create a reservation (or update existing one)
    $reservation = DeliveryTimeReservation::updateOrCreate(
        [
            'user_id' => $user->id,
            'delivery_time_id' => $slotId,
            'date' => $slotDate->toDateString(),
        ],
        [
            'expires_at' => Carbon::now()->addMinutes(15),
        ]
    );

    return response()->json([
        'message' => 'Slot reserved for 15 minutes.',
        'expires_at' => $reservation->expires_at->toDateTimeString(),
    ]);
}
```

> You’ll need a scheduled job (e.g. every minute or every 5 minutes) that:
>
> 1. Finds all `delivery_time_reservations` where `expires_at` ≤ `now()`.
> 2. Deletes them (and, if you pre-incremented the capacity cache, decrements `delivery_day_capacities.taken`).

---

#### 4.3 Placing an Order (Creating `orders` → `deliveries` → `delivery_items`)

**Goal:** Inside a DB transaction, create the order, copy address snapshot, assign a delivery record, then attach delivery items.

```php
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\OrderDeliveryAddress;
use App\Models\Delivery;
use App\Models\DeliveryItem;
use App\Models\DeliveryDayCapacity;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

public function placeOrder(Request $request)
{
    // Assume validation of cart contents, address fields, slot_id, etc.

    $user      = $request->user();
    $cartItems = $request->input('cart_items'); // e.g. [[sku_id, qty], …]
    $slotId    = $request->input('delivery_time_id');
    $slotDate  = $request->input('delivery_date'); // '2025-06-10'
    $address   = $request->only([
        'recipient_name',
        'address_line1',
        'address_line2',
        'city',
        'state',
        'postal_code',
        'country',
        'phone',
    ]);

    DB::transaction(function () use (
        $user, $cartItems, $slotId, $slotDate, $address
    ) {
        // 1) Create the Order
        $order = Order::create([
            'user_id' => $user->id,
            // … any other order‐level fields (subtotal, tax, etc.)
        ]);

        // 2) Create OrderItems
        foreach ($cartItems as $itemData) {
            // Assume you fetch product_price, etc.
            OrderItem::create([
                'order_id'   => $order->id,
                'product_id' => $itemData['product_id'],
                'quantity'   => $itemData['quantity'],
                'price'      => $itemData['unit_price'],
                // …
            ]);
        }

        // 3) Snapshot the delivery address into order_delivery_addresses
        $addressSnapshot = OrderDeliveryAddress::create(array_merge(
            ['order_id' => $order->id],
            $address,
            // If you already geocoded, include lat/lng here
            ['latitude'  => null],
            ['longitude' => null]
        ));

        // 4) Create the Delivery record
        $trackingCode = strtoupper(Str::random(12)); 
        $delivery = Delivery::create([
            'order_id'         => $order->id,
            'delivery_time_id' => $slotId,
            'user_id'          => $user->id,
            'address_id'       => $addressSnapshot->id,
            'tracking_code'    => $trackingCode,
            'type'             => 'standard',
            'date'             => $slotDate,
            'status'           => 'pending',
            'price'            => 0, // Compute shipping cost
            'provider_id'      => null, 
            // Optionally, compute and store origin/dest lat/lng if using mapping APIs
            'origin_lat'       => null,
            'origin_lon'       => null,
            'dest_lat'         => null,
            'dest_lon'         => null,
        ]);

        // 5) Attach DeliveryItems for each OrderItem
        foreach ($order->orderItems as $orderItem) {
            DeliveryItem::create([
                'delivery_id'   => $delivery->id,
                'order_item_id' => $orderItem->id,
                'weight'        => $orderItem->product->weight ?? null,
                'barcode'       => null,
            ]);
        }

        // 6) Increment capacity‐cache (if using caching)
        $cacheRow = DeliveryDayCapacity::firstOrCreate(
            ['date' => $slotDate, 'delivery_time_id' => $slotId],
            ['taken' => 0]
        );
        $cacheRow->increment('taken', 1);

        // 7) Optionally: Delete the user’s slot reservation (and/or cancel holds)
        DeliveryTimeReservation::where([
            ['user_id', '=', $user->id],
            ['delivery_time_id', '=', $slotId],
            ['date', '=', $slotDate],
        ])->delete();
    });

    return redirect()->route('order.confirmation')->with('message', 'Order placed successfully.');
}
```

> After the transaction:
>
> 1. An `order` record exists.
> 2. `order_delivery_addresses` holds a snapshot.
> 3. `deliveries` has status `pending`.
> 4. Capacity cache updated.
>
> Next, your warehouse logic or admin panel will pick up `deliveries` with `status = pending`.

---

#### 4.4 Warehouse / Courier Workflow

* **Warehouse picks & packs:**

  ```php
  // Admin action: mark as “Ready for Pickup”
  public function markReadyForPickup(Delivery $delivery)
  {
      $delivery->update([
          'status' => 'ready_for_pickup',
      ]);

      // Log an event
      DeliveryEvent::create([
          'delivery_id' => $delivery->id,
          'status'      => 'ready_for_pickup',
          'payload'     => null,
          'happened_at' => now(),
      ]);
  }
  ```

* **Courier handoff (webhook):**
  Courier sends a webhook when they “pick up” the parcel. Your endpoint does:

  ```php
  public function carrierWebhook(Request $request)
  {
      $tracking = $request->input('tracking_code');
      $newStatus = $request->input('status'); // e.g. “in_transit”
      $metadata  = $request->input('carrier_payload'); // entire JSON payload

      $delivery = Delivery::where('tracking_code', $tracking)->firstOrFail();

      // Update status
      $delivery->update([
          'status' => $newStatus,
          'carrier' => $delivery->provider ? $delivery->provider->name : $delivery->carrier,
          'carrier_code' => $delivery->carrier_code, // if any
      ]);

      // Log the event
      DeliveryEvent::create([
          'delivery_id' => $delivery->id,
          'status'      => $newStatus,
          'payload'     => $metadata,
          'happened_at' => now(),
      ]);
  }
  ```

* **Driver out for delivery**

  * Your last‐mile mobile app calls an API to mark “out\_for\_delivery.”
  * Optionally supply `origin_lat` / `origin_lon` (current driver position).
  * Example:

    ```php
    public function outForDelivery(Delivery $delivery, Request $request)
    {
        $delivery->update([
            'status'       => 'out_for_delivery',
            'origin_lat'   => $request->input('latitude'),
            'origin_lon'   => $request->input('longitude'),
        ]);
        DeliveryEvent::create([
            'delivery_id' => $delivery->id,
            'status'      => 'out_for_delivery',
            'payload'     => ['lat' => $delivery->origin_lat, 'lon' => $delivery->origin_lon],
            'happened_at' => now(),
        ]);
    }
    ```

* **Driver marks as “Delivered”**

  ```php
  public function markDelivered(Delivery $delivery)
  {
      $delivery->update([
          'status'       => 'delivered',
          'delivered_at' => now(),
      ]);
      DeliveryEvent::create([
          'delivery_id' => $delivery->id,
          'status'      => 'delivered',
          'payload'     => null,
          'happened_at' => now(),
      ]);
  }
  ```

---

### 5 Tracking & Timeline (Front-End)

To display a timeline on the customer’s “Order Details” page, you can fetch:

```php
$delivery    = Delivery::with('events')->findOrFail($deliveryId);
$timeline    = $delivery->events; // already ordered by happened_at ASC
```

Then render each event as a row in a vertical timeline:

```blade
<ul class="tracking-timeline">
  @foreach($timeline as $event)
    <li>
      <span class="timestamp">{{ $event->happened_at->format('Y-m-d H:i') }}</span>
      <span class="status-label">{{ Str::headline(str_replace('_', ' ', $event->status)) }}</span>
      @if($event->payload)
        <div class="payload-json">{{ json_encode($event->payload) }}</div>
      @endif
    </li>
  @endforeach
</ul>
```

---

## 6 Additional Notes & Best Practices

1. **Slot‐Capacity Enforcement**

* Always wrap creation of `delivery_day_capacities.taken` increments inside the same DB transaction that creates the `deliveries` row.  This ensures you never over-commit capacity.
* If two users race to the last spot, one transaction will fail the “unique cache row” or the “capacity < taken” check—catch that and prompt them to choose another slot.

2. **Reservation Cleanup**

* Set up a scheduled Laravel command (e.g. `php artisan schedule:run` with a job every minute) that purges any `delivery_time_reservations` where `expires_at <= now()`.  If you had pre-incremented capacity, decrement it here.

3. **Enumerations**

* Create PHP‐backed enums for `DeliveryStatus` and `DeliveryType`.  Example:

  ```php
  <?php
  // app/Enums/DeliveryStatus.php
  namespace App\Enums;

  enum DeliveryStatus: string
  {
      case Pending          = 'pending';
      case ReadyForPickup   = 'ready_for_pickup';
      case InTransit        = 'in_transit';
      case OutForDelivery   = 'out_for_delivery';
      case Delivered        = 'delivered';
      case Failed           = 'failed';
      case Returned         = 'returned';
  }
  ```
* Reference them in migrations (`enum('status', DeliveryStatus::values())`) and models (cast to `DeliveryStatus::class` if desired).

4. **Address Geocoding**

* If you want to auto‐geocode addresses (to fill `latitude`/`longitude`), consider a job that runs after `OrderDeliveryAddress::create()`, then updates the record.  You can cache those lookups for reusability.

5. **Soft Deletes**

* If your application relies on soft deletes for `orders` or `users`, consider whether you also want to soft‐delete `deliveries`, `delivery_items`, etc.  Currently we use `cascadeOnDelete()`, which hard‐deletes them.

6. **Carrier Webhooks**

* Build a dedicated controller to handle incoming status‐update webhooks from each provider.  Standardize the payload into your `delivery_events.payload` column.  Always verify signatures or API keys.

7. **Testing**

* Create seeders/factories for each model.  Example:

  ```php
  // in DeliveryTimeFactory:
  return [
    'day'      => fake()->numberBetween(0,6),
    'from'     => fake()->time('H:i:s'),
    'until'    => fake()->time('H:i:s', '+3 hours'),
    'capacity' => fake()->numberBetween(10, 50),
  ];
  ```
* Test:

  1. Fetching available slots (with/without exceptions).
  2. Making reservations, letting them expire, reclaiming capacity.
  3. Creating a delivery & ensuring `delivery_day_capacities.taken` increments.
  4. Full end-to-end: pick → pack → ship → delivered.

---

## 7 Summary

This documentation should give you everything you need to:

* Understand each new table, its columns, indexes, and relationships.
* Write or modify Eloquent models so that they link together correctly.
* Implement the critical flows: slot selection, reservation, order placement, capacity tracking, warehouse handoff, carrier webhooks, timeline display.
* Handle edge cases (exceptions, race conditions, address snapshots).

Once fully wired up, you’ll have a robust, scalable delivery module—complete with time‐slot scheduling, multiple carriers, detailed event logs, and capacity oversight.  Adjust enums, field lengths, or indices as your traffic grows.  Happy building!
