ROUTES 434: Route::get('live-tables', [Restaurant\LiveTableController::class, 'index']); 435: Route::post('live-tables/{id}/persons', [Restaurant\LiveTableController::class, 'updatePersons']); 436: Route::get('live-tables/{id}/availability', [Restaurant\LiveTableController::class, 'availability']); 437: Route::post('live-tables/{id}/status', [Restaurant\LiveTableController::class, 'updateStatus']); CONTROLLER 1 isModuleEnabled('live_tables')) { 18 abort(403, 'Live Tables module is disabled.'); 19 } 20 21 if (!auth()->user()->can('access_tables')) { 22 abort(403, 'Unauthorized action.'); 23 } 24 25 $business_id = $request->session()->get('user.business_id'); 26 $location_id = $request->get('location_id'); 27 $floor_id = $request->get('floor_id'); 28 29 $floors = ResFloor::where('business_id', $business_id) 30 ->where('is_active', 1) 31 ->when($location_id, function ($query) use ($location_id) { 32 $query->where('location_id', $location_id); 33 }) 34 ->orderBy('name') 35 ->get(); 36 37 $tables = ResTable::where('business_id', $business_id) 38 ->when($location_id, function ($query) use ($location_id) { 39 $query->where('location_id', $location_id); 40 }) 41 ->when($floor_id, function ($query) use ($floor_id) { 42 $query->where('floor_id', $floor_id); 43 }) 44 ->with('floor') 45 ->orderBy('floor_id') 46 ->orderBy('name') 47 ->get(); 48 49 $table_ids = $tables->pluck('id'); 50 51 /* 52 * Sirf draft/suspended bill table ko occupied rakhega. 53 * Final/Cash bill occupied nahi mana jayega. 54 */ 55 $active_orders = Transaction::where('business_id', $business_id) 56 ->where('type', 'sell') 57 ->whereIn('res_table_id', $table_ids) 58 ->where('status', 'draft') 59 ->with('contact') 60 ->orderByDesc('transaction_date') 61 ->get() 62 ->groupBy('res_table_id') 63 ->map(function ($orders) { 64 return $orders->first(); 65 }); 66 67 /* 68 * Paid/final bill ke baad purana occupied flag clear karein. 69 */ 70 $active_table_ids = $active_orders->keys() 71 ->map(function ($id) { 72 return (int) $id; 73 }) 74 ->all(); 75 76 foreach ($tables as $table) { 77 $table_id = (int) $table->id; 78 79 if ( 80 $table->table_status === 'occupied' 81 && !in_array($table_id, $active_table_ids, true) 82 ) { 83 $table->table_status = 'available'; 84 $table->current_persons = 0; 85 $table->save(); 86 } 87 } 88 89 $waiter_ids = $active_orders 90 ->pluck('res_waiter_id') 91 ->filter(); 92 93 $waiter_names = User::whereIn('id', $waiter_ids) 94 ->pluck('first_name', 'id'); 95 96 return view('restaurant.live_tables.index', compact( 97 'floors', 98 'tables', 99 'active_orders', 100 'waiter_names', 101 'floor_id', 102 'location_id' 103 )); 104 } 105 106 public function updatePersons(Request $request, $id) 107 { 108 if (!app(ModuleUtil::class)->isModuleEnabled('live_tables')) { 109 abort(403, 'Live Tables module is disabled.'); 110 } 111 112 if (!auth()->user()->can('access_tables')) { 113 abort(403, 'Unauthorized action.'); 114 } 115 116 $business_id = $request->session()->get('user.business_id'); 117 118 $table = ResTable::where('business_id', $business_id) 119 ->findOrFail($id); 120 121 $persons = (int) $request->input('persons', 0); 122 $persons = max(0, min($persons, (int) $table->seats)); 123 124 $table->current_persons = $persons; 125 $table->table_status = $persons > 0 126 ? 'occupied' 127 : 'available'; 128 129 $table->save(); 130 131 return [ 132 'success' => true, 133 'persons' => $persons, 134 'available' => max(0, $table->seats - $persons), 135 ]; 136 } 137 138 public function availability(Request $request, $id) 139 { 140 if (!app(ModuleUtil::class)->isModuleEnabled('live_tables')) { 141 abort(403, 'Live Tables module is disabled.'); 142 } 143 144 if (!auth()->user()->can('access_tables')) { 145 abort(403, 'Unauthorized action.'); 146 } 147 148 $business_id = $request->session()->get('user.business_id'); 149 150 $table = ResTable::where('business_id', $business_id) 151 ->findOrFail($id); 152 153 $current_transaction_id = $request->get('transaction_id'); 154 155 /* 156 * Sirf draft transaction running order hai. 157 * Final/Cash transaction ko ignore kiya jayega. 158 */ 159 $running_order = Transaction::where('business_id', $business_id) 160 ->where('type', 'sell') 161 ->where('res_table_id', $table->id) 162 ->where('status', 'draft') 163 ->when($current_transaction_id, function ($query, $transaction_id) { 164 $query->where('id', '!=', $transaction_id); 165 }) 166 ->orderByDesc('transaction_date') 167 ->first(); 168 169 /* 170 * Running draft order nahi hai to occupied table free karein. 171 */ 172 if ( 173 empty($running_order) 174 && $table->table_status === 'occupied' 175 ) { 176 $table->table_status = 'available'; 177 $table->current_persons = 0; 178 $table->save(); 179 } 180 181 $blocked_by_status = in_array( 182 $table->table_status, 183 ['cleaning', 'disabled'], 184 true 185 ); 186 187 return [ 188 'success' => true, 189 'occupied' => !empty($running_order) || $blocked_by_status, 190 'seats' => (int) $table->seats, 191 'persons' => (int) $table->current_persons, 192 'invoice_no' => !empty($running_order) 193 ? $running_order->invoice_no 194 : null, 195 'table_status' => $table->table_status, 196 ]; 197 } 198 199 public function updateStatus(Request $request, $id) 200 { 201 if (!app(ModuleUtil::class)->isModuleEnabled('live_tables')) { 202 abort(403, 'Live Tables module is disabled.'); 203 } 204 205 if (!auth()->user()->can('access_tables')) { 206 abort(403, 'Unauthorized action.'); 207 } 208 209 $request->validate([ 210 'table_status' => 'required|in:available,cleaning,disabled', 211 ]); 212 213 $business_id = $request->session()->get('user.business_id'); 214 215 $table = ResTable::where('business_id', $business_id) 216 ->findOrFail($id); 217 218 $table->table_status = $request->input('table_status'); 219 220 if ($table->table_status === 'available') { 221 $table->current_persons = 0; 222 } 223 224 $table->save(); 225 226 return [ 227 'success' => true, 228 'table_status' => $table->table_status, 229 ]; 230 } 231 } VIEW 1 @extends('layouts.app') 2 @section('title', 'Live Tables') 3 4 @section('content') 5
6

7 Live Tables Floor-wise restaurant table status 8

9
10 11
12 13
14 16 All Floors 17 18 19 @foreach($floors as $floor) 20 22 {{ $floor->name }} 23 24 @endforeach 25 26 28 Manage Floors 29 30
31 32
33 Available 34 Occupied 35 Cleaning 36 Disabled 37
38 39 {{-- OCCUPIED TABLE SEARCH --}} 40 66 67 70 71
72 @forelse($tables as $table) 73 @php 74 $order = $active_orders->get($table->id); 75 $status = $order ? 'occupied' : ($table->table_status ?: 'available'); 76 $availableSeats = max(0, (int)$table->seats - (int)$table->current_persons); 77 78 $customerName = $order ? (optional($order->contact)->name ?: 'Walk-In') : ''; 79 $customerMobile = $order ? (optional($order->contact)->mobile ?: '') : ''; 80 $waiterName = $order ? ($waiter_names[$order->res_waiter_id] ?? '') : ''; 81 $invoiceNo = $order ? ($order->invoice_no ?? '') : ''; 82 83 $searchText = strtolower( 84 ($table->name ?? '') . ' ' . 85 (optional($table->floor)->name ?? '') . ' ' . 86 $invoiceNo . ' ' . 87 $customerName . ' ' . 88 $customerMobile . ' ' . 89 $waiterName 90 ); 91 @endphp 92 93
96 97
99 100
101
102 {{ optional($table->floor)->name ?: 'No Floor' }} 103

{{ $table->name }}

104
105 {{ strtoupper($status) }} 106
107 108
109 Total Seats {{ $table->seats }} 110 Persons {{ $table->current_persons }} 111 Available {{ $availableSeats }} 112
113 114 @if($order) 115
116
Order: {{ $order->invoice_no }}
117
Customer: {{ optional($order->contact)->name ?: 'Walk-In' }}
118 @if(!empty(optional($order->contact)->mobile)) 119
Mobile: {{ optional($order->contact)->mobile }}
120 @endif 121
Waiter: {{ $waiter_names[$order->res_waiter_id] ?? '-' }}
122
Time: {{ @format_datetime($order->transaction_date) }}
123
Total: @format_currency($order->final_total)
124
125 @else 126
No running order
127 @endif 128 129
130 131 136 137
138 139
140 @if($order) 141 143 Open Order 144 145 @else 146 148 New Order 149 150 @endif 151 152 157
158 159
160
161 @empty 162
163
164 No tables found. Add floors and tables first. 165
166
167 @endforelse 168
169 170
171 @endsection 172 173 @section('css') 174 222 @endsection 223 224 @section('javascript') 225