File: //proc/self/root/home/workzeni/agency-erp-05.workzenix.com/app/Http/Controllers/HotelController.php
<?php
namespace App\Http\Controllers;
use App\Models\CompanyInfo;
use App\Models\Hotel;
use App\Models\Qouta;
use App\Models\Season;
use App\Models\TransactionHistory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class HotelController extends Controller
{
public function getQuotaByAgency(Request $request)
{
$agencyId = $request->query('agency_id');
$seasonId = $request->query('season_id');
if (! $agencyId || ! $seasonId) {
return response()->json(['error' => 'Agency or Season not provided'], 400);
}
$quota = Qouta::where('agency_id', $agencyId)
->where('season_id', $seasonId)
->first();
if (! $quota) {
return response()->json(['error' => 'Quota not found for this agency and season'], 404);
}
// Makka
if ($quota->makka_qouta >= $quota->makka_used_qouta) {
$makka_extra_qouta = $quota->makka_qouta - $quota->makka_used_qouta;
} else {
$makka_extra_qouta = $quota->makka_used_qouta - $quota->makka_qouta;
}
// modina
if ($quota->modina_qouta >= $quota->modina_used_qouta) {
$modina_extra_qouta = $quota->modina_qouta - $quota->modina_used_qouta;
} else {
$modina_extra_qouta = $quota->modina_used_qouta - $quota->modina_qouta;
}
return response()->json([
'makka_qouta' => $quota->makka_qouta,
'makka_used_qouta' => $quota->makka_used_qouta,
'makka_extra_qouta' => $makka_extra_qouta,
'modina_qouta' => $quota->modina_qouta,
'modina_used_qouta' => $quota->modina_used_qouta,
'modina_extra_qouta' => $modina_extra_qouta,
]);
}
public function index()
{
$hotels = Hotel::latest()->get();
return view('admin.hotel.list', compact('hotels'));
}
public function create()
{
$today = now()->toDateString();
$user = auth()->user();
$activeSeasons = Season::where('start_date', '<=', $today)
->where('end_date', '>=', $today)
->get(); // get full objects instead of just IDs
if (! in_array($user->role, [1, 2, 3])) {
$authCompanyId = $user->company_id;
$quotas = Qouta::where('agency_id', $authCompanyId)
->whereIn('season_id', $activeSeasons->pluck('id'))
->get();
} else {
$quotas = Qouta::whereIn('season_id', $activeSeasons->pluck('id'))->get();
}
$agencies = CompanyInfo::all();
return view('admin.hotel.create', compact('agencies', 'quotas', 'activeSeasons'));
}
public function store(Request $request)
{
// $validator = Validator::make($request->all(), [
// 'agency_id' => 'nullable|exists:company_infos,id',
// 'user_id' => 'nullable|exists:users,id',
// 'city' => 'required|string|max:255',
// 'season_id' => 'required|string',
// 'hotel_name' => 'required|string|max:255',
// 'owner_name' => 'required|string|max:255',
// 'representative_name' => 'required|string|max:255',
// 'phone_number' => 'required|string|max:20',
// 'tasria_owner' => 'required|string|max:255',
// 'tasria_number' => 'required|string|max:100',
// 'total_seat' => 'required|integer|min:0',
// 'double_qty' => 'nullable|integer|min:0',
// 'triple_qty' => 'nullable|integer|min:0',
// 'quad_qty' => 'nullable|integer|min:0',
// 'quintuple_qty' => 'nullable|integer|min:0',
// 'allocated_seats' => 'nullable|integer|min:0',
// 'subtotal' => 'nullable|numeric|min:0',
// 'total_amount' => 'nullable|numeric|min:0',
// ]);
// dd($validator);
// if ($validator->fails()) {
// return back()->withErrors($validator)->withInput();
// }
// Determine agency/user IDs (fall back to current user’s company & id)
$agencyID = $request->agency_id ?? optional(auth()->user())->company_id;
$userID = $request->user_id ?? optional(auth()->user())->id;
// Seat calculations (default 0 if null)
$double = (int) $request->double_qty * 2;
$triple = (int) $request->triple_qty * 3;
$quad = (int) $request->quad_qty * 4;
$quint = (int) $request->quintuple_qty * 5;
$allocated = $double + $triple + $quad + $quint;
// Validate allocated seats <= total seats
if ($allocated > (int) $request->total_seat) {
return back()
->withErrors(['allocated_seats' => 'Allocated seats cannot exceed total seats.'])
->withInput();
}
// check amount
$mainAmount = $this->determineMainAmount(
$request->subtotal ?? null,
$request->total_amount ?? null
);
// Update quota if present
// $today = now()->toDateString();
// $activeSeasonIds = Season::where('start_date', '<=', $today)
// ->where('end_date', '>=', $today)
// ->pluck('id');
// $quota = Qouta::where('agency_id', $agencyID)
// ->whereIn('season_id', $activeSeasonIds)
// ->first();
// if ($quota) {
// if (strtolower($request->city) === 'makkah') {
// $quota->makka_used_qouta = $allocated;
// } elseif (strtolower($request->city) === 'madinah') {
// $quota->modina_used_qouta = $allocated;
// }
// $quota->save();
// }
// Store hotel
Hotel::create([
'agency_id' => $agencyID,
'user_id' => $userID,
'city' => $request->city,
'season_id' => $request->season_id,
'hotel_name' => $request->hotel_name,
'owner_name' => $request->owner_name,
'representative_name' => $request->representative_name,
'phone_number' => $request->phone_number,
'tasria_owner' => $request->tasria_owner,
'tasria_number' => $request->tasria_number,
'total_seat' => $request->total_seat,
'double_bed' => $request->double_qty,
'triple_bed' => $request->triple_qty,
'quad_bed' => $request->quad_qty,
'quintuple_bed' => $request->quintuple_qty,
'allocated_seats' => $allocated,
'without_tax_amount' => $request->subtotal,
'with_tax_amount' => $request->total_amount,
'amount' => $mainAmount,
]);
return redirect()
->route('hotel.list')
->with('success', 'Hotel booking created successfully!');
}
public function show($id)
{
$hotel = Hotel::with(['agency', 'user', 'approvedBy'])->findOrFail($id);
return view('admin.hotel.view', compact('hotel'));
}
// public function updateStatus(Request $request, $id)
// {
// $user = auth()->user();
// $userRole = $user->role;
// $hotel = Hotel::findOrFail($id);
// $newStatus = $request->status;
// // Approve payment
// if ($newStatus === 1) {
// if (! in_array($userRole, [1, 2, 3])) {
// return back()->with('error', 'You are not authorized to approve payments.');
// }
// // prevent duplicate transaction
// $alreadyApproved = TransactionHistory::where([
// ['source_table', 'hotels'],
// ['source_id', $hotel->id],
// ])->exists();
// if ($alreadyApproved) {
// return back()->with('error', 'This payment has already been approved and recorded.');
// }
// $hotel->update([
// 'status' => 1,
// 'approved_by' => $user->id,
// ]);
// $agency = CompanyInfo::find($hotel->agency_id);
// $oldBal = $agency?->sar_acct ?? 0;
// $newBal = $oldBal - $hotel->amount;
// TransactionHistory::create([
// 'agency_id' => $hotel->agency_id,
// 'source_table' => 'hotels',
// 'source_id' => $hotel->id,
// 'currency' => 'SAR',
// 'tnx_type' => 'Spend',
// 'amount' => $hotel->amount,
// 'note' => sprintf(
// 'Payment of SAR %.2f has been successfully approved by %s for hotel booking: %s (%s).',
// $hotel->amount,
// auth()->user()->name,
// $hotel->hotel_name,
// $hotel->city
// ),
// 'old_balance' => $oldBal,
// 'new_balance' => $newBal,
// ]);
// if ($agency) {
// $agency->update(['sar_acct' => $newBal]);
// }
// // ✅ NEW: Update quota when approving
// $today = now()->toDateString();
// $activeSeasonIds = Season::where('start_date', '<=', $today)
// ->where('end_date', '>=', $today)
// ->pluck('id');
// $quota = Qouta::where('agency_id', $hotel->agency_id)
// ->whereIn('season_id', $activeSeasonIds)
// ->first();
// if ($quota) {
// $allocated = $hotel->double_bed + $hotel->triple_bed +
// $hotel->quad_bed + $hotel->quintuple_bed;
// if (strtolower($hotel->city) === 'makkah') {
// $quota->makka_used_quota = max(0, $quota->makka_used_quota + $allocated);
// } elseif (strtolower($hotel->city) === 'madinah') {
// $quota->modina_used_quota = max(0, $quota->modina_used_quota + $allocated);
// }
// $quota->save();
// } else {
// // Handle the case where no quota is found
// Log::warning('No quota found for the given agency and season.');
// }
// return $this->statusResponse($hotel);
// }
// // Reject / deactivate
// if (in_array($newStatus, [0, 2])) {
// if (in_array($userRole, [1, 2, 3])) {
// // If previously approved, rollback transaction
// if ($hotel->status == 1) {
// TransactionHistory::where([
// ['source_table', 'hotels'],
// ['source_id', $hotel->id],
// ])->delete();
// if ($agency = CompanyInfo::find($hotel->agency_id)) {
// $agency->increment('sar_acct', $hotel->amount);
// }
// // ✅ NEW: Update quota when panding
// $today = now()->toDateString();
// $activeSeasonIds = Season::where('start_date', '<=', $today)
// ->where('end_date', '>=', $today)
// ->pluck('id');
// $quota = Qouta::where('agency_id', $hotel->agency_id)
// ->whereIn('season_id', $activeSeasonIds)
// ->first();
// if ($quota) {
// $allocated = $hotel->double_bed + $hotel->triple_bed +
// $hotel->quad_bed + $hotel->quintuple_bed;
// if (strtolower($hotel->city) === 'makkah') {
// $quota->makka_used_qouta -= $allocated;
// } elseif (strtolower($hotel->city) === 'madinah') {
// $quota->modina_used_qouta -= $allocated;
// }
// $quota->save();
// }
// }
// $hotel->update([
// 'status' => $newStatus,
// 'approved_by' => null,
// ]);
// } elseif (in_array($userRole, [4, 5])) {
// $hotel->update([
// 'status' => 0,
// 'approved_by' => null,
// ]);
// } else {
// return back()->with('error', 'You are not authorized to change payment status.');
// }
// return $this->statusResponse($hotel);
// }
// // fallback (shouldn’t hit due to validation)
// return back()->with('error', 'Invalid status.');
// }
public function updateStatus(Request $request, $id)
{
$request->validate([
'status' => 'required|in:0,1,2',
]);
$user = auth()->user();
$userRole = $user->role;
$hotel = Hotel::findOrFail($id);
$newStatus = (int) $request->status;
// Approve payment
if ($newStatus === 1) {
if (! in_array($userRole, [1, 2, 3])) {
return back()->with('error', 'You are not authorized to approve payments.');
}
// prevent duplicate transaction
$alreadyApproved = TransactionHistory::where([
['source_table', 'hotels'],
['source_id', $hotel->id],
])->exists();
if ($alreadyApproved) {
return back()->with('error', 'This payment has already been approved.');
}
$hotel->update([
'status' => 1,
'approved_by' => $user->id,
]);
$agency = CompanyInfo::find($hotel->agency_id);
$oldBal = $agency?->sar_acct ?? 0;
$newBal = $oldBal - $hotel->amount;
TransactionHistory::create([
'agency_id' => $hotel->agency_id,
'source_table' => 'hotels',
'source_id' => $hotel->id,
'currency' => 'SAR',
'tnx_type' => 'Spend',
'amount' => $hotel->amount,
'note' => sprintf(
'Payment of SAR %.2f approved by %s for hotel: %s (%s).',
$hotel->amount,
$user->name,
$hotel->hotel_name,
$hotel->city
),
'old_balance' => $oldBal,
'new_balance' => $newBal,
]);
if ($agency) {
$agency->update(['sar_acct' => $newBal]);
}
// Update quota
$today = now()->toDateString();
$activeSeasonIds = Season::where('start_date', '<=', $today)
->where('end_date', '>=', $today)
->pluck('id');
$quota = Qouta::where('agency_id', $hotel->agency_id)
->whereIn('season_id', $activeSeasonIds)
->first();
if ($quota) {
$allocated = $hotel->allocated_seats;
if (strtolower($hotel->city) === 'makkah') {
$quota->makka_used_qouta += $allocated;
} elseif (strtolower($hotel->city) === 'madinah') {
$quota->modina_used_qouta += $allocated;
}
$quota->save();
}
return back()->with('success', 'Status updated to Approved.');
}
// Reject / deactivate
if (in_array($newStatus, [0, 2])) {
if (in_array($userRole, [1, 2, 3])) {
// rollback if previously approved
if ($hotel->status == 1) {
TransactionHistory::where([
['source_table', 'hotels'],
['source_id', $hotel->id],
])->delete();
if ($agency = CompanyInfo::find($hotel->agency_id)) {
$agency->increment('sar_acct', $hotel->amount);
}
// update quota rollback
$today = now()->toDateString();
$activeSeasonIds = Season::where('start_date', '<=', $today)
->where('end_date', '>=', $today)
->pluck('id');
$quota = Qouta::where('agency_id', $hotel->agency_id)
->whereIn('season_id', $activeSeasonIds)
->first();
if ($quota) {
$allocated = $hotel->allocated_seats;
if (strtolower($hotel->city) === 'makkah') {
$quota->makka_used_qouta -= $allocated;
} elseif (strtolower($hotel->city) === 'madinah') {
$quota->modina_used_qouta -= $allocated;
}
$quota->save();
}
}
$hotel->update([
'status' => $newStatus,
'approved_by' => null,
]);
} elseif (in_array($userRole, [4, 5])) {
$hotel->update([
'status' => 0,
'approved_by' => null,
]);
} else {
return back()->with('error', 'You are not authorized to change status.');
}
return back()->with('success', 'Status updated successfully.');
}
return back()->with('error', 'Invalid status.');
}
private function statusResponse(Hotel $hotel)
{
return response()->json([
'success' => true,
'message' => 'Status updated successfully.',
'status_label' => match ($hotel->status) {
1 => '<span class="badge bg-success">Active</span>',
2 => '<span class="badge bg-danger">Blocked</span>',
default => '<span class="badge bg-secondary">Inactive</span>',
},
]);
}
/**
* Decide whether to use subtotal or total_amount.
*
* @throws \Illuminate\Validation\ValidationException
*/
private function determineMainAmount(?float $subtotal, ?float $totalAmount): float
{
// Use subtotal if provided and total is empty/zero
if ($subtotal > 0 && ($totalAmount === null || $totalAmount <= 0)) {
return $subtotal;
}
// Use total if provided and subtotal is empty/zero
if ($totalAmount > 0 && ($subtotal === null || $subtotal <= 0)) {
return $totalAmount;
}
// Both positive: not allowed
if ($subtotal > 0 && $totalAmount > 0) {
throw ValidationException::withMessages([
'amount' => 'Please enter only one: "Total Amount (without tax & VAT)" or "Total Amount (with tax & VAT)".',
]);
}
// Neither valid: also not allowed
throw ValidationException::withMessages([
'amount' => 'Please provide a positive Subtotal or Total Amount.',
]);
}
}