'date', 'start_time' => 'datetime:H:i', ]; const STATUS_PENDING = 'pending'; const STATUS_CONFIRMED = 'confirmed'; const STATUS_COMPLETED = 'completed'; const STATUS_CANCELLED = 'cancelled'; /** * Get the user that owns the reservation. */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * Get the table that is reserved. */ public function meja(): BelongsTo { return $this->belongsTo(Meja::class, 'meja_id'); } /** * Get the transaction associated with the reservation. */ public function transaksi(): HasOne { return $this->hasOne(Transaksi::class); } /** * Get the transaction items through the transaction. */ public function detailPesanan(): HasManyThrough { return $this->hasManyThrough( TransaksiItem::class, Transaksi::class, 'reservasi_id', // Foreign key on transaksi table... 'transaksi_id', // Foreign key on transaksi_items table... 'id', // Local key on reservasi table... 'id' // Local key on transaksi table... ); } /** * Check if a time slot is available for a specific table. */ public static function isTimeSlotAvailable($mejaId, $date, $startTime): bool { // Convert times to Carbon instances for comparison $start = Carbon::parse($startTime); $requestedDate = Carbon::parse($date); // Check if the requested date and time is in the past if ($requestedDate->isPast() && $requestedDate->isToday() && $start->isPast()) { return false; } // Check for existing reservations at the same time slot $conflictingReservations = self::where('meja_id', $mejaId) ->whereDate('date', $date) ->whereNotIn('status', [self::STATUS_CANCELLED]) ->where('start_time', $startTime) ->exists(); return !$conflictingReservations; } /** * Get all available time slots for a specific table and date */ public static function getAvailableTimeSlots($mejaId, $date) { // Get all reservations for this table on this date $reservations = self::where('meja_id', $mejaId) ->whereDate('date', $date) ->whereNotIn('status', [self::STATUS_CANCELLED]) ->get(['start_time']); $timeSlots = []; $startHour = 10; // 10:00 $endHour = 22; // 22:00 $interval = 30; // 30 minutes interval for ($hour = $startHour; $hour < $endHour; $hour++) { for ($minute = 0; $minute < 60; $minute += $interval) { $currentTime = sprintf('%02d:%02d', $hour, $minute); // Check if this time slot is available if (self::isTimeSlotAvailable($mejaId, $date, $currentTime)) { $timeSlots[] = [ 'start_time' => $currentTime, 'is_available' => true ]; } } } // If date is today, remove past time slots if ($date == Carbon::today()->format('Y-m-d')) { $currentTime = Carbon::now(); $timeSlots = array_filter($timeSlots, function($slot) use ($currentTime) { return Carbon::parse($slot['start_time'])->gt($currentTime); }); } return array_values($timeSlots); } /** * Update table status based on reservation status */ public function updateTableStatus() { if ($this->date == now()->format('Y-m-d')) { $status = match($this->status) { self::STATUS_CONFIRMED => 'dipesan', self::STATUS_COMPLETED, self::STATUS_CANCELLED => 'tersedia', default => null }; if ($status) { $this->meja()->update(['status' => $status]); } } } /** * Get payment method display name */ public function getPaymentMethodDisplayAttribute() { $methods = [ 'credit_card' => 'Kartu Kredit', 'mandiri_clickpay' => 'Mandiri Clickpay', 'cimb_clicks' => 'CIMB Clicks', 'bca_klikbca' => 'BCA KlikBCA', 'bca_klikpay' => 'BCA KlikPay', 'bri_epay' => 'BRI e-Pay', 'echannel' => 'Mandiri Bill Payment', 'permata_va' => 'Permata Virtual Account', 'bca_va' => 'BCA Virtual Account', 'bni_va' => 'BNI Virtual Account', 'bri_va' => 'BRI Virtual Account', 'other_va' => 'Virtual Account Bank Lain', 'gopay' => 'GoPay', 'shopeepay' => 'ShopeePay', 'qris' => 'QRIS', 'indomaret' => 'Indomaret', 'alfamart' => 'Alfamart', 'danamon_online' => 'Danamon Online Banking', 'akulaku' => 'Akulaku' ]; return $methods[$this->payment_method] ?? $this->payment_method; } }