Merge branch 'Qloapps:develop' into gli-version_upgrade

This commit is contained in:
Shazar Rahman
2026-11-04 12:58:53 +05:30
committed by GitHub
48 changed files with 833 additions and 254 deletions

View File

@@ -21,7 +21,8 @@
margin-bottom: 5px
.ui-tooltip-body
max-height: 250px;
overflow-y: auto;
> div
margin-bottom: 6px
margin-right: 10px
@@ -42,3 +43,6 @@
.ui-tooltip-label
font-weight: 600
margin: 0
.label.label-success.service-applied-days
font-size: 11px

View File

@@ -25,7 +25,7 @@
{if $hotel_names_remaining}
{capture name='hotel_names_tooltip'}
<div class="ui-tooltip-wrapper">
<div class="ui-tooltip-header"><div class="ui-tooltip-label">{l s='Selected Hotels'}</div></div>
<div class="ui-tooltip-header"><div class="ui-tooltip-label">{l s='Selected Properties'}</div></div>
<ul>
{foreach from=$selected_hotels item='hotel'}
<li class="ui-tooltip-elem-txt">{$hotel|escape:'html':'UTF-8'}</li>

View File

@@ -928,7 +928,7 @@
<td class="partial_refund_fields current-edit" style="display:none;"></td>
</tr>
{/if}
{if isset($total_room_services_tax_excl) && $total_room_services_tax_excl > 0}
{if isset($total_room_services_tax_excl) && ($total_room_services_tax_excl - $total_convenience_fee_tax_excl) > 0}
<tr id="total_products">
<td class="text-right">{l s='Total Extra services (Tax excl.)'}</td>
<td class="amount text-right nowrap">
@@ -1036,7 +1036,7 @@
<td class="partial_refund_fields current-edit" style="display:none;"></td>
</tr>
{/if}
{if isset($total_convenience_fee_tax_excl) && $total_convenience_fee_tax_excl > 0}
{if isset($total_convenience_fee_tax_only) && $total_convenience_fee_tax_only > 0}
<tr id="total_products">
<td class="text-left">{l s='Convenience Fee Tax'}</td>
<td class="amount text-right nowrap">

View File

@@ -47,6 +47,7 @@
</thead>
<tbody>
{foreach $serviceProducts as $product}
{if !empty($selectedRoomServiceProduct['date_from']) && !(isset($selectedRoomServiceProduct['selected_service'][$product['id_product']])) && $product['price_calculation_method'] != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING && !(Product::getServicePriceApplicableDates($product['price_calculation_method'], $selectedRoomServiceProduct['date_from'], $selectedRoomServiceProduct['date_to'])|@array_filter)}{continue}{/if}
{if isset($selectedRoomServiceProduct['selected_service']) && $selectedRoomServiceProduct['selected_service'] && ($product['id_product']|array_key_exists:$selectedRoomServiceProduct['selected_service'])}
{assign var='serviceSelected' value=true}
{$product.price_tax_incl = $selectedRoomServiceProduct['selected_service'][$product['id_product']]['unit_price_tax_incl']}
@@ -63,21 +64,7 @@
<td>
<div>
{$product['name']|escape:'html':'UTF-8'}
{capture name='room_type_service_tooltip_content'}
{assign var="priceCalcMethod" value=$product['price_calculation_method']|default:0}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=Product::getPriceCalculationMethodDaysLabel($priceCalcMethod) item='pcmDayLabel'}
<li>{$pcmDayLabel}</li>
{/foreach}
</ul>
</div>
{/capture}
{include file='helpers/ui-tooltip.tpl' tooltip_content=$smarty.capture.room_type_service_tooltip_content allow_html=true}
{include file='controllers/orders/modals/_partials/service-applied-days.tpl' price_calculation_method=$product['price_calculation_method'] date_from=$selectedRoomServiceProduct['date_from']|default:'' date_to=$selectedRoomServiceProduct['date_to']|default:''}
</div>
</td>
<td>
@@ -108,6 +95,7 @@
{$cartCurrency->sign}
</span>
<input class="service_cart_price_input" id="service_cart_price_{$selectedRoomServiceProduct['id']}_{$product['id_product']}" type="text" value="{$product.price_tax_exc}" name="service_price[{$product['id_product']|escape:'html':'UTF-8'}]"{if isset($can_edit) && !$can_edit} readonly{/if}/>
{if ($product['price_calculation_method']) != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING}<span class="input-group-addon">{l s='/ Night'}</span>{/if}
</div>
{/if}
</td>
@@ -143,40 +131,6 @@
</div>
</div>
<div class="row form-group">
<div class="col-sm-6">
<label class="control-label required">{l s='Price calculation method'}</label>
<select id="new_service_pcm_type" name="price_calculation_type">
<option value="{Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING|intval}" selected="selected">{l s='Once for the booking range'}</option>
<option value="0">{l s='Select applicable days'}</option>
</select>
<div id="new_service_pcm_days_container" style="margin-top: 10px; display:none;">
<div class="row">
<div class="col-sm-3">
<label class="control-label required">{l s='Applied on:'}</label>
</div>
<div class="col-sm-4">
<div class="checkbox">
<label>
<input type="checkbox" id="pcm_checkin" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_CHECKIN_DAY|intval}">
{l s='Check-in day'}
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="pcm_checkout" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_CHECKOUT_DAY|intval}">
{l s='Check-out day'}
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="pcm_duringstay" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_DURING_STAY|intval}"/>
{l s='During-stay days'}
</label>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<label class="control-label">{l s='Auto added service'}</label>
<div>
@@ -193,8 +147,6 @@
</span>
</div>
</div>
</div>
<div class="row form-group">
<div id="new_service_price_tax_rule_container" class="col-sm-6">
<label class="control-label">{l s='Tax rule'}</label>
<select name="new_service_price_tax_rule_group">
@@ -211,10 +163,40 @@
<option value="{Product::PRICE_ADDITION_TYPE_INDEPENDENT}">{l s='Add price as convenience Fee'}</option>
</select>
</div>
</div>
<div class="row form-group">
<div id="new_service_qty_container" class="col-sm-6">
<label class="control-label required">{l s='Quantity'}</label>
<input type="number" class="form-control qty" min="1" name="new_service_qty" value="1">
</div>
<div class="col-sm-6">
<label class="control-label required">{l s='Price calculation method'}</label>
<select id="new_service_pcm_type" name="price_calculation_type">
<option value="{Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING|intval}" selected="selected">{l s='Once for the booking range'}</option>
<option value="0">{l s='Select applicable days'}</option>
</select>
</div>
</div>
<div id="new_service_pcm_days_container" style="display:none;">
<div class="row form-group">
<div class="col-sm-12">
<label class="control-label required">{l s='Applied on:'}</label>
</div>
<div class="col-sm-12">
<label class="checkbox-inline">
<input type="checkbox" id="pcm_checkin" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_CHECKIN_DAY|intval}">
{l s='Check-in day'}
</label>
<label class="checkbox-inline">
<input type="checkbox" id="pcm_checkout" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_CHECKOUT_DAY|intval}">
{l s='Check-out day'}
</label>
<label class="checkbox-inline">
<input type="checkbox" id="pcm_duringstay" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_DURING_STAY|intval}"/>
{l s='During-stay days'}
</label>
</div>
</div>
</div>
<input type="hidden" name="id_hotel_cart_booking" value="{$id_hotel_cart_booking}">
<div class="row form-group">

View File

@@ -56,21 +56,7 @@
<td width="22%">
<div>
{$service['name']|escape:'html':'UTF-8'}
{capture name='room_type_service_tooltip_content'}
{assign var="priceCalcMethod" value=$service['price_calculation_method']|default:0}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=Product::getPriceCalculationMethodDaysLabel($priceCalcMethod) item='pcmDayLabel'}
<li>{$pcmDayLabel}</li>
{/foreach}
</ul>
</div>
{/capture}
{include file='helpers/ui-tooltip.tpl' tooltip_content=$smarty.capture.room_type_service_tooltip_content allow_html=true}
{include file='controllers/orders/modals/_partials/service-applied-days.tpl' price_calculation_method=$service['price_calculation_method']|default:0 date_from=$additionalServices['date_from'] date_to=$additionalServices['date_to']}
</div>
</div>
<input value="{$service['id_service_product_order_detail']|escape:'html':'UTF-8'}" name="id_service_product_order_detail[]" type="hidden"/>
@@ -101,6 +87,7 @@
<div class="input-group">
<span class="input-group-addon">{$currencySign}</span>
<input type="text" class="form-control unit_price" value="{Tools::ps_round($service['unit_price_tax_excl'], 2)}" data-id-product="{$service['id_product']}" name="service_price[{$service['id_service_product_order_detail']}]">
{if ($service['price_calculation_method']|default:0) != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING}<span class="input-group-addon">{l s='/ Night'}</span>{/if}
</div>
</td>
<td>{displayPrice price=$service['total_price_tax_excl']|escape:'html':'UTF-8' currency=$orderCurrency}</td>
@@ -142,26 +129,15 @@
</thead>
<tbody>
{foreach $serviceProducts as $product}
{* services applied on selected days only are not listed when none of those days exist in the stay *}
{if !empty($date_from) && $product['price_calculation_method'] != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING && !(Product::getServicePriceApplicableDates($product['price_calculation_method'], $date_from, $date_to)|@array_filter)}{continue}{/if}
<tr class="room_service_block">
<td>
<input data-id_booking_detail="{$id_booking_detail}" value="{$product['id_product']|escape:'html':'UTF-8'}" name="selected_service[]" type="checkbox" class="id_room_type_service"/>
</td>
<td>
{$product['name']|escape:'html':'UTF-8'}
{capture name='adm_pcm_tooltip_content'}
{assign var="priceCalcMethod" value=$product['price_calculation_method']|default:0}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=Product::getPriceCalculationMethodDaysLabel($priceCalcMethod) item='pcmDayLabel'}
<li>{$pcmDayLabel}</li>
{/foreach}
</ul>
</div>
{/capture}
{include file='helpers/ui-tooltip.tpl' tooltip_content=$smarty.capture.adm_pcm_tooltip_content allow_html=true}
{include file='controllers/orders/modals/_partials/service-applied-days.tpl' price_calculation_method=$product['price_calculation_method'] date_from=$date_from|default:'' date_to=$date_to|default:''}
</td>
<td class="text-center">
{if $product['auto_add_to_cart'] && $product['price_addition_type'] == Product::PRICE_ADDITION_TYPE_WITH_ROOM}
@@ -184,6 +160,7 @@
<div class="input-group">
<span class="input-group-addon">{$currencySign}</span>
<input type="text" class="form-control unit_price" name="service_price[{$product['id_product']|escape:'html':'UTF-8'}]" value="{$product['price_tax_exc']}" data-id-product="{$product.id_product}">
{if ($product['price_calculation_method']) != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING}<span class="input-group-addon">{l s='/ Night'}</span>{/if}
</div>
</td>
{hook h="displayAdminOrderAvailableServicesData" id_service_product=$product['id_product'] id_htl_booking_detail=$id_booking_detail}
@@ -218,40 +195,6 @@
</div>
</div>
<div class="row form-group">
<div class="col-sm-6">
<label class="control-label required">{l s='Price calculation method'}</label>
<select id="new_service_pcm_type" name="price_calculation_type">
<option value="{Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING|intval}" selected="selected">{l s='Once for the booking range'}</option>
<option value="0">{l s='Select applicable days'}</option>
</select>
<div id="new_service_pcm_days_container" style="margin-top: 10px; display:none;">
<div class="row">
<div class="col-sm-3">
<label class="control-label required">{l s='Applied on:'}</label>
</div>
<div class="col-sm-4">
<div class="checkbox">
<label>
<input type="checkbox" id="pcm_checkin" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_CHECKIN_DAY|intval}">
{l s='Check-in day'}
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="pcm_checkout" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_CHECKOUT_DAY|intval}">
{l s='Check-out day'}
</label>
</div>
<div class="checkbox">
<label>
<input type="checkbox" id="pcm_duringstay" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_DURING_STAY|intval}"/>
{l s='During-stay days'}
</label>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6">
<label class="control-label">{l s='Auto added service'}</label>
<div>
@@ -268,9 +211,7 @@
</span>
</div>
</div>
</div>
<div class="row form-group">
<div id="new_service_price_tax_rule_container" class="col-sm-6">
<div id="new_service_price_tax_rule_container" class="col-sm-6">
<label class="control-label">{l s='Tax rule'}</label>
<select name="new_service_price_tax_rule_group">
<option value="0">{l s='No Tax'}</option>
@@ -286,10 +227,40 @@
<option value="{Product::PRICE_ADDITION_TYPE_INDEPENDENT}">{l s='Add price as convenience Fee'}</option>
</select>
</div>
</div>
<div class="row form-group">
<div id="new_service_qty_container" class="col-sm-6">
<label class="control-label required">{l s='Quantity'}</label>
<input type="number" class="form-control qty" min="1" name="new_service_qty" value="1">
</div>
<div class="col-sm-6">
<label class="control-label required">{l s='Price calculation method'}</label>
<select id="new_service_pcm_type" name="price_calculation_type">
<option value="{Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING|intval}" selected="selected">{l s='Once for the booking range'}</option>
<option value="0">{l s='Select applicable days'}</option>
</select>
</div>
</div>
<div id="new_service_pcm_days_container" style="display:none;">
<div class="row form-group">
<div class="col-sm-12">
<label class="control-label required">{l s='Applied on:'}</label>
</div>
<div class="col-sm-12">
<label class="checkbox-inline">
<input type="checkbox" id="pcm_checkin" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_CHECKIN_DAY|intval}">
{l s='Check-in day'}
</label>
<label class="checkbox-inline">
<input type="checkbox" id="pcm_checkout" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_CHECKOUT_DAY|intval}">
{l s='Check-out day'}
</label>
<label class="checkbox-inline">
<input type="checkbox" id="pcm_duringstay" name="new_service_price_calc_method[]" value="{Product::PRICE_CALCULATION_METHOD_ON_DURING_STAY|intval}"/>
{l s='During-stay days'}
</label>
</div>
</div>
</div>
{if $roomTypeTaxRuleGroupExist}
<div class="row form-group">
@@ -328,20 +299,8 @@
</td>
<td>
{$service['name']|escape:'html':'UTF-8'}
{capture name='room_type_service_pcm_tooltip_content'}
{assign var="priceCalcMethod" value=$service['price_calculation_method']|default:0}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=Product::getPriceCalculationMethodDaysLabel($priceCalcMethod) item='pcmDayLabel'}
<li>{$pcmDayLabel}</li>
{/foreach}
</ul>
</div>
{/capture}
{include file='helpers/ui-tooltip.tpl' tooltip_content=$smarty.capture.room_type_service_pcm_tooltip_content allow_html=true} </td>
{include file='controllers/orders/modals/_partials/service-applied-days.tpl' price_calculation_method=$service['price_calculation_method']|default:0 date_from=$additionalServices['date_from'] date_to=$additionalServices['date_to']}
</td>
<td>
{if $service['product_auto_add'] && $service['product_price_addition_type'] == Product::PRICE_ADDITION_TYPE_INDEPENDENT}
<span class="badge badge-info label">{l s='Convenience fee'}</span>

View File

@@ -0,0 +1,56 @@
{**
* 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
*}
{if isset($price_calculation_method) && (int) $price_calculation_method != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING && !empty($date_from) && !empty($date_to)}
{assign var='serviceAppliedDates' value=Product::getServicePriceApplicableDates($price_calculation_method, $date_from, $date_to)}
{assign var='serviceAppliedDays' value=0}
{foreach from=$serviceAppliedDates item='serviceAppliedGroupDates'}
{assign var='serviceAppliedDays' value=$serviceAppliedDays + count($serviceAppliedGroupDates)}
{/foreach}
{if $serviceAppliedDays}
{capture name='adm_applied_days_tooltip'}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=$serviceAppliedDates key='serviceAppliedGroup' item='serviceAppliedGroupDates'}
{if $serviceAppliedGroupDates}
<li>
{if $serviceAppliedGroup == 'checkin'}{l s='Check-in day'}{elseif $serviceAppliedGroup == 'stay'}{l s='During-stay days'}{else}{l s='Check-out day'}{/if}
<ul>
{foreach from=$serviceAppliedGroupDates item='serviceAppliedDate'}
<li>{Tools::displayDate($serviceAppliedDate)}</li>
{/foreach}
</ul>
</li>
{/if}
{/foreach}
</ul>
</div>
{/capture}
{capture name='adm_applied_days_badge'}<span class="label label-success label service-applied-days">{if $serviceAppliedDays == 1}{l s='Applied for 1 Day'}{else}{l s='Applied for %s Days' sprintf=$serviceAppliedDays}{/if}</span>{/capture}
<div>
{include file='helpers/ui-tooltip.tpl' tooltip_title=$smarty.capture.adm_applied_days_badge tooltip_content=$smarty.capture.adm_applied_days_tooltip allow_html=true}
</div>
{/if}
{/if}

View File

@@ -6973,6 +6973,36 @@ class ProductCore extends ObjectModel
}
}
public static function getServicePriceApplicableDates($priceCalculationMethod, $dateFrom, $dateTo)
{
$priceCalculationMethod = (int) $priceCalculationMethod;
$dates = array();
if (empty($dateFrom) || empty($dateTo)) {
return $dates;
}
$checkinDate = new DateTime(date('Y-m-d', strtotime($dateFrom)));
$checkoutDate = new DateTime(date('Y-m-d', strtotime($dateTo)));
if ($priceCalculationMethod & self::PRICE_CALCULATION_METHOD_ON_CHECKIN_DAY) {
$dates['checkin'] = array($checkinDate->format('Y-m-d'));
}
if ($priceCalculationMethod & self::PRICE_CALCULATION_METHOD_ON_DURING_STAY) {
$dates['stay'] = array();
$stayDate = clone $checkinDate;
$stayDate->modify('+1 day');
while ($stayDate < $checkoutDate) {
$dates['stay'][] = $stayDate->format('Y-m-d');
$stayDate->modify('+1 day');
}
}
if ($priceCalculationMethod & self::PRICE_CALCULATION_METHOD_ON_CHECKOUT_DAY) {
$dates['checkout'] = array($checkoutDate->format('Y-m-d'));
}
return $dates;
}
public static function getPriceCalculationMethodDaysLabel($priceCalculationMethod)
{
$priceCalculationMethod = (int) $priceCalculationMethod;

View File

@@ -2831,6 +2831,27 @@ class OrderCore extends ObjectModel
return Db::getInstance()->executeS('SELECT * FROM '._DB_PREFIX_.'orders WHERE id_cart = '.(int)$id_cart);
}
/**
* Get all orders linked to a cart, either as the order's own cart or through a room
* added to an existing order from the back office (that creates a new cart whose
* rooms belong to the order, while orders.id_cart still points at the original cart).
*
* @param int $idCart
* @return array of orders
*/
public static function getAllOrdersLinkedToCart($idCart)
{
return Db::getInstance()->executeS(
'SELECT o.* FROM `'._DB_PREFIX_.'orders` o
WHERE o.`id_cart` = '.(int) $idCart.'
OR o.`id_order` IN (
SELECT hbd.`id_order` FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
WHERE hbd.`id_cart` = '.(int) $idCart.'
)
ORDER BY o.`id_order` ASC'
);
}
/**
* Function to check if order has been completely refunded
* @param integer action: can have 3 values as below

View File

@@ -47,7 +47,7 @@ class AdminCartRulesControllerCore extends AdminController
$this->fields_list = array(
'id_cart_rule' => array('title' => $this->l('ID'), 'align' => 'center', 'class' => 'fixed-width-xs'),
'name' => array('title' => $this->l('Name')),
'hotel_names' => array('title' => $this->l('Hotels'), 'orderby' => false, 'havingFilter' => true, 'filter_key' => 'hotel_names', 'callback' => 'formatHotelNames'),
'hotel_names' => array('title' => $this->l('Properties'), 'orderby' => false, 'havingFilter' => true, 'filter_key' => 'hotel_names', 'callback' => 'formatHotelNames'),
'priority' => array('title' => $this->l('Priority'), 'align' => 'center', 'class' => 'fixed-width-xs'),
'code' => array('title' => $this->l('Code'), 'class' => 'fixed-width-sm'),
'quantity' => array('title' => $this->l('Quantity'), 'align' => 'center', 'class' => 'fixed-width-xs'),
@@ -274,7 +274,7 @@ class AdminCartRulesControllerCore extends AdminController
Media::addJsDef(
array(
'admin_cart_rule_tab_link' => $this->context->link->getAdminLink('AdminCartRules'),
'room_access_err' => $this->l('You can only select room types which hotel(s) access is provided to this employee.'),
'room_access_err' => $this->l('You can only select room types of the properties this employee has access to.'),
'room_rmv_txt' => $this->l('Unselect below room types'),
)
);

View File

@@ -286,7 +286,7 @@ class AdminCartsControllerCore extends AdminController
$summary = $cart->getSummaryDetails();
/* Display orders information */
$cartOrders = Order::getAllOrdersByCartId($cart->id);
$cartOrders = Order::getAllOrdersLinkedToCart($cart->id);
if ($cartOrders) {
$objOrder = new Order($cartOrders[0]['id_order']);
if (Validate::isLoadedObject($objOrder)) {

View File

@@ -119,7 +119,7 @@ class AdminEmailsControllerCore extends AdminController
'type' => 'bool'
)
),
'submit' => array('title' => $this->l('Save'))
'submit' => array('title' => $this->l('Save'), 'name' => 'submitOptions')
),
'smtp' => array(
'title' => $this->l('Email'),
@@ -181,7 +181,7 @@ class AdminEmailsControllerCore extends AdminController
'class' => 'fixed-width-sm'
),
),
'submit' => array('title' => $this->l('Save'))
'submit' => array('title' => $this->l('Save'), 'name' => 'submitOptions')
),
'test' => array(
'title' => $this->l('Test your email configuration'),

View File

@@ -481,7 +481,15 @@ class AdminOrdersControllerCore extends AdminController
}
if ($this->tabAccess['edit'] === 1) {
if ((int) $order->isReturnable()) {
// nothing left to refund once every room and service line is deleted
// (amount is not checked: a free order can still be refunded)
$objHotelBookingDetail = new HotelBookingDetail();
$objServiceProductOrderDetail = new ServiceProductOrderDetail();
if ((int) $order->isReturnable()
&& ($objHotelBookingDetail->getBookingDataByOrderId($order->id)
|| $objServiceProductOrderDetail->getServiceProductsInOrder($order->id)
)
) {
// always "Initiate refund" now, regardless of order status —
// cancelling a booking happens through Room Status only
$this->page_header_toolbar_btn['cancel'] = array(
@@ -1051,6 +1059,8 @@ class AdminOrdersControllerCore extends AdminController
$smartyVars['link'] = $this->context->link;
$smartyVars['id_booking_detail'] = $productLineData['id'];
$smartyVars['date_from'] = $dateFrom;
$smartyVars['date_to'] = $dateTo;
// set context currency So that we can get prices in the order currency
$this->context->currency = $objCurrency;
@@ -7219,6 +7229,12 @@ class AdminOrdersControllerCore extends AdminController
$order_invoice->total_discount_tax_excl = 0;
$res &= $order_invoice->update();
}
// the order's own discount totals must follow the deleted cart rules
$order->total_discounts = 0;
$order->total_discounts_tax_incl = 0;
$order->total_discounts_tax_excl = 0;
$res &= $order->update();
}
if (isset($order_invoice) && $order_invoice->id) {
@@ -7867,6 +7883,8 @@ class AdminOrdersControllerCore extends AdminController
$smartyVars['link'] = $this->context->link;
$smartyVars['id_booking_detail'] = $idHtlBookingDetail;
$smartyVars['date_from'] = $dateFrom;
$smartyVars['date_to'] = $dateTo;
$objServiceProductOrderDetail = new ServiceProductOrderDetail();
if ($additionalServices = $objServiceProductOrderDetail->getRoomTypeServiceProducts(
0,

View File

@@ -43,7 +43,7 @@ class AdminPaymentReceiptsControllerCore extends AdminControllerCore
),
'PS_PAYMENT_RECEIPTS_PREFIX' => array(
'title' => $this->l('Receipt prefix'),
'desc' => $this->l('Prefix used for receipt name (e.g. #IN00001).'),
'desc' => $this->l('Prefix used for receipt name (e.g. REC000001).'),
'size' => 6,
'type' => 'textLang'
),

View File

@@ -335,11 +335,11 @@ class AdminSearchConfControllerCore extends AdminController
$this->errors[] = $this->l('Aliases and results are both required.');
}
if (!Validate::isValidSearch($search)) {
$this->errors[] = $search.' '.$this->l('Is not a valid result');
$this->errors[] = Tools::safeOutput($search).' '.$this->l('Is not a valid result');
}
foreach ($aliases as $alias) {
if (!Validate::isValidSearch($alias)) {
$this->errors[] = $alias.' '.$this->l('Is not a valid alias');
$this->errors[] = Tools::safeOutput($alias).' '.$this->l('Is not a valid alias');
}
}

View File

@@ -491,7 +491,7 @@ class ProductControllerCore extends FrontController
}
}
$this->assignBookingFormVars($this->product->id, $date_from, $date_to, $occupancy_value);
$this->assignRoomServiceProductVars();
$this->assignRoomServiceProductVars($date_from, $date_to);
// product price after imposing feature prices...
if ($useTax) {
@@ -614,7 +614,7 @@ class ProductControllerCore extends FrontController
$this->setTemplate(_PS_THEME_DIR_.'product.tpl');
}
public function assignRoomServiceProductVars()
public function assignRoomServiceProductVars($dateFrom = null, $dateTo = null)
{
// get service products for room type
$p = 1;
@@ -626,7 +626,12 @@ class ProductControllerCore extends FrontController
$this->product->id,
$p,
$n,
true
true,
2,
0,
false,
$dateFrom,
$dateTo
);
if ($serviceProductsByCategory) {
$smartyVars['service_products_exists'] = 1;
@@ -637,7 +642,13 @@ class ProductControllerCore extends FrontController
$this->product->id,
$p,
$n,
true
true,
2,
0,
false,
false,
$dateFrom,
$dateTo
);
$numTotalServiceProducts = $this->product->getProductServiceProducts(
$this->context->language->id,
@@ -839,10 +850,16 @@ class ProductControllerCore extends FrontController
$serviceNumAdults = isset($serviceOccupancy['adults']) ? (int) $serviceOccupancy['adults'] : 1;
$serviceChildAges = !empty($serviceOccupancy['child_ages']) ? (array) $serviceOccupancy['child_ages'] : array();
foreach ($roomServiceProducts as &$product) {
foreach ($roomServiceProducts as $keyServiceProduct => &$product) {
$priceCalcMethod = Product::getProductPriceCalculation((int) $product['id_product']);
if (!$objRoomTypeServiceProduct->isRoomTypeLinkedWithProduct($idProduct, $product['id_product'])) {
unset($product);
continue;
} elseif ($priceCalcMethod != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING && !array_filter(Product::getServicePriceApplicableDates($priceCalcMethod, $dateFrom, $dateTo))) {
// service is applied on selected days only and none of those days exist in the stay
unset($product);
unset($roomServiceProducts[$keyServiceProduct]);
continue;
} else {
$objServiceProduct = new ProductCore($product['id_product'], false, $this->context->language->id);
if (!$objServiceProduct->allow_multiple_quantity && $product['quantity'] > 1) {
@@ -854,6 +871,7 @@ class ProductControllerCore extends FrontController
}
$product['name'] = $objServiceProduct->name;
$product['allow_multiple_quantity'] = $objServiceProduct->allow_multiple_quantity;
$product['price_calculation_method'] = $objServiceProduct->price_calculation_method;
$productPrice = Product::getServiceProductPrice(
$product['id_product'],
@@ -1506,7 +1524,7 @@ class ProductControllerCore extends FrontController
$roomServiceProducts = Tools::getValue('room_service_products');
$dateFrom = date('Y-m-d H:i:s', strtotime($dateFrom));
$dateTo = date('Y-m-d H:i:s', strtotime($dateTo));
$this->assignRoomServiceProductVars();
$this->assignRoomServiceProductVars($dateFrom, $dateTo);
if ($this->assignBookingFormVars(
$idProduct,
$dateFrom,
@@ -1514,6 +1532,9 @@ class ProductControllerCore extends FrontController
$occupancy,
$roomServiceProducts
)) {
if (Tools::getValue('refresh_services')) {
$response['html_service_products'] = $this->getRefreshedServiceProductsHtml();
}
}
} else {
$idHotel = Tools::getValue('service_id_hotel');
@@ -1536,6 +1557,21 @@ class ProductControllerCore extends FrontController
$this->ajaxDie(json_encode($response));
}
/**
* Render the service products block using the service products already assigned to smarty.
*
* @return string
*/
protected function getRefreshedServiceProductsHtml()
{
$this->context->smarty->assign(array(
'product' => $this->product,
'PS_SERVICE_PRODUCT_CATEGORY_FILTER' => Configuration::get('PS_SERVICE_PRODUCT_CATEGORY_FILTER'),
));
return $this->context->smarty->fetch(_PS_THEME_DIR_.'_partials/service-products.tpl');
}
public function displayAjaxGetHotelImages()
{
$response = array('status' => false);
@@ -1582,6 +1618,8 @@ class ProductControllerCore extends FrontController
$p = Tools::getValue('p');
$id_product = Tools::getValue('id_product');
$id_category = Tools::getValue('id_category');
$date_from = Tools::getValue('date_from');
$date_to = Tools::getValue('date_to');
if ($this->isTokenValid()) {
if ($p && $id_product) {
@@ -1600,7 +1638,10 @@ class ProductControllerCore extends FrontController
true,
2,
0,
$id_category
$id_category,
false,
$date_from,
$date_to
)) {
$this->context->smarty->assign('service_products', $roomTypeServiceProducts);
if (Configuration::get('PS_SERVICE_PRODUCT_CATEGORY_FILTER')) {

View File

@@ -135,7 +135,7 @@
<value>#IN</value>
</configuration>
<configuration id="PS_PAYMENT_RECEIPTS_PREFIX" name="PS_PAYMENT_RECEIPTS_PREFIX">
<value>#IN</value>
<value>REC</value>
</configuration>
<configuration id="PS_INVCE_INVOICE_ADDR_RULES" name="PS_INVCE_INVOICE_ADDR_RULES">
<value>{"avoid":[]}</value>

View File

@@ -4,7 +4,7 @@
<value>#IN</value>
</configuration>
<configuration id="PS_PAYMENT_RECEIPTS_PREFIX" name="PS_PAYMENT_RECEIPTS_PREFIX">
<value>#IN</value>
<value>REC</value>
</configuration>
<configuration id="PS_DELIVERY_PREFIX">
<value>#DE</value>

View File

@@ -1643,10 +1643,16 @@ $(document).ready(function() {
});
$(document).on('click', '#initiateRefund', function(e) {
if ($(this).data('submitted')) {
return false;
}
if ($.trim($('.cancellation_reason').val()) == '') {
$('.cancellation_reason').focus().css('border', '1px solid red');
return false;
}
$(this).data('submitted', true);
});
// End: CancelRoomBookingModal: Processes
// ======================================

View File

@@ -127,7 +127,7 @@
<div class="col-lg-10">
{foreach from=$languages item=language}
{assign var="navigation_link_name" value="navigation_link_name_`$language.id_lang`"}
<input type="text" id="{$navigation_link_name}" name="{$navigation_link_name}" value="{if isset($navigationLinkInfo.name[$language.id_lang]) && $navigationLinkInfo.name[$language.id_lang]}{$navigationLinkInfo.name[$language.id_lang]}{else if isset($smarty.post.$navigation_link_name)}{$smarty.post.$navigation_link_name}{/if}" data-lang-name="{$language.name}" placeholder="{l s='Enter exlore link name' mod='blocknavigationmenu'}" class="form-control navigation_link_name_all" {if $currentLang.id_lang != $language.id_lang}style="display:none;"{/if}/>
<input type="text" id="{$navigation_link_name}" name="{$navigation_link_name}" value="{if isset($navigationLinkInfo.name[$language.id_lang]) && $navigationLinkInfo.name[$language.id_lang]}{$navigationLinkInfo.name[$language.id_lang]|escape:'htmlall':'UTF-8'}{else if isset($smarty.post.$navigation_link_name)}{$smarty.post.$navigation_link_name|escape:'htmlall':'UTF-8'}{/if}" data-lang-name="{$language.name}" placeholder="{l s='Enter exlore link name' mod='blocknavigationmenu'}" class="form-control navigation_link_name_all" {if $currentLang.id_lang != $language.id_lang}style="display:none;"{/if}/>
{/foreach}
</div>
{if $languages|@count > 1}
@@ -176,7 +176,7 @@
</span>
</label>
<div class="col-lg-5">
<input type="text" name="link" {if isset($smarty.post.link)}value="{$smarty.post.link}"{elseif isset($navigationLinkInfo['link'])}value="{$navigationLinkInfo['link']}"{/if}>
<input type="text" name="link" {if isset($smarty.post.link)}value="{$smarty.post.link|escape:'htmlall':'UTF-8'}"{elseif isset($navigationLinkInfo['link'])}value="{$navigationLinkInfo['link']|escape:'htmlall':'UTF-8'}"{/if}>
</div>
</div>
<div class="form-group custom_redirect_page_div {if isset($smarty.post.is_custom_redirect_link)}{if $smarty.post.is_custom_redirect_link}hidden{/if}{elseif isset($navigationLinkInfo['is_custom_link']) && $navigationLinkInfo['is_custom_link']}hidden{/if}">

View File

@@ -1312,30 +1312,79 @@ class HotelHelper
public function createHotelDefaultPropertyTypes()
{
$propertyTypes = array(
'Hotel',
'Motel',
'Resort',
'Hostel',
'Guest House',
'Bed & Breakfast',
'Lodge',
'Apartment',
'Vacation Rental',
'Homestay',
'Co-Living',
'Camping',
'Glamping',
'Farm Stay',
'Houseboat',
'Boat Stay',
array(
'name' => 'Hotel',
'plural_name' => 'Hotels',
),
array(
'name' => 'Motel',
'plural_name' => 'Motels',
),
array(
'name' => 'Resort',
'plural_name' => 'Resorts',
),
array(
'name' => 'Hostel',
'plural_name' => 'Hostels',
),
array(
'name' => 'Guest House',
'plural_name' => 'Guest Houses',
),
array(
'name' => 'Bed & Breakfast',
'plural_name' => 'Bed & Breakfasts',
),
array(
'name' => 'Lodge',
'plural_name' => 'Lodges',
),
array(
'name' => 'Apartment',
'plural_name' => 'Apartments',
),
array(
'name' => 'Vacation Rental',
'plural_name' => 'Vacation Rentals',
),
array(
'name' => 'Homestay',
'plural_name' => 'Homestays',
),
array(
'name' => 'Co-Living',
'plural_name' => 'Co-Livings',
),
array(
'name' => 'Camping',
'plural_name' => 'Campings',
),
array(
'name' => 'Glamping',
'plural_name' => 'Glampings',
),
array(
'name' => 'Farm Stay',
'plural_name' => 'Farm Stays',
),
array(
'name' => 'Houseboat',
'plural_name' => 'Houseboats',
),
array(
'name' => 'Boat Stay',
'plural_name' => 'Boat Stays',
),
);
$languages = Language::getLanguages(true);
foreach ($propertyTypes as $typeName) {
foreach ($propertyTypes as $propertyType) {
$objType = new HotelPropertyType();
$objType->active = 1;
foreach ($languages as $lang) {
$objType->name[$lang['id_lang']] = $typeName;
$objType->name[$lang['id_lang']] = $propertyType['name'];
$objType->plural_name[$lang['id_lang']] = $propertyType['plural_name'];
}
$objType->save();
}
@@ -1532,7 +1581,7 @@ class HotelHelper
),
array(
'id' => HotelBookingDetail::STATUS_NO_SHOW,
'color' => '#DC143C',
'color' => '#FF8C00',
'is_terminal' => 1,
'name' => array(
'en' => 'No-show',
@@ -1545,7 +1594,7 @@ class HotelHelper
),
array(
'id' => HotelBookingDetail::STATUS_CANCELLED,
'color' => '#FF8C00',
'color' => '#DC143C',
'is_terminal' => 1,
'name' => array(
'en' => 'Cancelled',

View File

@@ -27,6 +27,7 @@ class HotelPropertyType extends ObjectModel
public $date_add;
public $date_upd;
public $name;
public $plural_name;
public static $definition = array(
'table' => 'htl_property_type',
@@ -37,6 +38,7 @@ class HotelPropertyType extends ObjectModel
'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false),
'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'copy_post' => false),
'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 255),
'plural_name' => array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 255),
),
);
@@ -46,6 +48,7 @@ class HotelPropertyType extends ObjectModel
'objectMethods' => array('update' => 'updateWs', 'delete' => 'deleteWs'),
'fields' => array(
'name' => array('required' => true),
'plural_name' => array('required' => true),
'active' => array('required' => false),
),
);
@@ -121,7 +124,7 @@ class HotelPropertyType extends ObjectModel
$cacheKey = 'HotelPropertyType::getPropertyTypes'.$idLang.'_'.(int) $active;
if (!Cache::isStored($cacheKey)) {
$sql = 'SELECT hpt.`id_htl_property_type` AS `id_property_type`, hptl.`name`
$sql = 'SELECT hpt.`id_htl_property_type` AS `id_property_type`, hptl.`name`, hptl.`plural_name`
FROM `'._DB_PREFIX_.'htl_property_type` hpt
LEFT JOIN `'._DB_PREFIX_.'htl_property_type_lang` hptl
ON (hpt.`id_htl_property_type` = hptl.`id_htl_property_type`

View File

@@ -142,6 +142,7 @@ class HotelReservationSystemDb
`id_htl_property_type` int(10) unsigned NOT NULL,
`id_lang` int(10) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`plural_name` varchar(255) NOT NULL,
PRIMARY KEY (`id_htl_property_type`, `id_lang`)
) ENGINE="._MYSQL_ENGINE_." DEFAULT CHARSET=utf8;",
"CREATE TABLE IF NOT EXISTS `"._DB_PREFIX_."htl_branch_amenity` (

View File

@@ -184,7 +184,7 @@ class RoomTypeServiceProduct extends ObjectModel
return false;
}
public function getServiceProductsData($idProductRoomType, $p = 1, $n = 0, $front = false, $available_for_order = 2, $auto_add_to_cart = 0, $subCategory = false, $idLang = false)
public function getServiceProductsData($idProductRoomType, $p = 1, $n = 0, $front = false, $available_for_order = 2, $auto_add_to_cart = 0, $subCategory = false, $idLang = false, $dateFrom = null, $dateTo = null)
{
$context = Context::getContext();
if (!$idLang) {
@@ -209,6 +209,18 @@ class RoomTypeServiceProduct extends ObjectModel
continue;
}
if ($dateFrom && $dateTo) {
$priceCalcMethod = (int) $serviceProduct['price_calculation_method'];
if ($priceCalcMethod != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING && !array_filter(Product::getServicePriceApplicableDates($priceCalcMethod, $dateFrom, $dateTo))) {
unset($serviceProducts[$key]);
continue;
}
$serviceProduct['price_calculation_method'] = $priceCalcMethod;
$serviceProduct['stay_date_from'] = $dateFrom;
$serviceProduct['stay_date_to'] = $dateTo;
}
$serviceProduct['price_tax_exc'] = Product::getServiceProductPrice(
(int)$serviceProduct['id_product'],
0,
@@ -352,7 +364,7 @@ class RoomTypeServiceProduct extends ObjectModel
return $pricingByBookingAndProduct;
}
public function getServiceProductsGroupByCategory($idProduct, $p = 1, $n = 0, $front = false, $available_for_order = 2, $auto_add_to_cart = 0, $idLang = false)
public function getServiceProductsGroupByCategory($idProduct, $p = 1, $n = 0, $front = false, $available_for_order = 2, $auto_add_to_cart = 0, $idLang = false, $dateFrom = null, $dateTo = null)
{
if (!$idLang) {
$idLang = Context::getContext()->language->id;
@@ -361,7 +373,7 @@ class RoomTypeServiceProduct extends ObjectModel
$objProduct = new Product($idProduct);
if ($serviceProductsCategories = $objProduct->getAvailableServiceProductsCategories($idLang, 1)) {
foreach ($serviceProductsCategories as $key => $category) {
if ($products = $this->getServiceProductsData($idProduct, $p, $n, $front, $available_for_order, $auto_add_to_cart, $category['id_category'], $idLang)) {
if ($products = $this->getServiceProductsData($idProduct, $p, $n, $front, $available_for_order, $auto_add_to_cart, $category['id_category'], $idLang, $dateFrom, $dateTo)) {
$serviceProductsCategories[$key]['products'] = $products;
} else {
unset($serviceProductsCategories[$key]);

View File

@@ -45,6 +45,10 @@ class AdminHotelPropertyTypesController extends ModuleAdminController
'title' => $this->l('Name'),
'align' => 'left',
),
'plural_name' => array(
'title' => $this->l('Plural Name'),
'align' => 'left',
),
'active' => array(
'title' => $this->l('Status'),
'align' => 'center',
@@ -109,6 +113,14 @@ class AdminHotelPropertyTypesController extends ModuleAdminController
'required' => true,
'hint' => $this->l('Displayed name for this property type.'),
),
array(
'type' => 'text',
'label' => $this->l('Plural Name'),
'name' => 'plural_name',
'lang' => true,
'required' => true,
'hint' => $this->l('Displayed name when referring to more than one property of this type.'),
),
array(
'type' => 'switch',
'label' => $this->l('Enabled'),
@@ -166,6 +178,18 @@ class AdminHotelPropertyTypesController extends ModuleAdminController
}
}
if (!trim(Tools::getValue('plural_name_'.$defaultLangId))) {
$this->errors[] = $this->l('Plural Name is required at least in ').$objDefaultLanguage['name'];
} else {
foreach ($languages as $lang) {
if (trim(Tools::getValue('plural_name_'.$lang['id_lang']))) {
if (!Validate::isGenericName(Tools::getValue('plural_name_'.$lang['id_lang']))) {
$this->errors[] = $this->l('Invalid Plural Name in ').$lang['name'];
}
}
}
}
$this->display = $idPropertyType ? 'edit' : 'add';
if (!$this->errors && !Tools::getValue('active') && $idPropertyType) {

View File

@@ -183,7 +183,7 @@
id="short_description_{$language.id_lang}"
{if isset($PS_SHORT_DESC_LIMIT) && $PS_SHORT_DESC_LIMIT} maxlength="{$PS_SHORT_DESC_LIMIT|intval}"{/if}
cols="2" rows="3"
class="form-control">{if isset($smarty.post.$short_desc_name)}{$smarty.post.$short_desc_name}{elseif isset($edit)}{$hotel_info.short_description[{$language.id_lang}]}{/if}</textarea>
class="form-control">{if isset($smarty.post.$short_desc_name)}{$smarty.post.$short_desc_name|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$hotel_info.short_description[{$language.id_lang}]|escape:'htmlall':'UTF-8'}{/if}</textarea>
</div>
{/foreach}
</div>
@@ -201,7 +201,7 @@
name="description_{$language.id_lang}"
id="description_{$language.id_lang}"
cols="2" rows="3"
class="wk_tinymce form-control">{if isset($smarty.post.$description)}{$smarty.post.$description}{elseif isset($edit)}{$hotel_info.description[{$language.id_lang}]}{/if}</textarea>
class="wk_tinymce form-control">{if isset($smarty.post.$description)}{$smarty.post.$description|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$hotel_info.description[{$language.id_lang}]|escape:'htmlall':'UTF-8'}{/if}</textarea>
</div>
{/foreach}
</div>
@@ -209,7 +209,7 @@
<div class="form-group">
<label class="col-sm-3 control-label required">{l s='Phone :' mod='hotelreservationsystem'}</label>
<div class="col-sm-6">
<input type="text" name="phone" id="phone" value="{if isset($smarty.post.phone)}{$smarty.post.phone}{elseif isset($edit)}{$address_info.phone|escape:'htmlall':'UTF-8'}{/if}"/>
<input type="text" name="phone" id="phone" value="{if isset($smarty.post.phone)}{$smarty.post.phone|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$address_info.phone|escape:'htmlall':'UTF-8'}{/if}"/>
</div>
</div>
<div class="form-group">
@@ -219,7 +219,7 @@
<span class="input-group-addon">
<i class="icon-envelope-o"></i>
</span>
<input class="reg_sel_input form-control-static" type="text" name="email" id="hotel_email" value="{if isset($smarty.post.email)}{$smarty.post.email}{elseif isset($edit)}{$hotel_info.email|escape:'htmlall':'UTF-8'}{/if}"/>
<input class="reg_sel_input form-control-static" type="text" name="email" id="hotel_email" value="{if isset($smarty.post.email)}{$smarty.post.email|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$hotel_info.email|escape:'htmlall':'UTF-8'}{/if}"/>
</div>
</div>
</div>
@@ -232,13 +232,13 @@
<div class="form-group">
<label class="col-sm-3 control-label required">{l s='Address :' mod='hotelreservationsystem'}</label>
<div class="col-sm-6">
<textarea name="address" rows="4" cols="35" >{if isset($smarty.post.address)}{$smarty.post.address}{elseif isset($edit)}{$address_info.address1|escape:'htmlall':'UTF-8'}{/if}</textarea>
<textarea name="address" rows="4" cols="35" >{if isset($smarty.post.address)}{$smarty.post.address|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$address_info.address1|escape:'htmlall':'UTF-8'}{/if}</textarea>
</div>
</div>
<div class="form-group check_in_div" style="position:relative">
<label class="col-sm-3 control-label" for="fax">{l s='Fax' mod='hotelreservationsystem'}</label>
<div class="col-sm-6">
<input autocomplete="off" type="text" class="form-control" id="fax" name="fax" value="{if isset($smarty.post.fax)}{$smarty.post.fax}{elseif isset($edit)}{$hotel_info.fax|escape:'htmlall':'UTF-8'}{/if}" />
<input autocomplete="off" type="text" class="form-control" id="fax" name="fax" value="{if isset($smarty.post.fax)}{$smarty.post.fax|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$hotel_info.fax|escape:'htmlall':'UTF-8'}{/if}" />
</div>
</div>
<div class="form-group">
@@ -261,7 +261,7 @@
{l s='Check-in:' mod='hotelreservationsystem'}
</label>
<div class="col-sm-2">
<input autocomplete="off" type="text" class="form-control" id="check_in_time" name="check_in" value="{if isset($smarty.post.check_in)}{$smarty.post.check_in}{elseif isset($edit)}{$hotel_info.check_in|escape:'htmlall':'UTF-8'}{/if}" />
<input autocomplete="off" type="text" class="form-control" id="check_in_time" name="check_in" value="{if isset($smarty.post.check_in)}{$smarty.post.check_in|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$hotel_info.check_in|escape:'htmlall':'UTF-8'}{/if}" />
</div>
</div>
<div class="form-group check_out_div" style="position:relative">
@@ -269,7 +269,7 @@
{l s='Check-out:' mod='hotelreservationsystem'}
</label>
<div class="col-sm-2">
<input autocomplete="off" type="text" class="form-control" id="check_out_time" name="check_out" value="{if isset($smarty.post.check_out)}{$smarty.post.check_out}{elseif isset($edit)}{$hotel_info.check_out|escape:'htmlall':'UTF-8'}{/if}" />
<input autocomplete="off" type="text" class="form-control" id="check_out_time" name="check_out" value="{if isset($smarty.post.check_out)}{$smarty.post.check_out|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$hotel_info.check_out|escape:'htmlall':'UTF-8'}{/if}" />
</div>
</div>
<div class="form-group">
@@ -305,13 +305,13 @@
<div class="form-group">
<label class="control-label col-sm-3 required" for="hotel_city">{l s='City :' mod='hotelreservationsystem'}</label>
<div class="col-sm-6">
<input class="form-control" type="" data-validate="" id="hotel_city" name="hotel_city" value="{if isset($smarty.post.hotel_city)}{$smarty.post.hotel_city}{elseif isset($edit)}{$address_info.city|escape:'htmlall':'UTF-8'}{/if}" />
<input class="form-control" type="" data-validate="" id="hotel_city" name="hotel_city" value="{if isset($smarty.post.hotel_city)}{$smarty.post.hotel_city|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$address_info.city|escape:'htmlall':'UTF-8'}{/if}" />
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3 required" for="hotel_postal_code">{l s='Zip Code :' mod='hotelreservationsystem'}</label>
<div class="col-sm-6">
<input class="form-control" type="" data-validate="" id="hotel_postal_code" name="hotel_postal_code" value="{if isset($smarty.post.hotel_postal_code)}{$smarty.post.hotel_postal_code}{elseif isset($edit)}{$address_info.postcode|escape:'htmlall':'UTF-8'}{/if}" />
<input class="form-control" type="" data-validate="" id="hotel_postal_code" name="hotel_postal_code" value="{if isset($smarty.post.hotel_postal_code)}{$smarty.post.hotel_postal_code|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$address_info.postcode|escape:'htmlall':'UTF-8'}{/if}" />
</div>
</div>
<div class="form-group">
@@ -339,7 +339,7 @@
name="policies_{$language.id_lang}"
id="policies_{$language.id_lang}"
cols="2" rows="3"
class="wk_tinymce form-control">{if isset($smarty.post.$policies)}{$smarty.post.$policies}{elseif isset($edit)}{$hotel_info.policies[{$language.id_lang}]}{/if}</textarea>
class="wk_tinymce form-control">{if isset($smarty.post.$policies)}{$smarty.post.$policies|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$hotel_info.policies[{$language.id_lang}]|escape:'htmlall':'UTF-8'}{/if}</textarea>
</div>
{/foreach}
</div>
@@ -519,7 +519,7 @@
<div class="form-group" {if isset($smarty.post.enable_use_global_max_checkout_offset)} {if !$smarty.post.enable_use_global_max_checkout_offset} style="display:block;" {else} style="display:none;" {/if} {elseif isset($order_restrict_date_info.use_global_max_checkout_offset) && !$order_restrict_date_info.use_global_max_checkout_offset} style="display:block;" {else} style="display:none;" {/if}>
<label class="control-label col-sm-3 required" for="max_checkout_offset">{l s='Maximum checkout offset :' mod='hotelreservationsystem'}</label>
<div class="col-sm-2">
<input type="text" class="form-control" id="max_checkout_offset" name="max_checkout_offset" value="{if isset($smarty.post.max_checkout_offset)}{$smarty.post.max_checkout_offset}{elseif isset($order_restrict_date_info.max_checkout_offset)}{$order_restrict_date_info.max_checkout_offset|escape:'htmlall':'UTF-8'}{/if}" />
<input type="text" class="form-control" id="max_checkout_offset" name="max_checkout_offset" value="{if isset($smarty.post.max_checkout_offset)}{$smarty.post.max_checkout_offset|escape:'htmlall':'UTF-8'}{elseif isset($order_restrict_date_info.max_checkout_offset)}{$order_restrict_date_info.max_checkout_offset|escape:'htmlall':'UTF-8'}{/if}" />
</div>
</div>
<div class="form-group">

View File

@@ -40,7 +40,7 @@
<div class="col-lg-3">
{foreach from=$languages item=language}
{assign var="feature_price_name" value="feature_price_name_`$language.id_lang`"}
<input type="text" id="{$feature_price_name}" name="{$feature_price_name}" value="{if isset($objFeaturePrice->feature_price_name[$language.id_lang]) && $objFeaturePrice->feature_price_name[$language.id_lang]}{$objFeaturePrice->feature_price_name[$language.id_lang]}{else if isset($smarty.post.$feature_price_name)}{$smarty.post.$feature_price_name}{/if}" data-lang-name="{$language.name}" placeholder="{l s='Enter advanced price rule name' mod='hotelreservationsystem'}" class="form-control feature_price_name_all" {if $currentLang.id_lang != $language.id_lang}style="display:none;"{/if}/>
<input type="text" id="{$feature_price_name}" name="{$feature_price_name}" value="{if isset($objFeaturePrice->feature_price_name[$language.id_lang]) && $objFeaturePrice->feature_price_name[$language.id_lang]}{$objFeaturePrice->feature_price_name[$language.id_lang]|escape:'htmlall':'UTF-8'}{else if isset($smarty.post.$feature_price_name)}{$smarty.post.$feature_price_name|escape:'htmlall':'UTF-8'}{/if}" data-lang-name="{$language.name}" placeholder="{l s='Enter advanced price rule name' mod='hotelreservationsystem'}" class="form-control feature_price_name_all" {if $currentLang.id_lang != $language.id_lang}style="display:none;"{/if}/>
{/foreach}
</div>
{if $languages|@count > 1}
@@ -182,7 +182,7 @@
<div class="col-lg-3">
<div class="input-group">
<span class="input-group-addon payment_type_icon">{if isset($edit)} {if $objFeaturePrice->impact_type == HotelRoomTypeFeaturePricing::IMPACT_TYPE_FIXED_PRICE}{$defaultcurrency_sign}{else}%{/if}{else}%{/if}</span>
<input type="text" id="impact_value" name="impact_value" value="{if isset($smarty.post.impact_value) && $smarty.post.impact_value}{$smarty.post.impact_value}{elseif isset($objFeaturePrice->impact_value)}{$objFeaturePrice->impact_value}{/if}"/>
<input type="text" id="impact_value" name="impact_value" value="{if isset($smarty.post.impact_value) && $smarty.post.impact_value}{$smarty.post.impact_value|escape:'htmlall':'UTF-8'}{elseif isset($objFeaturePrice->impact_value)}{$objFeaturePrice->impact_value|escape:'htmlall':'UTF-8'}{/if}"/>
</div>
</div>
</div>

View File

@@ -65,7 +65,7 @@
name="description_{$language.id_lang|escape:'htmlall':'UTF-8'}"
id="description_{$language.id_lang|escape:'htmlall':'UTF-8'}"
cols="2" rows="3"
class="form-control">{if isset($smarty.post.$description)}{$smarty.post.$description}{elseif isset($edit)}{$refund_rules_info.description[$language.id_lang]|escape:'htmlall':'UTF-8'}{/if}</textarea>
class="form-control">{if isset($smarty.post.$description)}{$smarty.post.$description|escape:'htmlall':'UTF-8'}{elseif isset($edit)}{$refund_rules_info.description[$language.id_lang]|escape:'htmlall':'UTF-8'}{/if}</textarea>
</div>
{/foreach}
</div>

View File

@@ -23,6 +23,10 @@
class WkHotelInteriorImage extends ObjectModel
{
const NAV_TYPE_DOTS = 1;
const NAV_TYPE_ARROWS = 2;
const NAV_TYPE_BOTH = 3;
public $name;
public $display_name;
public $active;
@@ -172,6 +176,9 @@ class WkHotelInteriorImage extends ObjectModel
// update global configuration values in multilang
Configuration::updateValue('HOTEL_INTERIOR_HEADING', $HOTEL_INTERIOR_HEADING);
Configuration::updateValue('HOTEL_INTERIOR_DESCRIPTION', $HOTEL_INTERIOR_DESCRIPTION);
Configuration::updateValue('HOTEL_INTERIOR_NAV_TYPE', self::NAV_TYPE_DOTS);
Configuration::updateValue('HOTEL_INTERIOR_AUTOPLAY', 1);
Configuration::updateValue('HOTEL_INTERIOR_AUTOPLAY_INTERVAL', 5000);
// Database Entry
for ($i = 1; $i <= 12; $i++) {

View File

@@ -33,6 +33,7 @@ class AdminAboutHotelBlockSettingController extends ModuleAdminController
$this->context = Context::getContext();
$this->identifier_name = 'display_name';
$this->identifier = 'id_interior_image';
parent::__construct();
$this->fields_options = array(
'global' => array(
@@ -57,6 +58,33 @@ class AdminAboutHotelBlockSettingController extends ModuleAdminController
'validation' => 'isGenericName',
'hint' => $this->l('Enter a description for the interior block.')
),
'HOTEL_INTERIOR_NAV_TYPE' => array(
'title' => $this->l('Navigation Type'),
'type' => 'select',
'identifier' => 'id',
'list' => array(
array('id' => WkHotelInteriorImage::NAV_TYPE_DOTS, 'name' => $this->l('Dots')),
array('id' => WkHotelInteriorImage::NAV_TYPE_ARROWS, 'name' => $this->l('Arrows')),
array('id' => WkHotelInteriorImage::NAV_TYPE_BOTH, 'name' => $this->l('Arrows and dots')),
),
'hint' => $this->l('How the interior photos slider of the home page is navigated.'),
),
'HOTEL_INTERIOR_AUTOPLAY' => array(
'title' => $this->l('Auto slide'),
'cast' => 'intval',
'type' => 'bool',
'default' => '1',
'hint' => $this->l('Slide the interior photos automatically on the home page.'),
),
'HOTEL_INTERIOR_AUTOPLAY_INTERVAL' => array(
'title' => $this->l('Slide Interval'),
'cast' => 'intval',
'type' => 'text',
'class' => 'fixed-width-sm',
'suffix' => $this->l('ms'),
'default' => '5000',
'desc' => $this->l('Milliseconds between slides. Min: 500. Example: 5000 = 5 s.'),
),
),
'submit' => array(
'title' => $this->l('Save'),
@@ -124,7 +152,7 @@ class AdminAboutHotelBlockSettingController extends ModuleAdminController
'icon' => 'icon-power-off text-danger',
),
);
parent::__construct();
}
public function initContent()
@@ -335,6 +363,21 @@ class AdminAboutHotelBlockSettingController extends ModuleAdminController
}
}
}
if (!in_array((int) Tools::getValue('HOTEL_INTERIOR_NAV_TYPE'), array(
WkHotelInteriorImage::NAV_TYPE_DOTS,
WkHotelInteriorImage::NAV_TYPE_ARROWS,
WkHotelInteriorImage::NAV_TYPE_BOTH,
))) {
$this->errors[] = $this->l('Invalid navigation type.');
}
if (Tools::getValue('HOTEL_INTERIOR_AUTOPLAY')) {
$slideInterval = Tools::getValue('HOTEL_INTERIOR_AUTOPLAY_INTERVAL');
if (!Validate::isUnsignedInt($slideInterval) || $slideInterval < 500) {
$this->errors[] = $this->l('Slide interval must be a whole number of at least 500 milliseconds.');
}
} else {
$_POST['HOTEL_INTERIOR_AUTOPLAY_INTERVAL'] = Configuration::get('HOTEL_INTERIOR_AUTOPLAY_INTERVAL');
}
if (!count($this->errors)) {
foreach ($languages as $lang) {
// if lang fileds are at least in default language and not available in other languages then

View File

@@ -174,3 +174,35 @@ html body .htl-light-fancybox .fancybox-nav:focus:not(:focus-visible),
html body .htl-gallery-fancybox .fancybox-nav:focus:not(:focus-visible) {
outline: 0;
}
/* arrows of the interior slider */
.htlInterior-owlCarousel .owl-nav .owl-prev,
.htlInterior-owlCarousel .owl-nav .owl-next {
position: absolute;
top: 42%;
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 50%;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, .2);
cursor: pointer;
}
.htlInterior-owlCarousel .owl-nav .owl-prev {
left: -17px;
}
.htlInterior-owlCarousel .owl-nav .owl-next {
right: -17px;
}
.htlInterior-owlCarousel .owl-nav .nav-direction-secondary,
.htlInterior-owlCarousel .owl-nav .owl-prev:hover .nav-direction-secondary,
.htlInterior-owlCarousel .owl-nav .owl-next:hover .nav-direction-secondary {
padding: 0;
background: none;
color: #333;
}
.htlInterior-owlCarousel .owl-nav .disabled {
display: none;
}

View File

@@ -21,6 +21,14 @@
*/
$(document).ready(function(){
$(document).on('change', 'input[name="HOTEL_INTERIOR_AUTOPLAY"]', function () {
if ($(this).val() === '1') {
$('input[name="HOTEL_INTERIOR_AUTOPLAY_INTERVAL"]').closest('.form-group').show(200);
} else {
$('input[name="HOTEL_INTERIOR_AUTOPLAY_INTERVAL"]').closest('.form-group').hide(200);
}
});
$("#interior_img").on("change", function(event) {
if (typeof this.files[0] != 'undefined') {
if (this.files[0].size > maxSizeAllowed) {

View File

@@ -23,21 +23,32 @@
$(document).ready(function(){
$('.htlInterior-owlCarousel .owl-carousel').owlCarousel({
loop:true,
nav:false,
dots:true,
nav: htlInteriorSliderArrows,
navText: [
'<div class="nav-direction-wrapper"><div class="nav-direction-primary"><div class="nav-direction-secondary"><i class="icon-angle-left"></i></div></div></div>',
'<div class="nav-direction-wrapper"><div class="nav-direction-primary"><div class="nav-direction-secondary"><i class="icon-angle-right"></i></div></div></div>'
],
dots: htlInteriorSliderDots,
items: 1,
margin: 8,
slideBy: 'page',
responsive: {576: {items: 2}},
autoHeight:true,
autoplay:true,
autoplaySpeed:1000,
autoplayTimeout:5000,
autoplay: htlInteriorSliderAutoplay,
autoplaySpeed: 500,
smartSpeed: 500,
autoplayTimeout: htlInteriorSliderInterval,
autoplayHoverPause:true,
responsiveClass:true,
rtl: language_is_rtl
});
$('.htlInterior-owlCarousel .owl-carousel').on('translated.owl.carousel mouseleave', function() {
var owl = $(this).data('owl.carousel');
if (owl && owl.settings.autoplay && owl._plugins.autoplay && !owl._plugins.autoplay._paused) {
owl._plugins.autoplay._setAutoPlayInterval();
}
});
$('.interiorbox').on('mouseenter', function(){
$(this).find('div.interiorHoverBlockWrapper').show();
});

View File

@@ -67,6 +67,22 @@ class WkAboutHotelBlock extends Module
}
}
$navType = (int) Configuration::get('HOTEL_INTERIOR_NAV_TYPE');
if (!in_array($navType, array(
WkHotelInteriorImage::NAV_TYPE_DOTS,
WkHotelInteriorImage::NAV_TYPE_ARROWS,
WkHotelInteriorImage::NAV_TYPE_BOTH,
))) {
$navType = WkHotelInteriorImage::NAV_TYPE_DOTS;
}
$autoplay = Configuration::get('HOTEL_INTERIOR_AUTOPLAY');
Media::addJsDef(array(
'htlInteriorSliderArrows' => $navType != WkHotelInteriorImage::NAV_TYPE_DOTS,
'htlInteriorSliderDots' => $navType != WkHotelInteriorImage::NAV_TYPE_ARROWS,
'htlInteriorSliderAutoplay' => $autoplay === false ? true : (bool) $autoplay,
'htlInteriorSliderInterval' => max(500, (int) Configuration::get('HOTEL_INTERIOR_AUTOPLAY_INTERVAL') ?: 5000),
));
$this->context->smarty->assign(
array(
'HOTEL_INTERIOR_HEADING' => $HOTEL_INTERIOR_HEADING,
@@ -204,7 +220,13 @@ class WkAboutHotelBlock extends Module
public function deleteConfigKeys()
{
$var = array('HOTEL_INTERIOR_HEADING', 'HOTEL_INTERIOR_DESCRIPTION');
$var = array(
'HOTEL_INTERIOR_HEADING',
'HOTEL_INTERIOR_DESCRIPTION',
'HOTEL_INTERIOR_NAV_TYPE',
'HOTEL_INTERIOR_AUTOPLAY',
'HOTEL_INTERIOR_AUTOPLAY_INTERVAL',
);
foreach ($var as $key) {
if (!Configuration::deleteByName($key)) {
return false;

View File

@@ -23,6 +23,10 @@
class WkHotelRoomDisplay extends ObjectModel
{
const NAV_TYPE_DOTS = 1;
const NAV_TYPE_ARROWS = 2;
const NAV_TYPE_BOTH = 3;
public $id_product;
public $active;
public $position;
@@ -188,6 +192,10 @@ class WkHotelRoomDisplay extends ObjectModel
// update global configuration values in multilang
Configuration::updateValue('HOTEL_ROOM_DISPLAY_HEADING', $HOTEL_ROOM_DISPLAY_HEADING);
Configuration::updateValue('HOTEL_ROOM_DISPLAY_DESCRIPTION', $HOTEL_ROOM_DISPLAY_DESCRIPTION);
Configuration::updateValue('HOTEL_ROOM_DISPLAY_PROPERTY', 0);
Configuration::updateValue('HOTEL_ROOM_DISPLAY_NAV_TYPE', self::NAV_TYPE_ARROWS);
Configuration::updateValue('HOTEL_ROOM_DISPLAY_AUTOPLAY', 1);
Configuration::updateValue('HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL', 5000);
if ($roomTypes = HotelHelper::getPsProducts(Configuration::get('PS_LANG_DEFAULT'), 0, 5, 1)) {
foreach ($roomTypes as $product) {
if (Validate::isLoadedObject($objProduct = new Product($product['id_product']))) {

View File

@@ -31,6 +31,7 @@ class AdminHotelRoomModuleSettingController extends ModuleAdminController
$this->_defaultOrderBy = 'position';
$this->bootstrap = true;
$this->context = Context::getContext();
parent::__construct();
$this->_select = ' pl.`name`, hbil.`hotel_name` ';
@@ -64,6 +65,40 @@ class AdminHotelRoomModuleSettingController extends ModuleAdminController
'cols' => '2',
'hint' => $this->l('Enter a description for the property rooms block.'),
),
'HOTEL_ROOM_DISPLAY_PROPERTY' => array(
'title' => $this->l('Property location'),
'cast' => 'intval',
'type' => 'bool',
'default' => '0',
'hint' => $this->l('Display the property location below the room type name in the room cards, or hide it if it is not required.'),
),
'HOTEL_ROOM_DISPLAY_NAV_TYPE' => array(
'title' => $this->l('Navigation Type'),
'type' => 'select',
'identifier' => 'id',
'list' => array(
array('id' => WkHotelRoomDisplay::NAV_TYPE_ARROWS, 'name' => $this->l('Arrows')),
array('id' => WkHotelRoomDisplay::NAV_TYPE_DOTS, 'name' => $this->l('Dots')),
array('id' => WkHotelRoomDisplay::NAV_TYPE_BOTH, 'name' => $this->l('Arrows and dots')),
),
'hint' => $this->l('How the room cards slider of the home page is navigated.'),
),
'HOTEL_ROOM_DISPLAY_AUTOPLAY' => array(
'title' => $this->l('Auto slide'),
'cast' => 'intval',
'type' => 'bool',
'default' => '1',
'hint' => $this->l('Slide the room cards automatically on the home page.'),
),
'HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL' => array(
'title' => $this->l('Slide Interval'),
'cast' => 'intval',
'type' => 'text',
'class' => 'fixed-width-sm',
'suffix' => $this->l('ms'),
'default' => '5000',
'desc' => $this->l('Milliseconds between slides. Min: 500. Example: 5000 = 5 s.'),
),
),
'submit' => array('title' => $this->l('Save'))
),
@@ -131,7 +166,6 @@ class AdminHotelRoomModuleSettingController extends ModuleAdminController
);
$this->identifier = 'id_room_block';
parent::__construct();
if ($this->hotelList = HotelBranchInformation::getProfileAccessedHotels($this->context->employee->id_profile, 1)) {
$objHotelRoomType = new HotelRoomType();
if ($this->loadObject(true)
@@ -358,6 +392,21 @@ class AdminHotelRoomModuleSettingController extends ModuleAdminController
}
}
}
if (!in_array((int) Tools::getValue('HOTEL_ROOM_DISPLAY_NAV_TYPE'), array(
WkHotelRoomDisplay::NAV_TYPE_DOTS,
WkHotelRoomDisplay::NAV_TYPE_ARROWS,
WkHotelRoomDisplay::NAV_TYPE_BOTH,
))) {
$this->errors[] = $this->l('Invalid navigation type.');
}
if (Tools::getValue('HOTEL_ROOM_DISPLAY_AUTOPLAY')) {
$slideInterval = Tools::getValue('HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL');
if (!Validate::isUnsignedInt($slideInterval) || $slideInterval < 500) {
$this->errors[] = $this->l('Slide interval must be a whole number of at least 500 milliseconds.');
}
} else {
$_POST['HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL'] = Configuration::get('HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL');
}
if (!count($this->errors)) {
foreach ($languages as $lang) {
// if lang fileds are at least in default language and not available in other languages then

View File

@@ -134,6 +134,13 @@ a.htlRoomTypeBookNow {
margin: 0 0 12px;
font-size: 22px;
}
.htlRoomTypeNameText a:focus,
.htlRoomTypeNameText a:active,
.htlRoomCardImg:focus,
.htlRoomTypeBookNow:focus {
outline: none;
text-decoration: none;
}
.htlRoomCardFeatured {
padding: 12px 0;
overflow: hidden;
@@ -326,3 +333,51 @@ a.htlRoomTypeBookNow:hover {
.htlRoomCardFeatured .htlRoomFeatureMore {
margin-left: 5px;
}
/* property name and city under the room type name */
.htlRoomCardBody .htlRoomCardProperty {
margin: 0px 0 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #777;
font-size: 13px;
}
.htlRoomCardProperty > span + span:before {
margin: 0 10px;
color: #CCC;
}
.htlRoomCardProperty i {
margin-right: 4px;
color: #999;
}
/* with the property line, the features get a line above them */
.htlRoomCardProperty + .htlRoomCardFeatured {
border-top: 1px solid #E5E5E5;
}
/* the amenities keep their line under the property line */
.htlRoomCardProperty + .htlRoomCardMeta {
border-top: 1px solid #E5E5E5;
padding-top: 12px;
}
/* dots under the room cards */
.htlRoom-owlCarousel .owl-dots {
display: flex;
justify-content: center;
gap: 6px;
margin-top: 20px;
}
.htlRoom-owlCarousel .owl-dots .owl-dot span {
display: block;
width: 7px;
height: 7px;
margin: 0;
border-radius: 50%;
background: #C4C4C4;
}
.htlRoom-owlCarousel .owl-dots .owl-dot.active span {
background: #333;
}

View File

@@ -21,6 +21,13 @@
*/
$(document).ready(function() {
$(document).on('change', 'input[name="HOTEL_ROOM_DISPLAY_AUTOPLAY"]', function () {
if ($(this).val() === '1') {
$('input[name="HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL"]').closest('.form-group').show(200);
} else {
$('input[name="HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL"]').closest('.form-group').hide(200);
}
});
var trigger_ajax = '';
$('#productName').on('keyup', function(event) {
var suggestion_ul = $(this).siblings("ul.prod_suggest_ul");

View File

@@ -86,11 +86,14 @@ $(document).ready(function() {
fitRoomCards($(this));
}).owlCarousel({
loop: true,
nav: true,
nav: htlRoomSliderArrows,
navText: ['<i class="icon-angle-left"></i>', '<i class="icon-angle-right"></i>'],
dots: false,
autoplay: true,
autoplaySpeed: 1000,
dots: htlRoomSliderDots,
autoplay: htlRoomSliderAutoplay,
autoplayTimeout: htlRoomSliderInterval,
autoplaySpeed: 500,
smartSpeed: 500,
autoplayHoverPause: true,
margin: 24,
items: 1,
rtl: typeof language_is_rtl != 'undefined' && language_is_rtl,
@@ -99,6 +102,13 @@ $(document).ready(function() {
992: { items: 3 }
}
});
$('.htlRoom-owlCarousel .owl-carousel').on('translated.owl.carousel mouseleave', function() {
var owl = $(this).data('owl.carousel');
if (owl && owl.settings.autoplay && owl._plugins.autoplay && !owl._plugins.autoplay._paused) {
owl._plugins.autoplay._setAutoPlayInterval();
}
});
// fonts and icons can change the widths after the page is shown
$(window).on('load', function() {
fitRoomCards($('.htlRoom-owlCarousel .owl-carousel'));

View File

@@ -53,7 +53,12 @@
{/block}
<div class="htlRoomCardBody">
{block name='hotel_room_block_room_type_description'}
<p class="htlRoomTypeNameText">{$roomDisplay.name|escape:'htmlall':'UTF-8'}</p>
<p class="htlRoomTypeNameText"><a href="{$link->getProductLink($roomDisplay.id_product)|escape:'html':'UTF-8'}">{$roomDisplay.name|escape:'htmlall':'UTF-8'}</a></p>
{if isset($roomDisplay.property_name) && $roomDisplay.property_name}
<p class="htlRoomCardProperty" title="{$roomDisplay.property_name|escape:'htmlall':'UTF-8'}{if isset($roomDisplay.property_location) && $roomDisplay.property_location}, {$roomDisplay.property_location|escape:'htmlall':'UTF-8'}{/if}">
<span><i class="icon-map-marker"></i> {$roomDisplay.property_name|escape:'htmlall':'UTF-8'}{if isset($roomDisplay.property_location) && $roomDisplay.property_location}, {$roomDisplay.property_location|escape:'htmlall':'UTF-8'}</span>{/if}
</p>
{/if}
{if $roomDisplay.feature}
<div class="htlRoomCardFeatured">
{* one line: the row is cut with an ellipsis by the css when it is wider than the card *}

View File

@@ -51,6 +51,7 @@ class WkHotelRoom extends Module
$dateFrom = date('Y-m-d');
$dateTo = date('Y-m-d', strtotime($dateFrom) + 86400);
$useTax = HotelBookingDetail::useTax();
$displayProperty = (bool) Configuration::get('HOTEL_ROOM_DISPLAY_PROPERTY');
$maxFeatures = (int) Configuration::get('WK_MAX_FEATURES_ROOM_SEARCH');
$maxFeatureValues = (int) Configuration::get('WK_MAX_ROOM_TYPE_FEATURE_VALUES');
foreach ($hotelRoomDisplay as &$htlRoom) {
@@ -113,6 +114,13 @@ class WkHotelRoom extends Module
);
$objRoomType = new HotelRoomType();
$roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($idProduct);
if ($displayProperty && $roomTypeInfo) {
$objHotelBranch = new HotelBranchInformation((int) $roomTypeInfo['id_hotel'], $idLang);
$htlRoom['property_name'] = $objHotelBranch->hotel_name;
$addressInfo = HotelBranchInformation::getAddress((int) $roomTypeInfo['id_hotel'], $idLang);
$htlRoom['property_location'] = $addressInfo['city'].
($addressInfo['state'] ? ', '.$addressInfo['state'] : '').', '.$addressInfo['country'];
}
$htlRoom['max_adults'] = $roomTypeInfo ? (int) $roomTypeInfo['max_adults'] : 0;
$htlRoom['max_children'] = $roomTypeInfo ? (int) $roomTypeInfo['max_children'] : 0;
$features = array();
@@ -147,6 +155,22 @@ class WkHotelRoom extends Module
)
);
$autoplay = Configuration::get('HOTEL_ROOM_DISPLAY_AUTOPLAY');
$navType = (int) Configuration::get('HOTEL_ROOM_DISPLAY_NAV_TYPE');
if (!in_array($navType, array(
WkHotelRoomDisplay::NAV_TYPE_DOTS,
WkHotelRoomDisplay::NAV_TYPE_ARROWS,
WkHotelRoomDisplay::NAV_TYPE_BOTH,
))) {
$navType = WkHotelRoomDisplay::NAV_TYPE_ARROWS;
}
Media::addJsDef(array(
'htlRoomSliderArrows' => $navType != WkHotelRoomDisplay::NAV_TYPE_DOTS,
'htlRoomSliderDots' => $navType != WkHotelRoomDisplay::NAV_TYPE_ARROWS,
'htlRoomSliderAutoplay' => $autoplay === false ? true : (bool) $autoplay,
'htlRoomSliderInterval' => max(500, (int) Configuration::get('HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL') ?: 5000),
));
$this->context->controller->addCSS(_PS_JS_DIR_.'owl-carousel/assets/owl.carousel.min.css');
$this->context->controller->addCSS(_PS_JS_DIR_.'owl-carousel/assets/owl.theme.default.min.css');
$this->context->controller->addJS(_PS_JS_DIR_.'owl-carousel/owl.carousel.min.js');
@@ -287,7 +311,11 @@ class WkHotelRoom extends Module
{
$configVars = array(
'HOTEL_ROOM_DISPLAY_HEADING',
'HOTEL_ROOM_DISPLAY_DESCRIPTION'
'HOTEL_ROOM_DISPLAY_DESCRIPTION',
'HOTEL_ROOM_DISPLAY_PROPERTY',
'HOTEL_ROOM_DISPLAY_NAV_TYPE',
'HOTEL_ROOM_DISPLAY_AUTOPLAY',
'HOTEL_ROOM_DISPLAY_AUTOPLAY_INTERVAL',
);
foreach ($configVars as $key) {
if (!Configuration::deleteByName($key)) {

View File

@@ -99,6 +99,15 @@
<div class="services-list-row">
<div>
{$product['name']}
{assign var='appliedDays' value=0}
{if $product['price_calculation_method'] != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING}
{foreach Product::getServicePriceApplicableDates($product['price_calculation_method'], $date_from, $date_to) as $appliedGroupDates}
{assign var='appliedDays' value=$appliedDays + count($appliedGroupDates)}
{/foreach}
{/if}
{if $appliedDays}
<p><span class="badge badge-success service-applied-days">{if $appliedDays == 1}{l s='Applied for 1 Day'}{else}{l s='Applied for %s Days' sprintf=$appliedDays}{/if}</span></p>
{/if}
{if $product['allow_multiple_quantity']}
<p>{l s='qty'}: {$product['quantity']}</p>
{/if}

View File

@@ -56,6 +56,8 @@
{else}
{assign var='serviceSelected' value=false}
{/if}
{* services applied on selected days only are not shown when none of those days exist in the stay *}
{if $serviceSelected || $product['price_calculation_method'] == Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING || (Product::getServicePriceApplicableDates($product['price_calculation_method'], $cartRoom['date_from'], $cartRoom['date_to'])|@array_filter)}
<div class="row room_service_block">
<div class="col-xs-8">
<div class="row">
@@ -68,20 +70,8 @@
</div>
<div class="col-xs-10">
<span>{$product['name']|escape:'html':'UTF-8'}</span>
{assign var='priceCalcMethod' value=$product.price_calculation_method|default:0}
{capture name='htl_pcm_tooltip'}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=Product::getPriceCalculationMethodDaysLabel($priceCalcMethod) item='pcmDayLabel'}
<li>{$pcmDayLabel}</li>
{/foreach}
</ul>
</div>
{/capture}
{include file='_partials/ui-tooltip.tpl' tooltip_content=$smarty.capture.htl_pcm_tooltip allow_html=true} {if $product.allow_multiple_quantity}
{include file='_partials/service-applied-days.tpl' price_calculation_method=$product.price_calculation_method|default:0 date_from=$cartRoom['date_from'] date_to=$cartRoom['date_to']}
{if $product.allow_multiple_quantity}
<div class="qty_container">
<input type="text" class="form-control qty" id="qty_{$product.id_product}" name="room_service_product_qty_{$product.id_product}" data-id-product="{$product.id_product}" data-max_quantity="{$product.max_quantity}" value="{if $serviceSelected}{$cartRoom['selected_service'][$product['id_product']]['quantity']}{else}1{/if}">
<input type="hidden" class="qty_hidden" id="qty_{$product.id_product}_hidden" value="{if $serviceSelected}{$cartRoom['selected_service'][$product['id_product']]['quantity']}{else}0{/if}">
@@ -96,10 +86,25 @@
</div>
<div class="col-xs-4">
{if ($product.show_price && !isset($restricted_country_mode)) || isset($groups)}
<span class="pull-right">{if !$priceDisplay}{convertPrice price=$product.price_tax_incl}{else}{convertPrice price=$product.price_tax_exc}{/if}</span>
{assign var='serviceDayBased' value=(($product.price_calculation_method|default:0) != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING)}
{assign var='serviceTotal' value=false}
{if $serviceSelected}
{assign var='serviceTotal' value=$cartRoom['selected_service'][$product['id_product']]}
{elseif isset($cartRoom['available_service_pricing'][$product['id_product']])}
{assign var='serviceTotal' value=$cartRoom['available_service_pricing'][$product['id_product']]}
{/if}
<div class="pull-right text-right">
{if $serviceDayBased && $serviceTotal}
{if !$priceDisplay}{convertPrice price=$serviceTotal.total_price_tax_incl}{else}{convertPrice price=$serviceTotal.total_price_tax_excl}{/if}
<br><small>{if !$priceDisplay}{convertPrice price=$product.price_tax_incl}{else}{convertPrice price=$product.price_tax_exc}{/if} {l s='/Night'}</small>
{else}
{if !$priceDisplay}{convertPrice price=$product.price_tax_incl}{else}{convertPrice price=$product.price_tax_exc}{/if}
{/if}
</div>
{/if}
</div>
</div>
{/if}
{/foreach}
{/if}
</div>

View File

@@ -53,20 +53,7 @@
{if $additionalService['allow_multiple_quantity']}
<span class="quantity">{l s='(Quantity: %s)' sprintf=[$additionalService['quantity']|string_format:'%02d']}</span>
{/if}
{assign var='priceCalcMethod' value=$additionalService.price_calculation_method|default:0}
{capture name='htl_pcm_tooltip'}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=Product::getPriceCalculationMethodDaysLabel($priceCalcMethod) item='pcmDayLabel'}
<li>{$pcmDayLabel}</li>
{/foreach}
</ul>
</div>
{/capture}
{include file='_partials/ui-tooltip.tpl' tooltip_content=$smarty.capture.htl_pcm_tooltip allow_html=true}
{include file='_partials/service-applied-days.tpl' price_calculation_method=$additionalService.price_calculation_method|default:0 date_from=$roomAdditionalService['date_from'] date_to=$roomAdditionalService['date_to']}
</div>
{hook h='displayOrderDetailServiceProductNameBlockAfter' product_order_detail=$additionalService}
</div>
@@ -78,6 +65,9 @@
{displayPrice price=$additionalService['total_price_tax_excl']|escape:'html':'UTF-8' currency=$objOrder->id_currency}
{/if}
</span>
{if ($additionalService.price_calculation_method|default:0) != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING}
<br><small>{if $useTax}{displayPrice price=$additionalService['unit_price_tax_incl']|escape:'html':'UTF-8' currency=$objOrder->id_currency}{else}{displayPrice price=$additionalService['unit_price_tax_excl']|escape:'html':'UTF-8' currency=$objOrder->id_currency}{/if} {l s='/Night'}</small>
{/if}
</div>
</div>
{/foreach}

View File

@@ -0,0 +1,56 @@
{**
* 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
*}
{if isset($price_calculation_method) && (int) $price_calculation_method != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING && !empty($date_from) && !empty($date_to)}
{assign var='serviceAppliedDates' value=Product::getServicePriceApplicableDates($price_calculation_method, $date_from, $date_to)}
{assign var='serviceAppliedDays' value=0}
{foreach from=$serviceAppliedDates item='serviceAppliedGroupDates'}
{assign var='serviceAppliedDays' value=$serviceAppliedDays + count($serviceAppliedGroupDates)}
{/foreach}
{if $serviceAppliedDays}
{capture name='htl_applied_days_tooltip'}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=$serviceAppliedDates key='serviceAppliedGroup' item='serviceAppliedGroupDates'}
{if $serviceAppliedGroupDates}
<li>
{if $serviceAppliedGroup == 'checkin'}{l s='Check-in day'}{elseif $serviceAppliedGroup == 'stay'}{l s='During-stay days'}{else}{l s='Check-out day'}{/if}
<ul>
{foreach from=$serviceAppliedGroupDates item='serviceAppliedDate'}
<li>{Tools::displayDate($serviceAppliedDate)}</li>
{/foreach}
</ul>
</li>
{/if}
{/foreach}
</ul>
</div>
{/capture}
{capture name='htl_applied_days_badge'}<span class="badge badge-success service-applied-days">{if $serviceAppliedDays == 1}{l s='Applied for 1 Day'}{else}{l s='Applied for %s Days' sprintf=$serviceAppliedDays}{/if}</span>{/capture}
<div class="service-applied-days-wrap">
{include file='_partials/ui-tooltip.tpl' tooltip_title=$smarty.capture.htl_applied_days_badge tooltip_content=$smarty.capture.htl_applied_days_tooltip allow_html=true}
</div>
{/if}
{/if}

View File

@@ -39,19 +39,6 @@
<div class="col-sm-12 clearfix service-product-block">
<span class="service-product-name">{$service_product['name']}</span>
{assign var='priceCalcMethod' value=$service_product.price_calculation_method|default:0}
{capture name='htl_pcm_tooltip'}
<div class="ui-tooltip-header">
<div class="ui-tooltip-label">{l s='Applied on:'}</div>
</div>
<div class="ui-tooltip-body">
<ul>
{foreach from=Product::getPriceCalculationMethodDaysLabel($priceCalcMethod) item='pcmDayLabel'}
<li>{$pcmDayLabel}</li>
{/foreach}
</ul>
</div>
{/capture}
{include file='_partials/ui-tooltip.tpl' tooltip_content=$smarty.capture.htl_pcm_tooltip allow_html=true}
</div>
{/block}
{block name='service_product_description'}
@@ -68,12 +55,17 @@
<div class="service-product-price">
{block name='service_product_price'}
{if !$priceDisplay}{convertPrice price=$service_product.price_tax_incl}{else}{convertPrice price=$service_product.price_tax_exc}{/if}
{if $priceCalcMethod != Product::PRICE_CALCULATION_METHOD_ONCE_FOR_BOOKING}<span class="price-label">{l s='/Night'}</span>{/if}
{/block}
{include file='_partials/service-applied-days.tpl' price_calculation_method=$priceCalcMethod date_from=$service_product.stay_date_from|default:'' date_to=$service_product.stay_date_to|default:''}
{if $service_product.allow_multiple_quantity && $service_product.available_for_order && $service_product.max_quantity > 0}
<div class="service-max-quantity-info">
{l s='Maximum'} {$service_product.max_quantity} {l s='quantity can be added'}.
</div>
{/if}
{block name='displayServiceProductActionsAfter'}
{hook h='displayServiceProductActionsAfter' id_service_product=$service_product['id_product']}
{/block}
</div>
{/if}
{/block}

View File

@@ -9495,6 +9495,8 @@ div a.our_properties_link{
display: grid;
grid-template-columns: auto 1fr;
gap: 5px 15px;
max-height: 250px;
overflow-y: auto;
}
.ui-tooltip-elem-txt {
@@ -9503,3 +9505,15 @@ div a.our_properties_link{
text-align: right;
white-space: nowrap;
}
.service-applied-days {
display: inline-block;
font-size: 11px;
font-weight: 600;
line-height: 1.3;
background-color: #72c279;
color: #fff;
border-radius: 4px;
padding: 3px 9px;
vertical-align: text-top;
}

View File

@@ -679,6 +679,10 @@ $(document).ready(function()
success: function(jsonData) {
if (!jsonData.hasError) {
showSuccessMessage(txtExtraServiceSucc);
// refresh the header cart dropdown too
if (typeof ajaxCart !== 'undefined') {
ajaxCart.refresh();
}
} else {
showErrorMessage(jsonData.errors);

View File

@@ -1114,7 +1114,7 @@ $(document).ready(function() {
var ajax_check_var = '';
$('#service_products_cont .get-service-products').on('click', function(e) {
$('#service_products_cont').on('click', '.get-service-products', function(e) {
var triggerElement = $(this);
var p = $(this).data('page');
var id_category = $(this).data('id_category');
@@ -1131,6 +1131,8 @@ $(document).ready(function() {
id_category: id_category,
p: p,
id_product: $('#product_page_product_id').val(),
date_from: $('#room_check_in').val(),
date_to: $('#room_check_out').val(),
action: 'getServiceProducts',
ajax: true,
token: static_token
@@ -1393,7 +1395,7 @@ var BookingForm = {
}).on('datepicker-change', function(event,obj){
$('#room_check_in').val($.datepicker.formatDate('yy-mm-dd', obj.date1));
$('#room_check_out').val($.datepicker.formatDate('yy-mm-dd', obj.date2));
BookingForm.refresh();
BookingForm.refresh(false, true);
});
$('body button').on('click', '', function() {
@@ -1413,9 +1415,12 @@ var BookingForm = {
formData.append('room_service_products', JSON.stringify(getRoomsServiceProducts()));
return formData;
},
refresh: function(resetOccupancy = false) {
refresh: function(resetOccupancy = false, refreshServices = false) {
return new Promise((resolve, reject) => {
let formData = BookingForm.getFormData();
if (refreshServices) {
formData.append('refresh_services', 1);
}
formData.append('ajax', true);
formData.append('action', 'refreshBookingForm');
BookingForm.currentRequest = $.ajax({
@@ -1438,6 +1443,19 @@ var BookingForm = {
$('.booking-form').replaceWith(response.html_booking_form);
BookingForm.init();
if (typeof response.html_service_products !== 'undefined') {
$('#service_products_cont').html(response.html_service_products);
initUITooltip();
$('#additional_products input.service_product').each(function () {
let id = $(this).data('id_product');
$('#room_service_product_qty_' + id).val($(this).val()).closest('.qty_container').find('.qty_count span').text($(this).val());
$('.select_room_service_' + id).text(unselect_txt).removeClass('btn-success add_roomtype_product').addClass('btn-danger remove_roomtype_product');
});
if (!$('.max_avail_type_qty').length || $('.max_avail_type_qty').val() < 1) {
disableRoomTypeServices(1);
}
}
if (resetOccupancy) {
BookingForm.resetOccupancy();
}