$room_id, 'availability_date' => $date, 'available_rooms' => isset($availability['available_rooms']) ? $availability['available_rooms'] : 0, 'stop_sell' => isset($availability['stop_sell']) ? $availability['stop_sell'] : 0, 'closed_to_arrival' => isset($availability['closed_to_arrival']) ? $availability['closed_to_arrival'] : 0, 'closed_to_departure' => isset($availability['closed_to_departure']) ? $availability['closed_to_departure'] : 0, 'updated_at' => current_time('mysql') ); // Check if availability already exists $existing = $wpdb->get_row($wpdb->prepare( "SELECT id FROM " . EB_AP_TABLE_AVAILABILITY . " WHERE room_id = %d AND availability_date = %s", $room_id, $date )); if ($existing) { $result = $wpdb->update( EB_AP_TABLE_AVAILABILITY, $data, array('id' => $existing->id), array('%d', '%s', '%d', '%d', '%d', '%d', '%s'), array('%d') ); } else { $data['created_at'] = current_time('mysql'); $result = $wpdb->insert( EB_AP_TABLE_AVAILABILITY, $data, array('%d', '%s', '%d', '%d', '%d', '%d', '%s', '%s') ); } return $result !== false; } /** * Update availability for a date range * * @param int $room_id * @param string $start_date * @param string $end_date * @param array $availability * @return bool */ public function update_availability_bulk($room_id, $start_date, $end_date, $availability) { $start = new DateTime($start_date); $end = new DateTime($end_date); $end->add(new DateInterval('P1D')); // Include end date $period = new DatePeriod($start, new DateInterval('P1D'), $end); $success_count = 0; $total_count = 0; foreach ($period as $date) { $total_count++; if ($this->update_availability($room_id, $date->format('Y-m-d'), $availability)) { $success_count++; } } return $success_count === $total_count; } /** * Get availability for a specific room and date * * @param int $room_id * @param string $date * @return array|null */ public function get_availability($room_id, $date) { global $wpdb; $availability = $wpdb->get_row($wpdb->prepare( "SELECT * FROM " . EB_AP_TABLE_AVAILABILITY . " WHERE room_id = %d AND availability_date = %s", $room_id, $date ), ARRAY_A); if (!$availability) { // Return default availability if none found return $this->get_default_availability($room_id, $date); } return $availability; } /** * Get availability for a date range * * @param int $room_id * @param string $start_date * @param string $end_date * @return array */ public function get_availability_range($room_id, $start_date, $end_date) { global $wpdb; $availability = $wpdb->get_results($wpdb->prepare( "SELECT * FROM " . EB_AP_TABLE_AVAILABILITY . " WHERE room_id = %d AND availability_date >= %s AND availability_date <= %s ORDER BY availability_date ASC", $room_id, $start_date, $end_date ), ARRAY_A); // Fill in missing dates with default availability $availability_map = array(); foreach ($availability as $avail) { $availability_map[$avail['availability_date']] = $avail; } $start = new DateTime($start_date); $end = new DateTime($end_date); $end->add(new DateInterval('P1D')); $period = new DatePeriod($start, new DateInterval('P1D'), $end); $complete_availability = array(); foreach ($period as $date) { $date_str = $date->format('Y-m-d'); if (isset($availability_map[$date_str])) { $complete_availability[] = $availability_map[$date_str]; } else { $complete_availability[] = $this->get_default_availability($room_id, $date_str); } } return $complete_availability; } /** * Get default availability for a room * * @param int $room_id * @param string $date * @return array */ private function get_default_availability($room_id, $date) { // Get room quantity from Eagle Booking $room_quantity = get_post_meta($room_id, 'eagle_booking_mtb_room_quantity', true); $default_availability = get_option('eb_ap_default_availability', 10); // Calculate already booked rooms for this date $booked_rooms = $this->get_booked_rooms($room_id, $date); $available_rooms = max(0, ($room_quantity ? $room_quantity : $default_availability) - $booked_rooms); return array( 'room_id' => $room_id, 'availability_date' => $date, 'available_rooms' => $available_rooms, 'stop_sell' => 0, 'closed_to_arrival' => 0, 'closed_to_departure' => 0, 'is_default' => true ); } /** * Get number of booked rooms for a specific date * * @param int $room_id * @param string $date * @return int */ private function get_booked_rooms($room_id, $date) { global $wpdb; $booked_count = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM " . EAGLE_BOOKING_TABLE . " WHERE id_post = %d AND %s >= STR_TO_DATE(date_from, '%%m/%%d/%%Y') AND %s < STR_TO_DATE(date_to, '%%m/%%d/%%Y') AND (paypal_payment_status = 'Completed' OR paypal_payment_status = 'Pending Payment')", $room_id, $date, $date )); return $booked_count ? $booked_count : 0; } /** * Check if a room is available for booking * * @param int $room_id * @param string $date * @param int $rooms_needed * @return bool */ public function is_available($room_id, $date, $rooms_needed = 1) { $availability = $this->get_availability($room_id, $date); if (!$availability) { return false; } // Check stop sell if ($availability['stop_sell']) { return false; } // Check available rooms if ($availability['available_rooms'] < $rooms_needed) { return false; } return true; } /** * Check if a room is available for a date range * * @param int $room_id * @param string $start_date * @param string $end_date * @param int $rooms_needed * @return bool */ public function is_available_range($room_id, $start_date, $end_date, $rooms_needed = 1) { $availability = $this->get_availability_range($room_id, $start_date, $end_date); foreach ($availability as $avail) { if (!$this->is_available($room_id, $avail['availability_date'], $rooms_needed)) { return false; } } return true; } /** * Check if arrival is allowed on a specific date * * @param int $room_id * @param string $date * @return bool */ public function is_arrival_allowed($room_id, $date) { $availability = $this->get_availability($room_id, $date); if (!$availability) { return false; } return !$availability['closed_to_arrival']; } /** * Check if departure is allowed on a specific date * * @param int $room_id * @param string $date * @return bool */ public function is_departure_allowed($room_id, $date) { $availability = $this->get_availability($room_id, $date); if (!$availability) { return false; } return !$availability['closed_to_departure']; } /** * Set stop sell for a date range * * @param int $room_id * @param string $start_date * @param string $end_date * @param bool $stop_sell * @return bool */ public function set_stop_sell($room_id, $start_date, $end_date, $stop_sell = true) { return $this->update_availability_bulk($room_id, $start_date, $end_date, array( 'stop_sell' => $stop_sell ? 1 : 0 )); } /** * Set closed to arrival for a date range * * @param int $room_id * @param string $start_date * @param string $end_date * @param bool $closed * @return bool */ public function set_closed_to_arrival($room_id, $start_date, $end_date, $closed = true) { return $this->update_availability_bulk($room_id, $start_date, $end_date, array( 'closed_to_arrival' => $closed ? 1 : 0 )); } /** * Set closed to departure for a date range * * @param int $room_id * @param string $start_date * @param string $end_date * @param bool $closed * @return bool */ public function set_closed_to_departure($room_id, $start_date, $end_date, $closed = true) { return $this->update_availability_bulk($room_id, $start_date, $end_date, array( 'closed_to_departure' => $closed ? 1 : 0 )); } /** * Reserve rooms for a booking * * @param int $room_id * @param string $start_date * @param string $end_date * @param int $rooms_count * @return bool */ public function reserve_rooms($room_id, $start_date, $end_date, $rooms_count = 1) { $availability = $this->get_availability_range($room_id, $start_date, $end_date); $success_count = 0; $total_count = count($availability); foreach ($availability as $avail) { $new_available = max(0, $avail['available_rooms'] - $rooms_count); $updated_availability = array( 'available_rooms' => $new_available, 'stop_sell' => $avail['stop_sell'], 'closed_to_arrival' => $avail['closed_to_arrival'], 'closed_to_departure' => $avail['closed_to_departure'] ); if ($this->update_availability($room_id, $avail['availability_date'], $updated_availability)) { $success_count++; } } return $success_count === $total_count; } /** * Release rooms from a booking * * @param int $room_id * @param string $start_date * @param string $end_date * @param int $rooms_count * @return bool */ public function release_rooms($room_id, $start_date, $end_date, $rooms_count = 1) { $availability = $this->get_availability_range($room_id, $start_date, $end_date); $success_count = 0; $total_count = count($availability); foreach ($availability as $avail) { $new_available = $avail['available_rooms'] + $rooms_count; $updated_availability = array( 'available_rooms' => $new_available, 'stop_sell' => $avail['stop_sell'], 'closed_to_arrival' => $avail['closed_to_arrival'], 'closed_to_departure' => $avail['closed_to_departure'] ); if ($this->update_availability($room_id, $avail['availability_date'], $updated_availability)) { $success_count++; } } return $success_count === $total_count; } /** * Get availability statistics for a room * * @param int $room_id * @param string $start_date * @param string $end_date * @return array */ public function get_availability_statistics($room_id, $start_date, $end_date) { global $wpdb; $stats = $wpdb->get_row($wpdb->prepare( "SELECT SUM(available_rooms) as total_available, AVG(available_rooms) as avg_available, SUM(CASE WHEN stop_sell = 1 THEN 1 ELSE 0 END) as stop_sell_days, SUM(CASE WHEN closed_to_arrival = 1 THEN 1 ELSE 0 END) as cta_days, SUM(CASE WHEN closed_to_departure = 1 THEN 1 ELSE 0 END) as ctd_days, COUNT(*) as total_days FROM " . EB_AP_TABLE_AVAILABILITY . " WHERE room_id = %d AND availability_date >= %s AND availability_date <= %s", $room_id, $start_date, $end_date ), ARRAY_A); return $stats ? $stats : array( 'total_available' => 0, 'avg_available' => 0, 'stop_sell_days' => 0, 'cta_days' => 0, 'ctd_days' => 0, 'total_days' => 0 ); } /** * Copy availability from one date to another * * @param int $room_id * @param string $from_date * @param string $to_date * @return bool */ public function copy_availability($room_id, $from_date, $to_date) { $source_availability = $this->get_availability($room_id, $from_date); if (!$source_availability) { return false; } unset($source_availability['id']); unset($source_availability['created_at']); unset($source_availability['updated_at']); return $this->update_availability($room_id, $to_date, $source_availability); } /** * Delete availability for a specific date * * @param int $room_id * @param string $date * @return bool */ public function delete_availability($room_id, $date) { global $wpdb; $result = $wpdb->delete( EB_AP_TABLE_AVAILABILITY, array( 'room_id' => $room_id, 'availability_date' => $date ), array('%d', '%s') ); return $result !== false; } /** * Auto-close availability when sold out * * @param int $room_id * @param string $date * @return bool */ public function auto_close_sold_out($room_id, $date) { $availability = $this->get_availability($room_id, $date); if (!$availability || $availability['available_rooms'] > 0) { return false; } $auto_close = get_option('eb_ap_auto_close_sold_out', 'yes'); if ($auto_close === 'yes') { return $this->update_availability($room_id, $date, array( 'available_rooms' => 0, 'stop_sell' => 1, 'closed_to_arrival' => $availability['closed_to_arrival'], 'closed_to_departure' => $availability['closed_to_departure'] )); } return false; } }