Files
hotel/modules/hotelreservationsystem/classes/HotelRoomInformation.php
2026-10-27 15:43:20 +05:30

532 lines
24 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License version 3.0
* that is bundled with this package in the file LICENSE.md
* It is also available through the world-wide-web at this URL:
* https://opensource.org/license/osl-3-0-php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to support@qloapps.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade this module to a newer
* versions in the future. If you wish to customize this module for your needs
* please refer to https://store.webkul.com/customisation-guidelines for more information.
*
* @author Webkul IN
* @copyright Since 2010 Webkul
* @license https://opensource.org/license/osl-3-0-php Open Software License version 3.0
*/
class HotelRoomInformation extends ObjectModel
{
public $id;
public $id_product;
public $id_hotel;
public $room_num;
public $id_status;
public $floor;
public $comment;
public $date_add;
public $date_upd;
const STATUS_ACTIVE = 1;
const STATUS_INACTIVE = 2;
const STATUS_SEARCH_LOS_UNSATISFIED = 4;
const STATUS_SEARCH_OCCUPANCY_UNSATISFIED = 5;
public static $definition = array(
'table' => 'htl_room_information',
'primary' => 'id',
'fields' => array(
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_hotel' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'room_num' => array('type' => self::TYPE_STRING),
'id_status' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
'floor' => array('type' => self::TYPE_STRING),
'comment' => array('type' => self::TYPE_STRING),
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate')
),
);
protected $webserviceParameters = array(
'objectMethods' => array(
'add' => 'addWs',
'update' => 'updateWs',
'delete' => 'deleteWs',
),
'objectsNodeName' => 'rooms',
'objectNodeName' => 'room',
'fields' => array(
'id_product' => array(
'xlink_resource' => array(
'resourceName' => 'products',
)
),
'id_hotel' => array(
'setter' => false,
'xlink_resource' => array(
'resourceName' => 'hotels',
)
),
),
);
public function update($null_values = false)
{
if ($idRoom = $this->id) {
// delete rooms from cart which are set inactive
if ($this->id_status == HotelRoomInformation::STATUS_INACTIVE) {
$objCartBookingData = new HotelCartBookingData();
if (!$objCartBookingData->deleteCartBookingData(0, 0, $idRoom)) {
return false;
}
}
}
return parent::update();
}
//Overrided ObjectModel::delete()
public function delete()
{
if ($idRoom = $this->id) {
$objCartBookingData = new HotelCartBookingData();
if (!$this->deleteRoomDisableDates()
|| !$objCartBookingData->deleteCartBookingData(0, 0, $idRoom)
|| !parent::delete()
) {
return false;
}
}
return true;
}
public function deleteRoomDisableDates($idRoom = false)
{
if (!$idRoom) {
$idRoom = $this->id;
if (!$idRoom) {
return false;
}
}
$objRoomDisableDates = new HotelRoomDisableDates();
$objRoomDisableDates->deleteRoomDisableDates((int)$idRoom);
return true;
}
/**
* [deleteByProductId :: To delete all rooms information which belongs to a room type(which is a product in real) By product id]
* @param [int] $id_product [Id of the product form which all rooms information to be deleted]
* @return [Boolean] [Returns true if deleted successfully else returns false]
*/
public function deleteByProductId($idProduct)
{
if ($rooms = Db::getInstance()->executeS(
'SELECT * FROM `'._DB_PREFIX_.'htl_room_information` WHERE `id_product`='.(int) $idProduct
)) {
foreach ($rooms as $room) {
$objRoomInfo = new HotelRoomInformation($room['id']);
if (!$objRoomInfo->delete()) {
return false;
}
}
}
return true;
}
public function getAllRoomStatus()
{
$status = array(
'STATUS_ACTIVE' => array(
'id' => HotelRoomInformation::STATUS_ACTIVE,
'status' => self::getRoomStatusTitle(HotelRoomInformation::STATUS_ACTIVE)
),
'STATUS_INACTIVE' => array(
'id' => HotelRoomInformation::STATUS_INACTIVE,
'status' => self::getRoomStatusTitle(HotelRoomInformation::STATUS_INACTIVE)
),
);
Hook::exec('actionAdminRoomStatusModifier', array('status' => &$status));
return $status;
}
public static function getRoomStatusTitle($idStatus)
{
$moduleInstance = Module::getInstanceByName('hotelreservationsystem');
$status = array(
HotelRoomInformation::STATUS_ACTIVE => $moduleInstance->l('Active', 'hotelreservationsystem'),
HotelRoomInformation::STATUS_INACTIVE => $moduleInstance->l('Inactive', 'hotelreservationsystem'),
HotelRoomInformation::STATUS_SEARCH_LOS_UNSATISFIED => $moduleInstance->l('Length of stay restriction not satisfied', 'hotelreservationsystem'),
HotelRoomInformation::STATUS_SEARCH_OCCUPANCY_UNSATISFIED => $moduleInstance->l('Occupancy exceeds room capacity', 'hotelreservationsystem'),
);
Hook::exec('actionAdminRoomStatusTitle', array('status' => &$status, 'id_status' => $idStatus));
return isset($status[$idStatus]) ? $status[$idStatus] : '';
}
/**
* [getHotelRoomInfoByProductId :: To get all rooms information belong to a room type(product) by product id]
* @param [int] $id_product [Id of the product]
* @return [array|false] [If data found returns array containing all rooms information belongs to a room type(product) which product id is passed else returns false]
*/
public function getHotelRoomInfoByProductId($id_product)
{
$result = Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'htl_room_information` WHERE `id_product`='.(int) $id_product);
if ($result) {
return $result;
}
return false;
}
/**
* [getHotelRoomInfo :: To get all rooms information belong to a room type(product) and belongss to a hotel wwhich hotel id is passed]
* @param [type] $id_product [Id of the room type(product)]
* @param [type] $id_hotel [Id of the hotel]
* @param [int] $is_getNum [
* If $is_getNum is passed
* then returns number of rooms belong to a room type(product) and belongss to a hotel wwhich hotel id is passed
* else
* Returns array containing all rooms information belong to a room type(product) and belongss to a hotel wwhich hotel id is passed
* ]
* @return [array|int|boolean] [If $is_getNum is passed
* then returns number of rooms belong to a room type(product) and belongss to a hotel wwhich hotel id is passed
* else
* if data found
* Returns array containing all rooms information belong to a room type(product) and belongss to a hotel wwhich hotel id is passed
* else
* returns false; ]
*/
public function getHotelRoomInfo($id_product, $id_hotel, $is_getNum = 0)
{
$sql = "SELECT * FROM `"._DB_PREFIX_."htl_room_information` WHERE `id_product` = ".(int) $id_product." AND `id_hotel` = ".(int) $id_hotel;
$rm_info = Db::getInstance()->executeS($sql);
if ($is_getNum) {
$no_row = Db::getInstance()->NumRows();
return $no_row;
} else {
if ($rm_info) {
return $rm_info;
} else {
return false;
}
}
}
/**
* [getHotelRoomInfoById :: To get Information of a room by its id(primary key)]
* @param [int] $id [id of the room in the table(primary key)]
* @return [array|false] [If data found returns array containing information of the room which id is passed else returns false]
*/
public function getHotelRoomInfoById($id)
{
$sql = "SELECT `room_num` FROM `"._DB_PREFIX_."htl_room_information` WHERE `id` = ".(int) $id;
$rm_num = Db::getInstance()->getValue($sql);
if ($rm_num) {
return $rm_num;
} else {
return false;
}
}
public static function getHotelRoomsInfo($idHotel = null, $idProduct = null, $idLang = null)
{
if (!$idLang) {
$idLang = Context::getContext()->language->id;
}
$sql = 'SELECT hri.*, hri.`id_product`, hri.`id_hotel`, hrt.`adults`, hrt.`children`, hrt.`max_adults`,
hrt.`max_children`, hrt.`max_guests`, hrt.`min_los`, hrt.`max_los`, pl.`name` AS room_type_name, hbil.`hotel_name` AS hotel_name
FROM `'._DB_PREFIX_.'htl_room_information` hri
INNER JOIN `'._DB_PREFIX_.'htl_room_type` hrt ON (hrt.`id_product` = hri.`id_product`)
INNER JOIN `'._DB_PREFIX_.'htl_branch_info` hbi ON (hbi.`id` = hri.`id_hotel`)
INNER JOIN `'._DB_PREFIX_.'htl_branch_info_lang` hbil ON (hbil.`id` = hri.`id_hotel` AND hbil.`id_lang` = '.(int) $idLang.')
INNER JOIN `'._DB_PREFIX_.'product` p ON (p.`id_product` = hri.`id_product`)
INNER JOIN `'._DB_PREFIX_.'product_lang` pl ON (pl.`id_product` = p.`id_product` AND pl.`id_lang` = '.(int) $idLang.')
WHERE 1 '.($idHotel ? ' AND hri.`id_hotel` = '.(int) $idHotel : '').
($idProduct ? ' AND hri.`id_product` = '.(int) $idProduct : '').'
ORDER BY hri.`id_product`, hri.`id`';
return Db::getInstance()->executeS($sql);
}
/**
* Deprecated
* [deleteHotelRoomInfoById :: To delete room information which id is passed]
* @param [int] $id_room_info [Id of the room which information(row in the table) to be deleted]
* @return [Boolean] [Returns true if deleted successfully else returns false]
*/
public function deleteHotelRoomInfoById($id_room_info)
{
$result = Db::getInstance()->delete('htl_room_information', '`id` = '.(int)$id_room_info, 1);
return $result;
}
public function getRoomTypeAvailableRoomsForDateRange($id_hotel, $id_product, $date_from, $date_to)
{
return Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'htl_room_information` where `id_hotel`='.(int) $id_hotel.' AND `id_product`='.(int) $id_product.' AND `id` NOT IN (SELECT `id_room` from `'._DB_PREFIX_.'htl_booking_detail` where `date_from`< \''.pSQL($date_to).'\' AND `date_to`>\''.pSQL($date_from).'\' AND `id_product`='.(int) $id_product.' AND `id_hotel`='.(int) $id_hotel.')');
}
public function getRoomTypeDisabledRoomsForDateRange($id_hotel, $id_product, $date_from, $date_to)
{
return Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'htl_room_information` where `id_hotel`='.(int) $id_hotel.' AND `id_product`='.(int) $id_product.' AND `id_status`=3 AND `id` NOT IN (SELECT `id_room` from `'._DB_PREFIX_.'htl_booking_detail` where `date_from`< \''.pSQL($date_to).'\' AND `date_to`>\''.pSQL($date_from).'\' AND `id_product`='.(int) $id_product.' AND `id_hotel`='.(int) $id_hotel.')');
}
public function getRoomTypeBookedRoomsForDateRange($id_hotel, $id_product, $date_from, $date_to)
{
return Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'htl_booking_detail` where `date_from`< \''.pSQL($date_to).'\' AND `date_to`>\''.pSQL($date_from).'\' AND `id_product`='.(int) $id_product.' AND `id_hotel`='.(int) $id_hotel);
}
public function getFutureBookings($idRoom)
{
return Db::getInstance()->executeS('SELECT `id`, `id_order`, `date_from`, `date_to` FROM `'._DB_PREFIX_.'htl_booking_detail` where `date_to` > \''.pSQL(date('Y-m-d')).'\' AND `id` NOT IN ('.OrderReturn::getRefundedBookingIdsSubquery().') AND `id_room`='.(int) $idRoom);
}
// Webservice :: webservice add function
public function addWs($autodate = true, $null_values = false)
{
$objRoomType = new HotelRoomType();
if ($roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($this->id_product)) {
$this->id_hotel = $roomTypeInfo['id_hotel'];
return $this->add($autodate, $null_values);
} else {
WebserviceRequest::getInstance()->setError(400, 'Invalid id product', 134);
}
return false;
}
// Webservice :: webservice update function
public function updateWs($null_values = false)
{
$objRoomType = new HotelRoomType();
if ($roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($this->id_product)) {
$this->id_hotel = $roomTypeInfo['id_hotel'];
return $this->update($null_values);
} else {
WebserviceRequest::getInstance()->setError(400, 'Invalid id product', 134);
}
return false;
}
// Webservice :: webservice delete function
public function deleteWs()
{
return $this->delete();
}
// ── REPORT METHODS ────────────────────────────────────────────────────────
/**
* Room-type × date grid showing total / booked / available / OOO per row.
* Used by availability report tab.
*
* @param array $params date_from, date_to, id_hotel, id_product, id_lang
* @return array rows: date, id_product, room_type_name, total_rooms, rooms_booked, out_of_order, available, occupancy_pct
*/
public static function getAvailabilityReport(array $params)
{
$dateFrom = pSQL($params['date_from']);
$dateTo = pSQL(isset($params['date_to']) ? $params['date_to'] : $params['date_from']);
$idsHotel = isset($params['ids_hotel']) ? $params['ids_hotel'] : (isset($params['id_hotel']) ? $params['id_hotel'] : false);
$idProduct = isset($params['id_product']) ? (int) $params['id_product'] : 0;
$idLang = isset($params['id_lang']) ? (int) $params['id_lang'] : 0;
if (!$idLang) {
$idLang = Context::getContext()->language->id;
}
$roomTypes = Db::getInstance()->executeS(
'SELECT hri.`id_product`, pl.`name` AS room_type_name,
COUNT(hri.`id`) AS total_rooms,
SUM(CASE WHEN hri.`id_status` = '.(int) self::STATUS_INACTIVE.' THEN 1 ELSE 0 END) AS ooo_rooms
FROM `'._DB_PREFIX_.'htl_room_information` hri
INNER JOIN `'._DB_PREFIX_.'product` p ON (p.`id_product` = hri.`id_product` AND p.`active` = 1)
INNER JOIN `'._DB_PREFIX_.'product_lang` pl
ON (pl.`id_product` = hri.`id_product` AND pl.`id_lang` = '.(int) $idLang.')
WHERE 1'
.HotelBranchInformation::addHotelRestriction($idsHotel, 'hri')
.($idProduct ? ' AND hri.`id_product` = '.$idProduct : '').'
GROUP BY hri.`id_product`'
);
if (!$roomTypes) {
return array();
}
$dateToNext = pSQL(date('Y-m-d', strtotime('+1 day', strtotime($dateTo))));
$bookingsRaw = Db::getInstance()->executeS(
'SELECT hbd.`id_product`, hbd.`id_room`, hbd.`date_from`, hbd.`date_to`
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
INNER JOIN `'._DB_PREFIX_.'orders` o ON (o.`id_order` = hbd.`id_order` AND o.`valid` = 1)
WHERE hbd.`id` NOT IN ('.OrderReturn::getRefundedBookingIdsSubquery().')
AND hbd.`id_status` != '.HotelBookingDetail::STATUS_CANCELLED.'
AND hbd.`date_from` < "'.$dateToNext.'" AND hbd.`date_to` > "'.$dateFrom.'"'
.HotelBranchInformation::addHotelRestriction($idsHotel, 'hbd')
.($idProduct ? ' AND hbd.`id_product` = '.$idProduct : '')
);
$bookingsByProduct = array();
foreach ($bookingsRaw as $b) {
$bookingsByProduct[$b['id_product']][] = $b;
}
// Active rooms blocked by a disable date range (inactive rooms are already counted as out of order)
$disabledRaw = Db::getInstance()->executeS(
'SELECT hri.`id_product`, hrdd.`id_room`, hrdd.`date_from`, hrdd.`date_to`
FROM `'._DB_PREFIX_.'htl_room_disable_dates` hrdd
INNER JOIN `'._DB_PREFIX_.'htl_room_information` hri ON (hri.`id` = hrdd.`id_room`)
WHERE hri.`id_status` = '.(int) self::STATUS_ACTIVE.'
AND hrdd.`date_from` < "'.$dateToNext.'" AND hrdd.`date_to` > "'.$dateFrom.'"'
.HotelBranchInformation::addHotelRestriction($idsHotel, 'hri')
.($idProduct ? ' AND hri.`id_product` = '.$idProduct : '')
);
$disabledByProduct = array();
foreach ($disabledRaw as $d) {
$disabledByProduct[$d['id_product']][] = $d;
}
$result = array();
$current = $dateFrom;
while ($current <= $dateTo) {
$next = date('Y-m-d', strtotime('+1 day', strtotime($current)));
foreach ($roomTypes as $rt) {
$idProd = $rt['id_product'];
$bookedRooms = array();
if (isset($bookingsByProduct[$idProd])) {
foreach ($bookingsByProduct[$idProd] as $b) {
if ($b['date_from'] < $next && $b['date_to'] > $current) {
$bookedRooms[$b['id_room']] = true;
}
}
}
$disabledRooms = array();
if (isset($disabledByProduct[$idProd])) {
foreach ($disabledByProduct[$idProd] as $d) {
if ($d['date_from'] < $next && $d['date_to'] > $current) {
$disabledRooms[$d['id_room']] = true;
}
}
}
$booked = count($bookedRooms);
$total = (int) $rt['total_rooms'];
$ooo = (int) $rt['ooo_rooms'] + count($disabledRooms);
$available = max(0, $total - $booked - $ooo);
$result[] = array(
'date' => $current,
'id_product' => $idProd,
'room_type_name' => $rt['room_type_name'],
'total_rooms' => $total,
'rooms_booked' => $booked,
'out_of_order' => $ooo,
'available' => $available,
'occupancy_pct' => $total ? round($booked / $total * 100, 1) : 0.0,
);
}
$current = $next;
}
return $result;
}
/**
* Per-room status with current guest info for room-status report tab.
*
* @param array $params date_from, date_to, id_hotel, id_product, id_lang, housekeeping_installed
* @return array
*/
public static function getRoomStatus(array $params)
{
$dateFrom = pSQL($params['date_from']);
$dateTo = pSQL(isset($params['date_to']) ? $params['date_to'] : $params['date_from']);
$dateToNext = pSQL(date('Y-m-d', strtotime('+1 day', strtotime($dateTo))));
$idsHotel = isset($params['ids_hotel']) ? $params['ids_hotel'] : (isset($params['id_hotel']) ? $params['id_hotel'] : false);
$idProduct = isset($params['id_product']) ? (int) $params['id_product'] : 0;
$idLang = isset($params['id_lang']) ? (int) $params['id_lang'] : 0;
$housekeepingInstalled = !empty($params['housekeeping_installed']);
if (!$idLang) {
$idLang = Context::getContext()->language->id;
}
return Db::getInstance()->executeS(
'SELECT hri.`id` AS id_room, hri.`room_num`, hri.`floor`,
hri.`id_status`,
IF(hri.`id_status` = '.(int) self::STATUS_ACTIVE.' AND hrdd.`id_room` IS NOT NULL, 1, 0) AS is_disabled,
hri.`id_product`, hri.`id_hotel`,
pl.`name` AS room_type_name, hbil.`hotel_name`,
bkgs.`id_order`, bkgs.`date_from`, bkgs.`date_to`,
bkgs.`id_status` AS booking_status,
c.`id_customer`,
CONCAT(c.`firstname`, " ", c.`lastname`) AS guest_name'
.($housekeepingInstalled ? ', hri.`id_housekeeping_status`' : '').'
FROM `'._DB_PREFIX_.'htl_room_information` hri
INNER JOIN `'._DB_PREFIX_.'product` p ON (p.`id_product` = hri.`id_product`)
INNER JOIN `'._DB_PREFIX_.'product_lang` pl
ON (pl.`id_product` = hri.`id_product` AND pl.`id_lang` = '.(int) $idLang.')
INNER JOIN `'._DB_PREFIX_.'htl_branch_info` hbi ON (hbi.`id` = hri.`id_hotel`)
INNER JOIN `'._DB_PREFIX_.'htl_branch_info_lang` hbil
ON (hbil.`id` = hri.`id_hotel` AND hbil.`id_lang` = '.(int) $idLang.')
LEFT JOIN (
SELECT `id_room`
FROM `'._DB_PREFIX_.'htl_room_disable_dates`
WHERE `date_from` < "'.$dateToNext.'" AND `date_to` > "'.$dateFrom.'"
GROUP BY `id_room`
) hrdd ON (hrdd.`id_room` = hri.`id`)
LEFT JOIN (
SELECT hbd.`id_room`,
MIN(hbd.`id_order`) AS `id_order`,
MIN(hbd.`date_from`) AS `date_from`,
MAX(hbd.`date_to`) AS `date_to`,
MIN(hbd.`id_status`) AS `id_status`,
MIN(hbd.`id_customer`) AS `id_customer`
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
WHERE hbd.`id` NOT IN ('.OrderReturn::getRefundedBookingIdsSubquery().')
AND hbd.`date_from` < "'.$dateToNext.'" AND hbd.`date_to` > "'.$dateFrom.'"
GROUP BY hbd.`id_room`
) bkgs ON (bkgs.`id_room` = hri.`id`)
LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = bkgs.`id_customer`)
WHERE p.`active` = 1 AND p.`booking_product` = 1'
.($idProduct ? ' AND hri.`id_product` = '.$idProduct : '')
.HotelBranchInformation::addHotelRestriction($idsHotel, 'hri').'
ORDER BY hbil.`hotel_name`, pl.`name`, hri.`room_num`'
);
}
/**
* Floor list for filter dropdown in reports.
*
* @param array $params id_hotel, id_product
* @return array rows: floor
*/
public static function getDistinctFloors(array $params = array())
{
$idsHotel = isset($params['ids_hotel']) ? $params['ids_hotel'] : (isset($params['id_hotel']) ? $params['id_hotel'] : false);
$idProduct = isset($params['id_product']) ? (int) $params['id_product'] : 0;
return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
'SELECT DISTINCT hri.`floor`
FROM `'._DB_PREFIX_.'htl_room_information` hri
INNER JOIN `'._DB_PREFIX_.'product` p
ON (p.`id_product` = hri.`id_product` AND p.`active` = 1 AND p.`booking_product` = 1)
WHERE hri.`floor` != "" AND hri.`floor` IS NOT NULL'
.($idProduct ? ' AND hri.`id_product` = '.$idProduct : '')
.HotelBranchInformation::addHotelRestriction($idsHotel, 'hri').'
ORDER BY hri.`floor`'
);
}
}