429 lines
13 KiB
PHP
Raw Normal View History

Hotel Raxa - Advanced Booking System Implementation 🏨 Hotel Booking Enhancements: - Implemented Eagle Booking Advanced Pricing add-on - Added Booking.com-style rate management system - Created professional calendar interface for pricing - Integrated deals and discounts functionality 💰 Advanced Pricing Features: - Dynamic pricing models (per room, per person, per adult) - Base rates, adult rates, and child rates management - Length of stay discounts and early bird deals - Mobile rates and secret deals implementation - Seasonal promotions and flash sales 📅 Availability Management: - Real-time availability tracking - Stop sell and restriction controls - Closed to arrival/departure functionality - Minimum/maximum stay requirements - Automatic sold-out management 💳 Payment Integration: - Maintained Redsys payment gateway integration - Seamless integration with existing Eagle Booking - No modifications to core Eagle Booking plugin 🛠️ Technical Implementation: - Custom database tables for advanced pricing - WordPress hooks and filters integration - AJAX-powered admin interface - Data migration from existing Eagle Booking - Professional calendar view for revenue management 📊 Admin Interface: - Booking.com-style management dashboard - Visual rate and availability calendar - Bulk operations for date ranges - Statistics and analytics dashboard - Modal dialogs for quick editing 🔧 Code Quality: - WordPress coding standards compliance - Secure database operations with prepared statements - Proper input validation and sanitization - Error handling and logging - Responsive admin interface 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-11 07:43:22 +02:00
<?php
/**
* Eagle Booking Advanced Pricing - Rates Management
*
* @package EB_Advanced_Pricing
* @since 1.0.0
*/
defined('ABSPATH') || exit;
class EB_AP_Rates {
private static $instance = null;
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// Constructor
}
/**
* Update rates for a specific room and date
*
* @param int $room_id
* @param string $date
* @param array $rates
* @return bool
*/
public function update_rates($room_id, $date, $rates) {
global $wpdb;
$data = array(
'room_id' => $room_id,
'rate_date' => $date,
'base_rate' => isset($rates['base_rate']) ? $rates['base_rate'] : 0,
'adult_rate' => isset($rates['adult_rate']) ? $rates['adult_rate'] : 0,
'child_rate' => isset($rates['child_rate']) ? $rates['child_rate'] : 0,
'min_guests' => isset($rates['min_guests']) ? $rates['min_guests'] : 1,
'max_guests' => isset($rates['max_guests']) ? $rates['max_guests'] : 10,
'min_stay' => isset($rates['min_stay']) ? $rates['min_stay'] : 1,
'max_stay' => isset($rates['max_stay']) ? $rates['max_stay'] : 0,
'updated_at' => current_time('mysql')
);
// Check if rate already exists
$existing = $wpdb->get_row($wpdb->prepare(
"SELECT id FROM " . EB_AP_TABLE_RATES . " WHERE room_id = %d AND rate_date = %s",
$room_id,
$date
));
if ($existing) {
$result = $wpdb->update(
EB_AP_TABLE_RATES,
$data,
array('id' => $existing->id),
array('%d', '%s', '%f', '%f', '%f', '%d', '%d', '%d', '%d', '%s'),
array('%d')
);
} else {
$data['created_at'] = current_time('mysql');
$result = $wpdb->insert(
EB_AP_TABLE_RATES,
$data,
array('%d', '%s', '%f', '%f', '%f', '%d', '%d', '%d', '%d', '%s', '%s')
);
}
return $result !== false;
}
/**
* Update rates for a date range
*
* @param int $room_id
* @param string $start_date
* @param string $end_date
* @param array $rates
* @return bool
*/
public function update_rates_bulk($room_id, $start_date, $end_date, $rates) {
$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_rates($room_id, $date->format('Y-m-d'), $rates)) {
$success_count++;
}
}
return $success_count === $total_count;
}
/**
* Get rates for a specific room and date
*
* @param int $room_id
* @param string $date
* @return array|null
*/
public function get_rates($room_id, $date) {
global $wpdb;
$rates = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM " . EB_AP_TABLE_RATES . " WHERE room_id = %d AND rate_date = %s",
$room_id,
$date
), ARRAY_A);
if (!$rates) {
// Return default rates from room meta if no advanced rates found
return $this->get_default_rates($room_id);
}
return $rates;
}
/**
* Get rates for a date range
*
* @param int $room_id
* @param string $start_date
* @param string $end_date
* @return array
*/
public function get_rates_range($room_id, $start_date, $end_date) {
global $wpdb;
$rates = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM " . EB_AP_TABLE_RATES . "
WHERE room_id = %d
AND rate_date >= %s
AND rate_date <= %s
ORDER BY rate_date ASC",
$room_id,
$start_date,
$end_date
), ARRAY_A);
// Fill in missing dates with default rates
$rate_map = array();
foreach ($rates as $rate) {
$rate_map[$rate['rate_date']] = $rate;
}
$start = new DateTime($start_date);
$end = new DateTime($end_date);
$end->add(new DateInterval('P1D'));
$period = new DatePeriod($start, new DateInterval('P1D'), $end);
$complete_rates = array();
foreach ($period as $date) {
$date_str = $date->format('Y-m-d');
if (isset($rate_map[$date_str])) {
$complete_rates[] = $rate_map[$date_str];
} else {
$default_rates = $this->get_default_rates($room_id);
$default_rates['rate_date'] = $date_str;
$complete_rates[] = $default_rates;
}
}
return $complete_rates;
}
/**
* Get default rates from room meta
*
* @param int $room_id
* @return array
*/
private function get_default_rates($room_id) {
// Get Eagle Booking default rates
$base_rate = get_post_meta($room_id, 'eagle_booking_mtb_room_price', true);
$min_guests = get_post_meta($room_id, 'eagle_booking_mtb_room_min_guests', true);
$max_guests = get_post_meta($room_id, 'eagle_booking_mtb_room_max_guests', true);
return array(
'room_id' => $room_id,
'rate_date' => '',
'base_rate' => $base_rate ? $base_rate : 0,
'adult_rate' => $base_rate ? $base_rate : 0,
'child_rate' => $base_rate ? $base_rate * 0.5 : 0, // Default child rate is 50% of adult
'min_guests' => $min_guests ? $min_guests : 1,
'max_guests' => $max_guests ? $max_guests : 10,
'min_stay' => 1,
'max_stay' => 0,
'is_default' => true
);
}
/**
* Calculate price based on guest count and pricing model
*
* @param int $room_id
* @param string $date
* @param int $adults
* @param int $children
* @return float
*/
public function calculate_price($room_id, $date, $adults = 1, $children = 0) {
$rates = $this->get_rates($room_id, $date);
if (!$rates) {
return 0;
}
$pricing_model = get_option('eb_ap_pricing_model', 'per_room');
$total_price = 0;
switch ($pricing_model) {
case 'per_room':
// Fixed price per room regardless of guest count
$total_price = $rates['base_rate'];
break;
case 'per_person':
// Price per person
$total_price = ($adults * $rates['adult_rate']) + ($children * $rates['child_rate']);
break;
case 'per_adult':
// Price per adult, children free or discounted
$total_price = $adults * $rates['adult_rate'];
if ($children > 0) {
$total_price += $children * $rates['child_rate'];
}
break;
case 'base_plus_extra':
// Base price + extra for additional guests
$total_price = $rates['base_rate'];
$base_guests = $rates['min_guests'];
$extra_adults = max(0, $adults - $base_guests);
$total_price += ($extra_adults * $rates['adult_rate']) + ($children * $rates['child_rate']);
break;
default:
$total_price = $rates['base_rate'];
}
return $total_price;
}
/**
* Copy rates from one date to another
*
* @param int $room_id
* @param string $from_date
* @param string $to_date
* @return bool
*/
public function copy_rates($room_id, $from_date, $to_date) {
$source_rates = $this->get_rates($room_id, $from_date);
if (!$source_rates) {
return false;
}
unset($source_rates['id']);
unset($source_rates['created_at']);
unset($source_rates['updated_at']);
return $this->update_rates($room_id, $to_date, $source_rates);
}
/**
* Delete rates for a specific date
*
* @param int $room_id
* @param string $date
* @return bool
*/
public function delete_rates($room_id, $date) {
global $wpdb;
$result = $wpdb->delete(
EB_AP_TABLE_RATES,
array(
'room_id' => $room_id,
'rate_date' => $date
),
array('%d', '%s')
);
return $result !== false;
}
/**
* Get minimum rate for a room in a date range
*
* @param int $room_id
* @param string $start_date
* @param string $end_date
* @return float
*/
public function get_min_rate($room_id, $start_date, $end_date) {
global $wpdb;
$min_rate = $wpdb->get_var($wpdb->prepare(
"SELECT MIN(base_rate) FROM " . EB_AP_TABLE_RATES . "
WHERE room_id = %d
AND rate_date >= %s
AND rate_date <= %s",
$room_id,
$start_date,
$end_date
));
if ($min_rate === null) {
$default_rates = $this->get_default_rates($room_id);
return $default_rates['base_rate'];
}
return $min_rate;
}
/**
* Get maximum rate for a room in a date range
*
* @param int $room_id
* @param string $start_date
* @param string $end_date
* @return float
*/
public function get_max_rate($room_id, $start_date, $end_date) {
global $wpdb;
$max_rate = $wpdb->get_var($wpdb->prepare(
"SELECT MAX(base_rate) FROM " . EB_AP_TABLE_RATES . "
WHERE room_id = %d
AND rate_date >= %s
AND rate_date <= %s",
$room_id,
$start_date,
$end_date
));
if ($max_rate === null) {
$default_rates = $this->get_default_rates($room_id);
return $default_rates['base_rate'];
}
return $max_rate;
}
/**
* Apply percentage increase/decrease to rates
*
* @param int $room_id
* @param string $start_date
* @param string $end_date
* @param float $percentage
* @return bool
*/
public function apply_percentage_change($room_id, $start_date, $end_date, $percentage) {
$rates = $this->get_rates_range($room_id, $start_date, $end_date);
$success_count = 0;
$total_count = count($rates);
foreach ($rates as $rate) {
$multiplier = 1 + ($percentage / 100);
$updated_rates = array(
'base_rate' => $rate['base_rate'] * $multiplier,
'adult_rate' => $rate['adult_rate'] * $multiplier,
'child_rate' => $rate['child_rate'] * $multiplier,
'min_guests' => $rate['min_guests'],
'max_guests' => $rate['max_guests'],
'min_stay' => $rate['min_stay'],
'max_stay' => $rate['max_stay']
);
if ($this->update_rates($room_id, $rate['rate_date'], $updated_rates)) {
$success_count++;
}
}
return $success_count === $total_count;
}
/**
* Get rate statistics for a room
*
* @param int $room_id
* @param string $start_date
* @param string $end_date
* @return array
*/
public function get_rate_statistics($room_id, $start_date, $end_date) {
global $wpdb;
$stats = $wpdb->get_row($wpdb->prepare(
"SELECT
MIN(base_rate) as min_rate,
MAX(base_rate) as max_rate,
AVG(base_rate) as avg_rate,
COUNT(*) as total_days
FROM " . EB_AP_TABLE_RATES . "
WHERE room_id = %d
AND rate_date >= %s
AND rate_date <= %s",
$room_id,
$start_date,
$end_date
), ARRAY_A);
return $stats ? $stats : array(
'min_rate' => 0,
'max_rate' => 0,
'avg_rate' => 0,
'total_days' => 0
);
}
}