# POS Open Tab System Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add an open tab system to the POS so bar customers can order drinks over time and settle everything at the end.

**Architecture:** A new `open` status on `pos_transactions` lets the cashier create tabs, add items over multiple rounds, then settle with payment. Tabs are server-persisted so nothing is lost on refresh. Stock decrements at settlement time.

**Tech Stack:** Laravel 10.x, MySQL, TailwindCSS, vanilla JavaScript

---

### Task 1: Database Migration — Add `open` to Status ENUM

**Files:**
- Create: `database/migrations/2026_05_23_add_open_status_to_pos_transactions.php`

- [ ] **Step 1: Create migration file**

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

return new class extends Migration
{
    public function up(): void
    {
        DB::statement("ALTER TABLE pos_transactions MODIFY COLUMN status ENUM('completed', 'void', 'refunded', 'pending', 'open') DEFAULT 'completed'");
    }

    public function down(): void
    {
        DB::statement("ALTER TABLE pos_transactions MODIFY COLUMN status ENUM('completed', 'void', 'refunded', 'pending') DEFAULT 'completed'");
    }
};
```

- [ ] **Step 2: Run migration**

```bash
php artisan migrate
```

Expected output: `[2026_05_23_add_open_status_to_pos_transactions] ... Done`

---

### Task 2: PosTransaction Model Updates

**Files:**
- Modify: `app/Models/PosTransaction.php`

- [ ] **Step 1: Add STATUS_OPEN constant and isOpen method**

Add after line 46 (`STATUS_REFUNDED`):
```php
const STATUS_OPEN = 'open';
```

Add after `isPostCharge()` method (after line 82):
```php
public function isOpen(): bool
{
    return $this->status === self::STATUS_OPEN;
}

public function scopeOpen($query)
{
    return $query->where('status', self::STATUS_OPEN);
}
```

---

### Task 3: Routes — Add Tab Endpoints

**Files:**
- Modify: `routes/web.php`

- [ ] **Step 1: Add 5 new tab routes inside the POS prefix group (after line 127)**

Add after `Route::post('/transaction', ...)` (line 127):
```php
        Route::post('/tab/start', [PosController::class, 'startTab'])->name('pos.tab.start');
        Route::get('/tabs/open', [PosController::class, 'getOpenTabs'])->name('pos.tabs.open');
        Route::get('/tab/{transaction}/items', [PosController::class, 'getTabItems'])->name('pos.tab.items');
        Route::post('/tab/{transaction}/add', [PosController::class, 'addToTab'])->name('pos.tab.add');
        Route::post('/tab/{transaction}/settle', [PosController::class, 'settleTab'])->name('pos.tab.settle');
```

---

### Task 4: Controller — Start Tab & Get Open Tabs

**Files:**
- Modify: `app/Http/Controllers/POS/PosController.php`

- [ ] **Step 1: Add startTab method**

Add after the `store` method (after line 157):
```php
    public function startTab(Request $request)
    {
        $request->validate([
            'outlet_id' => 'required|exists:pos_outlets,id',
            'customer_name' => 'required|string|max:255',
        ]);

        $outlet = PosOutlet::findOrFail($request->outlet_id);

        $transaction = PosTransaction::create([
            'invoice_number' => PosTransaction::generateInvoiceNumber($outlet->type),
            'outlet_id' => $outlet->id,
            'staff_id' => Auth::id(),
            'customer_name' => $request->customer_name,
            'subtotal' => 0,
            'tax_amount' => 0,
            'discount_amount' => 0,
            'total_amount' => 0,
            'payment_method' => 'cash',
            'amount_paid' => 0,
            'change_amount' => 0,
            'status' => PosTransaction::STATUS_OPEN,
        ]);

        ActivityLogService::created($transaction, "Open tab started - {$transaction->customer_name} ({$transaction->invoice_number})");

        return response()->json([
            'success' => true,
            'transaction' => $transaction->only(['id', 'customer_name', 'created_at', 'invoice_number', 'total_amount']),
        ]);
    }
```

- [ ] **Step 2: Add getOpenTabs method**

```php
    public function getOpenTabs(Request $request)
    {
        $outletId = $request->outlet_id;
        $tabs = PosTransaction::open()
            ->when($outletId, fn($q) => $q->where('outlet_id', $outletId))
            ->with('items')
            ->orderBy('created_at', 'desc')
            ->get()
            ->map(fn($t) => [
                'id' => $t->id,
                'customer_name' => $t->customer_name,
                'invoice_number' => $t->invoice_number,
                'item_count' => $t->items->sum('quantity'),
                'total_amount' => $t->total_amount,
                'elapsed_minutes' => $t->created_at->diffInMinutes(now()),
                'created_at' => $t->created_at->format('H:i'),
            ]);

        return response()->json(['tabs' => $tabs]);
    }
```

---

### Task 5: Controller — Get Tab Items & Add to Tab

**Files:**
- Modify: `app/Http/Controllers/POS/PosController.php`

- [ ] **Step 1: Add getTabItems method**

Add after `getOpenTabs`:
```php
    public function getTabItems(PosTransaction $transaction)
    {
        if (!$transaction->isOpen()) {
            return response()->json(['success' => false, 'message' => 'Tab is not open'], 400);
        }

        $transaction->load('items.product');
        return response()->json([
            'success' => true,
            'transaction' => [
                'id' => $transaction->id,
                'customer_name' => $transaction->customer_name,
                'invoice_number' => $transaction->invoice_number,
                'subtotal' => $transaction->subtotal,
                'total_amount' => $transaction->total_amount,
                'items' => $transaction->items->map(fn($i) => [
                    'id' => $i->id,
                    'product_id' => $i->product_id,
                    'product_name' => $i->product_name,
                    'quantity' => $i->quantity,
                    'unit_price' => $i->unit_price,
                    'total_price' => $i->total_price,
                ]),
            ],
        ]);
    }
```

- [ ] **Step 2: Add addToTab method**

```php
    public function addToTab(Request $request, PosTransaction $transaction)
    {
        $request->validate([
            'items' => 'required|array|min:1',
            'items.*.product_id' => 'required|exists:pos_products,id',
            'items.*.quantity' => 'required|integer|min:1',
        ]);

        if (!$transaction->isOpen()) {
            return response()->json(['success' => false, 'message' => 'Tab is not open'], 400);
        }

        DB::beginTransaction();
        try {
            $items = collect($request->items);
            $additionalTotal = 0;

            foreach ($items as $item) {
                $product = PosProduct::findOrFail($item['product_id']);
                $quantity = $item['quantity'];
                $totalPrice = $product->price * $quantity;
                $additionalTotal += $totalPrice;

                PosTransactionItem::create([
                    'transaction_id' => $transaction->id,
                    'product_id' => $product->id,
                    'product_name' => $product->name,
                    'quantity' => $quantity,
                    'unit_price' => $product->price,
                    'total_price' => $totalPrice,
                ]);
            }

            $transaction->subtotal += $additionalTotal;
            $transaction->total_amount = $transaction->subtotal + $transaction->tax_amount;
            $transaction->save();

            DB::commit();

            $transaction->load('items');
            ActivityLogService::created($transaction, "Items added to open tab {$transaction->invoice_number}");

            return response()->json([
                'success' => true,
                'message' => 'Items added to tab',
                'transaction' => [
                    'id' => $transaction->id,
                    'subtotal' => $transaction->subtotal,
                    'total_amount' => $transaction->total_amount,
                    'items' => $transaction->items->map(fn($i) => [
                        'id' => $i->id,
                        'product_id' => $i->product_id,
                        'product_name' => $i->product_name,
                        'quantity' => $i->quantity,
                        'unit_price' => $i->unit_price,
                        'total_price' => $i->total_price,
                    ]),
                ],
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json(['success' => false, 'message' => 'Failed: '.$e->getMessage()], 500);
        }
    }
```

---

### Task 6: Controller — Settle Tab

**Files:**
- Modify: `app/Http/Controllers/POS/PosController.php`

- [ ] **Step 1: Add settleTab method**

Add after `addToTab`:
```php
    public function settleTab(Request $request, PosTransaction $transaction)
    {
        $request->validate([
            'payment_method' => 'required|in:cash,transfer,pos,card',
            'amount_paid' => 'required_if:payment_method,cash|numeric|min:0',
        ]);

        if (!$transaction->isOpen()) {
            return response()->json(['success' => false, 'message' => 'Tab is not open'], 400);
        }

        DB::beginTransaction();
        try {
            $amountPaid = $request->payment_method === 'cash' ? $request->amount_paid : $transaction->total_amount;
            $change = $amountPaid - $transaction->total_amount;

            if ($change < 0) {
                DB::rollBack();
                return response()->json(['success' => false, 'message' => 'Amount paid is less than total'], 400);
            }

            // Decrement stock for all items
            foreach ($transaction->items as $item) {
                if ($item->product && $item->product->stock_quantity > 0) {
                    $item->product->decrement('stock_quantity', $item->quantity);
                }
            }

            $transaction->update([
                'payment_method' => $request->payment_method,
                'amount_paid' => $amountPaid,
                'change_amount' => $change,
                'status' => PosTransaction::STATUS_COMPLETED,
            ]);

            DB::commit();

            ActivityLogService::created($transaction, "Tab settled - {$transaction->invoice_number} ({$request->payment_method})");

            return response()->json([
                'success' => true,
                'message' => 'Tab settled successfully',
                'transaction' => $transaction->fresh()->load('items'),
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json(['success' => false, 'message' => 'Settlement failed: '.$e->getMessage()], 500);
        }
    }
```

---

### Task 7: Frontend — Open Tabs Panel (Left Sidebar)

**Files:**
- Modify: `resources/views/admin/pos/outlet.blade.php`

- [ ] **Step 1: Restructure layout to 3-column grid**

Change the grid from `lg:grid-cols-3` to leave room. The new layout:
- Left column: Open tabs list (approx 1/5 width)
- Middle column: Products (approx 2/5 width)
- Right column: Cart (approx 2/5 width)

Replace `<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">` with:
```html
<div class="grid grid-cols-1 lg:grid-cols-5 gap-6">
```

- [ ] **Step 2: Add the open tabs panel as the first child**

Add before the Products div:
```html
    <!-- Open Tabs -->
    <div class="bg-white rounded-xl shadow-sm p-4">
        <div class="flex justify-between items-center mb-3">
            <h3 class="font-bold text-sm">Open Tabs</h3>
            <button onclick="showStartTabModal()" class="text-xs bg-gold text-white px-3 py-1 rounded-lg hover:bg-gold-dark">
                + New Tab
            </button>
        </div>
        <div id="open-tabs-list" class="space-y-2 max-h-[500px] overflow-y-auto">
            <p class="text-gray-400 text-center text-sm py-4">No open tabs</p>
        </div>
    </div>
```

- [ ] **Step 3: Update the Products and Cart divs for the new grid**

Change the Products div from `lg:col-span-2` to `lg:col-span-2` (stays same), and the Cart div stays as the last column.

---

### Task 8: Frontend — Start Tab Modal & Settle Tab Modal

**Files:**
- Modify: `resources/views/admin/pos/outlet.blade.php`

- [ ] **Step 1: Add Start Tab modal**

Add after the cart div, before `@endsection`:
```html
<!-- Start Tab Modal -->
<div id="start-tab-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-xl p-6 w-96 shadow-xl">
        <h3 class="font-bold text-lg mb-4">Start New Tab</h3>
        <div class="space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">Customer Name</label>
                <input type="text" id="new-tab-name" class="w-full border rounded-lg px-4 py-2" placeholder="Enter customer name">
            </div>
            <div class="flex gap-3 justify-end">
                <button onclick="hideStartTabModal()" class="px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200">Cancel</button>
                <button onclick="startNewTab()" class="px-4 py-2 bg-gold text-white rounded-lg hover:bg-gold-dark">Start Tab</button>
            </div>
        </div>
    </div>
</div>
```

- [ ] **Step 2: Add Settle Tab modal**

```html
<!-- Settle Tab Modal -->
<div id="settle-tab-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
    <div class="bg-white rounded-xl p-6 w-96 shadow-xl">
        <h3 class="font-bold text-lg mb-4">Settle Tab — <span id="settle-customer-name"></span></h3>
        <div class="space-y-4">
            <div class="flex justify-between text-lg border-b pb-2">
                <span>Total Due</span>
                <span id="settle-total" class="font-bold text-gold">₦ 0</span>
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-2">Payment Method</label>
                <select id="settle-payment-method" class="w-full border rounded-lg px-4 py-2">
                    <option value="cash">Cash</option>
                    <option value="transfer">Bank Transfer</option>
                    <option value="pos">POS</option>
                    <option value="card">Card</option>
                </select>
            </div>
            <div id="settle-cash-fields">
                <label class="block text-sm font-medium text-gray-700 mb-2">Amount Paid</label>
                <input type="number" id="settle-amount-paid" class="w-full border rounded-lg px-4 py-2" min="0">
            </div>
            <div id="settle-change-display" class="hidden text-center p-4 bg-green-50 rounded-lg">
                <p class="text-sm text-gray-600">Change</p>
                <p id="settle-change-amount" class="text-2xl font-bold text-green-600">₦ 0</p>
            </div>
            <div class="flex gap-3 justify-end">
                <button onclick="hideSettleTabModal()" class="px-4 py-2 bg-gray-100 rounded-lg hover:bg-gray-200">Cancel</button>
                <button onclick="confirmSettleTab()" class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700">Settle Tab</button>
            </div>
        </div>
    </div>
</div>
```

---

### Task 9: Frontend — JavaScript Tab State Management

**Files:**
- Modify: `resources/views/admin/pos/outlet.blade.php`

This replaces the existing JS in the `@push('scripts')` section.

- [ ] **Step 1: Replace the entire script block with the new tab-aware JS**

```html
@push('scripts')
<script>
// ===== Tab State =====
let cart = [];
let activeTabId = null;

// ===== Open Tabs =====
function loadOpenTabs() {
    const outletId = document.querySelector('input[name="outlet_id"]').value;
    fetch(`{{ route('pos.tabs.open') }}?outlet_id=${outletId}`)
        .then(r => r.json())
        .then(data => {
            const container = document.getElementById('open-tabs-list');
            if (data.tabs.length === 0) {
                container.innerHTML = '<p class="text-gray-400 text-center text-sm py-4">No open tabs</p>';
                return;
            }
            container.innerHTML = data.tabs.map(t => `
                <div onclick="selectTab(${t.id})"
                     class="p-3 rounded-lg cursor-pointer border ${activeTabId === t.id ? 'border-gold bg-gold bg-opacity-5' : 'border-gray-200 hover:border-gold'} transition">
                    <div class="flex justify-between items-start">
                        <div>
                            <p class="font-medium text-sm">${t.customer_name}</p>
                            <p class="text-xs text-gray-400">${t.item_count} items • ₦${t.total_amount.toLocaleString()}</p>
                        </div>
                        <span class="text-xs text-gray-400">${t.elapsed_minutes}m</span>
                    </div>
                </div>
            `).join('');
        });
}

function selectTab(tabId) {
    activeTabId = tabId;
    loadOpenTabs();

    fetch(`{{ url('admin/pos/tab') }}/${tabId}/items`)
        .then(r => r.json())
        .then(data => {
            if (data.success) {
                cart = data.transaction.items.map(i => ({
                    id: i.product_id,
                    cartItemId: i.id,
                    name: i.product_name,
                    price: i.unit_price,
                    quantity: i.quantity,
                }));
                updateCart();
                document.getElementById('tab-actions').classList.remove('hidden');
                document.getElementById('sale-actions').classList.add('hidden');
                document.getElementById('active-tab-name').textContent = data.transaction.customer_name;
                document.getElementById('active-tab-name').dataset.tabId = tabId;
            }
        });
}

// ===== Start Tab =====
function showStartTabModal() {
    document.getElementById('start-tab-modal').classList.remove('hidden');
    document.getElementById('start-tab-modal').classList.add('flex');
    document.getElementById('new-tab-name').focus();
}

function hideStartTabModal() {
    document.getElementById('start-tab-modal').classList.add('hidden');
    document.getElementById('start-tab-modal').classList.remove('flex');
}

function startNewTab() {
    const name = document.getElementById('new-tab-name').value.trim();
    if (!name) { alert('Please enter a customer name'); return; }

    const formData = new FormData();
    formData.append('outlet_id', document.querySelector('input[name="outlet_id"]').value);
    formData.append('customer_name', name);

    fetch('{{ route('pos.tab.start') }}', {
        method: 'POST',
        headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', 'Accept': 'application/json' },
        body: formData
    })
    .then(r => r.json())
    .then(data => {
        if (data.success) {
            hideStartTabModal();
            document.getElementById('new-tab-name').value = '';
            selectTab(data.transaction.id);
        } else {
            alert('Error: ' + data.message);
        }
    });
}

// ===== Add to Tab =====
function addToTab() {
    if (!activeTabId || cart.length === 0) return;

    const formData = new FormData();
    cart.forEach((item, index) => {
        formData.append(`items[${index}][product_id]`, item.id);
        formData.append(`items[${index}][quantity]`, item.quantity);
    });

    fetch(`{{ url('admin/pos/tab') }}/${activeTabId}/add`, {
        method: 'POST',
        headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', 'Accept': 'application/json' },
        body: formData
    })
    .then(r => r.json())
    .then(data => {
        if (data.success) {
            cart = data.transaction.items.map(i => ({
                id: i.product_id,
                cartItemId: i.id,
                name: i.product_name,
                price: i.unit_price,
                quantity: i.quantity,
            }));
            updateCart();
            loadOpenTabs();
        } else {
            alert('Error: ' + data.message);
        }
    });
}

// ===== Settle Tab =====
function showSettleTabModal() {
    const total = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
    const customerName = document.getElementById('active-tab-name').textContent;
    document.getElementById('settle-customer-name').textContent = customerName;
    document.getElementById('settle-total').textContent = `₦ ${total.toLocaleString()}`;
    document.getElementById('settle-amount-paid').value = total;
    document.getElementById('settle-amount-paid').min = total;
    document.getElementById('settle-change-display').classList.add('hidden');
    document.getElementById('settle-modal').classList.remove('hidden');
    document.getElementById('settle-modal').classList.add('flex');
}

function hideSettleTabModal() {
    document.getElementById('settle-tab-modal').classList.add('hidden');
    document.getElementById('settle-tab-modal').classList.remove('flex');
}

function confirmSettleTab() {
    if (!activeTabId) return;

    const paymentMethod = document.getElementById('settle-payment-method').value;
    const amountPaid = document.getElementById('settle-amount-paid').value;

    const formData = new FormData();
    formData.append('payment_method', paymentMethod);
    if (paymentMethod === 'cash') formData.append('amount_paid', amountPaid);

    fetch(`{{ url('admin/pos/tab') }}/${activeTabId}/settle`, {
        method: 'POST',
        headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}', 'Accept': 'application/json' },
        body: formData
    })
    .then(r => r.json())
    .then(data => {
        if (data.success) {
            hideSettleTabModal();
            window.location.href = '{{ route("pos.receipt", "") }}/' + data.transaction.id;
        } else {
            alert('Error: ' + data.message);
        }
    });
}

// ===== Cart Functions (modified) =====
function addToCart(id, name, price) {
    if (!activeTabId) {
        alert('Please select or start an open tab first');
        return;
    }
    const existing = cart.find(item => item.id === id);
    if (existing) {
        existing.quantity++;
    } else {
        cart.push({ id, name, price, quantity: 1 });
    }
    updateCart();
}

function removeFromCart(id) {
    cart = cart.filter(item => item.id !== id);
    updateCart();
}

function updateQuantity(id, change) {
    const item = cart.find(i => i.id === id);
    if (item) {
        item.quantity += change;
        if (item.quantity <= 0) {
            removeFromCart(id);
        } else {
            updateCart();
        }
    }
}

function updateCart() {
    const container = document.getElementById('cart-items');

    if (cart.length === 0) {
        container.innerHTML = '<p class="text-gray-400 text-center py-8">No items in cart</p>';
        document.getElementById('subtotal').textContent = '₦ 0';
        document.getElementById('total').textContent = '₦ 0';
        return;
    }

    container.innerHTML = cart.map(item => `
        <div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
            <div class="flex-1">
                <p class="font-medium">${item.name}</p>
                <p class="text-sm text-gray-500">₦ ${item.price.toLocaleString()}</p>
            </div>
            <div class="flex items-center gap-2">
                <button onclick="updateQuantity(${item.id}, -1)" class="w-8 h-8 bg-gray-200 rounded">-</button>
                <span class="w-8 text-center">${item.quantity}</span>
                <button onclick="updateQuantity(${item.id}, 1)" class="w-8 h-8 bg-gray-200 rounded">+</button>
                <button onclick="removeFromCart(${item.id})" class="text-red-500 ml-2"><i class="fas fa-trash"></i></button>
            </div>
        </div>
    `).join('');

    const subtotal = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
    document.getElementById('subtotal').textContent = `₦ ${subtotal.toLocaleString()}`;
    document.getElementById('total').textContent = `₦ ${subtotal.toLocaleString()}`;
}

// ===== Payment Method Toggle (Settle Modal) =====
document.getElementById('settle-payment-method')?.addEventListener('change', function() {
    const cashFields = document.getElementById('settle-cash-fields');
    const changeDisplay = document.getElementById('settle-change-display');
    if (this.value === 'cash') {
        cashFields.classList.remove('hidden');
    } else {
        cashFields.classList.add('hidden');
        changeDisplay.classList.add('hidden');
    }
});

document.getElementById('settle-amount-paid')?.addEventListener('input', function() {
    const totalEl = document.getElementById('settle-total');
    const total = parseFloat(totalEl.textContent.replace(/[₦,]/g, '')) || 0;
    const change = this.value - total;
    const changeDisplay = document.getElementById('settle-change-display');

    if (change >= 0 && this.value > 0) {
        changeDisplay.classList.remove('hidden');
        document.getElementById('settle-change-amount').textContent = `₦ ${change.toLocaleString()}`;
    } else {
        changeDisplay.classList.add('hidden');
    }
});

// ===== Customer Type (unchanged, keep existing) =====
// (keep the existing radio toggle code for walkin/guest)

// ===== Init =====
loadOpenTabs();
setInterval(loadOpenTabs, 30000);
</script>
@endpush
```

---

### Task 10: Frontend — Cart Section Tab Actions

**Files:**
- Modify: `resources/views/admin/pos/outlet.blade.php`

- [ ] **Step 1: Replace the cart submit button area with tab-aware buttons**

Replace the submit button block (lines 114-116):
```html
                <div id="sale-actions">
                    <button type="submit" id="complete-sale-btn" class="w-full bg-gold text-white py-3 rounded-lg font-semibold hover:bg-gold-dark transition">
                        Complete Sale
                    </button>
                </div>
                <div id="tab-actions" class="hidden space-y-2">
                    <div class="text-sm text-gray-600 mb-2">
                        Tab: <span id="active-tab-name" class="font-semibold text-navy"></span>
                    </div>
                    <button type="button" onclick="addToTab()" class="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 transition">
                        <i class="fas fa-plus-circle mr-2"></i>Add to Tab
                    </button>
                    <button type="button" onclick="showSettleTabModal()" class="w-full bg-green-600 text-white py-3 rounded-lg font-semibold hover:bg-green-700 transition">
                        <i class="fas fa-check-circle mr-2"></i>Settle Tab
                    </button>
                </div>
```

- [ ] **Step 2: Keep existing customer type radio toggle JS** (already present, lines 186-201)

---

### Task 11: Transactions View — Add Open Status Filter

**Files:**
- Modify: `resources/views/admin/pos/transactions.blade.php`

- [ ] **Step 1: Add 'Open' to the status filter dropdown**

Add after the `pending` option (around line 33):
```html
                <option value="open" {{ request('status') == 'open' ? 'selected' : '' }}>Open (Tab)</option>
```

- [ ] **Step 2: Add Open status badge styling to the table**

In the status column (around line 101-109), add an open case before the `else`:
```php
                        @elseif($tx->status == 'open')
                            <span class="px-2 py-1 bg-blue-100 text-blue-700 text-xs rounded-full">Open Tab</span>
```

---

### Task 12: Verify Everything Works

- [ ] **Step 1: Run migration**

```bash
php artisan migrate
```

- [ ] **Step 2: Navigate to any bar/outlet POS page**

Login as admin, go to `/admin/pos/outlet/bar`. Verify:
- Open tabs panel is visible on the left with "New Tab" button
- Products are clickable

- [ ] **Step 3: Start a tab**

Click "+ New Tab", enter a customer name. Verify:
- Tab appears in open tabs list
- Cart is empty but "Add to Tab" and "Settle Tab" buttons visible

- [ ] **Step 4: Add items to tab**

Click products to add to cart, click "Add to Tab". Verify:
- Items persist in cart after page behavior
- Tab updates with new total

- [ ] **Step 5: Refresh the page**

Verify the open tab is still in the list and items can be loaded by selecting it.

- [ ] **Step 6: Settle the tab**

Click "Settle Tab", select payment method, enter amount, confirm. Verify:
- Redirects to receipt page
- Tab disappears from open tabs list
- Stock is decremented

- [ ] **Step 7: Check transactions list**

Go to `/admin/pos/transactions`. Verify:
- The settled transaction appears as "Completed"
- Filter by "Open (Tab)" shows nothing (all settled)
