diff --git a/admin/themes/default/sass/vendor/bootstrap-sass/_buttons.scss b/admin/themes/default/sass/vendor/bootstrap-sass/_buttons.scss index 271ae468f..75c8d0700 100644 --- a/admin/themes/default/sass/vendor/bootstrap-sass/_buttons.scss +++ b/admin/themes/default/sass/vendor/bootstrap-sass/_buttons.scss @@ -50,6 +50,14 @@ } } +// buttons text align +.btn-left { + text-align: left; +} +.btn-right { + text-align: right; +} + // Alternate buttons // -------------------------------------------------- @@ -140,8 +148,6 @@ .btn-block { display: block; width: 100%; - padding-left: 0; - padding-right: 0; } // Vertically space out multiple block buttons diff --git a/admin/themes/default/template/controllers/orders/_current_cart_details_data.tpl b/admin/themes/default/template/controllers/orders/_current_cart_details_data.tpl index b4159912d..35a13e9ac 100644 --- a/admin/themes/default/template/controllers/orders/_current_cart_details_data.tpl +++ b/admin/themes/default/template/controllers/orders/_current_cart_details_data.tpl @@ -23,7 +23,7 @@ {l s='Cart Details'}
-
+
@@ -31,6 +31,9 @@ + {if $occupancy_required_for_booking} + + {/if} @@ -42,7 +45,7 @@ {if isset($cart_detail_data) && $cart_detail_data} {assign var=curr_id value=$cart->id_currency|intval} {foreach from=$cart_detail_data item=data} - + + {if $occupancy_required_for_booking} + + {/if} - - {*{if ($order->hasBeenPaid())}{/if} {if $display_warehouse}{/if} {if ($order->hasBeenDelivered())}{/if} *} - + + {if isset($refundReqBookings) && $refundReqBookings} @@ -1149,9 +1208,14 @@ {if $refund_allowed && !$hasCompletelyRefunded} - +
+ + +
{/if} {/if} @@ -1521,6 +1585,21 @@ {addJsDefL name=txtSomeErr}{l s='Some error occurred. Please try again.' js=1}{/addJsDefL} {addJsDefL name=txtDeleteSucc}{l s='Deleted successfully' js=1}{/addJsDefL} {addJsDefL name=txtInvalidDemandVal}{l s='Invalid demand value found' js=1}{/addJsDefL} + {addJsDefL name='select_age_txt'}{l s='Select age' js=1}{/addJsDefL} + {addJsDefL name='under_1_age'}{l s='Under 1' js=1}{/addJsDefL} + {addJsDefL name='room_txt'}{l s='Room' js=1}{/addJsDefL} + {addJsDefL name='rooms_txt'}{l s='Rooms' js=1}{/addJsDefL} + {addJsDefL name='remove_txt'}{l s='Remove' js=1}{/addJsDefL} + {addJsDefL name='adult_txt'}{l s='Adult' js=1}{/addJsDefL} + {addJsDefL name='adults_txt'}{l s='Adults' js=1}{/addJsDefL} + {addJsDefL name='child_txt'}{l s='Child' js=1}{/addJsDefL} + {addJsDefL name='children_txt'}{l s='Children' js=1}{/addJsDefL} + {addJsDefL name='below_txt'}{l s='Below' js=1}{/addJsDefL} + {addJsDefL name='years_txt'}{l s='years' js=1}{/addJsDefL} + {addJsDefL name='all_children_txt'}{l s='All Children' js=1}{/addJsDefL} + {addJsDefL name='invalid_occupancy_txt'}{l s='Invalid occupancy(adults/children) found.' js=1}{/addJsDefL} + {addJsDef max_child_age=$max_child_age|escape:'quotes':'UTF-8'} + {addJsDef max_child_in_room=$max_child_in_room|escape:'quotes':'UTF-8'} {/strip} {* Apply javascript for the page *} diff --git a/admin/themes/default/template/controllers/products/additionalfacilities.tpl b/admin/themes/default/template/controllers/products/additionalfacilities.tpl new file mode 100644 index 000000000..184db5f77 --- /dev/null +++ b/admin/themes/default/template/controllers/products/additionalfacilities.tpl @@ -0,0 +1,115 @@ +{** + * 2010-2022 Webkul. + * + * NOTICE OF LICENSE + * + * All right is reserved, + * Please go through LICENSE.txt file inside our module + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade this module to newer + * versions in the future. If you wish to customize this module for your + * needs please refer to CustomizationPolicy.txt file inside our module for more information. + * + * @author Webkul IN + * @copyright 2010-2022 Webkul IN + * @license LICENSE.txt + *} + + {if isset($product->id) && $product->id} + +
+
+ {l s='Room Type Additional Facilities'} +
+
+ {l s='To create new Additiona facilities '} {l s='click here.'} +
+ {if isset($allDemands) && $allDemands} +
+ {foreach $allDemands as $key => $demand} +
+ + + {$demand['name']|escape:'html':'UTF-8'} + +
+ {if isset($demand['adv_option']) && count($demand['adv_option'])} + + {else} +
+ +
+
+ {$defaultcurrencySign|escape:'html':'UTF-8'} + +
+
+
+ {/if} + {if isset($demand['adv_option']) && $demand['adv_option']} +
+ +
+
+
{l s='Room Image'} {l s='Room Type'} {l s='Duration'}{l s='Occupancy'}{l s='Unit Price (tax excl)'} {l s='Additinal Facilities (tax excl)'} {l s='Total Rooms Price (tax excl)'}
{$data.room_num|escape:'html':'UTF-8'} @@ -58,13 +61,71 @@ {/if} {dateFormat date=$data.date_from} - {dateFormat date=$data.date_to} + + {if $data.feature_price_diff != 0} {assign var=shown_room_type_price value=$data.feature_price_tax_excl} {else} {assign var=shown_room_type_price value=$data.product_price_tax_excl} {/if} - + {if isset($data.extra_demands) && $data.extra_demands} diff --git a/admin/themes/default/template/controllers/orders/_new_product.tpl b/admin/themes/default/template/controllers/orders/_new_product.tpl index e67cc63ff..141e9e68b 100644 --- a/admin/themes/default/template/controllers/orders/_new_product.tpl +++ b/admin/themes/default/template/controllers/orders/_new_product.tpl @@ -64,7 +64,7 @@ + {if $order->with_occupancy && $data['children']} + + {else} + {if $data['adults']}{$data['adults']}{/if} {if $data['adults'] > 1}{l s='Adults'}{else}{l s='Adult'}{/if}{if {$data['children']}}, {$data['children']} {if $data['children'] > 1}{l s='Children'}{else}{l s='Child'}{/if}{/if} + {/if} + {if $order->with_occupancy} + + {else} + + {/if} diff --git a/admin/themes/default/template/controllers/orders/form.tpl b/admin/themes/default/template/controllers/orders/form.tpl index 28280d9ba..48cd3737c 100644 --- a/admin/themes/default/template/controllers/orders/form.tpl +++ b/admin/themes/default/template/controllers/orders/form.tpl @@ -241,12 +241,13 @@ e.preventDefault(); var cart_row = $(this).closest('tr'); var params = { - id_booking_data: parseInt($(this).attr('data-id-booking-data')), - id_product: parseInt($(this).attr('data-id-product')), - id_room: parseInt($(this).attr('data-id-room')), - date_from: $(this).attr('data-date-from'), - date_to: $(this).attr('data-date-to'), - price: $(this).val(), + id_booking_data: parseInt($(cart_row).attr('data-id-booking-data')), + id_product: parseInt($(cart_row).attr('data-id-product')), + id_room: parseInt($(cart_row).attr('data-id-room')), + date_from: $(cart_row).attr('data-date-from'), + date_to: $(cart_row).attr('data-date-to'), + price: new Number($(this).val().replace(",",".")).toFixed(4).toString(), + id_cart: id_cart }; updateProductPrice(params, cart_row); }) @@ -272,6 +273,7 @@ } }); }); + resetBind(); $('#customer').focus(); @@ -332,6 +334,205 @@ $('#search-customer-form-group').show(); $(this).blur(); }); + + {literal} + $(document).on('click', '.booking_occupancy_wrapper .remove-room-link', function(e) { + e.preventDefault(); + + booking_occupancy_inner = $(this).closest('.booking_occupancy_inner'); + $(this).closest('.occupancy_info_block').remove(); + $(booking_occupancy_inner).find('.room_num_wrapper').each(function(key, val) { + $(this).text(room_txt + ' - '+ (key+1) ); + }); + setRoomTypeGuestOccupancy($(booking_occupancy_inner).closest('.booking_occupancy_wrapper')); + }); + + $(document).on('change', '.num_occupancy', function(e) { + let current_room_occupancy = 0; + $(this).closest('.occupancy_info_block').find('.num_occupancy').each(function(){ + current_room_occupancy += parseInt($(this).val()); + }); + let max_guests_in_room = $(this).closest(".booking_occupancy_wrapper").find('.max_guests').val(); + let max_allowed_for_current = (max_guests_in_room - current_room_occupancy) + parseInt($(this).val()); + if ($(this).val() > $(this).attr('max')) { + $(this).val($(this).attr('max')); + } + if ($(this).val() > max_allowed_for_current) { + $(this).val(max_allowed_for_current); + } + if ($(this).hasClass('num_children')) { + var totalChilds = $(this).closest('.occupancy_info_block').find('.guest_child_age').length; + if (totalChilds < $(this).val()) { + if (totalChilds < max_child_in_room) { + $(this).closest('.occupancy_info_block').find('.children_age_info_block').show(); + while ($(this).closest('.occupancy_info_block').find('.guest_child_age').length < $(this).val()) { + + + var roomBlockIndex = parseInt($(this).closest('.occupancy_info_block').attr('occ_block_index')); + + var childAgeSelect = '

'; + childAgeSelect += ''; + childAgeSelect += '

'; + $(this).closest('.occupancy_info_block').find('.children_ages').append(childAgeSelect); + } + } + } else { + let child = $(this).val(); + $(this).closest('.occupancy_info_block').find('.guest_child_age').each(function(ind, element) { + if (child <= ind) { + $(element).parent().remove(); + } + }); + if (child == 0) { + $(this).closest('.occupancy_info_block').find('.children_age_info_block').hide(); + } + + } + } + setRoomTypeGuestOccupancy($(this).closest('.booking_occupancy_wrapper')); + + }); + + + $(document).on('click', '.booking_guest_occupancy', function(e) { + $(this).parent().toggleClass('open'); + }); + + $(document).on('click', function(e) { + if ($('.booking_occupancy_wrapper:visible').length) { + var occupancy_wrapper = $('.booking_occupancy_wrapper:visible'); + $(occupancy_wrapper).find(".occupancy_info_block").addClass('selected'); + if (!($(e.target).closest(".booking_occupancy_wrapper").length || $(e.target).closest(".booking_guest_occupancy").length || $(e.target).closest(".ajax_add_to_cart_button").length || $(e.target).closest(".exclusive.book_now_submit").length)) { + let hasErrors = 0; + + let adults = $(occupancy_wrapper).find(".num_adults").map(function(){return $(this).val();}).get(); + let children = $(occupancy_wrapper).find(".num_children").map(function(){return $(this).val();}).get(); + let child_ages = $(occupancy_wrapper).find(".guest_child_age").map(function(){return $(this).val();}).get(); + + // start validating above values + if (!adults.length || (adults.length != children.length)) { + hasErrors = 1; + showErrorMessage(invalid_occupancy_txt); + } else { + $(occupancy_wrapper).find('.occupancy_count').removeClass('error_border'); + + // validate values of adults and children + adults.forEach(function (item, index) { + if (isNaN(item) || parseInt(item) < 1) { + hasErrors = 1; + $(occupancy_wrapper).find(".num_adults").eq(index).closest('.occupancy_count_block').find('.occupancy_count').addClass('error_border'); + } + if (isNaN(children[index])) { + hasErrors = 1; + $(occupancy_wrapper).find(".num_children").eq(index).closest('.occupancy_count_block').find('.occupancy_count').addClass('error_border'); + } + }); + + // validate values of selected child ages + $(occupancy_wrapper).find('.guest_child_age').parent().removeClass('has-error'); + child_ages.forEach(function (age, index) { + age = parseInt(age); + if (isNaN(age) || (age < 0) || (age >= parseInt(max_child_age))) { + hasErrors = 1; + $(occupancy_wrapper).find(".guest_child_age").eq(index).parent().addClass('has-error'); + } + }); + } + if (hasErrors == 0) { + $(occupancy_wrapper).parent().removeClass('open'); + $(document).trigger( "QloApps:updateRoomOccupancy", [occupancy_wrapper]); + } + } + } + }); + + $(document).on('QloApps:updateRoomOccupancy', function (e, occupancy_wrapper) { + e.preventDefault(); + let cart_row = $(occupancy_wrapper).closest('tr'); + let occupancy = getBookingOccupancyDetails(cart_row); + let params = { + id_cart: id_cart, + id_booking_data: parseInt($(cart_row).attr('data-id-booking-data')), + occupancy : occupancy.shift(), + }; + updateRoomOccupancy(params, cart_row); + }); + + function getBookingOccupancyDetails(bookingform) + { + let occupancy = []; + let selected_occupancy = $(bookingform).find(".occupancy_info_block.selected") + if (selected_occupancy.length) { + $(selected_occupancy).each(function(ind, element) { + if (parseInt($(element).find('.num_adults').val())) { + let child_ages = []; + $(element).find('.guest_child_age').each(function(index) { + if ($(this).val() > -1) { + child_ages.push($(this).val()); + } + }); + if ($(element).find('.num_children').val()) { + if (child_ages.length != $(element).find('.num_children').val()) { + $(bookingform).find('.booking_occupancy_wrapper').parent().addClass('open'); + } + } + occupancy.push({ + 'adults': $(element).find('.num_adults').val(), + 'children': $(element).find('.num_children').val(), + 'child_ages': child_ages + }); + } else { + $(bookingform).find('.booking_occupancy_wrapper').parent().addClass('open'); + } + }); + } else { + $(bookingform).find('.booking_occupancy_wrapper').parent().addClass('open'); + } + + return occupancy; + } + + function setRoomTypeGuestOccupancy(booking_occupancy_wrapper) + { + var adults = 0; + var children = 0; + var rooms = $(booking_occupancy_wrapper).find('.occupancy_info_block').length; + + $(booking_occupancy_wrapper).find(".num_adults" ).each(function(key, val) { + adults += parseInt($(this).val()); + }); + $(booking_occupancy_wrapper).find(".num_children" ).each(function(key, val) { + children += parseInt($(this).val()); + }); + + var guestButtonVal = parseInt(adults) + ' '; + if (parseInt(adults) > 1) { + guestButtonVal += adults_txt; + } else { + guestButtonVal += adult_txt; + } + if (parseInt(children) > 0) { + if (parseInt(children) > 1) { + guestButtonVal += ', ' + parseInt(children) + ' ' + children_txt; + } else { + guestButtonVal += ', ' + parseInt(children) + ' ' + child_txt; + } + } + // if (parseInt(rooms) > 1) { + // guestButtonVal += ', ' + parseInt(rooms) + ' ' + rooms_txt; + // } else { + // guestButtonVal += ', ' + parseInt(rooms) + ' ' + room_txt; + // } + // console.log($(booking_occupancy_wrapper).siblings('.booking_guest_occupancy > span')); + $(booking_occupancy_wrapper).siblings('.booking_guest_occupancy').find('span').text(guestButtonVal); + } + {/literal} }); function resetBind() @@ -389,12 +590,8 @@ }); } - function updateProductPrice(params, cart_row) { - $.extend(params, { - id_cart: id_cart, - price: new Number(params.price.replace(",",".")).toFixed(4).toString(), - }); - + function updateProductPrice(params, cart_row) + { $.ajax({ type:"POST", url: "{$link->getAdminLink('AdminCarts')|addslashes}", @@ -414,6 +611,29 @@ }); } + function updateRoomOccupancy(params, cart_row) + { + debugger; + $.ajax({ + type:"POST", + url: "{$link->getAdminLink('AdminCarts')|addslashes}", + async: true, + dataType: "JSON", + data: { + ajax: "1", + token: "{getAdminToken tab='AdminCarts'}", + tab: "AdminCarts", + action: "updateRoomOccupancy", + params: params, + }, + success : function(response) { + updateCartLine(response.curr_booking_info, cart_row); + updateCartSummaryData(response.cart_info); + } + }); + } + + function updateCartLine(data, cart_row) { $(cart_row).find('.cart_line_total_rooms_price').html(data.amt_with_qty); $(cart_row).find('.cart_line_total_price').html(data.total_price); @@ -1807,6 +2027,23 @@ +{strip} + {addJsDef max_child_age=$max_child_age} + {addJsDef max_child_in_room=$max_child_in_room} + {addJsDefL name='select_age_txt'}{l s='Select age' js=1}{/addJsDefL} + {addJsDefL name='under_1_age'}{l s='Under 1' js=1}{/addJsDefL} + {addJsDefL name='room_txt'}{l s='Room' js=1}{/addJsDefL} + {addJsDefL name='rooms_txt'}{l s='Rooms' js=1}{/addJsDefL} + {addJsDefL name='remove_txt'}{l s='Remove' js=1}{/addJsDefL} + {addJsDefL name='adult_txt'}{l s='Adult' js=1}{/addJsDefL} + {addJsDefL name='adults_txt'}{l s='Adults' js=1}{/addJsDefL} + {addJsDefL name='child_txt'}{l s='Child' js=1}{/addJsDefL} + {addJsDefL name='children_txt'}{l s='Children' js=1}{/addJsDefL} + {addJsDefL name='below_txt'}{l s='Below' js=1}{/addJsDefL} + {addJsDefL name='years_txt'}{l s='years' js=1}{/addJsDefL} + {addJsDefL name='all_children_txt'}{l s='All Children' js=1}{/addJsDefL} + {addJsDefL name='invalid_occupancy_txt'}{l s='Invalid occupancy(adults/children) found.' js=1}{/addJsDefL} +{/strip}
diff --git a/admin/themes/default/template/controllers/orders/helpers/view/view.tpl b/admin/themes/default/template/controllers/orders/helpers/view/view.tpl index 099c48a30..b84467ddd 100644 --- a/admin/themes/default/template/controllers/orders/helpers/view/view.tpl +++ b/admin/themes/default/template/controllers/orders/helpers/view/view.tpl @@ -1058,6 +1058,64 @@
+ {if is_array($returns) && count($returns)} +
+
+ {l s='Refunds'} +
+ + + + + + + + + {foreach from=$returns item=return_info} + + + + + {/foreach} + +
{l s='Refund ID'}{l s='Status'}
+ #{$return_info.id_order_return} + + {capture name=refund_status} + {if isset($return_info.payment_mode) && $return_info.payment_mode != ''}{l s='Payment Mode: '}{$return_info.payment_mode}
{/if} + {if isset($return_info.id_transaction) && $return_info.id_transaction != ''}{l s='Transaction ID: '}{$return_info.id_transaction}
{/if} + {if isset($return_info.id_return_type) && isset($return_info.return_type) && $return_info.id_return_type && $return_info.return_type} + {if $return_info.return_type == OrderReturn::RETURN_TYPE_CART_RULE} + {l s='Voucher ID: '} + + #{$return_info.id_return_type} + + {elseif $return_info.return_type == OrderReturn::RETURN_TYPE_ORDER_SLIP} + {l s='Credit Slip ID: '} + #{$return_info.id_return_type} + + {l s='Download' mod='hotelreservationsystem'} + + {/if} +
+ {/if} + {if isset($return_info.id_cart_rule) && $return_info.id_cart_rule} + {l s='Voucher ID: '} + + #{$return_info.id_cart_rule} + +
+ {/if} + {/capture} + + {if $smarty.capture.refund_status|trim != ''} + {$smarty.capture.refund_status} + {else} + {$return_info.state_name} + {/if} +
+
+ {/if} {hook h="displayAdminOrderRight" id_order=$order->id} @@ -1070,7 +1128,7 @@ -
+
{l s='Rooms In This Order'} {$order_detail_data|@count} @@ -1090,6 +1148,7 @@
{l s='Room Type'} {l s='Hotel Name'} {l s='Duration'}{l s='Occupancy'} {l s='Unit Price (Tax excl.)'} {l s='Total Price (Tax incl.)'}
+ + + + + + + {foreach from=$demand['adv_option'] key=key item=info} + + + + + {/foreach} +
+ {l s='Option Name'} + + {l s='Price'} +
+ {$info['name']|escape:'html':'UTF-8'} + +
+ {$defaultcurrencySign|escape:'html':'UTF-8'} + +
+
+
+
+
+ {/if} + + + {/foreach} + + + {else} +
+ {l s='No additional facilities created yet. To create please visit'} {l s='Additional facilities'} {l s='page'}. +
+ {/if} + +{/if} diff --git a/admin/themes/default/template/controllers/products/occupancy.tpl b/admin/themes/default/template/controllers/products/occupancy.tpl index c38cd0b92..1605e2818 100644 --- a/admin/themes/default/template/controllers/products/occupancy.tpl +++ b/admin/themes/default/template/controllers/products/occupancy.tpl @@ -24,27 +24,25 @@

{l s='Occupancy'}

- {l s='Set the occupancy of this room type. Enter how much adults and children this room type can accomodate.'} -

- {l s='Note'} : {l s='These occupancy settings will not affect "ROOM TYPE ADDITIONAL FACILITIES" any way. It will work separately.'} + {l s='Below, enter the base occupancy of this room type.'}
- +
@@ -53,6 +51,46 @@
+
+ +
+ {l s='Below, enter the maximum of adults or children which can be accommodated in a room of this room type. Also, enter maximum number of guests for this room type.'} +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ diff --git a/classes/Cart.php b/classes/Cart.php index 18b2c15cf..e6061841f 100644 --- a/classes/Cart.php +++ b/classes/Cart.php @@ -1731,6 +1731,7 @@ class CartCore extends ObjectModel $order_total_discount = 0; $order_shipping_discount = 0; + $advance_payment_products_discount = 0; if (!in_array($type, array(Cart::ONLY_SHIPPING, Cart::ONLY_PRODUCTS, Cart::ADVANCE_PAYMENT_ONLY_PRODUCTS)) && CartRule::isFeatureActive()) { // First, retrieve the cart rules associated to this "getOrderTotal" if ($with_shipping || $type == Cart::ONLY_DISCOUNTS) { @@ -1787,10 +1788,28 @@ class CartCore extends ObjectModel // If the cart rule offers a reduction, the amount is prorated (with the products in the package) if ($cart_rule['obj']->reduction_percent > 0 || $cart_rule['obj']->reduction_amount > 0) { $order_total_discount += Tools::ps_round($cart_rule['obj']->getContextualValue($with_taxes, $virtual_context, CartRule::FILTER_ACTION_REDUCTION, $package, $use_cache), $compute_precision); + + if ($type == Cart::ADVANCE_PAYMENT) { + $advance_payment_products_discount += Tools::ps_round($cart_rule['obj']->getContextualValue($with_taxes, $virtual_context, CartRule::FILTER_ACTION_REDUCTION, $package, $use_cache, true), $compute_precision); + } } } $order_total_discount = min(Tools::ps_round($order_total_discount, 2), (float)$order_total_products) + (float)$order_shipping_discount; - $order_total -= $order_total_discount; + if ($type == Cart::ADVANCE_PAYMENT) { + // get order total without discount + $total_without_discount = $this->getOrderTotal() + $order_total_discount; + // to find due amount substract advance payment without discuount from order total without discount + // Due Amount = order total - advance payment (without discount) + $due_amount = $total_without_discount - $order_total; + // if discount for advance payment products is greater than due amount then only decrease the price of advance payment amount + // otherwise discount will be applied on due amount not advance payment amount + if ($advance_payment_products_discount > $due_amount) { + $order_total -= ($advance_payment_products_discount - $due_amount); + } + $order_total -= $order_total_discount - $advance_payment_products_discount; + } else { + $order_total -= $order_total_discount; + } } if ($type == Cart::BOTH || $type == Cart::ADVANCE_PAYMENT) { @@ -4370,7 +4389,7 @@ class CartCore extends ObjectModel // lets validate the cart booking data first if (isset($bookingRows) && count($bookingRows)) { $objRoomType = new HotelRoomType(); - $objBookingDtl = new HotelBookingDetail(); + $objBookingDetail = new HotelBookingDetail(); $errors = array(); $idCustomer = $objCart->id_customer; $idCurrency = $objCart->id_currency; @@ -4412,24 +4431,20 @@ class CartCore extends ObjectModel } if (!count($errors)) { - $numDays = $objBookingDtl->getNumberOfDays($dateFrom, $dateTo); + $numDays = $objBookingDetail->getNumberOfDays($dateFrom, $dateTo); $prodQty = $prodQty * (int) $numDays; $reqRooms = 1; - if ($hotelRoomData = $objBookingDtl->DataForFrontSearch( - $dateFrom, - $dateTo, - $idHotel, - $idProduct, - 1, - 0, - 0, - -1, - 0, - 0, - $idCart, - $idGuest - )) { + $bookingParams = array( + 'date_from' => $dateFrom, + 'date_to' => $dateTo, + 'hotel_id' => $idHotel, + 'id_room_type' => $idProduct, + 'only_search_data' => 1, + 'id_cart' => $idCart, + 'id_guest' => $idGuest, + ); + if ($hotelRoomData = $objBookingDetail->dataForFrontSearch($bookingParams)) { if (isset($hotelRoomData['stats']['num_avail'])) { $totalAvailRooms = $hotelRoomData['stats']['num_avail']; if ($totalAvailRooms < $reqRooms) { @@ -4470,7 +4485,7 @@ class CartCore extends ObjectModel if (!$hasErrors) { $update_quantity = $objCart->updateQty($prodQty, $idProduct, 0, 0, Tools::getValue('op', 'up'), $this->id_address_delivery); $chkQty = 0; - if ($hotelAvailRomms = $hotelRoomData['rm_data'][0]['data']['available']) { + if ($hotelAvailRomms = $hotelRoomData['rm_data'][$idProduct]['data']['available']) { foreach ($hotelAvailRomms as $key_hotel_room_info => $val_hotel_room_info) { if ($chkQty < $reqRooms) { $objCartBooking = new HotelCartBookingData(); diff --git a/classes/CartRule.php b/classes/CartRule.php index 88670b0de..9086135ad 100644 --- a/classes/CartRule.php +++ b/classes/CartRule.php @@ -875,9 +875,10 @@ class CartRuleCore extends ObjectModel * @param bool $use_tax * @param Context $context * @param bool $use_cache Allow using cache to avoid multiple free gift using multishipping + * @param bool $only_advance_payment_products to calculate discount applied only on products that have advance payment * @return float|int|string */ - public function getContextualValue($use_tax, Context $context = null, $filter = null, $package = null, $use_cache = true) + public function getContextualValue($use_tax, Context $context = null, $filter = null, $package = null, $use_cache = true, $only_advance_payment_products = false) { if (!CartRule::isFeatureActive()) { return 0; @@ -890,15 +891,34 @@ class CartRuleCore extends ObjectModel } $all_products = $context->cart->getProducts(); - $package_products = (is_null($package) ? $all_products : $package['products']); + if (is_null($package)) { + $package = array( + 'products' => $all_products, + 'id_carrier' => null, + 'id_address' => 0 + ); + } $reduction_value = 0; - - $cache_id = 'getContextualValue_'.(int)$this->id.'_'.(int)$use_tax.'_'.(int)$context->cart->id.'_'.(int)$filter; - foreach ($package_products as $product) { + $objHotelAdvancePayment = new HotelAdvancedPayment(); + $cache_id = 'getContextualValue_'.(int)$this->id.'_'.(int)$use_tax.'_'.(int)$context->cart->id.'_'.(int)$filter.'_'.(int)$only_advance_payment_products; + foreach ($package['products'] as $key => $product) { + if ($only_advance_payment_products) { + if ($advancePaymentInfo = $objHotelAdvancePayment->getIdAdvPaymentByIdProduct($product['id_product'])) { + if (!$advancePaymentInfo['active']) { + unset($package['products'][$key]); + continue; + } + } else { + unset($package['products'][$key]); + continue; + } + } $cache_id .= '_'.(int)$product['id_product'].'_'.(int)$product['id_product_attribute'].(isset($product['in_stock']) ? '_'.(int)$product['in_stock'] : ''); } + $package_products = $package['products']; + if (Cache::isStored($cache_id)) { return Cache::retrieve($cache_id); } @@ -1001,7 +1021,7 @@ class CartRuleCore extends ObjectModel $reduction_value += $selected_products_reduction * $this->reduction_percent / 100; } - // Discount (¤) + // Discount (by amount) if ((float)$this->reduction_amount > 0) { $prorata = 1; if (!is_null($package) && count($all_products)) { @@ -1011,7 +1031,31 @@ class CartRuleCore extends ObjectModel } } + $restricted_product = null; $reduction_amount = $this->reduction_amount; + // If the cart rule is restricted to one room type it can't exceed this room type price + if ($this->reduction_product > 0) { + foreach ($package_products as $product) { + if ($product['id_product'] == $this->reduction_product) { + $restricted_product = $product; + + $prorata = 1; // do not split this cart rule + if ($this->reduction_tax) { + $max_reduction_amount = (int) $restricted_product['cart_quantity'] * (float) $restricted_product['price_wt']; + } else { + $max_reduction_amount = (int) $restricted_product['cart_quantity'] * (float) $restricted_product['price']; + } + $reduction_amount = min($reduction_amount, $max_reduction_amount); + + break; + } + } + } + + if (($this->reduction_product > 0) && !$restricted_product) { + $reduction_amount = 0; + } + // If we need to convert the voucher value to the cart currency if (isset($context->currency) && $this->reduction_currency != $context->currency->id) { $voucherCurrency = new Currency($this->reduction_currency); @@ -1058,7 +1102,7 @@ class CartRuleCore extends ObjectModel } } } - // Discount (¤) on the whole order + // Discount (by amount) on the whole order elseif ($this->reduction_product == 0) { $cart_amount_te = null; $cart_amount_ti = null; diff --git a/classes/Cookie.php b/classes/Cookie.php index 920415da6..29b89b697 100644 --- a/classes/Cookie.php +++ b/classes/Cookie.php @@ -26,6 +26,25 @@ class CookieCore { + const SAMESITE_NONE = 'None'; + const SAMESITE_LAX = 'Lax'; + const SAMESITE_STRICT = 'Strict'; + + const SAMESITE_AVAILABLE_VALUES = array( + array( + "type" => self::SAMESITE_NONE, + "name" => self::SAMESITE_NONE + ), + array( + "type" => self::SAMESITE_LAX, + "name" => self::SAMESITE_LAX + ), + array( + "type" => self::SAMESITE_STRICT, + "name" => self::SAMESITE_STRICT + ), + ); + /** @var array Contain cookie content in a key => value format */ protected $_content; @@ -73,6 +92,7 @@ class CookieCore $this->_path = str_replace('%2F', '/', $this->_path); $this->_path = str_replace('%7E', '~', $this->_path); $this->_domain = $this->getDomain($shared_urls); + $this->_sameSite = Configuration::get('PS_COOKIE_SAMESITE'); $this->_name = 'PrestaShop-'.md5(($this->_standalone ? '' : _PS_VERSION_).$name.$this->_domain); $this->_allow_writing = true; $this->_salt = $this->_standalone ? str_pad('', 8, md5('ps'.__FILE__)) : _COOKIE_IV_; @@ -82,7 +102,7 @@ class CookieCore } else { $this->_cipherTool = new PhpEncryption(_NEW_COOKIE_KEY_); } - + $this->_secure = (bool)$secure; $this->update(); @@ -342,11 +362,35 @@ class CookieCore $content = 0; $time = 1; } - if (PHP_VERSION_ID <= 50200) { /* PHP version > 5.2.0 */ - return setcookie($this->_name, $content, $time, $this->_path, $this->_domain, $this->_secure); - } else { - return setcookie($this->_name, $content, $time, $this->_path, $this->_domain, $this->_secure, true); + + /* + * The alternative signature supporting an options array is only available since + * PHP 7.3.0, before there is no support for SameSite attribute. + */ + if (PHP_VERSION_ID < 70300) { + return setcookie( + $this->_name, + $content, + $time, + $this->_path, + $this->_domain . '; SameSite=' . $this->_sameSite, + $this->_secure, + true + ); } + + return setcookie( + $this->_name, + $content, + [ + 'expires' => $time, + 'path' => $this->_path, + 'domain' => $this->_domain, + 'secure' => $this->_secure, + 'httponly' => true, + 'samesite' => $this->_sameSite, + ] + ); } public function __destruct() diff --git a/classes/PaymentModule.php b/classes/PaymentModule.php index 4ce1ab259..0649ab4a7 100644 --- a/classes/PaymentModule.php +++ b/classes/PaymentModule.php @@ -356,6 +356,20 @@ abstract class PaymentModuleCore extends Module PrestaShopLogger::addLog('PaymentModule::validateOrder - Order is about to be added', 1, null, 'Cart', (int)$id_cart, true); } + if ($this->name == 'wsorder') { + $order->with_occupancy = 0; + } else { + if (defined('_PS_ADMIN_DIR_')) { + if (Configuration::get('PS_BACKOFFICE_ROOM_BOOKING_TYPE') == HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY) { + $order->with_occupancy = 1; + } + } else { + if (Configuration::get('PS_FRONT_ROOM_UNIT_SELECTION_TYPE') == HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY) { + $order->with_occupancy = 1; + } + } + } + // advance payment information $order->is_advance_payment = $this->context->cart->is_advance_payment; if ($order->is_advance_payment) { @@ -585,9 +599,11 @@ abstract class PaymentModuleCore extends Module } $cart_rules_list = array(); - $total_reduction_value_ti = 0; - $total_reduction_value_tex = 0; foreach ($cart_rules as $cart_rule) { + if ($cart_rule['obj']->reduction_product > 0 && !$order->orderContainProduct($cart_rule['obj']->reduction_product)) { + continue; + } + $package = array('id_carrier' => $order->id_carrier, 'id_address' => $order->id_address_delivery, 'products' => $order->product_list); $values = array( 'tax_incl' => $cart_rule['obj']->getContextualValue(true, $this->context, CartRule::FILTER_ACTION_ALL_NOCAP, $package), @@ -600,13 +616,25 @@ abstract class PaymentModuleCore extends Module } // IF - // This is not multi-shipping // The value of the voucher is greater than the total of the order // Partial use is allowed // This is an "amount" reduction, not a reduction in % or a gift // THEN // The voucher is cloned with a new value corresponding to the remainder - if (count($order_list) == 1 && $values['tax_incl'] > ($order->total_products_wt - $total_reduction_value_ti) && $cart_rule['obj']->partial_use == 1 && $cart_rule['obj']->reduction_amount > 0) { + $reduction_amount_converted = $cart_rule['obj']->reduction_amount; + if ((int) $cart_rule['obj']->reduction_currency !== (int) $cart->id_currency) { + $reduction_amount_converted = Tools::convertPriceFull( + $cart_rule['obj']->reduction_amount, + new Currency($cart_rule['obj']->reduction_currency), + new Currency($cart->id_currency) + ); + } + if ($cart_rule['obj']->reduction_tax) { + $remaining_amount = $reduction_amount_converted - $values['tax_incl']; + } else { + $remaining_amount = $reduction_amount_converted - $values['tax_excl']; + } + if ($remaining_amount > 0 && $cart_rule['obj']->partial_use == 1 && $reduction_amount_converted > 0) { // Create a new voucher from the original $voucher = new CartRule((int)$cart_rule['obj']->id); // We need to instantiate the CartRule without lang parameter to allow saving it unset($voucher->id); @@ -618,16 +646,13 @@ abstract class PaymentModuleCore extends Module } // Set the new voucher value + $voucher->reduction_amount = $remaining_amount; if ($voucher->reduction_tax) { - $voucher->reduction_amount = ($total_reduction_value_ti + $values['tax_incl']) - $order->total_products_wt; - // Add total shipping amout only if reduction amount > total shipping if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_incl) { $voucher->reduction_amount -= $order->total_shipping_tax_incl; } } else { - $voucher->reduction_amount = ($total_reduction_value_tex + $values['tax_excl']) - $order->total_products; - // Add total shipping amout only if reduction amount > total shipping if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_excl) { $voucher->reduction_amount -= $order->total_shipping_tax_excl; @@ -669,12 +694,7 @@ abstract class PaymentModuleCore extends Module null, null, null, null, _PS_MAIL_DIR_, false, (int)$order->id_shop ); } - - $values['tax_incl'] = $order->total_products_wt - $total_reduction_value_ti; - $values['tax_excl'] = $order->total_products - $total_reduction_value_tex; } - $total_reduction_value_ti += $values['tax_incl']; - $total_reduction_value_tex += $values['tax_excl']; $order->addCartRule($cart_rule['obj']->id, $cart_rule['obj']->name, $values, 0, $cart_rule['obj']->free_shipping); @@ -781,6 +801,9 @@ abstract class PaymentModuleCore extends Module $objBookingDetail->date_to = $objCartBookingData->date_to; $objBookingDetail->total_price_tax_excl = Tools::ps_round($total_price['total_price_tax_excl'], 6); $objBookingDetail->total_price_tax_incl = Tools::ps_round($total_price['total_price_tax_incl'], 6); + $objBookingDetail->adults = $objCartBookingData->adults; + $objBookingDetail->children = $objCartBookingData->children; + $objBookingDetail->child_ages = $objCartBookingData->child_ages; // Save hotel information/location/contact if (Validate::isLoadedObject($objRoom = new HotelRoomInformation($objCartBookingData->id_room))) { @@ -803,10 +826,6 @@ abstract class PaymentModuleCore extends Module $objBookingDetail->phone = $hotelAddress['phone']; } } - if ($roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($idProduct)) { - $objBookingDetail->adult = $roomTypeInfo['adult']; - $objBookingDetail->children = $roomTypeInfo['children']; - } /*for saving details of the advance payment product wise*/ $objBookingDetail->total_paid_amount = Tools::ps_round($total_price['total_price_tax_incl'], 5); @@ -890,14 +909,6 @@ abstract class PaymentModuleCore extends Module // delete cart feature prices after booking creation success HotelRoomTypeFeaturePricing::deleteByIdCart($id_cart); - if (isset($_COOKIE['wk_id_cart'])) { - setcookie('wk_id_cart', ' ', time() - 86400, '/'); - setcookie('wk_id_guest', ' ', time() - 86400, '/'); - - unset($_COOKIE['wk_id_cart']); - unset($_COOKIE['wk_id_guest']); - } - if (self::DEBUG_MODE) { PrestaShopLogger::addLog('PaymentModule::validateOrder - Hook validateOrder is about to be called', 1, null, 'Cart', (int)$id_cart, true); } @@ -1431,8 +1442,6 @@ abstract class PaymentModuleCore extends Module $cart_htl_data[$type_key]['cover_img'] = $cover_img; $cart_htl_data[$type_key]['name'] = $product->name; $cart_htl_data[$type_key]['hotel_name'] = $rm_dtl['hotel_name']; - $cart_htl_data[$type_key]['adult'] = $rm_dtl['adult']; - $cart_htl_data[$type_key]['children'] = $rm_dtl['children']; foreach ($cart_bk_data as $data_k => $data_v) { $date_join = strtotime($data_v['date_from']).strtotime($data_v['date_to']); @@ -1442,6 +1451,9 @@ abstract class PaymentModuleCore extends Module $num_days = $cart_htl_data[$type_key]['date_diff'][$date_join]['num_days']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['adults'] += $data_v['adults']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['children'] += $data_v['children']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; $cart_htl_data[$type_key]['date_diff'][$date_join]['avg_paid_unit_price_tax_incl'] += $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl']; $cart_htl_data[$type_key]['date_diff'][$date_join]['amount_tax_incl'] += $data_v['total_price_tax_incl']; @@ -1481,6 +1493,8 @@ abstract class PaymentModuleCore extends Module $cart_htl_data[$type_key]['date_diff'][$date_join]['data_form'] = $data_v['date_from']; $cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = $data_v['date_to']; $cart_htl_data[$type_key]['date_diff'][$date_join]['num_days'] = $num_days; + $cart_htl_data[$type_key]['date_diff'][$date_join]['adults'] = $data_v['adults']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['children'] = $data_v['children']; $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; $cart_htl_data[$type_key]['date_diff'][$date_join]['avg_paid_unit_price_tax_incl'] = $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl']; diff --git a/classes/PrestaShopBackup.php b/classes/PrestaShopBackup.php index adb0f4edd..6bc12601e 100644 --- a/classes/PrestaShopBackup.php +++ b/classes/PrestaShopBackup.php @@ -218,7 +218,8 @@ class PrestaShopBackupCore $this->id = realpath($backupfile); fwrite($fp, '/* Backup for '.Tools::getHttpHost(false, false).__PS_BASE_URI__."\n * at ".date($date)."\n */\n"); - fwrite($fp, "\n".'SET NAMES \'utf8\';'."\n\n"); + fwrite($fp, "\n".'SET NAMES \'utf8\';'); + fwrite($fp, "\n".'SET SQL_MODE=\'ALLOW_INVALID_DATES\';'."\n\n"); // Find all tables $tables = Db::getInstance()->executeS('SHOW TABLES'); diff --git a/classes/Product.php b/classes/Product.php index 52f318af5..cf6dfb3c3 100644 --- a/classes/Product.php +++ b/classes/Product.php @@ -5623,7 +5623,7 @@ class ProductCore extends ObjectModel WHERE `id_product` = '.(int)$this->id ); - // save room type info adult, child, hotel info + // save room type info adults, child, hotel info $postData = trim(file_get_contents('php://input')); libxml_use_internal_errors(true); $xml = simplexml_load_string(utf8_decode($postData)); @@ -5631,7 +5631,7 @@ class ProductCore extends ObjectModel $roomtypeData = json_decode(json_encode($xml), true); // set room map info for the hotel from request - return Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'htl_room_type` (`id_product`, `id_hotel`, `adult`, `children`, `date_add`, `date_upd`) VALUES ('.(int)$this->id.', '.(int) $roomtypeData['room_type']['id_hotel'].', '.(int) $roomtypeData['room_type']['adults'].', '.(int) $roomtypeData['room_type']['children'].', \''.date('Y-m-d h:i:s').'\', \''.date('Y-m-d h:i:s').'\')'); + return Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'htl_room_type` (`id_product`, `id_hotel`, `adults`, `children`, `date_add`, `date_upd`) VALUES ('.(int)$this->id.', '.(int) $roomtypeData['room_type']['id_hotel'].', '.(int) $roomtypeData['room_type']['adults'].', '.(int) $roomtypeData['room_type']['children'].', \''.date('Y-m-d h:i:s').'\', \''.date('Y-m-d h:i:s').'\')'); } return true; @@ -6060,7 +6060,7 @@ class ProductCore extends ObjectModel public function getWsAdults() { return Db::getInstance()->getValue( - 'SELECT `adult` FROM `'._DB_PREFIX_.'htl_room_type` WHERE `id_product` = '.(int)$this->id.' ORDER BY `id` ASC' + 'SELECT `adults` FROM `'._DB_PREFIX_.'htl_room_type` WHERE `id_product` = '.(int)$this->id.' ORDER BY `id` ASC' ); } diff --git a/classes/controller/AdminController.php b/classes/controller/AdminController.php index 45479e0fa..69d5d01f9 100644 --- a/classes/controller/AdminController.php +++ b/classes/controller/AdminController.php @@ -2147,7 +2147,7 @@ class AdminControllerCore extends Controller $tab_modules_list = $this->filterTabModuleList($tab_modules_list); - if ((is_array($tab_modules_list['slider_list']) && count($tab_modules_list['slider_list'])) + if (!empty($tab_modules_list) && (is_array($tab_modules_list['slider_list']) && count($tab_modules_list['slider_list'])) || !Tab::isTabModuleListAvailable() ) { $this->page_header_toolbar_btn['modules-list'] = array( diff --git a/classes/controller/FrontController.php b/classes/controller/FrontController.php index 9663e7e4d..cdd22e7a8 100644 --- a/classes/controller/FrontController.php +++ b/classes/controller/FrontController.php @@ -435,6 +435,15 @@ class FrontControllerCore extends Controller $compared_products = CompareProduct::getCompareProducts($this->context->cookie->id_compare); } + $isOccupancyWiseSearch = false; + if (Configuration::get('PS_FRONT_SEARCH_TYPE') == HotelBookingDetail::SEARCH_TYPE_OWS) { + $isOccupancyWiseSearch = true; + } + + $occupancyRequiredForBooking = false; + if (Configuration::get('PS_FRONT_ROOM_UNIT_SELECTION_TYPE') == HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY) { + $occupancyRequiredForBooking = true; + } $this->context->smarty->assign(array( // Useful for layout.tpl 'mobile_device' => $this->context->getMobileDevice(), @@ -482,6 +491,10 @@ class FrontControllerCore extends Controller 'currencySign' => $currency->sign, // backward compat, see global.tpl 'currencyFormat' => $currency->format, // backward compat 'currencyBlank' => $currency->blank, // backward compat + 'is_occupancy_wise_search' => $isOccupancyWiseSearch, + 'occupancy_required_for_booking' => $occupancyRequiredForBooking, + 'max_child_age' => Configuration::get('WK_GLOBAL_CHILD_MAX_AGE'), + 'max_child_in_room' => Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM'), )); // Add the tpl files directory for mobile @@ -1059,6 +1072,12 @@ class FrontControllerCore extends Controller } } + $this->addJS(_PS_JS_DIR_.'daterangepicker/moment.min.js'); + $this->addjqueryPlugin('daterangepicker.min', _PS_JS_DIR_.'daterangepicker/'); + $this->addJS(_PS_JS_DIR_.'daterangepicker/qlodaterangepicker.js'); + + $this->addCSS(_THEME_CSS_DIR_.'daterangepicker.css'); + if (Tools::isSubmit('live_edit') && Tools::getValue('ad') && Tools::getAdminToken('AdminModulesPositions'.(int)Tab::getIdFromClassName('AdminModulesPositions').(int)Tools::getValue('id_employee'))) { $this->addJqueryUI('ui.sortable'); $this->addjqueryPlugin('fancybox'); diff --git a/classes/order/Order.php b/classes/order/Order.php index 10aea87d1..5ad465fcd 100644 --- a/classes/order/Order.php +++ b/classes/order/Order.php @@ -193,6 +193,11 @@ class OrderCore extends ObjectModel */ public $advance_paid_amount; + /** + * @var int is occupancy provided in this order + */ + public $with_occupancy; + /** * @see ObjectModel::$definition */ @@ -246,8 +251,9 @@ class OrderCore extends ObjectModel 'source' => array('type' => self::TYPE_STRING), 'valid' => array('type' => self::TYPE_BOOL), 'reference' => array('type' => self::TYPE_STRING), - 'is_advance_payment' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'default' => 0), - 'advance_paid_amount' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'), + 'is_advance_payment' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'default' => 0), + 'advance_paid_amount' => array('type' => self::TYPE_FLOAT, 'validate' => 'isPrice'), + 'with_occupancy' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'default' => 0), 'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), 'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), ), diff --git a/classes/order/OrderReturn.php b/classes/order/OrderReturn.php index 89b94ea76..bd06ec1c4 100644 --- a/classes/order/OrderReturn.php +++ b/classes/order/OrderReturn.php @@ -26,6 +26,9 @@ class OrderReturnCore extends ObjectModel { + /** @var int */ + public $id_order_return; + /** @var int */ public $id_customer; @@ -48,12 +51,22 @@ class OrderReturnCore extends ObjectModel /** @var int if request raised by the admin */ public $by_admin; + /** @var int id_cart_rule or id_order_slip */ + public $id_return_type; + + /** @var int whether $id_return_type is cart_rule or order_slip */ + public $return_type; + /** @var string Object creation date */ public $date_add; /** @var string Object last modification date */ public $date_upd; + /** possible values for $return_type */ + const RETURN_TYPE_CART_RULE = 1; + const RETURN_TYPE_ORDER_SLIP = 2; + /** * @see ObjectModel::$definition */ @@ -69,6 +82,8 @@ class OrderReturnCore extends ObjectModel 'refunded_amount' => array('type' => self::TYPE_FLOAT), 'state' => array('type' => self::TYPE_INT), 'by_admin' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true), + 'id_return_type' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), + 'return_type' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), 'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), 'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), ), @@ -232,14 +247,16 @@ class OrderReturnCore extends ObjectModel if (!$context) { $context = Context::getContext(); } - $data = Db::getInstance()->executeS(' - SELECT * - FROM `'._DB_PREFIX_.'order_return` - WHERE `id_customer` = '.(int)$customer_id. - ($only_customer ? ' AND `by_admin` = 0' : ''). - ($order_id ? ' AND `id_order` = '.(int)$order_id : ''). - ($no_denied ? ' AND `state` != 4' : '').' - ORDER BY `date_add` DESC'); + $sql = 'SELECT * + FROM `'._DB_PREFIX_.'order_return` orr + LEFT JOIN `'._DB_PREFIX_.'order_slip` ors + ON (ors.`id_order_slip` = orr.`id_return_type` AND orr.`return_type` = '.(int) self::RETURN_TYPE_ORDER_SLIP.') + WHERE orr.`id_customer` = '.(int)$customer_id. + ($only_customer ? ' AND orr.`by_admin` = 0' : ''). + ($order_id ? ' AND orr.`id_order` = '.(int)$order_id : ''). + ($no_denied ? ' AND orr.`state` != 4' : '').' + ORDER BY orr.`date_add` DESC'; + $data = Db::getInstance()->executeS($sql); foreach ($data as $k => $or) { $state = new OrderReturnState($or['state']); $data[$k]['state_name'] = $state->name[$context->language->id]; @@ -373,144 +390,112 @@ class OrderReturnCore extends ObjectModel /** * Sets the new order return state * @param int $newOrderReturnState - * @param int/object $idOrderReturn */ - public function changeIdOrderReturnState($newOrderReturnState, $idOrderReturn, $idLang = 0) + public function changeIdOrderReturnState($newOrderReturnState, $idLang = 0) { - if (Validate::isLoadedObject($objOrderReturn = new OrderReturn((int)$idOrderReturn))) { - $objOrder = new Order($objOrderReturn->id_order); + $objOrder = new Order($this->id_order); + if (!$idLang) { + $idLang = $objOrder->id_lang; + } + if (Validate::isLoadedObject( + $objOrderReturnState = new OrderReturnState((int)$newOrderReturnState, $idLang) + )) { + if ($this->state != $newOrderReturnState) { + $this->state = $newOrderReturnState; + $this->save(); - if (!$idLang) { - $idLang = $objOrder->id_lang; - } - if (Validate::isLoadedObject( - $objOrderReturnState = new OrderReturnState((int)$newOrderReturnState, $idLang) - )) { - if ($objOrderReturn->state != $newOrderReturnState) { - $objOrderReturn->state = $newOrderReturnState; - $objOrderReturn->save(); + if ($objOrderReturnState->send_email_to_customer + || $objOrderReturnState->send_email_to_superadmin + || $objOrderReturnState->send_email_to_employee + || $objOrderReturnState->send_email_to_hotelier + ) { + // Lets create data for the email templates + $objMail = new Mail(); + $idHotel = 0; - if ($objOrderReturnState->send_email_to_customer - || $objOrderReturnState->send_email_to_superadmin - || $objOrderReturnState->send_email_to_employee - || $objOrderReturnState->send_email_to_hotelier - ) { - // Lets create data for the email templates - $objMail = new Mail(); - $idHotel = 0; + $data = array ( + '{status_name}' => $objOrderReturnState->name, + '{status_color}' => $objOrderReturnState->color, + '{order_reference}' => $objOrder->reference, + '{order_date}' => date('Y-m-d h:i:s', strtotime($objOrder->date_add)), + '{refunded_amount}' => Tools::displayPrice($this->refunded_amount, new Currency($objOrder->id_currency)), + '{payment_mode}' => $this->payment_mode, + '{id_transaction}' => $this->id_transaction, + ); - $data = array ( - '{status_name}' => $objOrderReturnState->name, - '{status_color}' => $objOrderReturnState->color, - '{order_reference}' => $objOrder->reference, - '{order_date}' => date('Y-m-d h:i:s', strtotime($objOrder->date_add)), - '{refunded_amount}' => Tools::displayPrice($objOrderReturn->refunded_amount, new Currency($objOrder->id_currency)), - '{payment_mode}' => $objOrderReturn->payment_mode, - '{id_transaction}' => $objOrderReturn->id_transaction, - ); - - // if mail is true for the customer then send mail to customer with selected template - $objCustomer = new Customer($objOrderReturn->id_customer); - if ($objOrderReturnState->send_email_to_customer && $objOrderReturnState->customer_template) { - if ($refundReqBookings = $objOrderReturn->getOrderRefundRequestedBookings( - $objOrderReturn->id_order, - $objOrderReturn->id, - 0, - 1 - )) { - $idHotel = reset($refundReqBookings)['id_hotel']; - } - - $refBookHtml = $objMail->getEmailTemplateContent('refund_request_detail_customer', Mail::TYPE_HTML, $refundReqBookings); - $refBookTxt = $objMail->getEmailTemplateContent('refund_request_detail_customer', Mail::TYPE_TEXT, $refundReqBookings); - - $data['{refundBookingHtml}'] = $refBookHtml; - $data['{refundBookingTxt}'] = $refBookTxt; - - // send customer information - $link = new Link(); - $data['{refund_reqests_url}'] = $link->getPageLink('order-follow'); - $data['{firstname}'] = $objCustomer->firstname; - $data['{lastname}'] = $objCustomer->lastname; - - Mail::Send( - (int)$idLang, - $objOrderReturnState->customer_template, - $objOrderReturnState->name, - $data, - $objCustomer->email, - $objCustomer->firstname.' '.$objCustomer->lastname, - null, - null, - null, - null, - _PS_MAIL_DIR_, - false, - (int)$objOrder->id_shop - ); + // if mail is true for the customer then send mail to customer with selected template + $objCustomer = new Customer($this->id_customer); + if ($objOrderReturnState->send_email_to_customer && $objOrderReturnState->customer_template) { + if ($refundReqBookings = $this->getOrderRefundRequestedBookings( + $this->id_order, + $this->id, + 0, + 1 + )) { + $idHotel = reset($refundReqBookings)['id_hotel']; } - if ($objOrderReturnState->admin_template) { - if ($refundReqBookings = $objOrderReturn->getOrderRefundRequestedBookings( - $objOrderReturn->id_order, - $objOrderReturn->id - )) { - $idHotel = $refundReqBookings[0]['id_hotel']; - } + $refBookHtml = $objMail->getEmailTemplateContent('refund_request_detail_customer', Mail::TYPE_HTML, $refundReqBookings); + $refBookTxt = $objMail->getEmailTemplateContent('refund_request_detail_customer', Mail::TYPE_TEXT, $refundReqBookings); - $refBookHtml = $objMail->getEmailTemplateContent('refund_request_detail_admin', Mail::TYPE_HTML, $refundReqBookings); - $refBookTxt = $objMail->getEmailTemplateContent('refund_request_detail_admin', Mail::TYPE_TEXT, $refundReqBookings); + $data['{refundBookingHtml}'] = $refBookHtml; + $data['{refundBookingTxt}'] = $refBookTxt; - $data['{cancelation_reason}'] = $objOrderReturn->question; - $data['{refundBookingHtml}'] = $refBookHtml; - $data['{refundBookingTxt}'] = $refBookTxt; + // send customer information + $link = new Link(); + $data['{refund_reqests_url}'] = $link->getPageLink('order-follow'); + $data['{firstname}'] = $objCustomer->firstname; + $data['{lastname}'] = $objCustomer->lastname; - // send mail to the super admin - if ($objOrderReturnState->send_email_to_superadmin) { - // send superadmin information - if (Validate::isLoadedObject($superAdmin = new Employee(_PS_ADMIN_PROFILE_))) { - if (Validate::isEmail($superAdmin->email)) { - $data['{customer_name}'] = $objCustomer->firstname.' '.$objCustomer->lastname; - $data['{customer_email}'] = $objCustomer->email; - $data['{firstname}'] = $superAdmin->firstname; - $data['{lastname}'] = $superAdmin->lastname; + Mail::Send( + (int)$idLang, + $objOrderReturnState->customer_template, + $objOrderReturnState->name, + $data, + $objCustomer->email, + $objCustomer->firstname.' '.$objCustomer->lastname, + null, + null, + null, + null, + _PS_MAIL_DIR_, + false, + (int)$objOrder->id_shop + ); + } - Mail::Send( - (int)$idLang, - $objOrderReturnState->admin_template, - $objOrderReturnState->name, - $data, - $superAdmin->email, - $superAdmin->firstname.' '.$superAdmin->lastname, - null, - null, - null, - null, - _PS_MAIL_DIR_, - false, - (int)$objOrder->id_shop - ); - } - } - } - if ($idHotel - && Validate::isLoadedObject($objHotel = new HotelBranchInformation($idHotel)) - ) { - // send mail to the hotelier - if ($objOrderReturnState->send_email_to_hotelier) { + if ($objOrderReturnState->admin_template) { + if ($refundReqBookings = $this->getOrderRefundRequestedBookings( + $this->id_order, + $this->id + )) { + $idHotel = $refundReqBookings[0]['id_hotel']; + } + + $refBookHtml = $objMail->getEmailTemplateContent('refund_request_detail_admin', Mail::TYPE_HTML, $refundReqBookings); + $refBookTxt = $objMail->getEmailTemplateContent('refund_request_detail_admin', Mail::TYPE_TEXT, $refundReqBookings); + + $data['{cancelation_reason}'] = $this->question; + $data['{refundBookingHtml}'] = $refBookHtml; + $data['{refundBookingTxt}'] = $refBookTxt; + + // send mail to the super admin + if ($objOrderReturnState->send_email_to_superadmin) { + // send superadmin information + if (Validate::isLoadedObject($superAdmin = new Employee(_PS_ADMIN_PROFILE_))) { + if (Validate::isEmail($superAdmin->email)) { $data['{customer_name}'] = $objCustomer->firstname.' '.$objCustomer->lastname; $data['{customer_email}'] = $objCustomer->email; - $data['{firstname}'] = ''; - $data['{lastname}'] = ''; - $data['{email}'] = $objHotel->email; + $data['{firstname}'] = $superAdmin->firstname; + $data['{lastname}'] = $superAdmin->lastname; Mail::Send( (int)$idLang, $objOrderReturnState->admin_template, $objOrderReturnState->name, $data, - $objHotel->email, - null, + $superAdmin->email, + $superAdmin->firstname.' '.$superAdmin->lastname, null, null, null, @@ -520,38 +505,66 @@ class OrderReturnCore extends ObjectModel (int)$objOrder->id_shop ); } + } + } + if ($idHotel + && Validate::isLoadedObject($objHotel = new HotelBranchInformation($idHotel)) + ) { + // send mail to the hotelier + if ($objOrderReturnState->send_email_to_hotelier) { + $data['{customer_name}'] = $objCustomer->firstname.' '.$objCustomer->lastname; + $data['{customer_email}'] = $objCustomer->email; + $data['{firstname}'] = ''; + $data['{lastname}'] = ''; + $data['{email}'] = $objHotel->email; - // send mail to the employee - if ($objOrderReturnState->send_email_to_employee) { - if ($htlAccesses = $objHotel->getHotelAccess($idHotel)) { - $data['{customer_name}'] = $objCustomer->firstname.' '.$objCustomer->lastname; - $data['{customer_email}'] = $objCustomer->email; - foreach ($htlAccesses as $access) { - if ($access['id_profile'] != _PS_ADMIN_PROFILE_) { - if ($htlEmployees = Employee::getEmployeesByProfile($access['id_profile'])) { - foreach ($htlEmployees as $empl) { - if (Validate::isEmail($empl['email'])) { - // send hotel manager (employee) have permission for this hotel - $data['{firstname}'] = $empl['firstname']; - $data['{lastname}'] = $empl['lastname']; - $data['{email}'] = $empl['email']; + Mail::Send( + (int)$idLang, + $objOrderReturnState->admin_template, + $objOrderReturnState->name, + $data, + $objHotel->email, + null, + null, + null, + null, + null, + _PS_MAIL_DIR_, + false, + (int)$objOrder->id_shop + ); + } - Mail::Send( - (int)$idLang, - $objOrderReturnState->admin_template, - $objOrderReturnState->name, - $data, - $empl['email'], - $empl['firstname'].' '.$empl['lastname'], - null, - null, - null, - null, - _PS_MAIL_DIR_, - false, - (int)$objOrder->id_shop - ); - } + // send mail to the employee + if ($objOrderReturnState->send_email_to_employee) { + if ($htlAccesses = $objHotel->getHotelAccess($idHotel)) { + $data['{customer_name}'] = $objCustomer->firstname.' '.$objCustomer->lastname; + $data['{customer_email}'] = $objCustomer->email; + foreach ($htlAccesses as $access) { + if ($access['id_profile'] != _PS_ADMIN_PROFILE_) { + if ($htlEmployees = Employee::getEmployeesByProfile($access['id_profile'])) { + foreach ($htlEmployees as $empl) { + if (Validate::isEmail($empl['email'])) { + // send hotel manager (employee) have permission for this hotel + $data['{firstname}'] = $empl['firstname']; + $data['{lastname}'] = $empl['lastname']; + $data['{email}'] = $empl['email']; + + Mail::Send( + (int)$idLang, + $objOrderReturnState->admin_template, + $objOrderReturnState->name, + $data, + $empl['email'], + $empl['firstname'].' '.$empl['lastname'], + null, + null, + null, + null, + _PS_MAIL_DIR_, + false, + (int)$objOrder->id_shop + ); } } } @@ -561,18 +574,21 @@ class OrderReturnCore extends ObjectModel } } } - - // executes hook - Hook::exec( - 'actionOrderReFundStatusPostUpdate', - array( - 'new_order_return_status' => $newOrderReturnState, - 'id_order_return' => (int)$idOrderReturn - ) - ); } + + // executes hook + Hook::exec( + 'actionOrderReFundStatusPostUpdate', + array( + 'new_order_return_status' => $newOrderReturnState, + 'id_order_return' => (int) $this->id_order_return, + ) + ); + + return true; } } + return false; } diff --git a/classes/order/OrderSlip.php b/classes/order/OrderSlip.php index f64269d75..b7b89bc49 100644 --- a/classes/order/OrderSlip.php +++ b/classes/order/OrderSlip.php @@ -27,7 +27,7 @@ class OrderSlipCore extends ObjectModel { /** @var int */ - public $id; + public $id_order_slip; /** @var int */ public $id_customer; @@ -62,6 +62,12 @@ class OrderSlipCore extends ObjectModel /** @var int */ public $partial; + /** @var int */ + public $redeem_status = self::REDEEM_STATUS_ACTIVE; + + /** @var int */ + public $id_cart_rule; + /** @var string Object creation date */ public $date_add; @@ -71,6 +77,9 @@ class OrderSlipCore extends ObjectModel /** @var int */ public $order_slip_type = 0; + const REDEEM_STATUS_ACTIVE = 1; + const REDEEM_STATUS_REDEEMED = 2; + /** * @see ObjectModel::$definition */ @@ -90,6 +99,8 @@ class OrderSlipCore extends ObjectModel 'shipping_cost_amount' => array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat'), 'amount' => array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat'), 'partial' => array('type' => self::TYPE_INT), + 'redeem_status' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), + 'id_cart_rule' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), 'date_add' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), 'date_upd' => array('type' => self::TYPE_DATE, 'validate' => 'isDate'), 'order_slip_type' => array('type' => self::TYPE_INT, 'validate' => 'isInt'), @@ -336,6 +347,7 @@ class OrderSlipCore extends ObjectModel $order_slip->{'total_products_tax_'.$inc_or_ex_1} = 0; $order_slip->{'total_products_tax_'.$inc_or_ex_2} = 0; + $total_products = array(); foreach ($booking_list as &$booking) { $objHtlBooking = new HotelBookingDetail($booking['id_htl_booking']); $order_detail = new OrderDetail((int)$objHtlBooking->id_order_detail); @@ -543,6 +555,95 @@ class OrderSlipCore extends ObjectModel return $ecotax_detail; } + /** + * generate voucher and return id of the created rule if successful, false otherwise + */ + public function generateVoucher($sendMail = true) + { + if (!Validate::isLoadedObject($this)) { + return false; + } + + if ($this->redeem_status != self::REDEEM_STATUS_ACTIVE) { + return false; + } + + $objOrder = new Order($this->id_order); + if (!Validate::isLoadedObject($objOrder)) { + return false; + } + + $objCustomer = new Customer($this->id_customer); + if (!Validate::isLoadedObject($objCustomer)) { + return false; + } + + $context = Context::getContext(); + $objCartRule = new CartRule(); + $languages = Language::getLanguages(); + + $creditSlipPrefix = Configuration::get('PS_CREDIT_SLIP_PREFIX', $context->language->id); + $creditSlipID = sprintf(('%1$s%2$06d'), $creditSlipPrefix, (int) $this->id_order_slip); + + $dateFrom = $this->date_add; + $dateTo = date('Y-m-d H:i:s', strtotime($dateFrom) + (3600 * 24 * 365.25)); /* 1 year */ + + foreach ($languages as $language) { + $objCartRule->name[$language['id_lang']] = sprintf('Voucher for credit slip #%s', $creditSlipID); + } + + $objCartRule->description = sprintf('Order: #%s', $objOrder->id); + $objCartRule->code = 'CS'.(int) ($this->id_order_slip).'C'.(int) ($objCustomer->id).'O'.(int) ($objOrder->id); + $objCartRule->quantity = 1; + $objCartRule->quantity_per_user = 1; + $objCartRule->id_customer = $objCustomer->id; + $objCartRule->date_from = $dateFrom; + $objCartRule->date_to = $dateTo; + $objCartRule->highlight = 1; + $objCartRule->active = 1; + $objCartRule->reduction_amount = $this->amount; + $objCartRule->reduction_tax = true; + $objCartRule->minimum_amount_currency = $objOrder->id_currency; + $objCartRule->reduction_currency = $objOrder->id_currency; + + if (!$objCartRule->save()) { + return false; + } + + $this->redeem_status = self::REDEEM_STATUS_REDEEMED; + $this->id_cart_rule = (int) $objCartRule->id; + $this->save(); + + if ($sendMail) { + $objCartRule = new CartRule($this->id_cart_rule); + $objCustomer = new Customer($objCartRule->id_customer); + + $objCurrency = new Currency($objCartRule->reduction_currency, $context->language->id); + $mailVars['{firstname}'] = $objCustomer->firstname; + $mailVars['{lastname}'] = $objCustomer->lastname; + $mailVars['{credit_slip_id}'] = $creditSlipID; + $mailVars['{voucher_code}'] = $objCartRule->code; + $mailVars['{voucher_amount}'] = Tools::displayPrice($objCartRule->reduction_amount, $objCurrency, false); + + Mail::Send( + $context->language->id, + 'credit_slip_voucher', + sprintf(Mail::l('New voucher for your credit slip #%s', $context->language->id), $creditSlipID), + $mailVars, + $objCustomer->email, + $objCustomer->firstname.' '.$objCustomer->lastname, + null, + null, + null, + null, + _PS_MAIL_DIR_, + true + ); + } + + return $this->id_cart_rule; + } + public function getWsOrderSlipDetails() { $query = 'SELECT id_order_slip as id, id_order_detail, product_quantity, amount_tax_excl, amount_tax_incl diff --git a/classes/pdf/HTMLTemplateInvoice.php b/classes/pdf/HTMLTemplateInvoice.php index 0e24e7f91..8d13c291e 100644 --- a/classes/pdf/HTMLTemplateInvoice.php +++ b/classes/pdf/HTMLTemplateInvoice.php @@ -352,8 +352,6 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate $cart_htl_data[$type_key]['id_product'] = $type_value['product_id']; $cart_htl_data[$type_key]['cover_img'] = $cover_img; - $cart_htl_data[$type_key]['adult'] = $rm_dtl['adult']; - $cart_htl_data[$type_key]['children'] = $rm_dtl['children']; $cart_htl_data[$type_key]['hotel_name'] = $rm_dtl['hotel_name']; $objBookingDemand = new HotelBookingDemands(); foreach ($order_bk_data as $data_k => $data_v) { @@ -400,6 +398,8 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate ); $cart_htl_data[$type_key]['date_diff'][$date_join]['num_rm'] += 1; + $cart_htl_data[$type_key]['date_diff'][$date_join]['adults'] += $data_v['adults']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['children'] += $data_v['children']; $num_days = $cart_htl_data[$type_key]['date_diff'][$date_join]['num_days']; @@ -446,6 +446,9 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate $cart_htl_data[$type_key]['date_diff'][$date_join]['data_form'] = Tools::displayDate($data_v['date_from']); $cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = Tools::displayDate($data_v['date_to']); $cart_htl_data[$type_key]['date_diff'][$date_join]['num_days'] = $num_days; + $cart_htl_data[$type_key]['date_diff'][$date_join]['adults'] = $data_v['adults']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['children'] = $data_v['children']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_excl']/$num_days; diff --git a/classes/webservice/WebserviceOutputJSON.php b/classes/webservice/WebserviceOutputJSON.php index f1e8cf7c5..0e2da03c4 100644 --- a/classes/webservice/WebserviceOutputJSON.php +++ b/classes/webservice/WebserviceOutputJSON.php @@ -40,7 +40,7 @@ class WebserviceOutputJSON implements WebserviceOutputInterface /** * Current association */ - protected $currentAssociatedEntity; + protected $currentAssociatedEntity = array(); /** * Json content diff --git a/config/config.inc.php b/config/config.inc.php index defb8d3a1..b8ef2014b 100644 --- a/config/config.inc.php +++ b/config/config.inc.php @@ -161,10 +161,10 @@ if ($cookie_lifetime > 0) { $cookie_lifetime = time() + (max($cookie_lifetime, 1) * 3600); } +$force_ssl = Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE'); if (defined('_PS_ADMIN_DIR_')) { - $cookie = new Cookie('psAdmin', '', $cookie_lifetime); + $cookie = new Cookie('psAdmin', '', $cookie_lifetime, null, false, $force_ssl); } else { - $force_ssl = Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE'); if ($context->shop->getGroup()->share_order) { $cookie = new Cookie('ps-sg'.$context->shop->getGroup()->id, '', $cookie_lifetime, $context->shop->getUrlsSharedCart(), false, $force_ssl); } else { diff --git a/config/xml/themes/default.xml b/config/xml/themes/default.xml index 98ef61c2d..72ce2aae3 100644 --- a/config/xml/themes/default.xml +++ b/config/xml/themes/default.xml @@ -65,7 +65,6 @@ - @@ -77,8 +76,6 @@ - - @@ -154,14 +151,15 @@ - + + - + - + diff --git a/controllers/admin/AdminAdminPreferencesController.php b/controllers/admin/AdminAdminPreferencesController.php index 9e3124ecb..8179c1f26 100644 --- a/controllers/admin/AdminAdminPreferencesController.php +++ b/controllers/admin/AdminAdminPreferencesController.php @@ -85,6 +85,16 @@ class AdminAdminPreferencesControllerCore extends AdminController 'default' => '480', 'visibility' => Shop::CONTEXT_ALL ), + 'PS_COOKIE_SAMESITE' => array( + 'title' => $this->l('Same site cookie'), + 'hint' => $this->l('Allows you to declare if your cookie should be restricted to a first-party or same-site context.'), + 'validation' => 'isGenericName', + 'type' => 'select', + 'cast' => 'strval', + 'identifier' => 'type', + 'list' => CookieCore::SAMESITE_AVAILABLE_VALUES, + 'visibility' => Shop::CONTEXT_ALL + ), ), 'submit' => array('title' => $this->l('Save')) ), @@ -159,6 +169,12 @@ class AdminAdminPreferencesControllerCore extends AdminController $upload_max_size = (int)str_replace('M', '', ini_get('upload_max_filesize')); $post_max_size = (int)str_replace('M', '', ini_get('post_max_size')); $max_size = $upload_max_size < $post_max_size ? $upload_max_size : $post_max_size; + $forceSsl = Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE'); + + if (!$forceSsl && Tools::getValue('PS_COOKIE_SAMESITE') === Cookie::SAMESITE_NONE) { + $this->errors[] = Tools::displayError('The SameSite=None is only available in secure mode.'); + return; + } if (Tools::getValue('PS_LIMIT_UPLOAD_FILE_VALUE') > $max_size || Tools::getValue('PS_LIMIT_UPLOAD_IMAGE_VALUE') > $max_size) { $this->errors[] = Tools::displayError('The limit chosen is larger than the server\'s maximum upload limit. Please increase the limits of your server.'); diff --git a/controllers/admin/AdminCartsController.php b/controllers/admin/AdminCartsController.php index 5f5b3b82c..31e471fc8 100644 --- a/controllers/admin/AdminCartsController.php +++ b/controllers/admin/AdminCartsController.php @@ -592,7 +592,7 @@ class AdminCartsControllerCore extends AdminController $id_cart = Tools::getValue('id_cart');//get cart id from url $cart_detail_data = array(); $cart_detail_data_obj = new HotelCartBookingData(); - $update_htl_cart_currency = $cart_detail_data_obj->updateIdCurrencyByIdCart($id_cart, $currency->id); + $cart_detail_data_obj->updateIdCurrencyByIdCart($id_cart, $currency->id); $cart_detail_data = $cart_detail_data_obj->getCartFormatedBookinInfoByIdCart((int) $id_cart); $this->context->smarty->assign(array( 'cart_detail_data' => $cart_detail_data, @@ -951,6 +951,79 @@ class AdminCartsControllerCore extends AdminController } } + public function ajaxProcessupdateRoomOccupancy() + { + $params = Tools::getValue('params'); + $idBookingData = (int) $params['id_booking_data']; + $idCart = (int) $params['id_cart']; + $occupancy = $params['occupancy']; + + $this->context->cart = new Cart($idCart); + + if ($this->tabAccess['edit'] === '1') { + // validate occupancy is correct + $hasError = false; + if (is_array($occupancy)) { + if (!$occupancy['adults'] || !Validate::isUnsignedInt($occupancy['adults'])) { + $hasError = true; + } + if (!Validate::isUnsignedInt($occupancy['children'])) { + $hasError = true; + } + if ($occupancy['children']) { + if (!isset($occupancy['child_ages']) || $occupancy['children'] != count($occupancy['child_ages'])) { + $hasError = true; + } else { + foreach($occupancy['child_ages'] as $age) { + if (!Validate::isUnsignedInt($occupancy['adults'])) { + $hasError = true; + } + } + } + } + } else { + $hasError = true; + } + + if (!$hasError) { + $objHotelCartBookingData = new HotelCartBookingData($idBookingData); + $objRoomType = new HotelRoomType(); + if ($roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($objHotelCartBookingData->id_product)) { + if ($occupancy['adults'] > $roomTypeInfo['max_adults']) { + $hasError = true; + } + if ($occupancy['children'] > $roomTypeInfo['max_children']) { + $hasError = true; + } + if ($occupancy['adults'] + $occupancy['children'] > $roomTypeInfo['max_guests']) { + $hasError = true; + } + if (!$hasError) { + $objHotelCartBookingData->adults = $occupancy['adults']; + $objHotelCartBookingData->children = $occupancy['children']; + $objHotelCartBookingData->child_ages = json_encode($occupancy['child_ages']); + $objHotelCartBookingData->save(); + } + } + } + $objHotelCartBookingData = new HotelCartBookingData(); + $bookingsInfo = $objHotelCartBookingData->getCartFormatedBookinInfoByIdCart($idCart); + foreach ($bookingsInfo as &$bookingInfo) { + if ($bookingInfo['id'] == $idBookingData) { + $amtWithQty = $bookingInfo['amt_with_qty']; + $bookingInfo['amt_with_qty'] = Tools::displayPrice($amtWithQty); + $bookingInfo['total_price'] = Tools::displayPrice($amtWithQty + $bookingInfo['demand_price']); + $response = array( + 'curr_booking_info' => $bookingInfo, + 'cart_info' => $this->ajaxReturnVars(), + ); + + die(json_encode($response)); + } + } + } + } + // Process to get extra demands of any room while order creation process form.tpl public function ajaxProcessGetRoomTypeCartDemands() { diff --git a/controllers/admin/AdminOrdersController.php b/controllers/admin/AdminOrdersController.php index b957fe98a..e261e9ae3 100644 --- a/controllers/admin/AdminOrdersController.php +++ b/controllers/admin/AdminOrdersController.php @@ -219,6 +219,11 @@ class AdminOrdersControllerCore extends AdminController $cart_detail_data = array(); $cart_detail_data_obj = new HotelCartBookingData(); if ($cart_detail_data = $cart_detail_data_obj->getCartFormatedBookinInfoByIdCart((int) $id_cart)) { + $objRoomType = new HotelRoomType(); + foreach ($cart_detail_data as $key => $cart_data) { + + $cart_detail_data[$key]['room_type_info'] = $objRoomType->getRoomTypeInfoByIdProduct($cart_data['id_product']); + } $this->context->smarty->assign('cart_detail_data', $cart_detail_data); } else { // if no rooms added in the cart and user visits add order page then redirect to BOOK NOW page @@ -240,6 +245,11 @@ class AdminOrdersControllerCore extends AdminController $payment_modules[] = Module::getInstanceById((int)$p_module['id_module']); } + $occupancyRequiredForBooking = false; + if (Configuration::get('PS_BACKOFFICE_ROOM_BOOKING_TYPE') == HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY) { + $occupancyRequiredForBooking = true; + } + $this->context->smarty->assign(array( 'recyclable_pack' => (int)Configuration::get('PS_RECYCLABLE_PACK'), 'gift_wrapping' => (int)Configuration::get('PS_GIFT_WRAPPING'), @@ -253,8 +263,10 @@ class AdminOrdersControllerCore extends AdminController 'toolbar_btn' => $this->toolbar_btn, 'toolbar_scroll' => $this->toolbar_scroll, 'PS_CATALOG_MODE' => Configuration::get('PS_CATALOG_MODE'), - 'title' => array($this->l('Orders'), $this->l('Create order')) - + 'title' => array($this->l('Orders'), $this->l('Create order')), + 'max_child_in_room' => Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM'), + 'max_child_age' => Configuration::get('WK_GLOBAL_CHILD_MAX_AGE'), + 'occupancy_required_for_booking' => $occupancyRequiredForBooking, )); $this->content .= $this->createTemplate('form.tpl')->fetch(); } @@ -454,8 +466,8 @@ class AdminOrdersControllerCore extends AdminController } if (!count($this->errors)) { - $obj_booking_dtl = new HotelBookingDetail(); - $room_swapped = $obj_booking_dtl->reallocateRoomWithAvailableSameRoomType($current_room_id, $date_from, $date_to, $realloc_room_id); + $objBookingDetail = new HotelBookingDetail(); + $room_swapped = $objBookingDetail->reallocateRoomWithAvailableSameRoomType($current_room_id, $date_from, $date_to, $realloc_room_id); if (!$room_swapped) { $this->errors[] = Tools::displayError('Some error occured. Please try again.'); } else { @@ -489,8 +501,8 @@ class AdminOrdersControllerCore extends AdminController } if (!count($this->errors)) { - $obj_booking_dtl = new HotelBookingDetail(); - $room_swapped = $obj_booking_dtl->swapRoomWithAvailableSameRoomType($current_room_id, $date_from, $date_to, $swapped_room_id); + $objBookingDetail = new HotelBookingDetail(); + $room_swapped = $objBookingDetail->swapRoomWithAvailableSameRoomType($current_room_id, $date_from, $date_to, $swapped_room_id); if (!$room_swapped) { $this->errors[] = Tools::displayError('Some error occured. Please try again.'); } else { @@ -746,7 +758,7 @@ class AdminOrdersControllerCore extends AdminController $objOrderReturn = new OrderReturn(); $objOrderReturn->id_customer = $order->id_customer; $objOrderReturn->id_order = $order->id; - $objOrderReturn->state = 1; + $objOrderReturn->state = Configuration::get('PS_ORS_PENDING'); $objOrderReturn->by_admin = 1; $objOrderReturn->question = $refundReason; $objOrderReturn->save(); @@ -879,13 +891,14 @@ class AdminOrdersControllerCore extends AdminController false, $cart->secure_key ); - /* - setcookie('wk_id_cart', ' ', time() - 86400, '/'); - setcookie('wk_id_guest', ' ', time() - 86400, '/'); - unset($_COOKIE['wk_id_cart']); - unset($_COOKIE['wk_id_guest']); - */ + if (isset($this->context->cookie->id_cart)) { + unset($this->context->cookie->id_cart); + } + if (isset($this->context->cookie->id_guest)) { + unset($this->context->cookie->id_guest); + } + if ($payment_module->currentOrder) { Tools::redirectAdmin(self::$currentIndex.'&id_order='.$payment_module->currentOrder.'&vieworder'.'&token='.$this->token.'&conf=3'); } @@ -1459,6 +1472,7 @@ class AdminOrdersControllerCore extends AdminController $totalDemandsPriceTI = 0; if ($order_detail_data = $objBookingDetail->getOrderFormatedBookinInfoByIdOrder($order->id)) { $objBookingDemand = new HotelBookingDemands(); + $objHotelRoomType = new HotelRoomType(); foreach ($order_detail_data as $key => $value) { $order_detail_data[$key]['extra_demands'] = $objBookingDemand->getRoomTypeBookingExtraDemands( $order->id, @@ -1512,6 +1526,8 @@ class AdminOrdersControllerCore extends AdminController $order_detail_data[$key]['unit_amt_tax_incl'] = $value['total_price_tax_incl']/$num_days; $order_detail_data[$key]['amt_with_qty_tax_excl'] = $value['total_price_tax_excl']; $order_detail_data[$key]['amt_with_qty_tax_incl'] = $value['total_price_tax_incl']; + $order_detail_data[$key]['room_type_info'] = $objHotelRoomType->getRoomTypeInfoByIdProduct($value['id_product']); + } } @@ -1527,7 +1543,6 @@ class AdminOrdersControllerCore extends AdminController // hotel booking statuses $htlOrderStatus = HotelBookingDetail::getAllHotelOrderStatus(); - $this->tpl_view_vars = array( // refund info 'refund_allowed' => (int) $order->isReturnable(), @@ -1543,6 +1558,8 @@ class AdminOrdersControllerCore extends AdminController 'htl_booking_order_data' => $bookingOrderInfo, 'hotel_order_status' => $htlOrderStatus, 'order_detail_data' => $order_detail_data, + 'max_child_in_room' => Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM'), + 'max_child_age' => Configuration::get('WK_GLOBAL_CHILD_MAX_AGE'), /*END*/ 'order' => $order, 'cart' => new Cart($order->id_cart), @@ -1678,7 +1695,13 @@ class AdminOrdersControllerCore extends AdminController } if ($products = Product::searchByName((int)$this->context->language->id, pSQL(Tools::getValue('product_search')), null, $idHotel)) { + $objRoomType = new HotelRoomType(); foreach ($products as &$product) { + // get product room type informatin + if ($roomTypeDetail = $objRoomType->getRoomTypeInfoByIdProduct($product['id_product'])) { + $product['room_type_info'] = $roomTypeDetail; + } + // Formatted price $product['formatted_price'] = Tools::displayPrice(Tools::convertPrice($product['price_tax_incl'], $currency), $currency); // Concret price @@ -1811,6 +1834,7 @@ class AdminOrdersControllerCore extends AdminController $date_from = date('Y-m-d', strtotime($product_informations['date_from'])); $date_to = date('Y-m-d', strtotime($product_informations['date_to'])); $curr_date = date('Y-m-d'); + $occupancy = Tools::getValue('occupancy'); /*Validations*/ if ($date_from == '') { die(json_encode(array( @@ -1842,6 +1866,45 @@ class AdminOrdersControllerCore extends AdminController 'result' => false, 'error' => Tools::displayError('Check out Date Should be after Check In date.'), ))); + } + if ($order->with_occupancy) { + if ($occupancy) { + foreach($occupancy as $key =>$roomOccupancy) { + if (!$roomOccupancy['adults'] || !Validate::isUnsignedInt($roomOccupancy['adults'])) { + die(json_encode(array( + 'result' => false, + 'error' => sprintf(Tools::displayError('Invalid number of adults for Room %s.'), ($key + 1)), + ))); + } elseif (!Validate::isUnsignedInt($roomOccupancy['children'])) { + die(json_encode(array( + 'result' => false, + 'error' => sprintf(Tools::displayError('Invalid number of children for Room %s.'), ($key + 1)), + ))); + } + if ($roomOccupancy['children'] > 0) { + if (!isset($roomOccupancy['child_ages']) || ($roomOccupancy['children'] != count($roomOccupancy['child_ages']))) { + die(json_encode(array( + 'result' => false, + 'error' => sprintf(Tools::displayError('Please provide all children age for Room %s.'), ($key + 1)), + ))); + } else { + foreach($roomOccupancy['child_ages'] as $childAge) { + if (!Validate::isUnsignedInt($childAge)) { + die(json_encode(array( + 'result' => false, + 'error' => sprintf(Tools::displayError('Invalid children age for Room %s.'), ($key + 1)), + ))); + } + } + } + } + } + } else { + die(json_encode(array( + 'result' => false, + 'error' => Tools::displayError('Invalid occupancy.'), + ))); + } } elseif (!Validate::isUnsignedInt($product_informations['product_quantity'])) { die(json_encode(array( 'result' => false, @@ -1849,8 +1912,13 @@ class AdminOrdersControllerCore extends AdminController ))); } + + if ($order->with_occupancy) { + $req_rm = count($occupancy); + } else { + $req_rm = $product_informations['product_quantity']; + } $obj_booking_detail = new HotelBookingDetail(); - $req_rm = $product_informations['product_quantity']; $num_days = $obj_booking_detail->getNumberOfDays($date_from, $date_to); $product_informations['product_quantity'] = $product_informations['product_quantity'] * (int) $num_days; @@ -1861,9 +1929,17 @@ class AdminOrdersControllerCore extends AdminController $id_hotel = $room_info_by_id_product['id_hotel']; if ($id_hotel) { - $obj_booking_dtl = new HotelBookingDetail(); - $hotel_room_data = $obj_booking_dtl->DataForFrontSearch($date_from, $date_to, $id_hotel, $product_informations['product_id'], 1, 0, 0, -1, 0, 0, $id_cart, $id_guest); - + $objBookingDetail = new HotelBookingDetail(); + $bookingParams = array( + 'date_from' => $date_from, + 'date_to' => $date_to, + 'hotel_id' => $id_hotel, + 'id_room_type' => $product_informations['product_id'], + 'only_search_data' => 1, + 'id_cart' => $id_cart, + 'id_guest' => $id_guest, + ); + $hotel_room_data = $objBookingDetail->dataForFrontSearch($bookingParams); $total_available_rooms = $hotel_room_data['stats']['num_avail']; if ($total_available_rooms < $req_rm) { @@ -1934,54 +2010,40 @@ class AdminOrdersControllerCore extends AdminController $this->context->cart = $cart; $this->context->customer = new Customer($order->id_customer); - // always add taxes even if there are not displayed to the customer - $use_taxes = true; - $initial_product_price_tax_incl = Product::getPriceStatic( - $product->id, - $use_taxes, - isset($combination) ? $combination->id : null, - 2, - null, - false, - true, - 1, - false, - $order->id_customer, - $cart->id, - $order->{Configuration::get('PS_TAX_ADDRESS_TYPE', null, null, $order->id_shop)} - ); - - // create feature price if needed - $createFeaturePrice = $product_informations['product_price_tax_incl'] != $initial_product_price_tax_incl; - $featurePriceParams = array(); - if ($createFeaturePrice) { - $featurePriceParams = array( - 'id_cart' => $this->context->cart->id, - 'id_guest' => $this->context->cookie->id_guest, - 'price' => $product_informations['product_price_tax_excl'], - 'id_product' => $product->id, - ); + if ($order->with_occupancy) { + $objRoomType = new HotelRoomType(); + $roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($id_product); } - /*By Webkul to make entries in HotelCartBookingData */ - $hotel_room_info_arr = $hotel_room_data['rm_data'][0]['data']['available']; + $hotel_room_info_arr = $hotel_room_data['rm_data'][$idProduct]['data']['available']; $chkQty = 0; if ($hotel_room_info_arr) { - foreach ($hotel_room_info_arr as $room_info) { + foreach ($hotel_room_info_arr as $key => $room_info) { if ($chkQty < $req_rm) { - $obj_htl_cart_booking_data = new HotelCartBookingData(); - $obj_htl_cart_booking_data->id_cart = $this->context->cart->id; - $obj_htl_cart_booking_data->id_guest = $this->context->cookie->id_guest; - $obj_htl_cart_booking_data->id_customer = $this->context->customer->id; - $obj_htl_cart_booking_data->id_currency = $order->id_currency; - $obj_htl_cart_booking_data->id_product = $room_info['id_product']; - $obj_htl_cart_booking_data->id_room = $room_info['id_room']; - $obj_htl_cart_booking_data->id_hotel = $room_info['id_hotel']; - $obj_htl_cart_booking_data->booking_type = HotelBookingDetail::ALLOTMENT_AUTO; - $obj_htl_cart_booking_data->quantity = $num_days; - $obj_htl_cart_booking_data->date_from = $date_from; - $obj_htl_cart_booking_data->date_to = $date_to; - $obj_htl_cart_booking_data->save(); + $objCartBookingData = new HotelCartBookingData(); + $objCartBookingData->id_cart = $this->context->cart->id; + $objCartBookingData->id_guest = $this->context->cookie->id_guest; + $objCartBookingData->id_customer = $this->context->customer->id; + $objCartBookingData->id_currency = $order->id_currency; + $objCartBookingData->id_product = $room_info['id_product']; + $objCartBookingData->id_room = $room_info['id_room']; + $objCartBookingData->id_hotel = $room_info['id_hotel']; + $objCartBookingData->booking_type = HotelBookingDetail::ALLOTMENT_AUTO; + $objCartBookingData->quantity = $num_days; + $objCartBookingData->date_from = $date_from; + $objCartBookingData->date_to = $date_to; + + if ($order->with_occupancy) { + $room_occupancy = array_shift($occupancy); + $objCartBookingData->adults = $room_occupancy['adults']; + $objCartBookingData->children = $room_occupancy['children']; + $objCartBookingData->child_ages = $room_occupancy['children'] ? json_encode($room_occupancy['child_ages']) : json_encode(array()); + } else { + $objCartBookingData->adults = $roomTypeInfo['adults']; + $objCartBookingData->children = $roomTypeInfo['children']; + $objCartBookingData->child_ages = json_encode(array()); + } + $objCartBookingData->save(); ++$chkQty; // create feature price if needed @@ -1996,10 +2058,52 @@ class AdminOrdersControllerCore extends AdminController } } /*END*/ + // always add taxes even if there are not displayed to the customer + $use_taxes = true; + + $initial_product_price_tax_incl = Product::getPriceStatic( + $product->id, + $use_taxes, + isset($combination) ? $combination->id : null, + 2, + null, + false, + true, + 1, + false, + $order->id_customer, + $cart->id, + $order->id_address_tax + ); + + // Creating specific price if needed + if ($product_informations['product_price_tax_incl'] != $initial_product_price_tax_incl) { + $specific_price = new SpecificPrice(); + $specific_price->id_shop = 0; + $specific_price->id_shop_group = 0; + $specific_price->id_currency = 0; + $specific_price->id_country = 0; + $specific_price->id_group = 0; + $specific_price->id_customer = $order->id_customer; + $specific_price->id_product = $product->id; + if (isset($combination)) { + $specific_price->id_product_attribute = $combination->id; + } else { + $specific_price->id_product_attribute = 0; + } + $specific_price->price = $product_informations['product_price_tax_excl']; + $specific_price->from_quantity = 1; + $specific_price->reduction = 0; + $specific_price->reduction_type = 'amount'; + $specific_price->reduction_tax = 0; + $specific_price->from = '0000-00-00 00:00:00'; + $specific_price->to = '0000-00-00 00:00:00'; + $specific_price->add(); + } // Add product to cart $update_quantity = $cart->updateQty( - $product_informations['product_quantity'], + ($req_rm * $num_days), $product->id, isset($product_informations['product_attribute_id']) ? $product_informations['product_attribute_id'] : null, isset($combination) ? $combination->id : null, @@ -2226,76 +2330,79 @@ class AdminOrdersControllerCore extends AdminController /*By Webkul Entry into table HotelbookingDetail*/ $objRoomType = new HotelRoomType(); - $objHtlBkDtl = new HotelBookingDetail(); - $inserted_id_order_detail = $objHtlBkDtl->getLastInsertedIdOrderDetail($order->id); + $objBookingDetail = new HotelBookingDetail(); + $inserted_id_order_detail = $objBookingDetail->getLastInsertedIdOrderDetail($order->id); $idLang = (int)$this->context->cart->id_lang; - $obj_cart_bk_data = new HotelCartBookingData(); - if ($cart_bk_data = $obj_cart_bk_data->getOnlyCartBookingData( + $objCartBookingData = new HotelCartBookingData(); + if ($cartBookingData = $objCartBookingData->getOnlyCartBookingData( $this->context->cart->id, $this->context->cart->id_guest, $idProduct )) { - foreach ($cart_bk_data as $cb_k => $cb_v) { - $obj_cart_bk_data = new HotelCartBookingData($cb_v['id']); - $obj_cart_bk_data->id_order = $order->id; - $obj_cart_bk_data->save(); + foreach ($cartBookingData as $cb_k => $cb_v) { + $objCartBookingData = new HotelCartBookingData($cb_v['id']); + $objCartBookingData->id_order = $order->id; + $objCartBookingData->save(); - $objHtlBkDtl = new HotelBookingDetail(); - $objHtlBkDtl->id_product = $idProduct; - $objHtlBkDtl->id_order = $order->id; - $objHtlBkDtl->id_order_detail = $inserted_id_order_detail; - $objHtlBkDtl->id_cart = $this->context->cart->id; - $objHtlBkDtl->id_room = $obj_cart_bk_data->id_room; - $objHtlBkDtl->id_hotel = $obj_cart_bk_data->id_hotel; - $objHtlBkDtl->id_customer = $this->context->customer->id; - $objHtlBkDtl->booking_type = $obj_cart_bk_data->booking_type; - $objHtlBkDtl->id_status = 1; - $objHtlBkDtl->comment = $obj_cart_bk_data->comment; - $objHtlBkDtl->room_type_name = Product::getProductName($idProduct, null, $order->id_lang); + $objBookingDetail = new HotelBookingDetail(); + $objBookingDetail->id_product = $idProduct; + $objBookingDetail->id_order = $order->id; + $objBookingDetail->id_order_detail = $inserted_id_order_detail; + $objBookingDetail->id_cart = $this->context->cart->id; + $objBookingDetail->id_room = $objCartBookingData->id_room; + $objBookingDetail->id_hotel = $objCartBookingData->id_hotel; + $objBookingDetail->id_customer = $this->context->customer->id; + $objBookingDetail->booking_type = $objCartBookingData->booking_type; + $objBookingDetail->id_status = 1; + $objBookingDetail->comment = $objCartBookingData->comment; + $objBookingDetail->room_type_name = Product::getProductName($idProduct, null, $order->id_lang); - $objHtlBkDtl->date_from = $obj_cart_bk_data->date_from; - $objHtlBkDtl->date_to = $obj_cart_bk_data->date_to; + $objBookingDetail->date_from = $objCartBookingData->date_from; + $objBookingDetail->date_to = $objCartBookingData->date_to; + $objBookingDetail->adults = $objCartBookingData->adults; + $objBookingDetail->children = $objCartBookingData->children; + $objBookingDetail->child_ages = $objCartBookingData->child_ages; $total_price = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( $idProduct, - $obj_cart_bk_data->date_from, - $obj_cart_bk_data->date_to, + $objCartBookingData->date_from, + $objCartBookingData->date_to, 0, Group::getCurrent()->id, $this->context->cart->id, $this->context->cookie->id_guest, - $obj_cart_bk_data->id_room + $objCartBookingData->id_room ); - - $objHtlBkDtl->total_price_tax_excl = $total_price['total_price_tax_excl']; - $objHtlBkDtl->total_price_tax_incl = $total_price['total_price_tax_incl']; - $objHtlBkDtl->total_paid_amount = Tools::ps_round($total_price['total_price_tax_incl'], 5); + $objBookingDetail->total_price_tax_excl = $total_price['total_price_tax_excl']; + $objBookingDetail->total_price_tax_incl = $total_price['total_price_tax_incl']; + $objBookingDetail->total_paid_amount = Tools::ps_round($total_price['total_price_tax_incl'], 5); // Save hotel information/location/contact - if (Validate::isLoadedObject($objRoom = new HotelRoomInformation($obj_cart_bk_data->id_room))) { - $objHtlBkDtl->room_num = $objRoom->room_num; + if (Validate::isLoadedObject($objRoom = new HotelRoomInformation($objCartBookingData->id_room))) { + $objBookingDetail->room_num = $objRoom->room_num; } if (Validate::isLoadedObject($objHotelBranch = new HotelBranchInformation( - $obj_cart_bk_data->id_hotel, + $objCartBookingData->id_hotel, $idLang ))) { - $addressInfo = $objHotelBranch->getAddress($obj_cart_bk_data->id_hotel); - $objHtlBkDtl->hotel_name = $objHotelBranch->hotel_name; - $objHtlBkDtl->city = $addressInfo['city']; - $objHtlBkDtl->state = State::getNameById($addressInfo['id_state']); - $objHtlBkDtl->country = Country::getNameById($idLang, $addressInfo['id_country']); - $objHtlBkDtl->zipcode = $addressInfo['postcode'];; - $objHtlBkDtl->phone = $addressInfo['phone']; - $objHtlBkDtl->email = $objHotelBranch->email; - $objHtlBkDtl->check_in_time = $objHotelBranch->check_in; - $objHtlBkDtl->check_out_time = $objHotelBranch->check_out; + $addressInfo = $objHotelBranch->getAddress($objCartBookingData->id_hotel); + $objBookingDetail->hotel_name = $objHotelBranch->hotel_name; + $objBookingDetail->city = $addressInfo['city']; + $objBookingDetail->state = State::getNameById($addressInfo['id_state']); + $objBookingDetail->country = Country::getNameById($idLang, $addressInfo['id_country']); + $objBookingDetail->zipcode = $addressInfo['postcode'];; + $objBookingDetail->phone = $addressInfo['phone']; + $objBookingDetail->email = $objHotelBranch->email; + $objBookingDetail->check_in_time = $objHotelBranch->check_in; + $objBookingDetail->check_out_time = $objHotelBranch->check_out; } if ($roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($idProduct)) { - $objHtlBkDtl->adult = $roomTypeInfo['adult']; - $objHtlBkDtl->children = $roomTypeInfo['children']; + $objBookingDetail->adults = $objCartBookingData->adults; + $objBookingDetail->children = $objCartBookingData->children; + $objBookingDetail->child_ages = $objCartBookingData->child_ages; } - $objHtlBkDtl->save(); + $objBookingDetail->save(); } } @@ -2406,7 +2513,7 @@ class AdminOrdersControllerCore extends AdminController } /*By webkul To edit Order and cart entries when edit rooms from the orderLine when editing the order*/ - $product_informations = $_POST['add_product']; + $product_informations = $_POST['edit_product']; $new_date_from = trim(date('Y-m-d', strtotime($product_informations['date_from']))); $new_date_to = trim(date('Y-m-d', strtotime($product_informations['date_to']))); $old_date_from = trim(Tools::getValue('date_from')); @@ -2419,6 +2526,10 @@ class AdminOrdersControllerCore extends AdminController $product_quantity = (int) $obj_booking_detail->getNumberOfDays($new_date_from, $new_date_to); $old_product_quantity = (int) $obj_booking_detail->getNumberOfDays($old_date_from, $old_date_to); $qty_diff = $product_quantity - $old_product_quantity; + $occupancy = array_shift(Tools::getValue('occupancy')); + $adults = $occupancy['adults']; + $children = $occupancy['children']; + $child_ages = $occupancy['child_ages']; /*By webkul to validate fields before deleting the cart and order data form the tables*/ if ($id_hotel == '') { @@ -2476,6 +2587,33 @@ class AdminOrdersControllerCore extends AdminController 'result' => false, 'error' => Tools::displayError('Invalid quantity.'), ))); + } elseif (!isset($adults) || !$adults || !Validate::isUnsignedInt($adults)) { + die(json_encode(array( + 'result' => false, + 'error' => Tools::displayError('Invalid number of adults.'), + ))); + } elseif (!Validate::isUnsignedInt($children)) { + die(json_encode(array( + 'result' => false, + 'error' => Tools::displayError('Invalid number of children.'), + ))); + } + if ($children > 0) { + if (!isset($child_ages) || ($children != count($child_ages))) { + die(json_encode(array( + 'result' => false, + 'error' => Tools::displayError('Please provide all children age.'), + ))); + } else { + foreach($child_ages as $childAge) { + if (!Validate::isUnsignedInt($childAge)) { + die(json_encode(array( + 'result' => false, + 'error' => Tools::displayError('Invalid children age.'), + ))); + } + } + } } $rooms_booked = $obj_booking_detail->getRoomBookinInformationForDateRangeByOrder($id_room, $old_date_from, $old_date_to, $new_date_from, $new_date_to); @@ -2704,6 +2842,13 @@ class AdminOrdersControllerCore extends AdminController $old_date_to ); + // set occupancy details + $occupancy = array( + 'adults' => $adults, + 'children' => $children, + 'child_ages' => $child_ages + ); + /*By webkul to edit the Hotel Cart and Hotel Order tables when editing the room for the order detail page*/ $new_total_price = array( 'tax_excl' => $totalRoomPriceAfterTE, @@ -2716,6 +2861,7 @@ class AdminOrdersControllerCore extends AdminController $old_date_to, $new_date_from, $new_date_to, + $occupancy, $new_total_price )) { // update extra demands total prices if dates are changes (price calc method for each day) diff --git a/controllers/admin/AdminPPreferencesController.php b/controllers/admin/AdminPPreferencesController.php index a2da76a85..d57aa0d2e 100644 --- a/controllers/admin/AdminPPreferencesController.php +++ b/controllers/admin/AdminPPreferencesController.php @@ -109,15 +109,91 @@ class AdminPPreferencesControllerCore extends AdminController 'cast' => 'intval', 'required' => false, 'type' => 'bool' + ) + ), + 'submit' => array('title' => $this->l('Save')) + ), + 'search' => array( + 'title' => $this->l('Search'), + 'fields' => array( + 'PS_FRONT_SEARCH_TYPE' => array( + 'title' => $this->l('Front end search type'), + 'hint' => $this->l('Select search type for frontend. In "Occupancy wise search", occupancy field will be shown in the search panel and search will be based on required occupancy by the guest. In "Search without occupancy", All available room types will be shown for the search dates without any occupancy filter.'), + 'cast' => 'intval', + 'type' => 'select', + 'list' => array( + array('id' => HotelBookingDetail::SEARCH_TYPE_OWS, 'name' => $this->l('Occupancy wise search')), + array('id' => HotelBookingDetail::SEARCH_TYPE_NORMAL, 'name' => $this->l('Search without occupancy')) + ), + 'identifier' => 'id', + 'desc' => $this->l('Choose "Occupancy wise search" or "Search without occupancy". Occupancy search restriction will depend on this option selection at front end.'), + ), + 'PS_FRONT_OWS_SEARCH_ALGO_TYPE' => array( + 'title' => $this->l('Front end occupancy wise search algorithm'), + 'hint' => $this->l('In occupancy wise search at front end, you want to display only room types which are fully satisfying searched occupancy or you want to display all the available room types for the dates searched'), + 'cast' => 'intval', + 'type' => 'select', + 'list' => array( + array('id' => HotelBookingDetail::SEARCH_EXACT_ROOM_TYPE_ALGO, 'name' => $this->l('Show room types satisfying required occupancy')), + array('id' => HotelBookingDetail::SEARCH_ALL_ROOM_TYPE_ALGO, 'name' => $this->l('Show all available room types')) + ), + 'identifier' => 'id', + 'desc' => $this->l('This option is only for fully available rooms. For partially available rooms, always all possible rooms will be displayed.'), + ), + 'PS_FRONT_ROOM_UNIT_SELECTION_TYPE' => array( + 'title' => $this->l('In front-end, add rooms to cart with'), + 'hint' => $this->l('In Room occupancy, while adding rooms in cart customer has to select per room occupancy and in room quantity customer only has to select number of rooms.'), + 'cast' => 'intval', + 'type' => 'select', + 'list' => array( + array('id' => HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY, 'name' => $this->l('Room Occupancy')), + array('id' => HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_QUANTITY, 'name' => $this->l('Rooms Quantity (No. of rooms)')) + ), + 'identifier' => 'id', + ), + 'PS_BACKOFFICE_SEARCH_TYPE' => array( + 'title' => $this->l('Back-office search type'), + 'hint' => $this->l('Select search type for Back-office. In "Occupancy wise search", occupancy field will be shown in the search panel and search will be based on required occupancy by the employee. In "Search without occupancy", All available room types will be shown for the search dates without any occupancy filter.'), + 'cast' => 'intval', + 'type' => 'select', + 'list' => array( + array('id' => HotelBookingDetail::SEARCH_TYPE_OWS, 'name' => $this->l('Occupancy wise search')), + array('id' => HotelBookingDetail::SEARCH_TYPE_NORMAL, 'name' => $this->l('Search without occupancy')) + ), + 'identifier' => 'id', + 'desc' => $this->l('Choose "Occupancy wise search" or "Search without occupancy". Occupancy search restriction will depend on this option selection at back-office.'), + ), + 'PS_BACKOFFICE_OWS_SEARCH_ALGO_TYPE' => array( + 'title' => $this->l('Back-office occupancy wise search algorithm'), + 'hint' => $this->l('In occupancy wise search at back-office, you want to display only room types which are fully satisfying searched occupancy or you want to display all the available room types for the dates searched.'), + 'cast' => 'intval', + 'type' => 'select', + 'list' => array( + array('id' => HotelBookingDetail::SEARCH_EXACT_ROOM_TYPE_ALGO, 'name' => $this->l('Show room types satisfying required occupancy')), + array('id' => HotelBookingDetail::SEARCH_ALL_ROOM_TYPE_ALGO, 'name' => $this->l('Show all available room types')) + ), + 'identifier' => 'id', + 'desc' => $this->l('This option is only for fully available rooms. For partially available rooms, always all possible rooms will be displayed.'), + ), + 'PS_BACKOFFICE_ROOM_BOOKING_TYPE' => array( + 'title' => $this->l('In back-office, add rooms to cart with'), + 'hint' => $this->l('In Room occupancy, while adding rooms in cart customer has to select per room occupancy and in room quantity customer only has to select number of rooms.'), + 'cast' => 'intval', + 'type' => 'select', + 'list' => array( + array('id' => HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY, 'name' => $this->l('Room Occupancy')), + array('id' => HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_QUANTITY, 'name' => $this->l('Rooms Quantity (No. of rooms)')) + ), + 'identifier' => 'id', ), 'PS_LOS_RESTRICTION_BO' => array( - 'title' => $this->l('Apply Min and Max lenght of stay restrictions for back-office bookings'), - 'hint' => $this->l('While creating bookings from back-office, apply minimum and maximum lenght of stay restrictions'), + 'title' => $this->l('Apply Min and Max lenght of stay restrictions for back-office search'), + 'hint' => $this->l('While searching for available rooms from back-office, apply minimum and maximum lenght of stay restrictions'), 'validation' => 'isBool', 'cast' => 'intval', 'required' => false, 'type' => 'bool' - ) + ), ), 'submit' => array('title' => $this->l('Save')) ), @@ -306,6 +382,29 @@ class AdminPPreferencesControllerCore extends AdminController // 'bottom' => '', // 'submit' => array('title' => $this->l('Save')) // ), + 'fo_search_filters' => array( + 'title' => $this->l('Search Results Page Filters'), + 'icon' => 'icon-search', + 'fields' => array( + 'SHOW_AMENITIES_FILTER' => array( + 'title' => $this->l('Show Amenities filter'), + 'hint' => $this->l('Enable to display Amenities filter.'), + 'validation' => 'isBool', + 'cast' => 'intval', + 'required' => false, + 'type' => 'bool', + ), + 'SHOW_PRICE_FILTER' => array( + 'title' => $this->l('Show Price filter'), + 'hint' => $this->l('Enable to display Price filter.'), + 'validation' => 'isBool', + 'cast' => 'intval', + 'required' => false, + 'type' => 'bool', + ), + ), + 'submit' => array('title' => $this->l('Save')) + ), ); } @@ -333,9 +432,28 @@ class AdminPPreferencesControllerCore extends AdminController Db::getInstance()->execute($sql); } + if (Tools::getValue('PS_FRONT_SEARCH_TYPE') == HotelBookingDetail::SEARCH_TYPE_OWS) { + $_POST['PS_FRONT_ROOM_UNIT_SELECTION_TYPE'] = HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY; + } + + if (Tools::getValue('PS_BACKOFFICE_SEARCH_TYPE') == HotelBookingDetail::SEARCH_TYPE_OWS) { + $_POST['PS_BACKOFFICE_ROOM_BOOKING_TYPE'] = HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY; + } + if (Tools::getIsset('PS_CATALOG_MODE')) { Tools::clearSmartyCache(); Media::clearCache(); } } + + public function setMedia() + { + parent::setMedia(); + + Media::addJsDef(array( + 'SEARCH_TYPE_OWS' => HotelBookingDetail::SEARCH_TYPE_OWS + )); + + $this->addJS(_PS_JS_DIR_.'admin/ppreferences.js'); + } } diff --git a/controllers/admin/AdminProductsController.php b/controllers/admin/AdminProductsController.php index 7fcb98d1f..1d4b293c3 100644 --- a/controllers/admin/AdminProductsController.php +++ b/controllers/admin/AdminProductsController.php @@ -113,6 +113,7 @@ class AdminProductsControllerCore extends AdminController 'Configuration' => $this->l('Configuration'), 'Occupancy' => $this->l('Occupancy'), 'LengthOfStay' => $this->l('Length of Stay'), + 'AdditionalFacilities' => $this->l('Additional Facilities'), ); $this->available_tabs = array('Warehouses' => 14); @@ -134,6 +135,7 @@ class AdminProductsControllerCore extends AdminController 'Configuration' => 14, 'Occupancy' => 15, 'LengthOfStay' => 16, + 'AdditionalFacilities' => 17, )); } @@ -211,7 +213,7 @@ class AdminProductsControllerCore extends AdminController LEFT JOIN `'._DB_PREFIX_.'address` aa ON (aa.`id_hotel` = hb.`id`)'; $this->_select .= ' (SELECT COUNT(hri.`id`) FROM `'._DB_PREFIX_.'htl_room_information` hri WHERE hri.`id_product` = a.`id_product`) as num_rooms, '; - $this->_select .= 'hrt.`adult`, hrt.`children`, hb.`id` as id_hotel, aa.`city`, hbl.`hotel_name`, '; + $this->_select .= 'hrt.`adults`, hrt.`children`, hb.`id` as id_hotel, aa.`city`, hbl.`hotel_name`, '; $this->_select .= 'shop.`name` AS `shopname`, a.`id_shop_default`, '; $this->_select .= $alias_image.'.`id_image` AS `id_image`, cl.`name` AS `name_category`, '.$alias.'.`price`, 0 AS `price_final`, a.`is_virtual`, pd.`nb_downloadable`, sav.`quantity` AS `sav_quantity`, '.$alias.'.`active`, IF(sav.`quantity`<=0, 1, 0) AS `badge_danger`'; @@ -253,9 +255,9 @@ class AdminProductsControllerCore extends AdminController 'callback' => 'getHotelName', ); } - $this->fields_list['adult'] = array( + $this->fields_list['adults'] = array( 'title' => $this->l('Adults'), - 'filter_key' => 'hrt!adult', + 'filter_key' => 'hrt!adults', 'align' => 'center', ); $this->fields_list['child'] = array( @@ -2228,6 +2230,9 @@ class AdminProductsControllerCore extends AdminController if ($this->isTabSubmitted('Configuration')) { $this->processConfiguration(); } + if ($this->isTabSubmitted('AdditionalFacilities')) { + $this->processAdditionalFacilities(); + } $this->updatePackItems($object); // Disallow avanced stock management if the product become a pack @@ -3433,6 +3438,7 @@ class AdminProductsControllerCore extends AdminController } else { $this->displayWarning($this->l('You must save this room type before managing occupancy.')); } + $smartyVars['currency'] = $this->context->currency; $data->assign($smartyVars); $this->tpl_form_vars['custom_form'] = $data->fetch(); } @@ -3449,16 +3455,48 @@ class AdminProductsControllerCore extends AdminController if (Validate::isLoadedObject($objRoomType = new HotelRoomType($idHtlRoomType))) { $baseAdults = Tools::getValue('base_adults'); $baseChildren = Tools::getValue('base_children'); + $maxAdults = Tools::getValue('max_adults'); + $maxChildren = Tools::getValue('max_children'); + $maxGuests = Tools::getValue('max_guests'); if (!$baseAdults || !Validate::isUnsignedInt($baseAdults)) { $this->errors[] = Tools::displayError('Invalid base adults'); } - if (!Validate::isUnsignedInt($baseChildren)) { + if ($baseChildren == '' || !Validate::isUnsignedInt($baseChildren)) { $this->errors[] = Tools::displayError('Invalid base children'); + } else if (Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM')) { + if ($baseChildren > Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM')) { + $this->errors[] = sprintf(Tools::displayError('Base children cannot be greater than max childern allowed on your website (Max: %s)'), Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM')); + } } + if (!$maxAdults || !Validate::isUnsignedInt($maxAdults)) { + $this->errors[] = Tools::displayError('Invalid maximum number of adults'); + } elseif ($maxAdults < $baseAdults) { + $this->errors[] = Tools::displayError('Maximum number of adults cannot be less than base adults'); + } + if ($maxChildren == '' || !Validate::isUnsignedInt($maxChildren)) { + $this->errors[] = Tools::displayError('Invalid maximum number of children'); + } else if (Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM')) { + if ($maxChildren > Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM')) { + $this->errors[] = sprintf(Tools::displayError('Maximum number of children cannot be greater than max childern allowed on your website (Max: %s)'), Configuration::get('WK_GLOBAL_MAX_CHILD_IN_ROOM')); + } + } elseif ($maxChildren < $baseChildren) { + $this->errors[] = Tools::displayError('Maximum number of children cannot be less than base children'); + } + if (!$maxGuests || !Validate::isUnsignedInt($maxGuests)) { + $this->errors[] = Tools::displayError('Invalid maximum number of guests'); + } elseif ($maxGuests < ($baseAdults + $baseChildren)) { + $this->errors[] = Tools::displayError('Maximum number of guests cannot be less than base occupancy of adults and children'); + } elseif ($maxGuests > ($maxChildren + $maxAdults)) { + $this->errors[] = Tools::displayError('Maximum number of guests cannot be more than max occupancy of adults and children'); + } + if (!count($this->errors)) { - $objRoomType->adult = $baseAdults; + $objRoomType->adults = $baseAdults; $objRoomType->children = $baseChildren; + $objRoomType->max_adults = $maxAdults; + $objRoomType->max_children = $maxChildren; + $objRoomType->max_guests = $maxGuests; $objRoomType->save(); } } else { @@ -3469,7 +3507,7 @@ class AdminProductsControllerCore extends AdminController } } } - + // send information for the length of stay tab public function initFormLengthOfStay($product) { @@ -3751,6 +3789,122 @@ class AdminProductsControllerCore extends AdminController die('0'); } + public function initFormAdditionalFacilities($obj) + { + $data = $this->createTemplate($this->tpl_form); + + if ($obj->id) { + $objGlobalDemand = new HotelRoomTypeGlobalDemand(); + $allDemands = $objGlobalDemand->getAllDemands(); + $objCurrency = new Currency(Configuration::get('PS_CURRENCY_DEFAULT')); + + // get room type additional facilities + $objRoomDemand = new HotelRoomTypeDemand(); + $roomDemandPrices = $objRoomDemand->getRoomTypeDemands($obj->id, 0, 0); + + $data->assign(array( + 'product' => $obj, + 'roomDemandPrices' => $roomDemandPrices, + 'allDemands' => $allDemands, + 'defaultcurrencySign' => $objCurrency->sign, + )); + } else { + $this->displayWarning($this->l('You must save this room type before managing additional facilities.')); + } + + $this->tpl_form_vars['custom_form'] = $data->fetch(); + } + + public function processAdditionalFacilities() + { + if ($idProduct = Tools::getValue('id_product')) { + $objRoomTypeDemand = new HotelRoomTypeDemand(); + $objRoomTypeDemandPrice = new HotelRoomTypeDemandPrice(); + // first delete all the previously saved prices and demands of this room type + $objRoomTypeDemand->deleteRoomTypeDemands($idProduct); + $objRoomTypeDemandPrice->deleteRoomTypeDemandPrices($idProduct); + if ($selectedDemands = Tools::getValue('selected_demand')) { + $objAdvOption = new HotelRoomTypeGlobalDemandAdvanceOption(); + foreach ($selectedDemands as $idGlobalDemand) { + if (Validate::isLoadedObject($objGlobalDemand = new HotelRoomTypeGlobalDemand($idGlobalDemand))) { + // save selected demands for this room type + $objRoomTypeDemand = new HotelRoomTypeDemand(); + $objRoomTypeDemand->id_product = $idProduct; + $objRoomTypeDemand->id_global_demand = $idGlobalDemand; + $objRoomTypeDemand->save(); + + // save selected demands prices for this room type + $demandPrice = Tools::getValue('demand_price_'.$idGlobalDemand); + if (Validate::isPrice($demandPrice)) { + if ($objGlobalDemand->price != $demandPrice) { + $objRoomTypeDemandPrice = new HotelRoomTypeDemandPrice(); + $objRoomTypeDemandPrice->id_product = $idProduct; + $objRoomTypeDemandPrice->id_global_demand = $idGlobalDemand; + $objRoomTypeDemandPrice->id_option = 0; + $objRoomTypeDemandPrice->price = $demandPrice; + $objRoomTypeDemandPrice->save(); + } + } else { + $this->errors[] = Tools::displayError('Invalid demand price of facility.'). + ' : '.$objGlobalDemand->name[$this->context->language->id]; + } + if ($advOptions = $objAdvOption->getGlobalDemandAdvanceOptions($idGlobalDemand)) { + foreach ($advOptions as $option) { + if (Validate::isLoadedObject($objAdvOption = new HotelRoomTypeGlobalDemandAdvanceOption($option['id']))) { + $optionPrice = Tools::getValue('option_price_'.$option['id']); + if (Validate::isPrice($optionPrice)) { + if ($optionPrice != $objAdvOption->price) { + $objRoomTypeDemandPrice = new HotelRoomTypeDemandPrice(); + $objRoomTypeDemandPrice->id_product = $idProduct; + $objRoomTypeDemandPrice->id_global_demand = $idGlobalDemand; + $objRoomTypeDemandPrice->id_option = $option['id']; + $objRoomTypeDemandPrice->price = $optionPrice; + $objRoomTypeDemandPrice->save(); + } + } else { + $this->errors[] = Tools::displayError('Invalid price of advanced option: ').$objAdvOption->name[$this->context->language->id]; + } + } + } + } + } + } + if (count($this->errors)) { + $this->warnings[] = Tools::displayError('Invalid price values are not saved. Please correct them and save again.'); + } + + $objCartBookingData = new HotelCartBookingData(); + if ($cartExtraDemands = $objCartBookingData->getCartExtraDemands(0, $idProduct)) { + // delete the demands from cart if not available in cart + $objRoomDemand = new HotelRoomTypeDemand(); + $roomTypeDemandIds = array(); + if ($roomTypeDemands = $objRoomDemand->getRoomTypeDemands($idProduct)) { + $roomTypeDemandIds = array_keys($roomTypeDemands); + } + foreach ($cartExtraDemands as &$demandInfo) { + if (isset($demandInfo['extra_demands']) && $demandInfo['extra_demands']) { + $cartChanged = 0; + foreach ($demandInfo['extra_demands'] as $key => $demand) { + if (!in_array($demand['id_global_demand'], $roomTypeDemandIds)) { + $cartChanged = 1; + unset($demandInfo['extra_demands'][$key]); + } + } + if ($cartChanged) { + if (Validate::isLoadedObject( + $objCartBooking = new HotelCartBookingData($demandInfo['id']) + )) { + $objCartBooking->extra_demands = json_encode($demandInfo['extra_demands']); + $objCartBooking->save(); + } + } + } + } + } + } + } + } + /** * @param Product $obj * diff --git a/controllers/admin/AdminSlipController.php b/controllers/admin/AdminSlipController.php index d17cd3252..a64d044c2 100644 --- a/controllers/admin/AdminSlipController.php +++ b/controllers/admin/AdminSlipController.php @@ -34,11 +34,14 @@ class AdminSlipControllerCore extends AdminController $this->bootstrap = true; $this->table = 'order_slip'; $this->className = 'OrderSlip'; + $this->list_no_link = true; $this->_select = ' o.`id_shop`'; $this->_join .= ' LEFT JOIN '._DB_PREFIX_.'orders o ON (o.`id_order` = a.`id_order`)'; $this->_group = ' GROUP BY a.`id_order_slip`'; + $this->addRowAction('statusChange'); + $this->fields_list = array( 'id_order_slip' => array( 'title' => $this->l('ID'), @@ -63,7 +66,26 @@ class AdminSlipControllerCore extends AdminController 'callback' => 'printPDFIcons', 'orderby' => false, 'search' => false, - 'remove_onclick' => true) + 'remove_onclick' => true + ), + 'redeem_status' => array( + 'title' => $this->l('Status'), + 'type' => 'select', + 'list' => array( + OrderSlip::REDEEM_STATUS_ACTIVE => $this->l('Active'), + OrderSlip::REDEEM_STATUS_REDEEMED => $this->l('Redeemed'), + ), + 'align' => 'center', + 'filter_key' => 'a!redeem_status', + 'callback' => 'displayRedeemStatus', + 'class' => 'fixed-width-md', + ), + 'id_cart_rule' => array( + 'title' => $this->l('Voucher'), + 'align' => 'center', + 'callback' => 'displayVoucherLink', + 'class' => 'fixed-width-lg', + ), ); $this->_select = 'a.id_order_slip AS id_pdf'; @@ -162,6 +184,38 @@ class AdminSlipControllerCore extends AdminController } } + public function processStatusChange() + { + $idOrderSlip = Tools::getValue('id_order_slip'); + $objOrderSlip = new OrderSlip($idOrderSlip); + + if (!Validate::isLoadedObject($objOrderSlip)) { + $this->errors[] = $this->l('The credit slip can not be loaded.'); + } elseif ($objOrderSlip->redeem_status != OrderSlip::REDEEM_STATUS_ACTIVE) { + $this->errors[] = $this->l('The status for this credit slip can not be changed.'); + } else { + $objOrder = new Order($objOrderSlip->id_order); + $objCustomer = new Customer($objOrderSlip->id_customer); + + if (!Validate::isLoadedObject($objOrder)) { + $this->errors[] = $this->l('The related order for this credit slip can not be loaded.'); + } + + if (!Validate::isLoadedObject($objCustomer)) { + $this->errors[] = $this->l('The related customer for this credit slip can not be loaded.'); + } + } + + if (!count($this->errors)) { + $objOrderSlip->redeem_status = OrderSlip::REDEEM_STATUS_REDEEMED; + if ($objOrderSlip->save()) { + Tools::redirectAdmin(self::$currentIndex.'&token='.$this->token.'&conf=4'); + } + + $this->errors[] = $this->l('Something went wrong while changing status.'); + } + } + public function initContent() { $this->initTabModuleList(); @@ -202,4 +256,43 @@ class AdminSlipControllerCore extends AdminController return $this->createTemplate('_print_pdf_icon.tpl')->fetch(); } + + public function displayRedeemStatus($redeemStatus, $row) + { + $this->context->smarty->assign(array( + 'redeem_status' => $redeemStatus, + )); + + return $this->createTemplate('_redeem_status.tpl')->fetch(); + } + + public function displayVoucherLink($idCartRule, $row) + { + if ($row['redeem_status'] == OrderSlip::REDEEM_STATUS_REDEEMED && $idCartRule) { + $this->context->smarty->assign(array( + 'id_cart_rule' => (int) $idCartRule, + )); + + return $this->createTemplate('_display_voucher_link.tpl')->fetch(); + } + + return '--'; + } + + public function displayStatusChangeLink($token = null, $id) + { + $objOrderSlip = new OrderSlip($id); + if ($objOrderSlip->redeem_status != OrderSlip::REDEEM_STATUS_REDEEMED) { + $statusChangeLink = self::$currentIndex.'&'.$this->identifier.'='.$id.'&action=statusChange'. + '&token='.($token != null ? $token : $this->token); + + $this->context->smarty->assign(array( + 'status_change_link' => $statusChangeLink, + )); + + return $this->createTemplate('_status_change_link.tpl')->fetch(); + } + + return '--'; + } } diff --git a/controllers/admin/AdminStatsController.php b/controllers/admin/AdminStatsController.php index 30fe17e2d..693b8ce0f 100644 --- a/controllers/admin/AdminStatsController.php +++ b/controllers/admin/AdminStatsController.php @@ -842,7 +842,9 @@ class AdminStatsControllerCore extends AdminStatsTabController $totalDepartures = Db::getInstance()->getValue( 'SELECT COUNT(hbd.`id_room`) FROM `'._DB_PREFIX_.'htl_booking_detail` hbd - WHERE hbd.`is_refunded` = 0 AND hbd.`is_back_order` = 0 + WHERE hbd.`is_refunded` = 0 + AND ((hbd.`id_status` = '.(int) HotelBookingDetail::STATUS_CHECKED_IN.') OR + (hbd.`check_in` != "0000:00:00 00:00:00" AND hbd.`check_out` != "0000:00:00 00:00:00")) AND hbd.`date_to` BETWEEN "'.pSQL($date).' 00:00:00" AND "'.pSQL($date).' 23:59:59"'. HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') ); @@ -850,9 +852,10 @@ class AdminStatsControllerCore extends AdminStatsTabController $departed = Db::getInstance()->getValue( 'SELECT COUNT(hbd.`id_room`) FROM `'._DB_PREFIX_.'htl_booking_detail` hbd - WHERE hbd.`is_refunded` = 0 AND hbd.`is_back_order` = 0 + WHERE hbd.`is_refunded` = 0 AND hbd.`date_to` BETWEEN "'.pSQL($date).' 00:00:00" AND "'.pSQL($date).' 23:59:59" - AND hbd.`id_status` = '.(int) HotelBookingDetail::STATUS_CHECKED_OUT. + AND hbd.`id_status` = '.(int) HotelBookingDetail::STATUS_CHECKED_OUT.' + AND hbd.`check_in` != "0000:00:00 00:00:00"'. HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') ); @@ -900,7 +903,7 @@ class AdminStatsControllerCore extends AdminStatsTabController public static function getGuestsByDate($date, $idHotel = false) { return Db::getInstance()->getRow( - 'SELECT SUM(hbd.`adult`) AS `adults`, SUM(hbd.`children`) AS `children` + 'SELECT SUM(hbd.`adults`) AS `adults`, SUM(hbd.`children`) AS `children` FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE hbd.`is_refunded` = 0 AND hbd.`is_back_order` = 0 AND hbd.`date_from` BETWEEN "'.pSQL($date).' 00:00:00" AND "'.pSQL($date).' 23:59:59"'. @@ -1136,6 +1139,87 @@ class AdminStatsControllerCore extends AdminStatsTabController ); } + public static function getArrivalsInfoByDate($date, $idHotel = null) + { + $sql = 'SELECT hbd.*, CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name, + DATEDIFF(hbd.`date_to`, hbd.`date_from`) AS los + FROM `'._DB_PREFIX_.'htl_booking_detail` hbd + LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = hbd.`id_customer`) + WHERE hbd.`is_refunded` = 0 AND hbd.`date_from` = "'.pSQL($date).' 00:00:00" + AND hbd.`id_status` != '.(int) HotelBookingDetail::STATUS_CHECKED_IN.' + AND hbd.`id_status` != '.(int) HotelBookingDetail::STATUS_CHECKED_OUT. + (!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : ''); + $result = Db::getInstance()->executeS($sql); + + return $result; + } + + public static function getDeparturesInfoByDate($date, $idHotel = null) + { + $sql = 'SELECT hbd.*, CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name, + DATEDIFF(hbd.`date_to`, hbd.`date_from`) AS los + FROM `'._DB_PREFIX_.'htl_booking_detail` hbd + LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = hbd.`id_customer`) + WHERE hbd.`is_refunded` = 0 AND hbd.`date_to` = "'.pSQL($date).' 00:00:00" + AND hbd.`id_status` = '.(int) HotelBookingDetail::STATUS_CHECKED_IN. + (!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : ''); + $result = Db::getInstance()->executeS($sql); + + return $result; + } + + public static function getInHousesInfo($idHotel = null) + { + $sql = 'SELECT hbd.*, CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name, + DATEDIFF(hbd.`date_to`, hbd.`date_from`) AS los + FROM `'._DB_PREFIX_.'htl_booking_detail` hbd + LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = hbd.`id_customer`) + WHERE hbd.`is_refunded` = 0 AND hbd.`id_status` = '.(int) HotelBookingDetail::STATUS_CHECKED_IN. + (!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : ''); + $result = Db::getInstance()->executeS($sql); + + return $result; + } + + public static function getNewBookingsInfoByDate($date, $idHotel = null) + { + $sql = 'SELECT hbd.`id_customer`, CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name, + COUNT(hbd.`id`) AS total_rooms, SUM(hbd.`adult` + hbd.`children`) AS total_guests, + hbd.`id_hotel`, hbd.`hotel_name`, hbd.`id_order`, o.`total_paid_tax_excl`, o.`id_currency`, osl.`name` AS `state_name` + FROM `'._DB_PREFIX_.'htl_booking_detail` hbd + LEFT JOIN `'._DB_PREFIX_.'orders` o ON (o.`id_order` = hbd.`id_order`) + LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl + ON (osl.`id_order_state` = o.`current_state` AND osl.`id_lang` = '.(int) Context::getContext()->language->id.') + LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = hbd.`id_customer`) + WHERE hbd.`date_add` BETWEEN "'.pSQL($date).' 00:00:00" AND "'.pSQL($date).' 23:59:59"'. + (!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').' + GROUP BY hbd.`id_order`'; + $result = Db::getInstance()->executeS($sql); + + return $result; + } + + public static function getCancellationsInfoByDate($date, $idHotel = null) + { + $sql = 'SELECT orr.`id_order_return`, orr.`id_customer`, hbd.`room_num`, hbd.`id_product`, hbd.`room_type_name`, + CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name, + hbd.`id_hotel`, hbd.`hotel_name`, SUM(hbd.`adult` + hbd.`children`) AS total_guests, + hbd.`date_from`, hbd.`date_to`, orr.`id_order` + FROM `'._DB_PREFIX_.'order_return` orr + LEFT JOIN `'._DB_PREFIX_.'order_return_detail` ord ON (ord.`id_order_return` = orr.`id_order_return`) + LEFT JOIN `'._DB_PREFIX_.'orders` o ON (o.`id_order` = orr.`id_order`) + LEFT JOIN `'._DB_PREFIX_.'htl_booking_detail` hbd ON (hbd.`id` = ord.`id_htl_booking`) + LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = orr.`id_customer`) + WHERE orr.`date_add` BETWEEN "'.pSQL($date).' 00:00:00" AND "'.pSQL($date).' 23:59:59" + AND orr.`state` = '.(int) OrderReturnState::ORDER_RETRUN_FIRST_STATUS. + (!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').' + GROUP BY ord.`id_htl_booking` + ORDER BY orr.`date_add` DESC'; + $result = Db::getInstance()->executeS($sql); + + return $result; + } + public static function getOccupiedRoomsForDiscreteDates($dateFrom, $dateTo = null, $idHotel = null, $useCache = true) { $dateTo = !$dateTo ? date('Y-m-d', strtotime('+1 day', strtotime($dateFrom))) : $dateTo; diff --git a/controllers/front/CartController.php b/controllers/front/CartController.php index a44865537..76b647759 100644 --- a/controllers/front/CartController.php +++ b/controllers/front/CartController.php @@ -176,15 +176,16 @@ class CartControllerCore extends FrontController $result = $objCartBooking->deleteCartBookingData($id_cart, $id_product, 0, 0, 0, 0); if ($roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($id_product)) { if ($id_hotel = $roomTypeInfo['id_hotel']) { - $obj_booking_dtl = new HotelBookingDetail(); - if ($hotel_room_data = $obj_booking_dtl->DataForFrontSearch( - $date_from, - $date_to, - $id_hotel, - $id_product, - 1 - )) { - $total_available_rooms = $hotel_room_data['stats']['num_avail']; + $objBookingDetail = new HotelBookingDetail(); + $bookingParams = array( + 'date_from' => $date_from, + 'date_to' => $date_to, + 'hotel_id' => $id_hotel, + 'id_room_type' => $id_product, + 'only_search_data' => 1, + ); + if ($hotelRoomData = $objBookingDetail->dataForFrontSearch($bookingParams)) { + $total_available_rooms = $hotelRoomData['stats']['num_avail']; } } } @@ -267,11 +268,27 @@ class CartControllerCore extends FrontController $date_to = Tools::getValue('dateTo'); $date_from = date("Y-m-d", strtotime($date_from)); $date_to = date("Y-m-d", strtotime($date_to)); + $operator = Tools::getValue('op', 'up'); + + $occupancyRequiredForBooking = false; + if (Configuration::get('PS_FRONT_ROOM_UNIT_SELECTION_TYPE') == HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY) { + $occupancyRequiredForBooking = true; + } + + if ($occupancyRequiredForBooking) { + if ($occupancy = json_decode(Tools::getValue('occupancy'), true)) { + $this->qty = count($occupancy); + } else { + $this->errors[] = Tools::displayError('Invalid occupnacy.'); + } + } else { + $occupancy = Tools::getValue('qty'); + $this->qty = $occupancy; + } + $id_cart = $this->context->cart->id; $id_guest = $this->context->cart->id_guest; - - if ($this->qty == 0) { $this->errors[] = Tools::displayError('Null quantity.', !Tools::getValue('ajax')); } elseif (!$this->id_product) { @@ -284,71 +301,106 @@ class CartControllerCore extends FrontController return; } - $qty_to_check = $this->qty; - $cart_products = $this->context->cart->getProducts(); - - // By Webkul : This code is to check available quantity of Room before adding it to cart. - // only check availability if qty is increasing - if (Module::isInstalled('hotelreservationsystem') && Module::isEnabled('hotelreservationsystem')) { - require_once _PS_MODULE_DIR_.'hotelreservationsystem/define.php'; - $objRoomType = new HotelRoomType(); - if ($roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($this->id_product)) { - if ($id_hotel = $roomTypeInfo['id_hotel']) { - if (strtotime($date_from) < strtotime(date('Y-m-d'))) { - $this->errors[] = Tools::displayError('You can\'t book room before current date'); - } elseif (strtotime($date_from) >= strtotime($date_to)) { - $this->errors[] = Tools::displayError('Check-out date must be after check-in date'); - } elseif ($maxOrdDate = HotelOrderRestrictDate::getMaxOrderDate($id_hotel)) { - // Check Order restrict condition before adding in to cart - if (strtotime('-1 day', strtotime($maxOrdDate)) < strtotime($date_from) - || strtotime($maxOrdDate) < strtotime($date_to) - ) { - $maxOrdDate = date('d-m-Y', strtotime($maxOrdDate)); - $this->errors[] = Tools::displayError('You can\'t book room after date '.$maxOrdDate); - } + // valdiate occupancy if providede + if ($operator == 'up') { + if ($occupancyRequiredForBooking) { + foreach($occupancy as $key => $roomOccupancy) { + if (!isset($roomOccupancy['adults']) || !$roomOccupancy['adults'] || !Validate::isUnsignedInt($roomOccupancy['adults'])) { + $this->errors[] = sprintf(Tools::displayError('Invalid number of adults for Room %s.'), ($key + 1)); + } + if (isset($roomOccupancy['children'])) { + if (!Validate::isUnsignedInt($roomOccupancy['children'])) { + $this->errors[] = sprintf(Tools::displayError('Invalid number of children for Room %s.'), ($key + 1)); } - if (!$this->errors) { - $objBookingDtl = new HotelBookingDetail(); - $num_days = $objBookingDtl->getNumberOfDays($date_from, $date_to); - $req_rm = $this->qty; - $this->qty = $this->qty * (int) $num_days; - $obj_booking_dtl = new HotelBookingDetail(); - if ($hotel_room_data = $obj_booking_dtl->DataForFrontSearch( - $date_from, - $date_to, - $id_hotel, - $this->id_product, - 1, - 0, - 0, - -1, - 0, - 0, - $id_cart, - $id_guest - )) { - if (isset($hotel_room_data['stats']['num_avail'])) { - $total_available_rooms = $hotel_room_data['stats']['num_avail']; - if (Tools::getValue('op', 'up') == 'up') { - if ($total_available_rooms < $req_rm) { - die(json_encode(array('status' => 'unavailable_quantity', 'avail_rooms' => $total_available_rooms))); + if ($roomOccupancy['children'] > 0) { + if (!isset($roomOccupancy['child_ages']) || ($roomOccupancy['children'] != count($roomOccupancy['child_ages'])) || !is_array($roomOccupancy['child_ages'])) { + $this->errors[] = sprintf(Tools::displayError('Please provide all children age for Room %s.'), ($key + 1)); + } else { + if (is_array($roomOccupancy['child_ages'])) { + foreach($roomOccupancy['child_ages'] as $childAge) { + if (!Validate::isUnsignedInt($childAge)) { + $this->errors[] = sprintf(Tools::displayError('Invalid children age for Room %s.'), ($key + 1)); } } - } else { - $this->errors[] = Tools::displayError('Rooms are unavailable. Please try with different dates'); + } + } + } + } + } + } + } + // By Webkul : This code is to check available quantity of Room before adding it to cart. + // only check availability if qty is increasing + if (!$this->errors) { + $objRoomType = new HotelRoomType(); + if ($roomTypeInfo = $objRoomType->getRoomTypeInfoByIdProduct($this->id_product)) { + if ($id_hotel = $roomTypeInfo['id_hotel']) { + if (strtotime($date_from) < strtotime(date('Y-m-d'))) { + $this->errors[] = Tools::displayError('You can\'t book room before current date'); + } elseif (strtotime($date_from) >= strtotime($date_to)) { + $this->errors[] = Tools::displayError('Check-out date must be after check-in date'); + } elseif ($maxOrdDate = HotelOrderRestrictDate::getMaxOrderDate($id_hotel)) { + // Check Order restrict condition before adding in to cart + if (strtotime('-1 day', strtotime($maxOrdDate)) < strtotime($date_from) + || strtotime($maxOrdDate) < strtotime($date_to) + ) { + $maxOrdDate = date('d-m-Y', strtotime($maxOrdDate)); + $this->errors[] = Tools::displayError('You can\'t book room after date '.$maxOrdDate); + } + } + if ($occupancyRequiredForBooking) { + foreach($occupancy as $key => $roomOccupancy) { + if ($roomOccupancy['adults'] > $roomTypeInfo['max_adults']) { + $this->errors[] = sprintf(Tools::displayError('Room %s cannot have adults more than %s'), $key + 1, $roomTypeInfo['max_adults']); + } + if ($roomOccupancy['children'] > $roomTypeInfo['max_children']) { + $this->errors[] = sprintf(Tools::displayError('Room %s cannot have children more than %s'), $key + 1, $roomTypeInfo['max_children']); + } + if ($roomOccupancy['adults'] + $roomOccupancy['children'] > $roomTypeInfo['max_guests']) { + $this->errors[] = sprintf(Tools::displayError('Room %s cannot have total guests more than %s'), $key + 1, $roomTypeInfo['max_guests']); + } + } + } + if (!$this->errors) { + $objBookingDetail = new HotelBookingDetail(); + $num_days = $objBookingDetail->getNumberOfDays($date_from, $date_to); + $req_rm = $this->qty; + $this->qty = $this->qty * (int) $num_days; + $objBookingDetail = new HotelBookingDetail(); + $bookingParams = array( + 'date_from' => $date_from, + 'date_to' => $date_to, + 'hotel_id' => $id_hotel, + 'id_room_type' => $this->id_product, + 'only_search_data' => 1, + 'id_cart' => $id_cart, + 'id_guest' => $id_guest, + ); + if ($hotelRoomData = $objBookingDetail->dataForFrontSearch($bookingParams)) { + if (isset($hotelRoomData['stats']['num_avail'])) { + $total_available_rooms = $hotelRoomData['stats']['num_avail']; + if (Tools::getValue('op', 'up') == 'up') { + if ($total_available_rooms < $req_rm) { + die(json_encode(array('status' => 'unavailable_quantity', 'avail_rooms' => $total_available_rooms))); + } } } else { $this->errors[] = Tools::displayError('Rooms are unavailable. Please try with different dates'); } + } else { + $this->errors[] = Tools::displayError('Rooms are unavailable. Please try with different dates'); } - } else { - die(json_encode(array('status' => 'failed3'))); } } else { - die(json_encode(array('status' => 'failed4'))); + die(json_encode(array('status' => 'failed3'))); } + } else { + die(json_encode(array('status' => 'failed4'))); + } } + $cart_products = $this->context->cart->getProducts(); + $qty_to_check = $this->qty; if (is_array($cart_products)) { foreach ($cart_products as $cart_product) { if ((!isset($this->id_product_attribute) || $cart_product['id_product_attribute'] == $this->id_product_attribute) && @@ -411,54 +463,37 @@ class CartControllerCore extends FrontController /* * To add Rooms in hotel cart */ - if (Module::isInstalled('hotelreservationsystem') && Module::isEnabled('hotelreservationsystem')) { - if (Tools::getValue('op', 'up') == 'up') { - $update_quantity = $this->context->cart->updateQty($this->qty, $this->id_product, $this->id_product_attribute, $this->customization_id, Tools::getValue('op', 'up'), $this->id_address_delivery); - $this->availQty = false; - $id_customer = $this->context->cart->id_customer; - $id_currency = $this->context->cart->id_currency; - $hotel_room_info_arr = $hotel_room_data['rm_data'][0]['data']['available']; - $chkQty = 0; - foreach ($hotel_room_info_arr as $key_hotel_room_info => $val_hotel_room_info) { - if ($chkQty < $req_rm) { - $roomDemand = Tools::getValue('roomDemands'); - $roomDemand = json_decode($roomDemand, true); - $roomDemand = json_encode($roomDemand); - $obj_htl_cart_booking_data = new HotelCartBookingData(); - $obj_htl_cart_booking_data->id_cart = $this->context->cart->id; - $obj_htl_cart_booking_data->id_guest = $this->context->cart->id_guest; - $obj_htl_cart_booking_data->id_customer = $id_customer; - $obj_htl_cart_booking_data->id_currency = $id_currency; - $obj_htl_cart_booking_data->id_product = $val_hotel_room_info['id_product']; - $obj_htl_cart_booking_data->id_room = $val_hotel_room_info['id_room']; - $obj_htl_cart_booking_data->id_hotel = $val_hotel_room_info['id_hotel']; - $obj_htl_cart_booking_data->booking_type = HotelBookingDetail::ALLOTMENT_AUTO; - $obj_htl_cart_booking_data->quantity = $num_days; - $obj_htl_cart_booking_data->extra_demands = $roomDemand; - $obj_htl_cart_booking_data->date_from = $date_from; - $obj_htl_cart_booking_data->date_to = $date_to; - $obj_htl_cart_booking_data->save(); - ++$chkQty; - } else { - break; - } - } - $this->availQty = $total_available_rooms - $req_rm; - } else { - $objCartBooking = new HotelCartBookingData(); - $update_quantity = $objCartBooking->deleteCartBookingData( - $id_cart, - $this->id_product, - 0, - $date_from, - $date_to - ); - $this->availQty = $total_available_rooms + $req_rm; - - } - $this->context->cookie->avail_rooms = $this->availQty; + $objHotelCartBookingData = new HotelCartBookingData(); + $roomDemand = json_decode(Tools::getValue('roomDemands'), true); + $roomDemand = json_encode($roomDemand); + $this->availQty = $total_available_rooms; + $update_quantity = $objHotelCartBookingData->updateRoomCartBookingData( + $this->id_product, + $occupancy, + $operator, + $id_hotel, + $date_from, + $date_to, + $roomDemand, + $id_cart, + $id_guest + ); + if ($operator == 'up') { + $this->availQty = $total_available_rooms - $req_rm; + $this->context->cookie->currentAddedProduct = json_encode(array( + 'date_from' => $date_from, + 'date_to' => $date_to, + 'id_product' => $this->id_product, + 'occupancy' => $occupancy, + 'room_demand' => $roomDemand, + 'req_rm' => $req_rm + )); + } else { + $this->availQty = $total_available_rooms + $req_rm; } + $this->context->cookie->avail_rooms = $this->availQty; + /*------ BY Webkul ------*/ if ($update_quantity < 0) { diff --git a/controllers/front/CategoryController.php b/controllers/front/CategoryController.php index 607257ac1..56512a942 100644 --- a/controllers/front/CategoryController.php +++ b/controllers/front/CategoryController.php @@ -57,6 +57,9 @@ class CategoryControllerCore extends FrontController )); } + $this->addCSS(_THEME_CSS_DIR_.'occupancy.css'); + $this->addJS(_THEME_JS_DIR_.'occupancy.js'); + $scenes = Scene::getScenes($this->category->id, $this->context->language->id, true, false); if ($scenes && count($scenes)) { $this->addJS(_THEME_JS_DIR_.'scenes.js'); @@ -99,11 +102,15 @@ class CategoryControllerCore extends FrontController $dateFrom = Tools::getValue('date_from'); $dateTo = Tools::getValue('date_to'); - if ($dateFrom != '' && !Validate::isDate($dateFrom)) { + $currentTimestamp = strtotime(date('Y-m-d')); + $dateFromTimestamp = strtotime($dateFrom); + $dateToTimestamp = strtotime($dateTo); + + if ($dateFrom != '' && ($dateFromTimestamp === false || ($dateFromTimestamp < $currentTimestamp))) { Tools::redirect($this->context->link->getPageLink('pagenotfound')); } - if ($dateTo != '' && !Validate::isDate($dateTo)) { + if ($dateTo != '' && ($dateToTimestamp === false || ($dateToTimestamp < $currentTimestamp))) { Tools::redirect($this->context->link->getPageLink('pagenotfound')); } @@ -150,6 +157,9 @@ class CategoryControllerCore extends FrontController $date_to = date('Y-m-d', strtotime($date_from) + 86400); } + // get occupancy of the search + $occupancy = Tools::getValue('occupancy'); + $currency = new Currency($this->context->currency->id); if (Module::isInstalled('hotelreservationsystem')) { @@ -160,8 +170,18 @@ class CategoryControllerCore extends FrontController $id_cart = $this->context->cart->id; $id_guest = $this->context->cookie->id_guest; - $obj_booking_dtl = new HotelBookingDetail(); - $booking_data = $obj_booking_dtl->DataForFrontSearch($date_from, $date_to, $id_hotel, 0, 0, 0, 0, -1, 0, 0, $id_cart, $id_guest); + $objBookingDetail = new HotelBookingDetail(); + $bookingParams = array( + 'date_from' => $date_from, + 'date_to' => $date_to, + 'occupancy' => $occupancy, + 'hotel_id' => $id_hotel, + 'get_total_rooms' => 0, + 'id_cart' => $id_cart, + 'id_guest' => $id_guest, + ); + + $booking_data = $objBookingDetail->dataForFrontSearch($bookingParams); $feat_img_dir = _PS_IMG_.'rf/'; $ratting_img = _MODULE_DIR_.'hotelreservationsystem/views/img/Slices/icons-sprite.png'; @@ -239,21 +259,24 @@ class CategoryControllerCore extends FrontController $date_to = Tools::getValue('date_to'); $htl_id_category = Tools::getValue('id_category'); + // occupancy of the search + $occupancy = Tools::getValue('occupancy'); + $sort_by = Tools::getValue('sort_by'); $sort_value = Tools::getValue('sort_value'); $filter_data = Tools::getValue('filter_data'); - $adult = 0; - $child = 0; + $adults = 0; + $children = 0; $amenities = 0; $price = 0; if (!empty($filter_data)) { foreach ($filter_data as $key => $value) { - if ($key == 'adult') { - $adult = min($value); + if ($key == 'adults') { + $adults = min($value); } elseif ($key == 'children') { - $child = min($value); + $children = min($value); } elseif ($key == 'amenities') { $amenities = array(); foreach ($value as $a_k => $a_v) { @@ -272,11 +295,21 @@ class CategoryControllerCore extends FrontController $id_hotel = HotelBranchInformation::getHotelIdByIdCategory($htl_id_category); - $id_cart = $this->context->cart->id; - $id_guest = $this->context->cookie->id_guest; + $objBookingDetail = new HotelBookingDetail(); - $obj_booking_dtl = new HotelBookingDetail(); - $booking_data = $obj_booking_dtl->DataForFrontSearch($date_from, $date_to, $id_hotel, 0, 0, $adult, $child, -1, $amenities, $price, $id_cart, $id_guest); + $bookingParams = array( + 'date_from' => $date_from, + 'date_to' => $date_to, + 'hotel_id' => $id_hotel, + 'occupancy' => $occupancy, + 'amenities' => $amenities, + 'price' => $price, + 'get_total_rooms' => 0, + 'id_cart' => $this->context->cart->id, + 'id_guest' => $this->context->cookie->id_guest, + ); + + $booking_data = $objBookingDetail->dataForFrontSearch($bookingParams); // reset array keys from 0 $booking_data['rm_data'] = array_values($booking_data['rm_data']); if ($sort_by && $sort_value) { diff --git a/controllers/front/GuestTrackingController.php b/controllers/front/GuestTrackingController.php index e497e0a1e..e227fe592 100644 --- a/controllers/front/GuestTrackingController.php +++ b/controllers/front/GuestTrackingController.php @@ -250,7 +250,7 @@ class GuestTrackingControllerCore extends FrontController $cartHotelData[$type_key]['id_product'] = $type_value['product_id']; $cartHotelData[$type_key]['cover_img'] = $cover_img; - $cartHotelData[$type_key]['adult'] = $rm_dtl['adult']; + $cartHotelData[$type_key]['adults'] = $rm_dtl['adults']; $cartHotelData[$type_key]['children'] = $rm_dtl['children']; $objBookingDemand = new HotelBookingDemands(); diff --git a/controllers/front/OrderConfirmationController.php b/controllers/front/OrderConfirmationController.php index 868eb6fee..228dde274 100644 --- a/controllers/front/OrderConfirmationController.php +++ b/controllers/front/OrderConfirmationController.php @@ -155,8 +155,9 @@ class OrderConfirmationControllerCore extends FrontController $cart_htl_data[$type_key]['id_product'] = $type_value['product_id']; $cart_htl_data[$type_key]['cover_img'] = $cover_img; - $cart_htl_data[$type_key]['adult'] = $rm_dtl['adult']; + $cart_htl_data[$type_key]['adults'] = $rm_dtl['adults']; $cart_htl_data[$type_key]['children'] = $rm_dtl['children']; + foreach ($order_bk_data as $data_k => $data_v) { $date_join = strtotime($data_v['date_from']).strtotime($data_v['date_to']); /*Product price when order was created*/ @@ -176,6 +177,9 @@ class OrderConfirmationControllerCore extends FrontController $num_days = $cart_htl_data[$type_key]['date_diff'][$date_join]['num_days']; $var_quant = (int) $cart_htl_data[$type_key]['date_diff'][$date_join]['num_rm']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['adults'] += $data_v['adults']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['children'] += $data_v['children']; + //// By webkul New way to calculate product prices with feature Prices $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; @@ -195,6 +199,10 @@ class OrderConfirmationControllerCore extends FrontController $cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = $data_v['date_to']; $cart_htl_data[$type_key]['date_diff'][$date_join]['num_days'] = $num_days; + $cart_htl_data[$type_key]['date_diff'][$date_join]['adults'] = $data_v['adults']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['children'] = $data_v['children']; + + // By webkul New way to calculate product prices with feature Prices $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; diff --git a/controllers/front/OrderDetailController.php b/controllers/front/OrderDetailController.php index 07d84ff97..fdb54ae3a 100644 --- a/controllers/front/OrderDetailController.php +++ b/controllers/front/OrderDetailController.php @@ -246,7 +246,7 @@ class OrderDetailControllerCore extends FrontController } } // Emails to customer, admin on refund request state change - $objOrderReturn->changeIdOrderReturnState(OrderReturnState::ORDER_RETRUN_FIRST_STATUS, $objOrderReturn->id); + $objOrderReturn->changeIdOrderReturnState(Configuration::get('PS_ORS_PENDING')); die(json_encode(array('status' => 1))); } @@ -321,8 +321,6 @@ class OrderDetailControllerCore extends FrontController $rm_dtl = $objRoomType->getRoomTypeInfoByIdProduct($type_value['product_id']); $cartHotelData[$type_key]['id_product'] = $type_value['product_id']; $cartHotelData[$type_key]['cover_img'] = $cover_img; - $cartHotelData[$type_key]['adult'] = $rm_dtl['adult']; - $cartHotelData[$type_key]['children'] = $rm_dtl['children']; $objBookingDemand = new HotelBookingDemands(); foreach ($order_bk_data as $data_k => $data_v) { @@ -339,6 +337,9 @@ class OrderDetailControllerCore extends FrontController $num_days = $cartHotelData[$type_key]['date_diff'][$date_join]['num_days']; + $cartHotelData[$type_key]['date_diff'][$date_join]['adults'] += $data_v['adults']; + $cartHotelData[$type_key]['date_diff'][$date_join]['children'] += $data_v['children']; + $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; $cartHotelData[$type_key]['date_diff'][$date_join]['avg_paid_unit_price_tax_excl'] += $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl']; @@ -355,6 +356,8 @@ class OrderDetailControllerCore extends FrontController $cartHotelData[$type_key]['date_diff'][$date_join]['data_form'] = $data_v['date_from']; $cartHotelData[$type_key]['date_diff'][$date_join]['data_to'] = $data_v['date_to']; $cartHotelData[$type_key]['date_diff'][$date_join]['num_days'] = $num_days; + $cartHotelData[$type_key]['date_diff'][$date_join]['adults'] = $data_v['adults']; + $cartHotelData[$type_key]['date_diff'][$date_join]['children'] = $data_v['children']; $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; @@ -428,7 +431,6 @@ class OrderDetailControllerCore extends FrontController $redirectTermsLink = $this->context->link->getCMSLink(new CMS(3, $this->context->language->id), null, $this->context->language->id); } - $this->context->smarty->assign( array( 'id_cms_refund_policy' => Configuration::get('WK_GLOBAL_REFUND_POLICY_CMS'), diff --git a/controllers/front/OrderSlipController.php b/controllers/front/OrderSlipController.php index 26a5372c8..2b33b6cad 100644 --- a/controllers/front/OrderSlipController.php +++ b/controllers/front/OrderSlipController.php @@ -53,4 +53,34 @@ class OrderSlipControllerCore extends FrontController $this->context->smarty->assign('ordersSlip', OrderSlip::getOrdersSlip((int)$this->context->cookie->id_customer)); $this->setTemplate(_PS_THEME_DIR_.'order-slip.tpl'); } + + public function postProcess() + { + if (Tools::isSubmit('generateVoucher')) { + $idOrderSlip = Tools::getValue('id_order_slip'); + $objOrderSlip = new OrderSlip($idOrderSlip); + if (!Validate::isLoadedObject($objOrderSlip)) { + $this->errors[] = Tools::displayError('The credit slip is invalid.'); + } elseif ($objOrderSlip->redeem_status == OrderSlip::REDEEM_STATUS_REDEEMED) { + $this->errors[] = Tools::displayError('The credit slip has already been redeemed.'); + } elseif ($objOrderSlip->id_customer != $this->context->customer->id) { + $this->errors[] = Tools::displayError('Invalid request.'); + } + + if (!count($this->errors)) { + if ($objOrderSlip->generateVoucher()) { + Tools::redirect($this->context->link->getPageLink( + $this->php_self, + $this->ssl, + $this->context->language->id, + 'confirmation=1' + )); + } + + $this->errors[] = Tools::displayError('The voucher code for this credit slip could not be generated.'); + } + } + + parent::postProcess(); + } } diff --git a/controllers/front/ProductController.php b/controllers/front/ProductController.php index 997053ca9..0b7c74664 100644 --- a/controllers/front/ProductController.php +++ b/controllers/front/ProductController.php @@ -46,10 +46,10 @@ class ProductControllerCore extends FrontController $this->addCSS(_THEME_CSS_DIR_.'product.css'); $this->addCSS(_THEME_CSS_DIR_.'print.css', 'print'); $this->addJqueryPlugin(array('fancybox', 'idTabs', 'scrollTo', 'serialScroll', 'bxslider')); - $this->addCSS(_THEME_CSS_DIR_.'datepicker.css'); + $this->addJS(array( _THEME_JS_DIR_.'tools.js', // retro compat themes 1.5 - _THEME_JS_DIR_.'product.js' + _THEME_JS_DIR_.'product.js', )); } else { $this->addJqueryPlugin(array('scrollTo', 'serialScroll')); @@ -60,6 +60,9 @@ class ProductControllerCore extends FrontController )); } + $this->addCSS(_THEME_CSS_DIR_.'occupancy.css'); + $this->addJS(_THEME_JS_DIR_.'occupancy.js'); + if (Configuration::get('PS_DISPLAY_JQZOOM') == 1) { $this->addJqueryPlugin('jqzoom'); } @@ -92,11 +95,15 @@ class ProductControllerCore extends FrontController $dateFrom = Tools::getValue('date_from'); $dateTo = Tools::getValue('date_to'); - if ($dateFrom != '' && !Validate::isDate($dateFrom)) { + $currentTimestamp = strtotime(date('Y-m-d')); + $dateFromTimestamp = strtotime($dateFrom); + $dateToTimestamp = strtotime($dateTo); + + if ($dateFrom != '' && ($dateFromTimestamp === false || ($dateFromTimestamp < $currentTimestamp))) { Tools::redirect($this->context->link->getPageLink('pagenotfound')); } - if ($dateTo != '' && !Validate::isDate($dateTo)) { + if ($dateTo != '' && ($dateToTimestamp === false || ($dateToTimestamp < $currentTimestamp))) { Tools::redirect($this->context->link->getPageLink('pagenotfound')); } @@ -287,7 +294,7 @@ class ProductControllerCore extends FrontController $obj_hotel_room_type = new HotelRoomType(); $room_info_by_product_id = $obj_hotel_room_type->getRoomTypeInfoByIdProduct($this->product->id); $productCapacity = array(); - $productCapacity['adult'] = $room_info_by_product_id['adult']; + $productCapacity['adults'] = $room_info_by_product_id['adults']; $productCapacity['children'] = $room_info_by_product_id['children']; $this->product->capacity = $productCapacity; $hotel_id = $room_info_by_product_id['id_hotel']; @@ -359,6 +366,7 @@ class ProductControllerCore extends FrontController $this->context->smarty->assign( array( + 'room_type_info' => $room_info_by_product_id, 'isHotelRefundable' => $hotel_branch_obj->isRefundable(), 'max_order_date' => $max_order_date, 'preparation_time' => $preparationTime, @@ -471,7 +479,7 @@ class ProductControllerCore extends FrontController $idProduct, $dateFrom, $dateTo, - $quantity = 1, + $occupancy = array(), $jsonDemands = '' ) { $objProduct = new Product($idProduct, true, $this->context->language->id, $this->context->shop->id); @@ -509,23 +517,22 @@ class ProductControllerCore extends FrontController } $numDays = $objBookingDetail->getNumberOfDays($dateFrom, $dateTo); - - $hotelRoomData = $objBookingDetail->DataForFrontSearch( - $dateFrom, - $dateTo, - $idHotel, - $idProduct, - 1, - 0, - 0, - -1, - 0, - 0, - $idCart, - $idGuest + $bookingParams = array( + 'date_from' => $dateFrom, + 'date_to' => $dateTo, + 'hotel_id' => $idHotel, + 'id_room_type' => $idProduct, + 'id_cart' => $idCart, + 'id_guest' => $idGuest, ); + if (Configuration::get('PS_FRONT_ROOM_UNIT_SELECTION_TYPE') == HotelBookingDetail::PS_FRONT_ROOM_UNIT_SELECTION_TYPE_OCCUPANCY) { + $bookingParams['occupancy'] = $occupancy; + $quantity = count($occupancy); + } else { + $quantity = $occupancy; + } - if ($hotelRoomData) { + if ($hotelRoomData = $objBookingDetail->DataForFrontSearch($bookingParams)) { $totalAvailableRooms = $hotelRoomData['stats']['num_avail']; $quantity = ($quantity > $totalAvailableRooms) ? $totalAvailableRooms : $quantity; } @@ -580,6 +587,13 @@ class ProductControllerCore extends FrontController // calculate total price $totalPrice = $totalRoomPrice + $demandsPrice; + // send occupancy information searched by the user + if ($this->ajax && $occupancy && is_array($occupancy)) { + $smartyVars['occupancies'] = $occupancy; + $smartyVars['occupancy_adults'] = array_sum(array_column($occupancy, 'adults')); + $smartyVars['occupancy_children'] = array_sum(array_column($occupancy, 'children')); + $smartyVars['occupancy_child_ages'] = array_sum(array_column($occupancy, 'child_ages')); + } $smartyVars['hotel_location'] = $hotelLocation; $smartyVars['order_date_restrict'] = $orderDateRestrict; @@ -1070,7 +1084,7 @@ class ProductControllerCore extends FrontController $idProduct = (int) Tools::getValue('id_product'); $dateFrom = Tools::getValue('date_from'); $dateTo = Tools::getValue('date_to'); - $quantity = (int) Tools::getValue('quantity'); + $occupancy = Tools::getValue('occupancy'); $roomTypeDemands = Tools::getValue('room_type_demands'); $dateFrom = date('Y-m-d', strtotime($dateFrom)); @@ -1080,7 +1094,7 @@ class ProductControllerCore extends FrontController $idProduct, $dateFrom, $dateTo, - $quantity, + $occupancy, $roomTypeDemands )) { $html = $this->context->smarty->fetch('_partials/booking-form.tpl'); @@ -1091,102 +1105,6 @@ class ProductControllerCore extends FrontController $this->ajaxDie(json_encode($response)); } - public function displayAjaxCheckRoomAvailabilityAndRate() - { - $result = array(); - if ($idProduct = Tools::getValue('id_product')) { - $objHotelRoomType = new HotelRoomType(); - if ($roomTypeInfo = $objHotelRoomType->getRoomTypeInfoByIdProduct($idProduct)) { - $dateFrom = Tools::getValue('date_from'); - $dateTo = Tools::getValue('date_to'); - $quantity = Tools::getValue('qty'); - if ($idHotel = $roomTypeInfo['id_hotel']) { - $objBookingDetail = new HotelBookingDetail(); - if ($hotelRoomData = $objBookingDetail->DataForFrontSearch( - $dateFrom, - $dateTo, - $idHotel, - $idProduct, - 1, - 0, - 0, - -1, - 0, - 0, - $this->context->cart->id, - $this->context->cart->id_guest - )) { - $totalAvailRooms = $hotelRoomData['stats']['num_avail']; - if ($totalAvailRooms >= $quantity) { - $totalPrice = 0; - $useTax = HotelBookingDetail::useTax(); - $product = new Product($idProduct); - $productPriceWithoutReduction = $product->getPriceWithoutReduct(!$useTax); - $numDays = $objBookingDetail->getNumberOfDays($dateFrom, $dateTo); - $roomTypeDateRangePrice = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( - $idProduct, - $dateFrom, - $dateTo - ); - if ($useTax) { - $priceProduct = Product::getPriceStatic($idProduct, true); - $featurePrice = HotelRoomTypeFeaturePricing::getRoomTypeFeaturePricesPerDay($idProduct, $dateFrom, $dateTo, true); - $roomTypeDateRangePrice = $roomTypeDateRangePrice['total_price_tax_incl']; - } else { - $priceProduct = Product::getPriceStatic($idProduct, false); - $featurePrice = HotelRoomTypeFeaturePricing::getRoomTypeFeaturePricesPerDay($idProduct, $dateFrom, $dateTo, false); - $roomTypeDateRangePrice = $roomTypeDateRangePrice['total_price_tax_excl']; - } - $featurePriceDiff = (float)($productPriceWithoutReduction - $featurePrice); - - //$price_tax_incl = Product::getPriceStatic($idProduct, $price_tax); - $totalRoomPrice = $roomTypeDateRangePrice * $quantity; - $totalPrice += $totalRoomPrice; - $demandsPrice = 0; - if ($roomDemand = Tools::getValue('room_demands')) { - if ($roomDemand = json_decode($roomDemand, true)) { - $objRoomDemandPrice = new HotelRoomTypeDemandPrice(); - $demandsPrice = $objRoomDemandPrice->getRoomTypeDemandsTotalPrice( - $idProduct, - $roomDemand, - $useTax, - $dateFrom, - $dateTo - ); - $demandsPrice *= $quantity; - $totalPrice += $demandsPrice; - } - } - $result['msg'] = 'success'; - $result['quantity'] = (int)$quantity; - $result['total_price'] = $totalPrice; - $result['total_room_price'] = $totalRoomPrice; - $result['extra_demand_price'] = $demandsPrice; - $result['num_days'] = $numDays; - $result['avail_rooms'] = $totalAvailRooms; - $result['original_product_price'] = $priceProduct; - $result['feature_price'] = $featurePrice; - $result['feature_price_diff'] = $featurePriceDiff; - } else { - $result['msg'] = 'unavailable_quantity'; - $result['avail_rooms'] = $totalAvailRooms; - } - } else { - $result['msg'] = 'failed1'; - $result['avail_rooms'] = $totalAvailRooms; - } - } else { - $result['msg'] = 'failed2'; - $result['avail_rooms'] = $totalAvailRooms; - } - } - } else { - $result['msg'] = 'failed3'; - $result['avail_rooms'] = 0; - } - die(json_encode($result)); - } - public function displayAjaxGetHotelImages() { $response = array('status' => false); diff --git a/install/data/db_structure.sql b/install/data/db_structure.sql index 0955d4198..68057f487 100644 --- a/install/data/db_structure.sql +++ b/install/data/db_structure.sql @@ -1166,6 +1166,7 @@ CREATE TABLE `PREFIX_orders` ( `valid` int(1) unsigned NOT NULL DEFAULT '0', `is_advance_payment` tinyint(1) NOT NULL DEFAULT '0', `advance_paid_amount` decimal(20,6) NOT NULL DEFAULT '0.00', + `with_occupancy` tinyint(1) NOT NULL DEFAULT '0', `date_add` datetime NOT NULL, `date_upd` datetime NOT NULL, PRIMARY KEY (`id_order`), @@ -1330,6 +1331,8 @@ CREATE TABLE `PREFIX_order_return` ( `refunded_amount` decimal(20,6) NOT NULL DEFAULT '0.000000', `question` text NOT NULL, `by_admin` tinyint(1) unsigned NOT NULL DEFAULT '0', + `id_return_type` tinyint(1) unsigned NOT NULL DEFAULT '0', + `return_type` tinyint(1) unsigned NOT NULL DEFAULT '0', `date_add` datetime NOT NULL, `date_upd` datetime NOT NULL, PRIMARY KEY (`id_order_return`), @@ -1384,6 +1387,8 @@ CREATE TABLE `PREFIX_order_slip` ( `amount` DECIMAL(10,2) NOT NULL, `shipping_cost_amount` DECIMAL(10,2) NOT NULL, `partial` TINYINT(1) NOT NULL, + `redeem_status` TINYINT(1) NOT NULL DEFAULT '0', + `id_cart_rule` int(10) unsigned NOT NULL DEFAULT '0', `order_slip_type` TINYINT(1) unsigned NOT NULL DEFAULT '0', `date_add` datetime NOT NULL, `date_upd` datetime NOT NULL, diff --git a/install/data/theme.sql b/install/data/theme.sql index 9ce4b813b..ec71efd71 100644 --- a/install/data/theme.sql +++ b/install/data/theme.sql @@ -3,6 +3,8 @@ SET NAMES 'utf8'; UPDATE `PREFIX_configuration` SET value = '1' WHERE name = 'PS_CONDITIONS'; UPDATE `PREFIX_configuration` SET value = '12' WHERE name = 'PS_PRODUCTS_PER_PAGE'; UPDATE `PREFIX_configuration` SET value = '9' WHERE name = 'PS_HOTEL_IMAGES_PER_PAGE'; +UPDATE `PREFIX_configuration` SET value = '1' WHERE name = 'SHOW_AMENITIES_FILTER'; +UPDATE `PREFIX_configuration` SET value = '1' WHERE name = 'SHOW_PRICE_FILTER'; UPDATE `PREFIX_configuration` SET value = '0' WHERE name = 'PS_PRODUCTS_ORDER_WAY'; UPDATE `PREFIX_configuration` SET value = '4' WHERE name = 'PS_PRODUCTS_ORDER_BY'; UPDATE `PREFIX_configuration` SET value = '1' WHERE name = 'PS_DISPLAY_QTIES'; diff --git a/install/data/xml/configuration.xml b/install/data/xml/configuration.xml index 1f47c24e9..4eaa78730 100644 --- a/install/data/xml/configuration.xml +++ b/install/data/xml/configuration.xml @@ -62,6 +62,12 @@ 9 + + 1 + + + 1 + 0 @@ -431,6 +437,15 @@ + + 1 + + + 2 + + + 3 + 0 @@ -449,6 +464,9 @@ 480 + + Lax + 0 diff --git a/install/data/xml/order_return_state.xml b/install/data/xml/order_return_state.xml index bac44cc62..831f53aea 100644 --- a/install/data/xml/order_return_state.xml +++ b/install/data/xml/order_return_state.xml @@ -4,8 +4,7 @@ - - + diff --git a/install/langs/bg/data/order_return_state.xml b/install/langs/bg/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/bg/data/order_return_state.xml +++ b/install/langs/bg/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/bn/data/order_return_state.xml b/install/langs/bn/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/bn/data/order_return_state.xml +++ b/install/langs/bn/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/br/data/order_return_state.xml b/install/langs/br/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/br/data/order_return_state.xml +++ b/install/langs/br/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/cs/data/order_return_state.xml b/install/langs/cs/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/cs/data/order_return_state.xml +++ b/install/langs/cs/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/de/data/order_return_state.xml b/install/langs/de/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/de/data/order_return_state.xml +++ b/install/langs/de/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/en/data/order_return_state.xml b/install/langs/en/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/en/data/order_return_state.xml +++ b/install/langs/en/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/es/data/order_return_state.xml b/install/langs/es/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/es/data/order_return_state.xml +++ b/install/langs/es/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/fa/data/order_return_state.xml b/install/langs/fa/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/fa/data/order_return_state.xml +++ b/install/langs/fa/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/fr/data/order_return_state.xml b/install/langs/fr/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/fr/data/order_return_state.xml +++ b/install/langs/fr/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/he/data/order_return_state.xml b/install/langs/he/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/he/data/order_return_state.xml +++ b/install/langs/he/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/hr/data/order_return_state.xml b/install/langs/hr/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/hr/data/order_return_state.xml +++ b/install/langs/hr/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/hu/data/order_return_state.xml b/install/langs/hu/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/hu/data/order_return_state.xml +++ b/install/langs/hu/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/id/data/order_return_state.xml b/install/langs/id/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/id/data/order_return_state.xml +++ b/install/langs/id/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/it/data/order_return_state.xml b/install/langs/it/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/it/data/order_return_state.xml +++ b/install/langs/it/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/lt/data/order_return_state.xml b/install/langs/lt/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/lt/data/order_return_state.xml +++ b/install/langs/lt/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/mk/data/order_return_state.xml b/install/langs/mk/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/mk/data/order_return_state.xml +++ b/install/langs/mk/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/nl/data/order_return_state.xml b/install/langs/nl/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/nl/data/order_return_state.xml +++ b/install/langs/nl/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/no/data/order_return_state.xml b/install/langs/no/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/no/data/order_return_state.xml +++ b/install/langs/no/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/pl/data/order_return_state.xml b/install/langs/pl/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/pl/data/order_return_state.xml +++ b/install/langs/pl/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/pt/data/order_return_state.xml b/install/langs/pt/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/pt/data/order_return_state.xml +++ b/install/langs/pt/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/qc/data/order_return_state.xml b/install/langs/qc/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/qc/data/order_return_state.xml +++ b/install/langs/qc/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/ro/data/order_return_state.xml b/install/langs/ro/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/ro/data/order_return_state.xml +++ b/install/langs/ro/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/ru/data/order_return_state.xml b/install/langs/ru/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/ru/data/order_return_state.xml +++ b/install/langs/ru/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/si/data/order_return_state.xml b/install/langs/si/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/si/data/order_return_state.xml +++ b/install/langs/si/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/sr/data/order_return_state.xml b/install/langs/sr/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/sr/data/order_return_state.xml +++ b/install/langs/sr/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/sv/data/order_return_state.xml b/install/langs/sv/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/sv/data/order_return_state.xml +++ b/install/langs/sv/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/tw/data/order_return_state.xml b/install/langs/tw/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/tw/data/order_return_state.xml +++ b/install/langs/tw/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/vn/data/order_return_state.xml b/install/langs/vn/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/vn/data/order_return_state.xml +++ b/install/langs/vn/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/install/langs/zh/data/order_return_state.xml b/install/langs/zh/data/order_return_state.xml index 370d7c3e1..c7b945970 100644 --- a/install/langs/zh/data/order_return_state.xml +++ b/install/langs/zh/data/order_return_state.xml @@ -1,14 +1,9 @@ - - Waiting for confirmation - order_refund_waiting_customer - order_refund_waiting_admin - - - Request received - order_refund_received_customer - order_refund_received_admin + + Pending + order_refund_pending_customer + order_refund_pending_admin Refund denied diff --git a/js/admin/orders.js b/js/admin/orders.js index c5cd90028..812db8ea6 100644 --- a/js/admin/orders.js +++ b/js/admin/orders.js @@ -383,6 +383,15 @@ function init() else { $('tr#new_product input, tr#new_product select, tr#new_product button').removeAttr('disabled'); + $('tr#new_product .booking_guest_occupancy').removeClass('disabled'); + if (data.room_type_info) { + $('tr#new_product .max_adults').val(data.room_type_info.max_adults); + $('tr#new_product .max_children').val(data.room_type_info.max_children); + $('tr#new_product .max_guests').val(data.room_type_info.max_guests); + $('tr#new_product .num_adults').attr('max', data.room_type_info.max_adults); + $('tr#new_product .num_children').attr('max', data.room_type_info.max_children); + } + // Keep product variable current_product = data; $('#add_product_product_id').val(data.id_product); @@ -458,10 +467,220 @@ function init() else $('#add_product_product_stock').css('font-weight', 'normal').css('color', 'black').css('font-size', '1em'); } + // update occupancy input + // updateOccupancyInput() // total update addProductRefreshTotal(); }); + function setRoomTypeGuestOccupancy(booking_occupancy_wrapper) + { + var adults = 0; + var children = 0; + var rooms = $(booking_occupancy_wrapper).find('.occupancy_info_block').length; + + $(booking_occupancy_wrapper).find(".num_adults" ).each(function(key, val) { + adults += parseInt($(this).val()); + }); + $(booking_occupancy_wrapper).find(".num_children" ).each(function(key, val) { + children += parseInt($(this).val()); + }); + + var guestButtonVal = parseInt(adults) + ' '; + if (parseInt(adults) > 1) { + guestButtonVal += adults_txt; + } else { + guestButtonVal += adult_txt; + } + if (parseInt(children) > 0) { + if (parseInt(children) > 1) { + guestButtonVal += ', ' + parseInt(children) + ' ' + children_txt; + } else { + guestButtonVal += ', ' + parseInt(children) + ' ' + child_txt; + } + } + if (parseInt(rooms) > 1) { + guestButtonVal += ', ' + parseInt(rooms) + ' ' + rooms_txt; + } + + $(booking_occupancy_wrapper).siblings('.booking_guest_occupancy').find('span').text(guestButtonVal); + } + + $(document).on('click', '.booking_occupancy_wrapper .remove-room-link', function(e) { + e.preventDefault(); + + booking_occupancy_inner = $(this).closest('.booking_occupancy_inner'); + $(this).closest('.occupancy_info_block').remove(); + $(booking_occupancy_inner).find('.room_num_wrapper').each(function(key, val) { + $(this).text(room_txt + ' - '+ (key+1) ); + }); + setRoomTypeGuestOccupancy($(booking_occupancy_inner).closest('.booking_occupancy_wrapper')); + }); + + $(document).on('change', '.num_occupancy', function(e) { + let current_room_occupancy = 0; + $(this).closest('.occupancy_info_block').find('.num_occupancy').each(function(){ + current_room_occupancy += parseInt($(this).val()); + }); + let max_guests_in_room = $(this).closest(".booking_occupancy_wrapper").find('.max_guests').val(); + let max_allowed_for_current = (max_guests_in_room - current_room_occupancy) + parseInt($(this).val()); + if ($(this).val() > $(this).attr('max')) { + $(this).val($(this).attr('max')); + } + if ($(this).val() < $(this).attr('min')) { + $(this).val($(this).attr('min')); + } + if ($(this).val() > max_allowed_for_current) { + $(this).val(max_allowed_for_current); + } + if ($(this).hasClass('num_children')) { + var totalChilds = $(this).closest('.occupancy_info_block').find('.guest_child_age').length; + if (totalChilds < $(this).val()) { + let max_child_in_room; + if ($(this).closest(".booking_occupancy_wrapper").find('.max_children').val()) { + max_child_in_room = $(this).closest(".booking_occupancy_wrapper").find('.max_children').val(); + } else { + max_child_in_room = window.max_child_in_room; + } + if (totalChilds < max_child_in_room) { + $(this).closest('.occupancy_info_block').find('.children_age_info_block').show(); + while ($(this).closest('.occupancy_info_block').find('.guest_child_age').length < $(this).val()) { + + + var roomBlockIndex = parseInt($(this).closest('.occupancy_info_block').attr('occ_block_index')); + + var childAgeSelect = '

'; + childAgeSelect += ''; + childAgeSelect += '

'; + $(this).closest('.occupancy_info_block').find('.children_ages').append(childAgeSelect); + } + } + } else { + let child = $(this).val(); + $(this).closest('.occupancy_info_block').find('.guest_child_age').each(function(ind, element) { + if (child <= ind) { + $(element).parent().remove(); + } + }); + if (child == 0) { + $(this).closest('.occupancy_info_block').find('.children_age_info_block').hide(); + } + + } + } + setRoomTypeGuestOccupancy($(this).closest('.booking_occupancy_wrapper')); + + }); + + + $(document).on('click', '.booking_guest_occupancy:not(.disabled)', function(e) { + $(this).parent().toggleClass('open'); + }); + + $(document).on('click', function(e) { + if ($('.booking_occupancy_wrapper:visible').length) { + var occupancy_wrapper = $('.booking_occupancy_wrapper:visible'); + $(occupancy_wrapper).find(".occupancy_info_block").addClass('selected'); + if (!($(e.target).closest(".booking_occupancy_wrapper").length || $(e.target).closest(".booking_guest_occupancy").length || $(e.target).closest(".ajax_add_to_cart_button").length || $(e.target).closest(".exclusive.book_now_submit").length)) { + let hasErrors = 0; + + let adults = $(occupancy_wrapper).find(".num_adults").map(function(){return $(this).val();}).get(); + let children = $(occupancy_wrapper).find(".num_children").map(function(){return $(this).val();}).get(); + let child_ages = $(occupancy_wrapper).find(".guest_child_age").map(function(){return $(this).val();}).get(); + + // start validating above values + if (!adults.length || (adults.length != children.length)) { + hasErrors = 1; + showErrorMessage(invalid_occupancy_txt); + } else { + $(occupancy_wrapper).find('.occupancy_count').removeClass('error_border'); + + // validate values of adults and children + adults.forEach(function (item, index) { + if (isNaN(item) || parseInt(item) < 1) { + hasErrors = 1; + $(occupancy_wrapper).find(".num_adults").eq(index).closest('.occupancy_count_block').find('.occupancy_count').addClass('error_border'); + } + if (isNaN(children[index])) { + hasErrors = 1; + $(occupancy_wrapper).find(".num_children").eq(index).closest('.occupancy_count_block').find('.occupancy_count').addClass('error_border'); + } + }); + + // validate values of selected child ages + $(occupancy_wrapper).find('.guest_child_age').parent().removeClass('has-error'); + child_ages.forEach(function (age, index) { + age = parseInt(age); + if (isNaN(age) || (age < 0) || (age >= parseInt(max_child_age))) { + hasErrors = 1; + $(occupancy_wrapper).find(".guest_child_age").eq(index).parent().addClass('has-error'); + } + }); + } + if (hasErrors == 0) { + $(occupancy_wrapper).parent().removeClass('open'); + $(document).trigger( "QloApps:updateRoomOccupancy", [occupancy_wrapper]); + } + } + } + }); + + $('.booking_occupancy_wrapper .add_new_occupancy_btn').on('click', function(e) { + e.preventDefault(); + + var booking_occupancy_wrapper = $(this).closest('.booking_occupancy_wrapper'); + var occupancy_block = ''; + var roomBlockIndex = parseInt($(booking_occupancy_wrapper).find(".occupancy_info_block").last().attr('occ_block_index')); + roomBlockIndex += 1; + let max_child_in_room; + if ($(this).closest(".booking_occupancy_wrapper").find('.max_children').val()) { + max_child_in_room = $(this).closest(".booking_occupancy_wrapper").find('.max_children').val(); + } else { + max_child_in_room = window.max_child_in_room; + } + var countRooms = parseInt($(booking_occupancy_wrapper).find('.occupancy_info_block').length); + countRooms += 1 + occupancy_block += '
'; + occupancy_block += '
' + remove_txt + '
'; + occupancy_block += '
'; + occupancy_block += '
'; + occupancy_block += '
'; + occupancy_block += ''; + occupancy_block += ''; + occupancy_block += '
'; + occupancy_block += '
'; + occupancy_block += ''; + occupancy_block += '(' + below_txt + ' ' + max_child_age + ' ' + years_txt + ')'; + occupancy_block += '
'; + occupancy_block += '
'; + occupancy_block += ''; + occupancy_block += '
'; + occupancy_block += '
'; + occupancy_block += '
'; + $(booking_occupancy_wrapper).find('.booking_occupancy_inner').append(occupancy_block); + + setRoomTypeGuestOccupancy(booking_occupancy_wrapper); + }); + + $('input#add_product_product_quantity').on('change', function(){ + // updateOccupancyInput() + addProductRefreshTotal(); + }); + $('#submitAddProduct').unbind('click').click(function(e) { e.preventDefault(); stopAjaxQuery(); @@ -635,16 +854,13 @@ function init() if (!element_list.length) { element_list = element.parents('.product-line-row'); - element_list.find('td .booking_duration_show').hide(); - element_list.find('td .booking_duration_edit').show(); - } - else - { - element_list.find('td .booking_duration_show').hide(); - element_list.find('td .booking_duration_edit').show(); } element_list.find('td .room_unit_price_show').hide(); element_list.find('td .room_unit_price_edit').show(); + element_list.find('td .booking_duration_show').hide(); + element_list.find('td .booking_duration_edit').show(); + element_list.find('td .booking_occupancy_show').hide(); + element_list.find('td .booking_occupancy_edit').show(); //element_list.find('td .product_price_show').hide(); //element_list.find('td .product_price_edit').show(); element_list.find('td.cancelCheck').hide(); @@ -685,6 +901,8 @@ function init() element_list.find('td .booking_duration_edit').hide(); element_list.find('td .room_unit_price_show').show(); element_list.find('td .room_unit_price_edit').hide(); + element_list.find('td .booking_occupancy_show').show(); + element_list.find('td .booking_occupancy_edit').hide(); element_list.find('td.product_invoice').hide(); element_list.find('td.cancelCheck').show(); element_list.find('td.cancelQuantity').show(); @@ -724,10 +942,12 @@ function init() var id_order_detail = $(this).closest('.product-line-row').find('td .edit_product_id_order_detail').val(); //some vaues are added to the query by webkul query = 'ajax=1&token='+token+'&action=editProductOnOrder&id_order='+id_order+'&id_room='+id_room+'&id_product='+id_product+'&id_hotel='+id_hotel+'&date_from='+date_from+'&date_to='+date_to+'&order_detail_id='+order_detail_id+'&'; - if (element_list.length) + + if (element_list.length) { query += element_list.parent().parent().find('input, select, .edit_product_id_order_detail').serialize(); - else + } else { query += element.parent().parent().find('input, select, .edit_product_id_order_detail').serialize(); + } $.ajax({ type: 'POST', @@ -942,21 +1162,19 @@ function init() var flagRefund = ''; $(document).ready(function() { + // Used for Cancel bookings/Initiate refund button $('#desc-order-standard_refund').click(function() { - $('.cancel_product_change_link:visible').trigger('click'); closeAddProduct(); - if (flagRefund == 'standard') { - flagRefund = ''; - $('.partial_refund_fields').hide(); - $('.standard_refund_fields').hide(); - } - else { - flagRefund = 'standard'; - $('.partial_refund_fields').hide(); - $('.standard_refund_fields').fadeIn(); - } - if (order_discount_price) + $('.cancel_product_change_link:visible').trigger('click'); + if (order_discount_price) { actualizeTotalRefundVoucher(); + } + $('.standard_refund_fields, .order_action').fadeIn(); + scroll_if_anchor('#refundForm', 170); + }); + + $('#cancelRefund').click(function() { + $('.standard_refund_fields').hide(); }); $('#desc-order-partial_refund').click(function() { diff --git a/js/admin/ppreferences.js b/js/admin/ppreferences.js new file mode 100644 index 000000000..7300ba9d2 --- /dev/null +++ b/js/admin/ppreferences.js @@ -0,0 +1,48 @@ +/** +* 2010-2023 Webkul. +* +* NOTICE OF LICENSE +* +* All right is reserved, +* Please go through LICENSE.txt file inside our module +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade this module to newer +* versions in the future. If you wish to customize this module for your +* needs please refer to CustomizationPolicy.txt file inside our module for more information. +* +* @author Webkul IN +* @copyright 2010-2023 Webkul IN +* @license LICENSE.txt +*/ + +$(document).ready(function() { + setFrontOccupancyView(); + $('#PS_FRONT_SEARCH_TYPE').on('change', setFrontOccupancyView); + setBackOccupancyView(); + $('#PS_BACKOFFICE_SEARCH_TYPE').on('change', setBackOccupancyView); + + function setFrontOccupancyView() + { + if ($('#PS_FRONT_SEARCH_TYPE').val() == SEARCH_TYPE_OWS) { + $('#PS_FRONT_OWS_SEARCH_ALGO_TYPE').closest('.form-group').show('fast'); + $('#PS_FRONT_ROOM_UNIT_SELECTION_TYPE').closest('.form-group').hide('fast'); + } else { + $('#PS_FRONT_OWS_SEARCH_ALGO_TYPE').closest('.form-group').hide('fast'); + $('#PS_FRONT_ROOM_UNIT_SELECTION_TYPE').closest('.form-group').show('fast'); + } + } + + function setBackOccupancyView() + { + if ($('#PS_BACKOFFICE_SEARCH_TYPE').val() == SEARCH_TYPE_OWS) { + $('#PS_BACKOFFICE_OWS_SEARCH_ALGO_TYPE').closest('.form-group').show('fast'); + $('#PS_BACKOFFICE_ROOM_BOOKING_TYPE').closest('.form-group').hide('fast'); + } else { + $('#PS_BACKOFFICE_OWS_SEARCH_ALGO_TYPE').closest('.form-group').hide('fast'); + $('#PS_BACKOFFICE_ROOM_BOOKING_TYPE').closest('.form-group').show('fast'); + } + } + +}); diff --git a/js/daterangepicker/LICENSE.txt b/js/daterangepicker/LICENSE.txt new file mode 100644 index 000000000..171f9923c --- /dev/null +++ b/js/daterangepicker/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Chunlong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/js/daterangepicker/jquery.daterangepicker.css b/js/daterangepicker/jquery.daterangepicker.css new file mode 100644 index 000000000..7bff17336 --- /dev/null +++ b/js/daterangepicker/jquery.daterangepicker.css @@ -0,0 +1,460 @@ +.date-picker { + width: 170px; + height: 25px; + padding: 0; + border: 0; + line-height: 25px; + padding-left: 10px; + font-size: 12px; + font-family: Arial, sans-serif; + font-weight: bold; + cursor: pointer; + color: #303030; + position: relative; + z-index: 2; +} + +.date-picker-wrapper { + position: absolute; + z-index: 1; + border: 1px solid #bfbfbf; + background-color: #efefef; + padding: 5px 12px; + font-size: 12px; + line-height: 20px; + color: #aaa; + font-family: Arial, sans-serif; + -webkit-box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5); + box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5); + -webkit-box-sizing: initial; + box-sizing: initial; +} + +.dp-clearfix { + clear: both; + height: 0; + font-size: 0; +} + +.date-picker-wrapper.inline-wrapper { + position: relative; + -webkit-box-shadow: none; + box-shadow: none; + display: inline-block; +} + +.date-picker-wrapper.single-date { + width: auto; +} + +.date-picker-wrapper.no-shortcuts { + padding-bottom: 12px; +} + +.date-picker-wrapper.no-topbar { + padding-top: 12px; +} + +.date-picker-wrapper .footer { + font-size: 11px; + padding-top: 3px; +} + +.date-picker-wrapper b { + color: #666; + font-weight: 700; +} + +.date-picker-wrapper a { + color: #6bb4d6; + text-decoration: underline; +} + +.date-picker-wrapper .month-name { + text-transform: uppercase; +} + +.date-picker-wrapper .select-wrapper { + position: relative; + overflow: hidden; + display: inline-block; + vertical-align: middle; +} + +.date-picker-wrapper .select-wrapper:hover { + text-decoration: underline; +} + +.date-picker-wrapper .month-element { + display: inline-block; + vertical-align: middle; +} + +.date-picker-wrapper .select-wrapper select { + position: absolute; + margin: 0; + padding: 0; + left: 0; + top: -1px; + font-size: inherit; + font-style: inherit; + font-weight: inherit; + text-transform: inherit; + color: inherit; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: transparent; + border: 0; + outline: 0; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=1)"; + filter: alpha(opacity=1); + opacity: 0.01; +} + +.date-picker-wrapper .month-wrapper { + border: 1px solid #bfbfbf; + border-radius: 3px; + background-color: #fff; + padding: 5px; + cursor: default; + position: relative; + _overflow: hidden; +} + +.date-picker-wrapper .month-wrapper table { + width: 190px; + float: left; +} + +.date-picker-wrapper .month-wrapper table.month2 { + width: 190px; + float: left; +} + +.date-picker-wrapper .month-wrapper table th, .date-picker-wrapper .month-wrapper table td { + vertical-align: middle; + text-align: center; + line-height: 14px; + margin: 0px; + padding: 0px; +} + +.date-picker-wrapper .month-wrapper table .day { + padding: 5px 0; + line-height: 1; + font-size: 12px; + margin-bottom: 1px; + color: #ccc; + cursor: default; +} + +.date-picker-wrapper .month-wrapper table div.day.lastMonth, .date-picker-wrapper .month-wrapper table div.day.nextMonth { + color: #999; + cursor: default; +} + +.date-picker-wrapper .month-wrapper table .day.checked { + background-color: #9cdbf7; +} + +.date-picker-wrapper .month-wrapper table .week-name { + height: 20px; + line-height: 20px; + font-weight: 100; + text-transform: uppercase; +} + +.date-picker-wrapper .month-wrapper table .day.has-tooltip { + cursor: help !important; +} + +.date-picker-wrapper .month-wrapper table .day.has-tooltip .tooltip { + white-space: nowrap; +} + +.date-picker-wrapper .time label { + white-space: nowrap; +} + +.date-picker-wrapper .month-wrapper table .day.toMonth.valid { + color: #333; + cursor: pointer; +} + +.date-picker-wrapper .month-wrapper table .day.toMonth.hovering { + background-color: #cdecfa; +} + +.date-picker-wrapper .month-wrapper table .day.nextMonth, .date-picker-wrapper .month-wrapper table .day.lastMonth { + display: none; +} + +.date-picker-wrapper .month-wrapper table .day.real-today { + background-color: #ffe684; +} + +.date-picker-wrapper .month-wrapper table .day.real-today.checked, .date-picker-wrapper .month-wrapper table .day.real-today.hovering { + background-color: #70ccd5; +} + +.date-picker-wrapper table .caption { + height: 40px; +} + +.date-picker-wrapper table .caption > th:first-of-type, .date-picker-wrapper table .caption > th:last-of-type { + width: 27px; +} + +.date-picker-wrapper table .caption .next, .date-picker-wrapper table .caption .prev { + padding: 0 5px; + cursor: pointer; +} + +.date-picker-wrapper table .caption .next:hover, .date-picker-wrapper table .caption .prev:hover { + background-color: #ccc; + color: white; +} + +.date-picker-wrapper .gap { + position: relative; + z-index: 1; + width: 15px; + height: 100%; + background-color: red; + font-size: 0; + line-height: 0; + float: left; + top: -5px; + margin: 0 10px -10px; + visibility: hidden; + height: 0; +} + +.date-picker-wrapper .gap .gap-lines { + height: 100%; + overflow: hidden; +} + +.date-picker-wrapper .gap .gap-line { + height: 15px; + width: 15px; + position: relative; +} + +.date-picker-wrapper .gap .gap-line .gap-1 { + z-index: 1; + height: 0; + border-left: 8px solid white; + border-top: 8px solid #eee; + border-bottom: 8px solid #eee; +} + +.date-picker-wrapper .gap .gap-line .gap-2 { + position: absolute; + right: 0; + top: 0px; + z-index: 2; + height: 0; + border-left: 8px solid transparent; + border-top: 8px solid white; +} + +.date-picker-wrapper .gap .gap-line .gap-3 { + position: absolute; + right: 0; + top: 8px; + z-index: 2; + height: 0; + border-left: 8px solid transparent; + border-bottom: 8px solid white; +} + +.date-picker-wrapper .gap .gap-top-mask { + width: 6px; + height: 1px; + position: absolute; + top: -1px; + left: 1px; + background-color: #eee; + z-index: 3; +} + +.date-picker-wrapper .gap .gap-bottom-mask { + width: 6px; + height: 1px; + position: absolute; + bottom: -1px; + left: 7px; + background-color: #eee; + z-index: 3; +} + +.date-picker-wrapper .selected-days { + display: none; +} + +.date-picker-wrapper .drp_top-bar { + line-height: 1.4; + position: relative; + padding: 10px 40px 10px 0; +} + +.date-picker-wrapper .drp_top-bar .error-top, .date-picker-wrapper .drp_top-bar .normal-top { + display: none; +} + +.date-picker-wrapper .drp_top-bar .default-top { + display: block; +} + +.date-picker-wrapper .drp_top-bar.error .default-top { + display: none; +} + +.date-picker-wrapper .drp_top-bar.error .error-top { + display: block; + color: red; +} + +.date-picker-wrapper .drp_top-bar.normal .default-top { + display: none; +} + +.date-picker-wrapper .drp_top-bar.normal .normal-top { + display: block; +} + +.date-picker-wrapper .drp_top-bar.normal .normal-top .selection-top { + color: #333; +} + +.date-picker-wrapper .drp_top-bar .apply-btn { + position: absolute; + right: 0px; + top: 6px; + padding: 3px 5px; + margin: 0; + font-size: 12px; + border-radius: 4px; + cursor: pointer; + color: #d9eef7; + border: solid 1px #0076a3; + background: #0095cd; + background: -moz-linear-gradient(top, #00adee, #0078a5); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00adee', endColorstr='#0078a5'); + color: white; + line-height: initial; +} + +.date-picker-wrapper .drp_top-bar .apply-btn.disabled { + cursor: pointer; + color: #606060; + border: solid 1px #b7b7b7; + background: #fff; + background: -moz-linear-gradient(top, #fff, #ededed); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed'); +} + +.date-picker-wrapper .time { + position: relative; +} + +.date-picker-wrapper.single-month .time { + display: block; +} + +.date-picker-wrapper .time input[type=range] { + vertical-align: middle; + width: 129px; + padding: 0; + margin: 0; + height: 20px; +} + +.date-picker-wrapper .time1 { + width: 180px; + padding: 0 5px; + text-align: center; +} + +/*time styling*/ +.time2 { + width: 180px; + padding: 0 5px; + text-align: center; +} + +.date-picker-wrapper .time1 { + float: left; +} + +.date-picker-wrapper .time2 { + float: right; +} + +.date-picker-wrapper .hour { + text-align: right; +} + +.minute { + text-align: right; +} + +.date-picker-wrapper .hide { + display: none; +} + +.date-picker-wrapper .first-date-selected, .date-picker-wrapper .last-date-selected { + background-color: #49e !important; + color: white !important; +} + +.date-picker-wrapper .date-range-length-tip { + position: absolute; + margin-top: -4px; + margin-left: -8px; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.3); + display: none; + background-color: yellow; + padding: 0 6px; + border-radius: 2px; + font-size: 12px; + line-height: 16px; + -webkit-filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3)); + -moz-filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3)); + -ms-filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3)); + -o-filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3)); + filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3)); +} + +.date-picker-wrapper .date-range-length-tip:after { + content: ''; + position: absolute; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid yellow; + left: 50%; + margin-left: -4px; + bottom: -4px; +} + +.date-picker-wrapper.two-months.no-gap .month1 .next, .date-picker-wrapper.two-months.no-gap .month2 .prev { + display: none; +} + +.date-picker-wrapper .week-number { + padding: 5px 0; + line-height: 1; + font-size: 12px; + margin-bottom: 1px; + color: #999; + cursor: pointer; +} + +.date-picker-wrapper .week-number.week-number-selected { + color: #49e; + font-weight: bold; +} diff --git a/js/daterangepicker/jquery.daterangepicker.js b/js/daterangepicker/jquery.daterangepicker.js new file mode 100755 index 000000000..c104e536b --- /dev/null +++ b/js/daterangepicker/jquery.daterangepicker.js @@ -0,0 +1,2638 @@ +// jquery.daterangepicker.js +// author : Chunlong Liu +// license : MIT +// www.jszen.com + +(function(factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery', 'moment'], factory); + } else if (typeof exports === 'object' && typeof module !== 'undefined') { + // CommonJS. Register as a module + module.exports = factory(require('jquery'), require('moment')); + } else { + // Browser globals + factory(jQuery, moment); + } +}(function($, moment) { + 'use strict'; + $.dateRangePickerLanguages = { + "default": //default language: English + { + "selected": "Selected:", + "day": "Day", + "days": "Days", + "apply": "Close", + "week-1": "mo", + "week-2": "tu", + "week-3": "we", + "week-4": "th", + "week-5": "fr", + "week-6": "sa", + "week-7": "su", + "week-number": "W", + "month-name": ["january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"], + "shortcuts": "Shortcuts", + "custom-values": "Custom Values", + "past": "Past", + "following": "Following", + "previous": "Previous", + "prev-week": "Week", + "prev-month": "Month", + "prev-year": "Year", + "next": "Next", + "next-week": "Week", + "next-month": "Month", + "next-year": "Year", + "less-than": "Date range should not be more than %d days", + "more-than": "Date range should not be less than %d days", + "default-more": "Please select a date range longer than %d days", + "default-single": "Please select a date", + "default-less": "Please select a date range less than %d days", + "default-range": "Please select a date range between %d and %d days", + "default-default": "Please select a date range", + "time": "Time", + "hour": "Hour", + "minute": "Minute" + }, + "id": { + "selected": "Terpilih:", + "day": "Hari", + "days": "Hari", + "apply": "Tutup", + "week-1": "sen", + "week-2": "sel", + "week-3": "rab", + "week-4": "kam", + "week-5": "jum", + "week-6": "sab", + "week-7": "min", + "week-number": "W", + "month-name": ["januari", "februari", "maret", "april", "mei", "juni", "juli", "agustus", "september", "oktober", "november", "desember"], + "shortcuts": "Pintas", + "custom-values": "Nilai yang ditentukan", + "past": "Yang Lalu", + "following": "Mengikuti", + "previous": "Sebelumnya", + "prev-week": "Minggu", + "prev-month": "Bulan", + "prev-year": "Tahun", + "next": "Selanjutnya", + "next-week": "Minggu", + "next-month": "Bulan", + "next-year": "Tahun", + "less-than": "Tanggal harus lebih dari %d hari", + "more-than": "Tanggal harus kurang dari %d hari", + "default-more": "Jarak tanggal harus lebih lama dari %d hari", + "default-single": "Silakan pilih tanggal", + "default-less": "Jarak rentang tanggal tidak boleh lebih lama dari %d hari", + "default-range": "Rentang tanggal harus antara %d dan %d hari", + "default-default": "Silakan pilih rentang tanggal", + "time": "Waktu", + "hour": "Jam", + "minute": "Menit" + }, + "az": { + "selected": "Seçildi:", + "day": " gün", + "days": " gün", + "apply": "tətbiq", + "week-1": "1", + "week-2": "2", + "week-3": "3", + "week-4": "4", + "week-5": "5", + "week-6": "6", + "week-7": "7", + "month-name": ["yanvar", "fevral", "mart", "aprel", "may", "iyun", "iyul", "avqust", "sentyabr", "oktyabr", "noyabr", "dekabr"], + "shortcuts": "Qısayollar", + "past": "Keçmiş", + "following": "Növbəti", + "previous": "   ", + "prev-week": "Öncəki həftə", + "prev-month": "Öncəki ay", + "prev-year": "Öncəki il", + "next": "   ", + "next-week": "Növbəti həftə", + "next-month": "Növbəti ay", + "next-year": "Növbəti il", + "less-than": "Tarix aralığı %d gündən çox olmamalıdır", + "more-than": "Tarix aralığı %d gündən az olmamalıdır", + "default-more": "%d gündən çox bir tarix seçin", + "default-single": "Tarix seçin", + "default-less": "%d gündən az bir tarix seçin", + "default-range": "%d və %d gün aralığında tarixlər seçin", + "default-default": "Tarix aralığı seçin" + }, + "bg": { + "selected": "Избрано:", + "day": "Ден", + "days": "Дни", + "apply": "Затвори", + "week-1": "пн", + "week-2": "вт", + "week-3": "ср", + "week-4": "чт", + "week-5": "пт", + "week-6": "сб", + "week-7": "нд", + "week-number": "С", + "month-name": ["януари", "февруари", "март", "април", "май", "юни", "юли", "август", "септември", "октомври", "ноември", "декември"], + "shortcuts": "Преки пътища", + "custom-values": "Персонализирани стойности", + "past": "Минал", + "following": "Следващ", + "previous": "Предишен", + "prev-week": "Седмица", + "prev-month": "Месец", + "prev-year": "Година", + "next": "Следващ", + "next-week": "Седмица", + "next-month": "Месец", + "next-year": "Година", + "less-than": "Периодът от време не трябва да е повече от %d дни", + "more-than": "Периодът от време не трябва да е по-малко от %d дни", + "default-more": "Моля изберете период по-дълъг от %d дни", + "default-single": "Моля изберете дата", + "default-less": "Моля изберете период по-къс от %d дни", + "default-range": "Моля изберете период между %d и %d дни", + "default-default": "Моля изберете период", + "time": "Време", + "hour": "Час", + "minute": "Минута" + }, + "cn": //simplified chinese + { + "selected": "已选择:", + "day": "天", + "days": "天", + "apply": "确定", + "week-1": "一", + "week-2": "二", + "week-3": "三", + "week-4": "四", + "week-5": "五", + "week-6": "六", + "week-7": "日", + "week-number": "周", + "month-name": ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], + "shortcuts": "快捷选择", + "past": "过去", + "following": "将来", + "previous": "   ", + "prev-week": "上周", + "prev-month": "上个月", + "prev-year": "去年", + "next": "   ", + "next-week": "下周", + "next-month": "下个月", + "next-year": "明年", + "less-than": "所选日期范围不能大于%d天", + "more-than": "所选日期范围不能小于%d天", + "default-more": "请选择大于%d天的日期范围", + "default-less": "请选择小于%d天的日期范围", + "default-range": "请选择%d天到%d天的日期范围", + "default-single": "请选择一个日期", + "default-default": "请选择一个日期范围", + "time": "时间", + "hour": "小时", + "minute": "分钟" + }, + "cz": { + "selected": "Vybráno:", + "day": "Den", + "days": "Dny", + "apply": "Zavřít", + "week-1": "po", + "week-2": "út", + "week-3": "st", + "week-4": "čt", + "week-5": "pá", + "week-6": "so", + "week-7": "ne", + "month-name": ["leden", "únor", "březen", "duben", "květen", "červen", "červenec", "srpen", "září", "říjen", "listopad", "prosinec"], + "shortcuts": "Zkratky", + "past": "po", + "following": "následující", + "previous": "předchozí", + "prev-week": "týden", + "prev-month": "měsíc", + "prev-year": "rok", + "next": "další", + "next-week": "týden", + "next-month": "měsíc", + "next-year": "rok", + "less-than": "Rozsah data by neměl být větší než %d dnů", + "more-than": "Rozsah data by neměl být menší než %d dnů", + "default-more": "Prosím zvolte rozsah data větší než %d dnů", + "default-single": "Prosím zvolte datum", + "default-less": "Prosím zvolte rozsah data menší než %d dnů", + "default-range": "Prosím zvolte rozsah data mezi %d a %d dny", + "default-default": "Prosím zvolte rozsah data" + }, + "de": { + "selected": "Auswahl:", + "day": "Tag", + "days": "Tage", + "apply": "Schließen", + "week-1": "mo", + "week-2": "di", + "week-3": "mi", + "week-4": "do", + "week-5": "fr", + "week-6": "sa", + "week-7": "so", + "month-name": ["januar", "februar", "märz", "april", "mai", "juni", "juli", "august", "september", "oktober", "november", "dezember"], + "shortcuts": "Schnellwahl", + "past": "Vorherige", + "following": "Folgende", + "previous": "Vorherige", + "prev-week": "Woche", + "prev-month": "Monat", + "prev-year": "Jahr", + "next": "Nächste", + "next-week": "Woche", + "next-month": "Monat", + "next-year": "Jahr", + "less-than": "Datumsbereich darf nicht größer sein als %d Tage", + "more-than": "Datumsbereich darf nicht kleiner sein als %d Tage", + "default-more": "Bitte mindestens %d Tage auswählen", + "default-single": "Bitte ein Datum auswählen", + "default-less": "Bitte weniger als %d Tage auswählen", + "default-range": "Bitte einen Datumsbereich zwischen %d und %d Tagen auswählen", + "default-default": "Bitte ein Start- und Enddatum auswählen", + "Time": "Zeit", + "hour": "Stunde", + "minute": "Minute" + }, + "es": { + "selected": "Seleccionado:", + "day": "Día", + "days": "Días", + "apply": "Cerrar", + "week-1": "lu", + "week-2": "ma", + "week-3": "mi", + "week-4": "ju", + "week-5": "vi", + "week-6": "sa", + "week-7": "do", + "month-name": ["enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"], + "shortcuts": "Accesos directos", + "past": "Pasado", + "following": "Siguiente", + "previous": "Anterior", + "prev-week": "Semana", + "prev-month": "Mes", + "prev-year": "Año", + "next": "Siguiente", + "next-week": "Semana", + "next-month": "Mes", + "next-year": "Año", + "less-than": "El rango no debería ser mayor de %d días", + "more-than": "El rango no debería ser menor de %d días", + "default-more": "Por favor selecciona un rango mayor a %d días", + "default-single": "Por favor selecciona un día", + "default-less": "Por favor selecciona un rango menor a %d días", + "default-range": "Por favor selecciona un rango entre %d y %d días", + "default-default": "Por favor selecciona un rango de fechas." + }, + "fr": { + "selected": "Sélection:", + "day": "Jour", + "days": "Jours", + "apply": "Fermer", + "week-1": "lu", + "week-2": "ma", + "week-3": "me", + "week-4": "je", + "week-5": "ve", + "week-6": "sa", + "week-7": "di", + "month-name": ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"], + "shortcuts": "Raccourcis", + "past": "Passé", + "following": "Suivant", + "previous": "Précédent", + "prev-week": "Semaine", + "prev-month": "Mois", + "prev-year": "Année", + "next": "Suivant", + "next-week": "Semaine", + "next-month": "Mois", + "next-year": "Année", + "less-than": "L'intervalle ne doit pas être supérieure à %d jours", + "more-than": "L'intervalle ne doit pas être inférieure à %d jours", + "default-more": "Merci de choisir une intervalle supérieure à %d jours", + "default-single": "Merci de choisir une date", + "default-less": "Merci de choisir une intervalle inférieure %d jours", + "default-range": "Merci de choisir une intervalle comprise entre %d et %d jours", + "default-default": "Merci de choisir une date" + }, + "hu": { + "selected": "Kiválasztva:", + "day": "Nap", + "days": "Nap", + "apply": "Ok", + "week-1": "h", + "week-2": "k", + "week-3": "sz", + "week-4": "cs", + "week-5": "p", + "week-6": "sz", + "week-7": "v", + "month-name": ["január", "február", "március", "április", "május", "június", "július", "augusztus", "szeptember", "október", "november", "december"], + "shortcuts": "Gyorsválasztó", + "past": "Múlt", + "following": "Következő", + "previous": "Előző", + "prev-week": "Hét", + "prev-month": "Hónap", + "prev-year": "Év", + "next": "Következő", + "next-week": "Hét", + "next-month": "Hónap", + "next-year": "Év", + "less-than": "A kiválasztás nem lehet több %d napnál", + "more-than": "A kiválasztás nem lehet több %d napnál", + "default-more": "Válassz ki egy időszakot ami hosszabb mint %d nap", + "default-single": "Válassz egy napot", + "default-less": "Válassz ki egy időszakot ami rövidebb mint %d nap", + "default-range": "Válassz ki egy %d - %d nap hosszú időszakot", + "default-default": "Válassz ki egy időszakot" + }, + "it": { + "selected": "Selezionati:", + "day": "Giorno", + "days": "Giorni", + "apply": "Chiudi", + "week-1": "lu", + "week-2": "ma", + "week-3": "me", + "week-4": "gi", + "week-5": "ve", + "week-6": "sa", + "week-7": "do", + "month-name": ["gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno", "luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre"], + "shortcuts": "Scorciatoie", + "past": "Scorso", + "following": "Successivo", + "previous": "Precedente", + "prev-week": "Settimana", + "prev-month": "Mese", + "prev-year": "Anno", + "next": "Prossimo", + "next-week": "Settimana", + "next-month": "Mese", + "next-year": "Anno", + "less-than": "L'intervallo non dev'essere maggiore di %d giorni", + "more-than": "L'intervallo non dev'essere minore di %d giorni", + "default-more": "Seleziona un intervallo maggiore di %d giorni", + "default-single": "Seleziona una data", + "default-less": "Seleziona un intervallo minore di %d giorni", + "default-range": "Seleziona un intervallo compreso tra i %d e i %d giorni", + "default-default": "Seleziona un intervallo di date" + }, + "ko": { + "selected": "기간:", + "day": "일", + "days": "일간", + "apply": "닫기", + "week-1": "월", + "week-2": "화", + "week-3": "수", + "week-4": "목", + "week-5": "금", + "week-6": "토", + "week-7": "일", + "week-number": "주", + "month-name": ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"], + "shortcuts": "단축키들", + "past": "지난(오늘기준)", + "following": "이후(오늘기준)", + "previous": "이전", + "prev-week": "1주", + "prev-month": "1달", + "prev-year": "1년", + "next": "다음", + "next-week": "1주", + "next-month": "1달", + "next-year": "1년", + "less-than": "날짜 범위는 %d 일보다 많을 수 없습니다", + "more-than": "날짜 범위는 %d 일보다 작을 수 없습니다", + "default-more": "날짜 범위를 %d 일보다 길게 선택해 주세요", + "default-single": "날짜를 선택해 주세요", + "default-less": "%d 일보다 작은 날짜를 선택해 주세요", + "default-range": "%d와 %d 일 사이의 날짜 범위를 선택해 주세요", + "default-default": "날짜 범위를 선택해 주세요", + "time": "시각", + "hour": "시", + "minute": "분" + }, + "no": { + "selected": "Valgt:", + "day": "Dag", + "days": "Dager", + "apply": "Lukk", + "week-1": "ma", + "week-2": "ti", + "week-3": "on", + "week-4": "to", + "week-5": "fr", + "week-6": "lø", + "week-7": "sø", + "month-name": ["januar", "februar", "mars", "april", "mai", "juni", "juli", "august", "september", "oktober", "november", "desember"], + "shortcuts": "Snarveier", + "custom-values": "Egendefinerte Verdier", + "past": "Over", // Not quite sure about the context of this one + "following": "Følger", + "previous": "Forrige", + "prev-week": "Uke", + "prev-month": "Måned", + "prev-year": "År", + "next": "Neste", + "next-week": "Uke", + "next-month": "Måned", + "next-year": "År", + "less-than": "Datoperioden skal ikkje være lengre enn %d dager", + "more-than": "Datoperioden skal ikkje være kortere enn %d dager", + "default-more": "Vennligst velg ein datoperiode lengre enn %d dager", + "default-single": "Vennligst velg ein dato", + "default-less": "Vennligst velg ein datoperiode mindre enn %d dager", + "default-range": "Vennligst velg ein datoperiode mellom %d og %d dager", + "default-default": "Vennligst velg ein datoperiode", + "time": "Tid", + "hour": "Time", + "minute": "Minutter" + }, + "nl": { + "selected": "Geselecteerd:", + "day": "Dag", + "days": "Dagen", + "apply": "Ok", + "week-1": "ma", + "week-2": "di", + "week-3": "wo", + "week-4": "do", + "week-5": "vr", + "week-6": "za", + "week-7": "zo", + "month-name": ["januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"], + "shortcuts": "Snelkoppelingen", + "custom-values": "Aangepaste waarden", + "past": "Verleden", + "following": "Komend", + "previous": "Vorige", + "prev-week": "Week", + "prev-month": "Maand", + "prev-year": "Jaar", + "next": "Volgende", + "next-week": "Week", + "next-month": "Maand", + "next-year": "Jaar", + "less-than": "Interval moet langer dan %d dagen zijn", + "more-than": "Interval mag niet minder dan %d dagen zijn", + "default-more": "Selecteer een interval langer dan %dagen", + "default-single": "Selecteer een datum", + "default-less": "Selecteer een interval minder dan %d dagen", + "default-range": "Selecteer een interval tussen %d en %d dagen", + "default-default": "Selecteer een interval", + "time": "Tijd", + "hour": "Uur", + "minute": "Minuut" + }, + "ru": { + "selected": "Выбрано:", + "day": "День", + "days": "Дней", + "apply": "Применить", + "week-1": "пн", + "week-2": "вт", + "week-3": "ср", + "week-4": "чт", + "week-5": "пт", + "week-6": "сб", + "week-7": "вс", + "month-name": ["январь", "февраль", "март", "апрель", "май", "июнь", "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"], + "shortcuts": "Быстрый выбор", + "custom-values": "Пользовательские значения", + "past": "Прошедшие", + "following": "Следующие", + "previous": "   ", + "prev-week": "Неделя", + "prev-month": "Месяц", + "prev-year": "Год", + "next": "   ", + "next-week": "Неделя", + "next-month": "Месяц", + "next-year": "Год", + "less-than": "Диапазон не может быть больше %d дней", + "more-than": "Диапазон не может быть меньше %d дней", + "default-more": "Пожалуйста выберите диапазон больше %d дней", + "default-single": "Пожалуйста выберите дату", + "default-less": "Пожалуйста выберите диапазон меньше %d дней", + "default-range": "Пожалуйста выберите диапазон между %d и %d днями", + "default-default": "Пожалуйста выберите диапазон", + "time": "Время", + "hour": "Часы", + "minute": "Минуты" + }, + "uk": { + "selected": "Вибрано:", + "day": "День", + "days": "Днів", + "apply": "Застосувати", + "week-1": "пн", + "week-2": "вт", + "week-3": "ср", + "week-4": "чт", + "week-5": "пт", + "week-6": "сб", + "week-7": "нд", + "month-name": ["січень", "лютий", "березень", "квітень", "травень", "червень", "липень", "серпень", "вересень", "жовтень", "листопад", "грудень"], + "shortcuts": "Швидкий вибір", + "custom-values": "Значення користувача", + "past": "Минулі", + "following": "Наступні", + "previous": "   ", + "prev-week": "Тиждень", + "prev-month": "Місяць", + "prev-year": "Рік", + "next": "   ", + "next-week": "Тиждень", + "next-month": "Місяць", + "next-year": "Рік", + "less-than": "Діапазон не може бути більш ніж %d днів", + "more-than": "Діапазон не може бути меньш ніж %d днів", + "default-more": "Будь ласка виберіть діапазон більше %d днів", + "default-single": "Будь ласка виберіть дату", + "default-less": "Будь ласка виберіть діапазон менше %d днів", + "default-range": "Будь ласка виберіть діапазон між %d та %d днями", + "default-default": "Будь ласка виберіть діапазон", + "time": "Час", + "hour": "Години", + "minute": "Хвилини" + }, + "pl": { + "selected": "Wybrany:", + "day": "Dzień", + "days": "Dni", + "apply": "Zamknij", + "week-1": "pon", + "week-2": "wt", + "week-3": "śr", + "week-4": "czw", + "week-5": "pt", + "week-6": "so", + "week-7": "nd", + "month-name": ["styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień"], + "shortcuts": "Skróty", + "custom-values": "Niestandardowe wartości", + "past": "Przeszłe", + "following": "Następne", + "previous": "Poprzednie", + "prev-week": "tydzień", + "prev-month": "miesiąc", + "prev-year": "rok", + "next": "Następny", + "next-week": "tydzień", + "next-month": "miesiąc", + "next-year": "rok", + "less-than": "Okres nie powinien być dłuższy niż %d dni", + "more-than": "Okres nie powinien być krótszy niż %d ni", + "default-more": "Wybierz okres dłuższy niż %d dni", + "default-single": "Wybierz datę", + "default-less": "Wybierz okres krótszy niż %d dni", + "default-range": "Wybierz okres trwający od %d do %d dni", + "default-default": "Wybierz okres", + "time": "Czas", + "hour": "Godzina", + "minute": "Minuta" + }, + "se": { + "selected": "Vald:", + "day": "dag", + "days": "dagar", + "apply": "godkänn", + "week-1": "ma", + "week-2": "ti", + "week-3": "on", + "week-4": "to", + "week-5": "fr", + "week-6": "lö", + "week-7": "sö", + "month-name": ["januari", "februari", "mars", "april", "maj", "juni", "juli", "augusti", "september", "oktober", "november", "december"], + "shortcuts": "genvägar", + "custom-values": "Anpassade värden", + "past": "över", + "following": "följande", + "previous": "förra", + "prev-week": "vecka", + "prev-month": "månad", + "prev-year": "år", + "next": "nästa", + "next-week": "vecka", + "next-month": "måned", + "next-year": "år", + "less-than": "Datumintervall bör inte vara mindre än %d dagar", + "more-than": "Datumintervall bör inte vara mer än %d dagar", + "default-more": "Välj ett datumintervall längre än %d dagar", + "default-single": "Välj ett datum", + "default-less": "Välj ett datumintervall mindre än %d dagar", + "default-range": "Välj ett datumintervall mellan %d och %d dagar", + "default-default": "Välj ett datumintervall", + "time": "tid", + "hour": "timme", + "minute": "minut" + }, + "pt": //Portuguese (European) + { + "selected": "Selecionado:", + "day": "Dia", + "days": "Dias", + "apply": "Fechar", + "week-1": "seg", + "week-2": "ter", + "week-3": "qua", + "week-4": "qui", + "week-5": "sex", + "week-6": "sab", + "week-7": "dom", + "week-number": "N", + "month-name": ["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"], + "shortcuts": "Atalhos", + "custom-values": "Valores Personalizados", + "past": "Passado", + "following": "Seguinte", + "previous": "Anterior", + "prev-week": "Semana", + "prev-month": "Mês", + "prev-year": "Ano", + "next": "Próximo", + "next-week": "Próxima Semana", + "next-month": "Próximo Mês", + "next-year": "Próximo Ano", + "less-than": "O período selecionado não deve ser maior que %d dias", + "more-than": "O período selecionado não deve ser menor que %d dias", + "default-more": "Selecione um período superior a %d dias", + "default-single": "Selecione uma data", + "default-less": "Selecione um período inferior a %d dias", + "default-range": "Selecione um período de %d a %d dias", + "default-default": "Selecione um período", + "time": "Tempo", + "hour": "Hora", + "minute": "Minuto" + }, + "tc": // traditional chinese + { + "selected": "已選擇:", + "day": "天", + "days": "天", + "apply": "確定", + "week-1": "一", + "week-2": "二", + "week-3": "三", + "week-4": "四", + "week-5": "五", + "week-6": "六", + "week-7": "日", + "week-number": "週", + "month-name": ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], + "shortcuts": "快速選擇", + "past": "過去", + "following": "將來", + "previous": "   ", + "prev-week": "上週", + "prev-month": "上個月", + "prev-year": "去年", + "next": "   ", + "next-week": "下週", + "next-month": "下個月", + "next-year": "明年", + "less-than": "所選日期範圍不能大於%d天", + "more-than": "所選日期範圍不能小於%d天", + "default-more": "請選擇大於%d天的日期範圍", + "default-less": "請選擇少於%d天的日期範圍", + "default-range": "請選擇%d天到%d天的日期範圍", + "default-single": "請選擇一個日期", + "default-default": "請選擇一個日期範圍", + "time": "日期", + "hour": "小時", + "minute": "分鐘" + }, + "ja": { + "selected": "選択しました:", + "day": "日", + "days": "日", + "apply": "確定", + "week-1": "月", + "week-2": "火", + "week-3": "水", + "week-4": "木", + "week-5": "金", + "week-6": "土", + "week-7": "日", + "month-name": ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"], + "shortcuts": "クイック選択", + "past": "過去", + "following": "将来", + "previous": "   ", + "prev-week": "先週", + "prev-month": "先月", + "prev-year": "昨年", + "next": "   ", + "next-week": "来週", + "next-month": "来月", + "next-year": "来年", + "less-than": "日付の範囲は%d日以上にすべきではありません", + "more-than": "日付の範囲は%d日を下回ってはいけません", + "default-more": "%d日よりも長い期間を選択してください", + "default-less": "%d日未満の期間を選択してください", + "default-range": "%d日と%d日の間の日付範囲を選択してください", + "default-single": "日付を選択してください", + "default-default": "日付範囲を選択してください", + "time": "時間", + "hour": "時間", + "minute": "分" + }, + "da": { + "selected": "Valgt:", + "day": "Dag", + "days": "Dage", + "apply": "Luk", + "week-1": "ma", + "week-2": "ti", + "week-3": "on", + "week-4": "to", + "week-5": "fr", + "week-6": "lø", + "week-7": "sø", + "month-name": ["januar", "februar", "marts", "april", "maj", "juni", "juli", "august", "september", "oktober", "november", "december"], + "shortcuts": "genveje", + "custom-values": "Brugerdefinerede værdier", + "past": "Forbi", + "following": "Følgende", + "previous": "Forrige", + "prev-week": "uge", + "prev-month": "måned", + "prev-year": "år", + "next": "Næste", + "next-week": "uge", + "next-month": "måned", + "next-year": "år", + "less-than": "Dato interval bør ikke være med end %d dage", + "more-than": "Dato interval bør ikke være mindre end %d dage", + "default-more": "Vælg datointerval længere end %d dage", + "default-single": "Vælg dato", + "default-less": "Vælg datointerval mindre end %d dage", + "default-range": "Vælg datointerval mellem %d og %d dage", + "default-default": "Vælg datointerval", + "time": "tid", + "hour": "time", + "minute": "minut" + }, + "fi": // Finnish + { + "selected": "Valittu:", + "day": "Päivä", + "days": "Päivää", + "apply": "Sulje", + "week-1": "ma", + "week-2": "ti", + "week-3": "ke", + "week-4": "to", + "week-5": "pe", + "week-6": "la", + "week-7": "su", + "week-number": "V", + "month-name": ["tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu"], + "shortcuts": "Pikavalinnat", + "custom-values": "Mukautetut Arvot", + "past": "Menneet", + "following": "Tulevat", + "previous": "Edellinen", + "prev-week": "Viikko", + "prev-month": "Kuukausi", + "prev-year": "Vuosi", + "next": "Seuraava", + "next-week": "Viikko", + "next-month": "Kuukausi", + "next-year": "Vuosi", + "less-than": "Aikajakson tulisi olla vähemmän kuin %d päivää", + "more-than": "Aikajakson ei tulisi olla vähempää kuin %d päivää", + "default-more": "Valitse pidempi aikajakso kuin %d päivää", + "default-single": "Valitse päivä", + "default-less": "Valitse lyhyempi aikajakso kuin %d päivää", + "default-range": "Valitse aikajakso %d ja %d päivän väliltä", + "default-default": "Valitse aikajakso", + "time": "Aika", + "hour": "Tunti", + "minute": "Minuutti" + }, + "cat": // Catala + { + "selected": "Seleccionats:", + "day": "Dia", + "days": "Dies", + "apply": "Tanca", + "week-1": "Dl", + "week-2": "Dm", + "week-3": "Dc", + "week-4": "Dj", + "week-5": "Dv", + "week-6": "Ds", + "week-7": "Dg", + "week-number": "S", + "month-name": ["gener", "febrer", "març", "abril", "maig", "juny", "juliol", "agost", "setembre", "octubre", "novembre", "desembre"], + "shortcuts": "Dreçeres", + "custom-values": "Valors personalitzats", + "past": "Passat", + "following": "Futur", + "previous": "Anterior", + "prev-week": "Setmana", + "prev-month": "Mes", + "prev-year": "Any", + "next": "Següent", + "next-week": "Setmana", + "next-month": "Mes", + "next-year": "Any", + "less-than": "El període no hauria de ser de més de %d dies", + "more-than": "El període no hauria de ser de menys de %d dies", + "default-more": "Perfavor selecciona un període més gran de %d dies", + "default-single": "Perfavor selecciona una data", + "default-less": "Perfavor selecciona un període de menys de %d dies", + "default-range": "Perfavor selecciona un període d'entre %d i %d dies", + "default-default": "Perfavor selecciona un període", + "time": "Temps", + "hour": "Hora", + "minute": "Minut" + }, + "sk": { + "selected": "Vybrané:", + "day": "Deň", + "days": "Dni", + "apply": "Zavrieť", + "week-1": "po", + "week-2": "út", + "week-3": "st", + "week-4": "št", + "week-5": "pi", + "week-6": "so", + "week-7": "ne", + "week-number": "T", + "month-name": ["január", "február", "marec", "apríl", "máj", "jún", "júl", "august", "september", "október", "november", "december"], + "shortcuts": "Skratky", + "custom-values": "Vlastné hodnoty", + "past": "po", + "following": "nasledujúci", + "previous": "predchádzajúci", + "prev-week": "týždeň", + "prev-month": "mesiac", + "prev-year": "rok", + "next": "ďalší", + "next-week": "týždeň", + "next-month": "mesiac", + "next-year": "rok", + "less-than": "Rozsah dátumu by nemal byť väčší ako %d dní", + "more-than": "Rozsah dátumu by nemal byť menší ako %d dní", + "default-more": "Prosím zvoľte rozsah dlhší ako %d dní", + "default-single": "Prosím zvoľte dátum", + "default-less": "Prosím zvoľte rozsah menší ako %d dní", + "default-range": "Prosím zvoľte rozsah medzi %d a %d dňami", + "default-default": "Prosím zvoľte rozsah", + "time": "Čas", + "hour": "Hodina", + "minute": "Minúta" + } + }; + + $.fn.dateRangePicker = function(opt) { + if (!opt) opt = {}; + opt = $.extend(true, { + autoClose: false, + format: 'YYYY-MM-DD', + separator: ' to ', + language: 'auto', + startOfWeek: 'sunday', // or monday + getValue: function() { + return $(this).val(); + }, + setValue: function(s) { + if (!$(this).attr('readonly') && !$(this).is(':disabled') && s != $(this).val()) { + $(this).val(s); + } + }, + startDate: false, + endDate: false, + time: { + enabled: false + }, + minDays: 0, + maxDays: 0, + showShortcuts: false, + shortcuts: { + //'prev-days': [1,3,5,7], + // 'next-days': [3,5,7], + //'prev' : ['week','month','year'], + // 'next' : ['week','month','year'] + }, + customShortcuts: [], + inline: false, + container: 'body', + alwaysOpen: false, + singleDate: false, + lookBehind: false, + batchMode: false, + duration: 200, + stickyMonths: false, + dayDivAttrs: [], + dayTdAttrs: [], + selectForward: false, + selectBackward: false, + applyBtnClass: '', + singleMonth: 'auto', + hoveringTooltip: function(days, startTime, hoveringTime) { + return days > 1 ? days + ' ' + translate('days') : ''; + }, + showTopbar: true, + swapTime: false, + showWeekNumbers: false, + getWeekNumber: function(date) //date will be the first day of a week + { + return moment(date).format('w'); + }, + customOpenAnimation: null, + customCloseAnimation: null, + customArrowPrevSymbol: null, + customArrowNextSymbol: null, + monthSelect: false, + yearSelect: false + }, opt); + + opt.start = false; + opt.end = false; + + opt.startWeek = false; + + //detect a touch device + opt.isTouchDevice = 'ontouchstart' in window || navigator.msMaxTouchPoints; + + //if it is a touch device, hide hovering tooltip + if (opt.isTouchDevice) opt.hoveringTooltip = false; + + //show one month on mobile devices + if (opt.singleMonth == 'auto') opt.singleMonth = $(window).width() < 480; + if (opt.singleMonth) opt.stickyMonths = false; + + if (!opt.showTopbar) opt.autoClose = true; + + if (opt.startDate && typeof opt.startDate == 'string') opt.startDate = moment(opt.startDate, opt.format).toDate(); + if (opt.endDate && typeof opt.endDate == 'string') opt.endDate = moment(opt.endDate, opt.format).toDate(); + + if (opt.yearSelect && typeof opt.yearSelect === 'boolean') { + opt.yearSelect = function(current) { return [current - 5, current + 5]; } + } + + var languages = getLanguages(); + var box; + var initiated = false; + var self = this; + var selfDom = $(self).get(0); + var domChangeTimer; + + $(this).off('.datepicker').on('click.datepicker', function(evt) { + var isOpen = box.is(':visible'); + if (!isOpen) open(opt.duration); + }).on('change.datepicker', function(evt) { + checkAndSetDefaultValue(); + }).on('keyup.datepicker', function() { + try { + clearTimeout(domChangeTimer); + } catch (e) {} + domChangeTimer = setTimeout(function() { + checkAndSetDefaultValue(); + }, 2000); + }); + + init_datepicker.call(this); + + if (opt.alwaysOpen) { + open(0); + } + + // expose some api + $(this).data('dateRangePicker', { + setStart: function(d1) { + if (typeof d1 == 'string') { + d1 = moment(d1, opt.format).toDate(); + } + + opt.end = false; + setSingleDate(d1); + + return this; + }, + setEnd: function(d2, silent) { + var start = new Date(); + start.setTime(opt.start); + if (typeof d2 == 'string') { + d2 = moment(d2, opt.format).toDate(); + } + setDateRange(start, d2, silent); + return this; + }, + setDateRange: function(d1, d2, silent) { + if (typeof d1 == 'string' && typeof d2 == 'string') { + d1 = moment(d1, opt.format).toDate(); + d2 = moment(d2, opt.format).toDate(); + } + setDateRange(d1, d2, silent); + }, + clear: clearSelection, + close: closeDatePicker, + open: open, + redraw: redrawDatePicker, + getDatePicker: getDatePicker, + resetMonthsView: resetMonthsView, + destroy: function() { + $(self).off('.datepicker'); + $(self).data('dateRangePicker', ''); + $(self).data('date-picker-opened', null); + box.remove(); + $(window).off('resize.datepicker', calcPosition); + $(document).off('click.datepicker', outsideClickClose); + } + }); + + $(window).on('resize.datepicker', calcPosition); + + return this; + + function IsOwnDatePickerClicked(evt, selfObj) { + return (selfObj.contains(evt.target) || evt.target == selfObj || (selfObj.childNodes != undefined && $.inArray(evt.target, selfObj.childNodes) >= 0)); + } + + function init_datepicker() { + var self = this; + + if ($(this).data('date-picker-opened')) { + closeDatePicker(); + return; + } + $(this).data('date-picker-opened', true); + + + box = createDom().hide(); + box.append('
'); + + $(opt.container).append(box); + + if (!opt.inline) { + calcPosition(); + } else { + box.addClass('inline-wrapper'); + } + + if (opt.alwaysOpen) { + box.find('.apply-btn').hide(); + } + + var defaultTime = getDefaultTime(); + resetMonthsView(defaultTime); + + if (opt.time.enabled) { + if ((opt.startDate && opt.endDate) || (opt.start && opt.end)) { + showTime(moment(opt.start || opt.startDate).toDate(), 'time1'); + showTime(moment(opt.end || opt.endDate).toDate(), 'time2'); + } else { + var defaultEndTime = opt.defaultEndTime ? opt.defaultEndTime : defaultTime; + showTime(defaultTime, 'time1'); + showTime(defaultEndTime, 'time2'); + } + } + + //showSelectedInfo(); + + + var defaultTopText = ''; + if (opt.singleDate) + defaultTopText = translate('default-single'); + else if (opt.minDays && opt.maxDays) + defaultTopText = translate('default-range'); + else if (opt.minDays) + defaultTopText = translate('default-more'); + else if (opt.maxDays) + defaultTopText = translate('default-less'); + else + defaultTopText = translate('default-default'); + + box.find('.default-top').html(defaultTopText.replace(/\%d/, opt.minDays).replace(/\%d/, opt.maxDays)); + if (opt.singleMonth) { + box.addClass('single-month'); + } else { + box.addClass('two-months'); + } + + + setTimeout(function() { + updateCalendarWidth(); + initiated = true; + }, 0); + + box.on('click', function(evt) { + evt.stopPropagation(); + }); + + //if user click other place of the webpage, close date range picker window + $(document).on('click.datepicker', outsideClickClose); + + box.find('.next').on('click', function() { + if (!opt.stickyMonths) + gotoNextMonth(this); + else + gotoNextMonth_stickily(this); + }); + + function gotoNextMonth(self) { + var isMonth2 = $(self).parents('table').hasClass('month2'); + var month = isMonth2 ? opt.month2 : opt.month1; + month = nextMonth(month); + if (!opt.singleMonth && !opt.singleDate && !isMonth2 && compare_month(month, opt.month2) >= 0 || isMonthOutOfBounds(month)) return; + showMonth(month, isMonth2 ? 'month2' : 'month1'); + showGap(); + } + + function gotoNextMonth_stickily(self) { + var nextMonth1 = nextMonth(opt.month1); + var nextMonth2 = nextMonth(opt.month2); + if (isMonthOutOfBounds(nextMonth2)) return; + if (!opt.singleDate && compare_month(nextMonth1, nextMonth2) >= 0) return; + showMonth(nextMonth1, 'month1'); + showMonth(nextMonth2, 'month2'); + showSelectedDays(); + } + + + box.find('.prev').on('click', function() { + if (!opt.stickyMonths) + gotoPrevMonth(this); + else + gotoPrevMonth_stickily(this); + }); + + function gotoPrevMonth(self) { + var isMonth2 = $(self).parents('table').hasClass('month2'); + var month = isMonth2 ? opt.month2 : opt.month1; + month = prevMonth(month); + if (isMonth2 && compare_month(month, opt.month1) <= 0 || isMonthOutOfBounds(month)) return; + showMonth(month, isMonth2 ? 'month2' : 'month1'); + showGap(); + } + + function gotoPrevMonth_stickily(self) { + var prevMonth1 = prevMonth(opt.month1); + var prevMonth2 = prevMonth(opt.month2); + if (isMonthOutOfBounds(prevMonth1)) return; + if (!opt.singleDate && compare_month(prevMonth2, prevMonth1) <= 0) return; + showMonth(prevMonth2, 'month2'); + showMonth(prevMonth1, 'month1'); + showSelectedDays(); + } + + box.attr('unselectable', 'on') + .css('user-select', 'none') + .on('selectstart', function(e) { + e.preventDefault(); + return false; + }); + + box.find('.apply-btn').on('click', function() { + closeDatePicker(); + var dateRange = getDateString(new Date(opt.start)) + opt.separator + getDateString(new Date(opt.end)); + $(self).trigger('datepicker-apply', { + 'value': dateRange, + 'date1': new Date(opt.start), + 'date2': new Date(opt.end) + }); + }); + + box.find('[custom]').on('click', function() { + var valueName = $(this).attr('custom'); + opt.start = false; + opt.end = false; + box.find('.day.checked').removeClass('checked'); + opt.setValue.call(selfDom, valueName); + checkSelectionValid(); + showSelectedInfo(true); + showSelectedDays(); + if (opt.autoClose) closeDatePicker(); + }); + + box.find('[shortcut]').on('click', function() { + var shortcut = $(this).attr('shortcut'); + var end = new Date(), + start = false; + var dir; + if (shortcut.indexOf('day') != -1) { + var day = parseInt(shortcut.split(',', 2)[1], 10); + start = new Date(new Date().getTime() + 86400000 * day); + end = new Date(end.getTime() + 86400000 * (day > 0 ? 1 : -1)); + } else if (shortcut.indexOf('week') != -1) { + dir = shortcut.indexOf('prev,') != -1 ? -1 : 1; + var stopDay; + if (dir == 1) + stopDay = opt.startOfWeek == 'monday' ? 1 : 0; + else + stopDay = opt.startOfWeek == 'monday' ? 0 : 6; + + end = new Date(end.getTime() - 86400000); + while (end.getDay() != stopDay) end = new Date(end.getTime() + dir * 86400000); + start = new Date(end.getTime() + dir * 86400000 * 6); + } else if (shortcut.indexOf('month') != -1) { + dir = shortcut.indexOf('prev,') != -1 ? -1 : 1; + if (dir == 1) + start = nextMonth(end); + else + start = prevMonth(end); + start.setDate(1); + end = nextMonth(start); + end.setDate(1); + end = new Date(end.getTime() - 86400000); + } else if (shortcut.indexOf('year') != -1) { + dir = shortcut.indexOf('prev,') != -1 ? -1 : 1; + start = new Date(); + start.setFullYear(end.getFullYear() + dir); + start.setMonth(0); + start.setDate(1); + end.setFullYear(end.getFullYear() + dir); + end.setMonth(11); + end.setDate(31); + } else if (shortcut == 'custom') { + var name = $(this).html(); + if (opt.customShortcuts && opt.customShortcuts.length > 0) { + for (var i = 0; i < opt.customShortcuts.length; i++) { + var sh = opt.customShortcuts[i]; + if (sh.name == name) { + var data = []; + // try + // { + data = sh['dates'].call(); + //}catch(e){} + if (data && data.length == 2) { + start = data[0]; + end = data[1]; + } + + // if only one date is specified then just move calendars there + // move calendars to show this date's month and next months + if (data && data.length == 1) { + var movetodate = data[0]; + showMonth(movetodate, 'month1'); + showMonth(nextMonth(movetodate), 'month2'); + showGap(); + } + + break; + } + } + } + } + if (start && end) { + setDateRange(start, end); + checkSelectionValid(); + } + }); + + box.find('.time1 input[type=range]').on('change touchmove', function(e) { + var target = e.target, + hour = target.name == 'hour' ? $(target).val().replace(/^(\d{1})$/, '0$1') : undefined, + min = target.name == 'minute' ? $(target).val().replace(/^(\d{1})$/, '0$1') : undefined; + setTime('time1', hour, min); + }); + + box.find('.time2 input[type=range]').on('change touchmove', function(e) { + var target = e.target, + hour = target.name == 'hour' ? $(target).val().replace(/^(\d{1})$/, '0$1') : undefined, + min = target.name == 'minute' ? $(target).val().replace(/^(\d{1})$/, '0$1') : undefined; + setTime('time2', hour, min); + }); + + } + + + function calcPosition() { + if (!opt.inline) { + var offset = $(self).offset(); + if ($(opt.container).css('position') == 'relative') { + var containerOffset = $(opt.container).offset(); + var leftIndent = Math.max(0, offset.left + box.outerWidth() - $('body').width() + 16); + box.css({ + top: offset.top - containerOffset.top + $(self).outerHeight() + 4, + left: offset.left - containerOffset.left - leftIndent + }); + } else { + if (offset.left < 460) //left to right + { + box.css({ + top: offset.top + $(self).outerHeight() + parseInt($('body').css('border-top') || 0, 10), + left: offset.left + }); + } else { + box.css({ + top: offset.top + $(self).outerHeight() + parseInt($('body').css('border-top') || 0, 10), + left: offset.left + $(self).width() - box.width() - 16 + }); + } + } + } + } + + // Return the date picker wrapper element + function getDatePicker() { + return box; + } + + function open(animationTime) { + redrawDatePicker(); + checkAndSetDefaultValue(); + if (opt.customOpenAnimation) { + opt.customOpenAnimation.call(box.get(0), function() { + $(self).trigger('datepicker-opened', { + relatedTarget: box + }); + }); + } else { + box.slideDown(animationTime, function() { + $(self).trigger('datepicker-opened', { + relatedTarget: box + }); + }); + } + $(self).trigger('datepicker-open', { + relatedTarget: box + }); + showGap(); + updateCalendarWidth(); + calcPosition(); + } + + function checkAndSetDefaultValue() { + var __default_string = opt.getValue.call(selfDom); + var defaults = __default_string ? __default_string.split(opt.separator) : ''; + + if (defaults && ((defaults.length == 1 && opt.singleDate) || defaults.length >= 2)) { + var ___format = opt.format; + if (___format.match(/Do/)) { + + ___format = ___format.replace(/Do/, 'D'); + defaults[0] = defaults[0].replace(/(\d+)(th|nd|st)/, '$1'); + if (defaults.length >= 2) { + defaults[1] = defaults[1].replace(/(\d+)(th|nd|st)/, '$1'); + } + } + // set initiated to avoid triggerring datepicker-change event + initiated = false; + if (defaults.length >= 2) { + setDateRange(getValidValue(defaults[0], ___format, moment.locale(opt.language)), getValidValue(defaults[1], ___format, moment.locale(opt.language))); + } else if (defaults.length == 1 && opt.singleDate) { + setSingleDate(getValidValue(defaults[0], ___format, moment.locale(opt.language))); + } + + initiated = true; + } + } + + function getValidValue(date, format, locale) { + if (moment(date, format, locale).isValid()) { + return moment(date, format, locale).toDate(); + } else { + return moment().toDate(); + } + } + + function updateCalendarWidth() { + var gapMargin = box.find('.gap').css('margin-left'); + if (gapMargin) gapMargin = parseInt(gapMargin); + var w1 = box.find('.month1').width(); + var w2 = box.find('.gap').width() + (gapMargin ? gapMargin * 2 : 0); + var w3 = box.find('.month2').width(); + box.find('.month-wrapper').width(w1 + w2 + w3); + } + + function renderTime(name, date) { + box.find('.' + name + ' input[type=range].hour-range').val(moment(date).hours()); + box.find('.' + name + ' input[type=range].minute-range').val(moment(date).minutes()); + setTime(name, moment(date).format('HH'), moment(date).format('mm')); + } + + function changeTime(name, date) { + opt[name] = parseInt( + moment(parseInt(date)) + .hour(moment(opt[name + 'Time']).format('HH'), 'h') + .minute(moment(opt[name + 'Time']).format('mm'), 'm').valueOf() + ); + } + + function swapTime() { + renderTime('time1', opt.start); + renderTime('time2', opt.end); + } + + function setTime(name, hour, minute) { + hour && (box.find('.' + name + ' .hour-val').text(hour)); + minute && (box.find('.' + name + ' .minute-val').text(minute)); + switch (name) { + case 'time1': + if (opt.start) { + setRange('start', moment(opt.start)); + } + setRange('startTime', moment(opt.startTime || moment().valueOf())); + break; + case 'time2': + if (opt.end) { + setRange('end', moment(opt.end)); + } + setRange('endTime', moment(opt.endTime || moment().valueOf())); + break; + } + + function setRange(name, timePoint) { + var h = timePoint.format('HH'), + m = timePoint.format('mm'); + opt[name] = timePoint + .hour(hour || h, 'h') + .minute(minute || m, 'm') + .valueOf(); + } + checkSelectionValid(); + showSelectedInfo(); + showSelectedDays(); + } + + function clearSelection() { + opt.start = false; + opt.end = false; + box.find('.day.checked').removeClass('checked'); + box.find('.day.last-date-selected').removeClass('last-date-selected'); + box.find('.day.first-date-selected').removeClass('first-date-selected'); + opt.setValue.call(selfDom, ''); + checkSelectionValid(); + showSelectedInfo(); + showSelectedDays(); + } + + function handleStart(time) { + var r = time; + if (opt.batchMode === 'week-range') { + if (opt.startOfWeek === 'monday') { + r = moment(parseInt(time)).startOf('isoweek').valueOf(); + } else { + r = moment(parseInt(time)).startOf('week').valueOf(); + } + } else if (opt.batchMode === 'month-range') { + r = moment(parseInt(time)).startOf('month').valueOf(); + } + return r; + } + + function handleEnd(time) { + var r = time; + if (opt.batchMode === 'week-range') { + if (opt.startOfWeek === 'monday') { + r = moment(parseInt(time)).endOf('isoweek').valueOf(); + } else { + r = moment(parseInt(time)).endOf('week').valueOf(); + } + } else if (opt.batchMode === 'month-range') { + r = moment(parseInt(time)).endOf('month').valueOf(); + } + return r; + } + + + function dayClicked(day) { + if (day.hasClass('invalid')) return; + var time = day.attr('time'); + day.addClass('checked'); + if (opt.singleDate) { + opt.start = time; + opt.end = false; + } else if (opt.batchMode === 'week') { + if (opt.startOfWeek === 'monday') { + opt.start = moment(parseInt(time)).startOf('isoweek').valueOf(); + opt.end = moment(parseInt(time)).endOf('isoweek').valueOf(); + } else { + opt.end = moment(parseInt(time)).endOf('week').valueOf(); + opt.start = moment(parseInt(time)).startOf('week').valueOf(); + } + } else if (opt.batchMode === 'workweek') { + opt.start = moment(parseInt(time)).day(1).valueOf(); + opt.end = moment(parseInt(time)).day(5).valueOf(); + } else if (opt.batchMode === 'weekend') { + opt.start = moment(parseInt(time)).day(6).valueOf(); + opt.end = moment(parseInt(time)).day(7).valueOf(); + } else if (opt.batchMode === 'month') { + opt.start = moment(parseInt(time)).startOf('month').valueOf(); + opt.end = moment(parseInt(time)).endOf('month').valueOf(); + } else if ((opt.start && opt.end) || (!opt.start && !opt.end)) { + opt.start = handleStart(time); + opt.end = false; + } else if (opt.start) { + opt.end = handleEnd(time); + if (opt.time.enabled) { + changeTime('end', opt.end); + } + } + + //Update time in case it is enabled and timestamps are available + if (opt.time.enabled) { + if (opt.start) { + changeTime('start', opt.start); + } + if (opt.end) { + changeTime('end', opt.end); + } + } + + //In case the start is after the end, swap the timestamps + if (!opt.singleDate && opt.start && opt.end && opt.start > opt.end) { + var tmp = opt.end; + opt.end = handleEnd(opt.start); + opt.start = handleStart(tmp); + if (opt.time.enabled && opt.swapTime) { + swapTime(); + } + } + + opt.start = parseInt(opt.start); + opt.end = parseInt(opt.end); + + clearHovering(); + if (opt.start && !opt.end) { + $(self).trigger('datepicker-first-date-selected', { + 'date1': new Date(opt.start) + }); + dayHovering(day); + } + updateSelectableRange(time); + + checkSelectionValid(); + showSelectedInfo(); + showSelectedDays(); + autoclose(); + } + + + function weekNumberClicked(weekNumberDom) { + var thisTime = parseInt(weekNumberDom.attr('data-start-time'), 10); + var date1, date2; + if (!opt.startWeek) { + opt.startWeek = thisTime; + weekNumberDom.addClass('week-number-selected'); + date1 = new Date(thisTime); + opt.start = moment(date1).day(opt.startOfWeek == 'monday' ? 1 : 0).valueOf(); + opt.end = moment(date1).day(opt.startOfWeek == 'monday' ? 7 : 6).valueOf(); + } else { + box.find('.week-number-selected').removeClass('week-number-selected'); + date1 = new Date(thisTime < opt.startWeek ? thisTime : opt.startWeek); + date2 = new Date(thisTime < opt.startWeek ? opt.startWeek : thisTime); + opt.startWeek = false; + opt.start = moment(date1).day(opt.startOfWeek == 'monday' ? 1 : 0).valueOf(); + opt.end = moment(date2).day(opt.startOfWeek == 'monday' ? 7 : 6).valueOf(); + } + updateSelectableRange(); + checkSelectionValid(); + showSelectedInfo(); + showSelectedDays(); + autoclose(); + } + + function isValidTime(time) { + time = parseInt(time, 10); + if (opt.startDate && compare_day(time, opt.startDate) < 0) return false; + if (opt.endDate && compare_day(time, opt.endDate) > 0) return false; + + if (opt.start && !opt.end && !opt.singleDate) { + //check maxDays and minDays setting + if (opt.maxDays > 0 && countDays(time, opt.start) > opt.maxDays) return false; + if (opt.minDays > 0 && countDays(time, opt.start) < opt.minDays) return false; + + //check selectForward and selectBackward + if (opt.selectForward && time < opt.start) return false; + if (opt.selectBackward && time > opt.start) return false; + + //check disabled days + if (opt.beforeShowDay && typeof opt.beforeShowDay == 'function') { + var valid = true; + var timeTmp = time; + while (countDays(timeTmp, opt.start) > 1) { + var arr = opt.beforeShowDay(new Date(timeTmp)); + if (!arr[0]) { + valid = false; + break; + } + if (Math.abs(timeTmp - opt.start) < 86400000) break; + if (timeTmp > opt.start) timeTmp -= 86400000; + if (timeTmp < opt.start) timeTmp += 86400000; + } + if (!valid) return false; + } + } + return true; + } + + + function updateSelectableRange() { + box.find('.day.invalid.tmp').removeClass('tmp invalid').addClass('valid'); + if (opt.start && !opt.end) { + box.find('.day.toMonth.valid').each(function() { + var time = parseInt($(this).attr('time'), 10); + if (!isValidTime(time)) + $(this).addClass('invalid tmp').removeClass('valid'); + else + $(this).addClass('valid tmp').removeClass('invalid'); + }); + } + + return true; + } + + + function dayHovering(day) { + var hoverTime = parseInt(day.attr('time')); + var tooltip = ''; + + if (day.hasClass('has-tooltip') && day.attr('data-tooltip')) { + tooltip = '' + day.attr('data-tooltip') + ''; + } else if (!day.hasClass('invalid')) { + if (opt.singleDate) { + box.find('.day.hovering').removeClass('hovering'); + day.addClass('hovering'); + } else { + box.find('.day').each(function() { + var time = parseInt($(this).attr('time')), + start = opt.start, + end = opt.end; + + if (time == hoverTime) { + $(this).addClass('hovering'); + } else { + $(this).removeClass('hovering'); + } + + if ( + (opt.start && !opt.end) && + ( + (opt.start < time && hoverTime >= time) || + (opt.start > time && hoverTime <= time) + ) + ) { + $(this).addClass('hovering'); + } else { + $(this).removeClass('hovering'); + } + }); + + if (opt.start && !opt.end) { + var days = countDays(hoverTime, opt.start); + if (opt.hoveringTooltip) { + if (typeof opt.hoveringTooltip == 'function') { + tooltip = opt.hoveringTooltip(days, opt.start, hoverTime); + } else if (opt.hoveringTooltip === true && days > 1) { + tooltip = days + ' ' + translate('days'); + } + } + } + } + } + + if (tooltip) { + var posDay = day.offset(); + var posBox = box.offset(); + + var _left = posDay.left - posBox.left; + var _top = posDay.top - posBox.top; + _left += day.width() / 2; + + + var $tip = box.find('.date-range-length-tip'); + var w = $tip.css({ + 'visibility': 'hidden', + 'display': 'none' + }).html(tooltip).width(); + var h = $tip.height(); + _left -= w / 2; + _top -= h; + setTimeout(function() { + $tip.css({ + left: _left, + top: _top, + display: 'block', + 'visibility': 'visible' + }); + }, 10); + } else { + box.find('.date-range-length-tip').hide(); + } + } + + function clearHovering() { + box.find('.day.hovering').removeClass('hovering'); + box.find('.date-range-length-tip').hide(); + } + + function dateChanged(date) { + var value = date.val(); + var name = date.attr('name'); + var type = date.parents('table').hasClass('month1') ? 'month1' : 'month2'; + var oppositeType = type === 'month1' ? 'month2' : 'month1'; + var startDate = opt.startDate ? moment(opt.startDate) : false; + var endDate = opt.endDate ? moment(opt.endDate) : false; + var newDate = moment(opt[type])[name](value); + + + if (startDate && newDate.isSameOrBefore(startDate)) { + newDate = startDate.add(type === 'month2' ? 1 : 0, 'month'); + } + + if (endDate && newDate.isSameOrAfter(endDate)) { + newDate = endDate.add(!opt.singleMonth && type === 'month1' ? -1 : 0, 'month'); + } + + showMonth(newDate, type); + + if (type === 'month1') { + if (opt.stickyMonths || moment(newDate).isSameOrAfter(opt[oppositeType], 'month')) { + showMonth(moment(newDate).add(1, 'month'), oppositeType); + } + } else { + if (opt.stickyMonths || moment(newDate).isSameOrBefore(opt[oppositeType], 'month')) { + showMonth(moment(newDate).add(-1, 'month'), oppositeType); + } + } + + showGap(); + } + + function autoclose() { + if (opt.singleDate === true) { + if (initiated && opt.start) { + if (opt.autoClose) closeDatePicker(); + } + } else { + if (initiated && opt.start && opt.end) { + if (opt.autoClose) closeDatePicker(); + } + } + } + + function checkSelectionValid() { + var days = Math.ceil((opt.end - opt.start) / 86400000) + 1; + if (opt.singleDate) { // Validate if only start is there + if (opt.start && !opt.end) + box.find('.drp_top-bar').removeClass('error').addClass('normal'); + else + box.find('.drp_top-bar').removeClass('error').removeClass('normal'); + } else if (opt.maxDays && days > opt.maxDays) { + opt.start = false; + opt.end = false; + box.find('.day').removeClass('checked'); + box.find('.drp_top-bar').removeClass('normal').addClass('error').find('.error-top').html(translate('less-than').replace('%d', opt.maxDays)); + } else if (opt.minDays && days < opt.minDays) { + opt.start = false; + opt.end = false; + box.find('.day').removeClass('checked'); + box.find('.drp_top-bar').removeClass('normal').addClass('error').find('.error-top').html(translate('more-than').replace('%d', opt.minDays)); + } else { + if (opt.start || opt.end) + box.find('.drp_top-bar').removeClass('error').addClass('normal'); + else + box.find('.drp_top-bar').removeClass('error').removeClass('normal'); + } + + if ((opt.singleDate && opt.start && !opt.end) || (!opt.singleDate && opt.start && opt.end)) { + box.find('.apply-btn').removeClass('disabled'); + } else { + box.find('.apply-btn').addClass('disabled'); + } + + if (opt.batchMode) { + if ( + (opt.start && opt.startDate && compare_day(opt.start, opt.startDate) < 0) || + (opt.end && opt.endDate && compare_day(opt.end, opt.endDate) > 0) + ) { + opt.start = false; + opt.end = false; + box.find('.day').removeClass('checked'); + } + } + } + + function showSelectedInfo(forceValid, silent) { + box.find('.start-day').html('...'); + box.find('.end-day').html('...'); + box.find('.selected-days').hide(); + if (opt.start) { + box.find('.start-day').html(getDateString(new Date(parseInt(opt.start)))); + } + if (opt.end) { + box.find('.end-day').html(getDateString(new Date(parseInt(opt.end)))); + } + var dateRange; + if (opt.start && opt.singleDate) { + box.find('.apply-btn').removeClass('disabled'); + dateRange = getDateString(new Date(opt.start)); + opt.setValue.call(selfDom, dateRange, getDateString(new Date(opt.start)), getDateString(new Date(opt.end))); + + if (initiated && !silent) { + $(self).trigger('datepicker-change', { + 'value': dateRange, + 'date1': new Date(opt.start) + }); + } + } else if (opt.start && opt.end) { + box.find('.selected-days').show().find('.selected-days-num').html(countDays(opt.end, opt.start)); + box.find('.apply-btn').removeClass('disabled'); + dateRange = getDateString(new Date(opt.start)) + opt.separator + getDateString(new Date(opt.end)); + opt.setValue.call(selfDom, dateRange, getDateString(new Date(opt.start)), getDateString(new Date(opt.end))); + if (initiated && !silent) { + $(self).trigger('datepicker-change', { + 'value': dateRange, + 'date1': new Date(opt.start), + 'date2': new Date(opt.end) + }); + } + } else if (forceValid) { + box.find('.apply-btn').removeClass('disabled'); + } else { + box.find('.apply-btn').addClass('disabled'); + } + } + + function countDays(start, end) { + return Math.abs(moment(start).diff(moment(end), 'd')) + 1; + } + + function setDateRange(date1, date2, silent) { + if (date1.getTime() > date2.getTime()) { + var tmp = date2; + date2 = date1; + date1 = tmp; + tmp = null; + } + var valid = true; + if (opt.startDate && compare_day(date1, opt.startDate) < 0) valid = false; + if (opt.endDate && compare_day(date2, opt.endDate) > 0) valid = false; + if (!valid) { + showMonth(opt.startDate, 'month1'); + showMonth(nextMonth(opt.startDate), 'month2'); + showGap(); + return; + } + + opt.start = date1.getTime(); + opt.end = date2.getTime(); + + if (opt.time.enabled) { + renderTime('time1', date1); + renderTime('time2', date2); + } + + if (opt.stickyMonths || (compare_day(date1, date2) > 0 && compare_month(date1, date2) === 0)) { + if (opt.lookBehind) { + date1 = prevMonth(date2); + } else { + date2 = nextMonth(date1); + } + } + + if (opt.stickyMonths && opt.endDate !== false && compare_month(date2, opt.endDate) > 0) { + date1 = prevMonth(date1); + date2 = prevMonth(date2); + } + + if (!opt.stickyMonths) { + if (compare_month(date1, date2) === 0) { + if (opt.lookBehind) { + date1 = prevMonth(date2); + } else { + date2 = nextMonth(date1); + } + } + } + + showMonth(date1, 'month1'); + showMonth(date2, 'month2'); + showGap(); + checkSelectionValid(); + showSelectedInfo(false, silent); + autoclose(); + } + + function setSingleDate(date1) { + + var valid = true; + if (opt.startDate && compare_day(date1, opt.startDate) < 0) valid = false; + if (opt.endDate && compare_day(date1, opt.endDate) > 0) valid = false; + if (!valid) { + showMonth(opt.startDate, 'month1'); + return; + } + + opt.start = date1.getTime(); + + + if (opt.time.enabled) { + renderTime('time1', date1); + + } + showMonth(date1, 'month1'); + if (opt.singleMonth !== true) { + var date2 = nextMonth(date1); + showMonth(date2, 'month2'); + } + showGap(); + showSelectedInfo(); + autoclose(); + } + + function showSelectedDays() { + if (!opt.start && !opt.end) return; + box.find('.day').each(function() { + var time = parseInt($(this).attr('time')), + start = opt.start, + end = opt.end; + if (opt.time.enabled) { + time = moment(time).startOf('day').valueOf(); + start = moment(start || moment().valueOf()).startOf('day').valueOf(); + end = moment(end || moment().valueOf()).startOf('day').valueOf(); + } + if ( + (opt.start && opt.end && end >= time && start <= time) || + (opt.start && !opt.end && moment(start).format('YYYY-MM-DD') == moment(time).format('YYYY-MM-DD')) + ) { + $(this).addClass('checked'); + } else { + $(this).removeClass('checked'); + } + + //add first-date-selected class name to the first date selected + if (opt.start && moment(start).format('YYYY-MM-DD') == moment(time).format('YYYY-MM-DD')) { + $(this).addClass('first-date-selected'); + } else { + $(this).removeClass('first-date-selected'); + } + //add last-date-selected + if (opt.end && moment(end).format('YYYY-MM-DD') == moment(time).format('YYYY-MM-DD')) { + $(this).addClass('last-date-selected'); + } else { + $(this).removeClass('last-date-selected'); + } + }); + + box.find('.week-number').each(function() { + if ($(this).attr('data-start-time') == opt.startWeek) { + $(this).addClass('week-number-selected'); + } + }); + } + + function showMonth(date, month) { + date = moment(date).toDate(); + var monthElement = generateMonthElement(date, month); + var yearElement = generateYearElement(date, month); + + box.find('.' + month + ' .month-name').html(monthElement + ' ' + yearElement); + box.find('.' + month + ' tbody').html(createMonthHTML(date)); + opt[month] = date; + updateSelectableRange(); + bindEvents(); + } + + function generateMonthElement(date, month) { + date = moment(date); + var currentMonth = date.get('month'); + var currentMonthName = nameMonth(currentMonth); + var nonSelectableMonth = '
' + currentMonthName + '
'; + + if (!opt.monthSelect) { return nonSelectableMonth; } + + var startDate = opt.startDate ? moment(opt.startDate).add(!opt.singleMonth && month === 'month2' ? 1 : 0, 'month') : false; + var endDate = opt.endDate ? moment(opt.endDate).add(!opt.singleMonth && month === 'month1' ? -1 : 0, 'month') : false; + + var minSelectableMonth = startDate && date.isSame(startDate, 'year') ? startDate.get('month') : 0; + var maxSelectableMonth = endDate && date.isSame(endDate, 'year') ? endDate.get('month') : 11; + var minVisibleMonth = Math.min(minSelectableMonth, currentMonth); + var maxVisibleMonth = Math.max(maxSelectableMonth, currentMonth); + + if (minVisibleMonth === maxVisibleMonth) { return nonSelectableMonth; } + + var selectData = generateSelectData( + { + minSelectable: minSelectableMonth, + maxSelectable: maxSelectableMonth, + minVisible: minVisibleMonth, + maxVisible: maxVisibleMonth, + }, + currentMonth, + function (value) { return nameMonth(value); } + ) + return generateSelect('month', selectData); + } + + function generateYearElement(date, month) { + date = moment(date); + var currentYear = date.get('year'); + var nonSelectableMonth = '
' + currentYear + '
'; + + if (!opt.yearSelect) { return nonSelectableMonth; } + + var isYearFunction = opt.yearSelect && typeof opt.yearSelect === 'function'; + var startDate = opt.startDate ? moment(opt.startDate).add(!opt.singleMonth && month === 'month2' ? 1 : 0, 'month') : false; + var endDate = opt.endDate ? moment(opt.endDate).add(!opt.singleMonth && month === 'month1' ? -1 : 0, 'month') : false; + var range = isYearFunction ? opt.yearSelect(currentYear) : opt.yearSelect.slice(); + + var minSelectableYear = startDate ? Math.max(range[0], startDate.get('year')) : Math.min(range[0], currentYear); + var maxSelectableYear = endDate ? Math.min(range[1], endDate.get('year')) : Math.max(range[1], currentYear); + var minVisibleYear = Math.min(minSelectableYear, currentYear); + var maxVisibleYear = Math.max(maxSelectableYear, currentYear); + + if (minVisibleYear === maxVisibleYear) { return nonSelectableMonth; } + + var selectData = generateSelectData( + { + minSelectable: minSelectableYear, + maxSelectable: maxSelectableYear, + minVisible: minVisibleYear, + maxVisible: maxVisibleYear, + }, + currentYear + ) + return generateSelect('year', selectData); + } + + + function generateSelectData(range, current, valueBeautifier) { + var data = []; + valueBeautifier = valueBeautifier || function(value) { return value; }; + + for (var i = range.minVisible; i <= range.maxVisible; i++) { + data.push({ + value: i, + text: valueBeautifier(i), + selected: i === current, + disabled: ((i < range.minSelectable) || (i > range.maxSelectable)), + }); + } + + return data; + } + + function generateSelect(name, data) { + var select = '
' + current + '
'; + + return select; + } + + function bindEvents() { + box.find('.day').off("click").on('click', function(evt) { + dayClicked($(this)); + }); + + box.find('.day').off("mouseenter").on('mouseenter', function(evt) { + dayHovering($(this)); + }); + + box.find('.day').off("mouseleave").on('mouseleave', function(evt) { + box.find('.date-range-length-tip').hide(); + if (opt.singleDate) { + clearHovering(); + } + }); + + box.find('.week-number').off("click").on('click', function(evt) { + weekNumberClicked($(this)); + }); + + box.find('.month').off("change").on('change', function(evt) { + dateChanged($(this)); + }); + + box.find('.year').off("change").on('change', function(evt) { + dateChanged($(this)); + }); + } + + function showTime(date, name) { + box.find('.' + name).append(getTimeHTML()); + renderTime(name, date); + } + + function nameMonth(m) { + return translate('month-name')[m]; + } + + function getDateString(d) { + return moment(d).format(opt.format); + } + + function showGap() { + showSelectedDays(); + var m1 = parseInt(moment(opt.month1).format('YYYYMM')); + var m2 = parseInt(moment(opt.month2).format('YYYYMM')); + var p = Math.abs(m1 - m2); + var shouldShow = (p > 1 && p != 89); + if (shouldShow) { + box.addClass('has-gap').removeClass('no-gap').find('.gap').css('visibility', 'visible'); + } else { + box.removeClass('has-gap').addClass('no-gap').find('.gap').css('visibility', 'hidden'); + } + var h1 = box.find('table.month1').height(); + var h2 = box.find('table.month2').height(); + box.find('.gap').height(Math.max(h1, h2) + 10); + } + + function closeDatePicker() { + if (opt.alwaysOpen) return; + + var afterAnim = function() { + $(self).data('date-picker-opened', false); + $(self).trigger('datepicker-closed', { + relatedTarget: box + }); + }; + if (opt.customCloseAnimation) { + opt.customCloseAnimation.call(box.get(0), afterAnim); + } else { + $(box).slideUp(opt.duration, afterAnim); + } + $(self).trigger('datepicker-close', { + relatedTarget: box + }); + } + + function redrawDatePicker() { + showMonth(opt.month1, 'month1'); + showMonth(opt.month2, 'month2'); + } + + function compare_month(m1, m2) { + var p = parseInt(moment(m1).format('YYYYMM')) - parseInt(moment(m2).format('YYYYMM')); + if (p > 0) return 1; + if (p === 0) return 0; + return -1; + } + + function compare_day(m1, m2) { + var p = parseInt(moment(m1).format('YYYYMMDD')) - parseInt(moment(m2).format('YYYYMMDD')); + if (p > 0) return 1; + if (p === 0) return 0; + return -1; + } + + function nextMonth(month) { + return moment(month).add(1, 'months').toDate(); + } + + function prevMonth(month) { + return moment(month).add(-1, 'months').toDate(); + } + + function getTimeHTML() { + return '
' + + '' + translate('Time') + ': 00:00' + + '
' + + '
' + + '' + + '
' + + '
' + + '' + + '
'; + } + + function createDom() { + var html = '
'; + + if (opt.showTopbar) { + html += '
'; + + if (opt.customTopBar) { + if (typeof opt.customTopBar == 'function') opt.customTopBar = opt.customTopBar(); + html += '
' + opt.customTopBar + '
'; + } else { + html += '
' + + '' + translate('selected') + ' ...'; + if (!opt.singleDate) { + html += ' ' + opt.separator + ' ... (3 ' + translate('days') + ')'; + } + html += '
'; + html += '
error
' + + '
default
'; + } + + html += ''; + html += '
'; + } + + var _colspan = opt.showWeekNumbers ? 6 : 5; + + var arrowPrev = '<'; + if (opt.customArrowPrevSymbol) arrowPrev = opt.customArrowPrevSymbol; + + var arrowNext = '>'; + if (opt.customArrowNextSymbol) arrowNext = opt.customArrowNextSymbol; + + html += '
' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + getWeekHead() + + ' ' + + ' ' + + '
' + + ' ' + + arrowPrev + + ' ' + + ' ' + + ' ' + + (opt.singleDate || !opt.stickyMonths ? '' + arrowNext + '' : '') + + '
'; + + if (hasMonth2()) { + html += '
' + getGapHTML() + '
' + + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + getWeekHead() + + ' ' + + ' ' + + '
' + + (!opt.stickyMonths ? '' + arrowPrev + '' : '') + + ' ' + + ' ' + + ' ' + arrowNext + '' + + '
'; + + } + //+'
' + html += '
' + + '
' + + '
'; + if (!opt.singleDate) { + html += '
'; + } + html += '
' + + '
' + + '
'; + + html += ''; + + + return $(html); + } + + function getApplyBtnClass() { + var klass = ''; + if (opt.autoClose === true) { + klass += ' hide'; + } + if (opt.applyBtnClass !== '') { + klass += ' ' + opt.applyBtnClass; + } + return klass; + } + + function getWeekHead() { + var prepend = opt.showWeekNumbers ? '' + translate('week-number') + '' : ''; + if (opt.startOfWeek == 'monday') { + return prepend + '' + translate('week-1') + '' + + '' + translate('week-2') + '' + + '' + translate('week-3') + '' + + '' + translate('week-4') + '' + + '' + translate('week-5') + '' + + '' + translate('week-6') + '' + + '' + translate('week-7') + ''; + } else { + return prepend + '' + translate('week-7') + '' + + '' + translate('week-1') + '' + + '' + translate('week-2') + '' + + '' + translate('week-3') + '' + + '' + translate('week-4') + '' + + '' + translate('week-5') + '' + + '' + translate('week-6') + ''; + } + } + + function isMonthOutOfBounds(month) { + month = moment(month); + if (opt.startDate && month.endOf('month').isBefore(opt.startDate)) { + return true; + } + if (opt.endDate && month.startOf('month').isAfter(opt.endDate)) { + return true; + } + return false; + } + + function getGapHTML() { + var html = ['
']; + for (var i = 0; i < 20; i++) { + html.push('
' + + '
' + + '
' + + '
' + + '
'); + } + html.push('
'); + return html.join(''); + } + + function hasMonth2() { + return (!opt.singleMonth); + } + + function attributesCallbacks(initialObject, callbacksArray, today) { + var resultObject = $.extend(true, {}, initialObject); + + $.each(callbacksArray, function(cbAttrIndex, cbAttr) { + var addAttributes = cbAttr(today); + for (var attr in addAttributes) { + if (resultObject.hasOwnProperty(attr)) { + resultObject[attr] += addAttributes[attr]; + } else { + resultObject[attr] = addAttributes[attr]; + } + } + }); + + var attrString = ''; + + for (var attr in resultObject) { + if (resultObject.hasOwnProperty(attr)) { + attrString += attr + '="' + resultObject[attr] + '" '; + } + } + + return attrString; + } + + function createMonthHTML(d) { + var days = []; + d.setDate(1); + var lastMonth = new Date(d.getTime() - 86400000); + var now = new Date(); + + var dayOfWeek = d.getDay(); + if ((dayOfWeek === 0) && (opt.startOfWeek === 'monday')) { + // add one week + dayOfWeek = 7; + } + var today, valid; + + if (dayOfWeek > 0) { + for (var i = dayOfWeek; i > 0; i--) { + var day = new Date(d.getTime() - 86400000 * i); + valid = isValidTime(day.getTime()); + if (opt.startDate && compare_day(day, opt.startDate) < 0) valid = false; + if (opt.endDate && compare_day(day, opt.endDate) > 0) valid = false; + days.push({ + date: day, + type: 'lastMonth', + day: day.getDate(), + time: day.getTime(), + valid: valid + }); + } + } + var toMonth = d.getMonth(); + for (var i = 0; i < 40; i++) { + today = moment(d).add(i, 'days').toDate(); + valid = isValidTime(today.getTime()); + if (opt.startDate && compare_day(today, opt.startDate) < 0) valid = false; + if (opt.endDate && compare_day(today, opt.endDate) > 0) valid = false; + days.push({ + date: today, + type: today.getMonth() == toMonth ? 'toMonth' : 'nextMonth', + day: today.getDate(), + time: today.getTime(), + valid: valid + }); + } + var html = []; + for (var week = 0; week < 6; week++) { + if (days[week * 7].type == 'nextMonth') break; + html.push(''); + + for (var day = 0; day < 7; day++) { + var _day = (opt.startOfWeek == 'monday') ? day + 1 : day; + today = days[week * 7 + _day]; + var highlightToday = moment(today.time).format('L') == moment(now).format('L'); + today.extraClass = ''; + today.tooltip = ''; + if (today.valid && opt.beforeShowDay && typeof opt.beforeShowDay == 'function') { + var _r = opt.beforeShowDay(moment(today.time).toDate()); + today.valid = _r[0]; + today.extraClass = _r[1] || ''; + today.tooltip = _r[2] || ''; + if (today.tooltip !== '') today.extraClass += ' has-tooltip '; + } + + var todayDivAttr = { + time: today.time, + 'data-tooltip': today.tooltip, + 'class': 'day ' + today.type + ' ' + today.extraClass + ' ' + (today.valid ? 'valid' : 'invalid') + ' ' + (highlightToday ? 'real-today' : '') + }; + + if (day === 0 && opt.showWeekNumbers) { + html.push('
' + opt.getWeekNumber(today.date) + '
'); + } + + html.push('
' + showDayHTML(today.time, today.day) + '
'); + } + html.push(''); + } + return html.join(''); + } + + function showDayHTML(time, date) { + if (opt.showDateFilter && typeof opt.showDateFilter == 'function') return opt.showDateFilter(time, date); + return date; + } + + function getLanguages() { + if (opt.language == 'auto') { + var language = navigator.language ? navigator.language : navigator.browserLanguage; + if (!language) { + return $.dateRangePickerLanguages['default']; + } + language = language.toLowerCase(); + if(language in $.dateRangePickerLanguages){ + return $.dateRangePickerLanguages[language]; + } + + return $.dateRangePickerLanguages['default']; + } else if (opt.language && opt.language in $.dateRangePickerLanguages) { + return $.dateRangePickerLanguages[opt.language]; + } else { + return $.dateRangePickerLanguages['default']; + } + } + + /** + * Translate language string, try both the provided translation key, as the lower case version + */ + function translate(translationKey) { + var translationKeyLowerCase = translationKey.toLowerCase(); + console.log(languages); + var result = (translationKey in languages) ? languages[translationKey] : (translationKeyLowerCase in languages) ? languages[translationKeyLowerCase] : null; + var defaultLanguage = $.dateRangePickerLanguages['default']; + if (result == null) result = (translationKey in defaultLanguage) ? defaultLanguage[translationKey] : (translationKeyLowerCase in defaultLanguage) ? defaultLanguage[translationKeyLowerCase] : ''; + console.log(result); + + return result; + } + + function getDefaultTime() { + var defaultTime = opt.defaultTime ? opt.defaultTime : new Date(); + + if (opt.lookBehind) { + if (opt.startDate && compare_month(defaultTime, opt.startDate) < 0) defaultTime = nextMonth(moment(opt.startDate).toDate()); + if (opt.endDate && compare_month(defaultTime, opt.endDate) > 0) defaultTime = moment(opt.endDate).toDate(); + } else { + if (opt.startDate && compare_month(defaultTime, opt.startDate) < 0) defaultTime = moment(opt.startDate).toDate(); + if (opt.endDate && compare_month(nextMonth(defaultTime), opt.endDate) > 0) defaultTime = prevMonth(moment(opt.endDate).toDate()); + } + + if (opt.singleDate) { + if (opt.startDate && compare_month(defaultTime, opt.startDate) < 0) defaultTime = moment(opt.startDate).toDate(); + if (opt.endDate && compare_month(defaultTime, opt.endDate) > 0) defaultTime = moment(opt.endDate).toDate(); + } + + return defaultTime; + } + + function resetMonthsView(time) { + if (!time) { + time = getDefaultTime(); + } + + if (opt.lookBehind) { + showMonth(prevMonth(time), 'month1'); + showMonth(time, 'month2'); + } else { + showMonth(time, 'month1'); + showMonth(nextMonth(time), 'month2'); + } + + if (opt.singleDate) { + showMonth(time, 'month1'); + } + + showSelectedDays(); + showGap(); + } + + function outsideClickClose(evt) { + if (!IsOwnDatePickerClicked(evt, self[0])) { + if (box.is(':visible')) closeDatePicker(); + } + } + + }; +})); diff --git a/js/daterangepicker/jquery.daterangepicker.min.css b/js/daterangepicker/jquery.daterangepicker.min.css new file mode 100644 index 000000000..45d658857 --- /dev/null +++ b/js/daterangepicker/jquery.daterangepicker.min.css @@ -0,0 +1 @@ +.date-picker{width:170px;height:25px;padding:0;border:0;line-height:25px;padding-left:10px;font-size:12px;font-family:Arial,sans-serif;font-weight:700;cursor:pointer;color:#303030;position:relative;z-index:2}.date-picker-wrapper{position:absolute;z-index:1;border:1px solid #bfbfbf;background-color:#efefef;padding:5px 12px;font-size:12px;line-height:20px;color:#aaa;font-family:Arial,sans-serif;-webkit-box-shadow:3px 3px 10px rgba(0,0,0,.5);box-shadow:3px 3px 10px rgba(0,0,0,.5);-webkit-box-sizing:initial;box-sizing:initial}.dp-clearfix{clear:both;height:0;font-size:0}.date-picker-wrapper.inline-wrapper{position:relative;-webkit-box-shadow:none;box-shadow:none;display:inline-block}.date-picker-wrapper.single-date{width:auto}.date-picker-wrapper.no-shortcuts{padding-bottom:12px}.date-picker-wrapper.no-topbar{padding-top:12px}.date-picker-wrapper .footer{font-size:11px;padding-top:3px}.date-picker-wrapper b{color:#666;font-weight:700}.date-picker-wrapper a{color:#6bb4d6;text-decoration:underline}.date-picker-wrapper .month-name{text-transform:uppercase}.date-picker-wrapper .select-wrapper{position:relative;overflow:hidden;display:inline-block;vertical-align:middle}.date-picker-wrapper .select-wrapper:hover{text-decoration:underline}.date-picker-wrapper .month-element{display:inline-block;vertical-align:middle}.date-picker-wrapper .select-wrapper select{position:absolute;margin:0;padding:0;left:0;top:-1px;font-size:inherit;font-style:inherit;font-weight:inherit;text-transform:inherit;color:inherit;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:0 0;border:0;outline:0;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=1)";filter:alpha(opacity=1);opacity:.01}.date-picker-wrapper .month-wrapper{border:1px solid #bfbfbf;border-radius:3px;background-color:#fff;padding:5px;cursor:default;position:relative;_overflow:hidden}.date-picker-wrapper .month-wrapper table{width:190px;float:left}.date-picker-wrapper .month-wrapper table.month2{width:190px;float:left}.date-picker-wrapper .month-wrapper table td,.date-picker-wrapper .month-wrapper table th{vertical-align:middle;text-align:center;line-height:14px;margin:0;padding:0}.date-picker-wrapper .month-wrapper table .day{padding:5px 0;line-height:1;font-size:12px;margin-bottom:1px;color:#ccc;cursor:default}.date-picker-wrapper .month-wrapper table div.day.lastMonth,.date-picker-wrapper .month-wrapper table div.day.nextMonth{color:#999;cursor:default}.date-picker-wrapper .month-wrapper table .day.checked{background-color:#9cdbf7}.date-picker-wrapper .month-wrapper table .week-name{height:20px;line-height:20px;font-weight:100;text-transform:uppercase}.date-picker-wrapper .month-wrapper table .day.has-tooltip{cursor:help!important}.date-picker-wrapper .month-wrapper table .day.has-tooltip .tooltip{white-space:nowrap}.date-picker-wrapper .time label{white-space:nowrap}.date-picker-wrapper .month-wrapper table .day.toMonth.valid{color:#333;cursor:pointer}.date-picker-wrapper .month-wrapper table .day.toMonth.hovering{background-color:#cdecfa}.date-picker-wrapper .month-wrapper table .day.lastMonth,.date-picker-wrapper .month-wrapper table .day.nextMonth{display:none}.date-picker-wrapper .month-wrapper table .day.real-today{background-color:#ffe684}.date-picker-wrapper .month-wrapper table .day.real-today.checked,.date-picker-wrapper .month-wrapper table .day.real-today.hovering{background-color:#70ccd5}.date-picker-wrapper table .caption{height:40px}.date-picker-wrapper table .caption>th:first-of-type,.date-picker-wrapper table .caption>th:last-of-type{width:27px}.date-picker-wrapper table .caption .next,.date-picker-wrapper table .caption .prev{padding:0 5px;cursor:pointer}.date-picker-wrapper table .caption .next:hover,.date-picker-wrapper table .caption .prev:hover{background-color:#ccc;color:#fff}.date-picker-wrapper .gap{position:relative;z-index:1;width:15px;height:100%;background-color:red;font-size:0;line-height:0;float:left;top:-5px;margin:0 10px -10px;visibility:hidden;height:0}.date-picker-wrapper .gap .gap-lines{height:100%;overflow:hidden}.date-picker-wrapper .gap .gap-line{height:15px;width:15px;position:relative}.date-picker-wrapper .gap .gap-line .gap-1{z-index:1;height:0;border-left:8px solid #fff;border-top:8px solid #eee;border-bottom:8px solid #eee}.date-picker-wrapper .gap .gap-line .gap-2{position:absolute;right:0;top:0;z-index:2;height:0;border-left:8px solid transparent;border-top:8px solid #fff}.date-picker-wrapper .gap .gap-line .gap-3{position:absolute;right:0;top:8px;z-index:2;height:0;border-left:8px solid transparent;border-bottom:8px solid #fff}.date-picker-wrapper .gap .gap-top-mask{width:6px;height:1px;position:absolute;top:-1px;left:1px;background-color:#eee;z-index:3}.date-picker-wrapper .gap .gap-bottom-mask{width:6px;height:1px;position:absolute;bottom:-1px;left:7px;background-color:#eee;z-index:3}.date-picker-wrapper .selected-days{display:none}.date-picker-wrapper .drp_top-bar{line-height:1.4;position:relative;padding:10px 40px 10px 0}.date-picker-wrapper .drp_top-bar .error-top,.date-picker-wrapper .drp_top-bar .normal-top{display:none}.date-picker-wrapper .drp_top-bar .default-top{display:block}.date-picker-wrapper .drp_top-bar.error .default-top{display:none}.date-picker-wrapper .drp_top-bar.error .error-top{display:block;color:red}.date-picker-wrapper .drp_top-bar.normal .default-top{display:none}.date-picker-wrapper .drp_top-bar.normal .normal-top{display:block}.date-picker-wrapper .drp_top-bar.normal .normal-top .selection-top{color:#333}.date-picker-wrapper .drp_top-bar .apply-btn{position:absolute;right:0;top:6px;padding:3px 5px;margin:0;font-size:12px;border-radius:4px;cursor:pointer;color:#d9eef7;border:solid 1px #0076a3;background:#0095cd;background:-moz-linear-gradient(top,#00adee,#0078a5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00adee', endColorstr='#0078a5');color:#fff;line-height:initial}.date-picker-wrapper .drp_top-bar .apply-btn.disabled{cursor:pointer;color:#606060;border:solid 1px #b7b7b7;background:#fff;background:-moz-linear-gradient(top,#fff,#ededed);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed')}.date-picker-wrapper .time{position:relative}.date-picker-wrapper.single-month .time{display:block}.date-picker-wrapper .time input[type=range]{vertical-align:middle;width:129px;padding:0;margin:0;height:20px}.date-picker-wrapper .time1{width:180px;padding:0 5px;text-align:center}.time2{width:180px;padding:0 5px;text-align:center}.date-picker-wrapper .time1{float:left}.date-picker-wrapper .time2{float:right}.date-picker-wrapper .hour{text-align:right}.minute{text-align:right}.date-picker-wrapper .hide{display:none}.date-picker-wrapper .first-date-selected,.date-picker-wrapper .last-date-selected{background-color:#49e!important;color:#fff!important}.date-picker-wrapper .date-range-length-tip{position:absolute;margin-top:-4px;margin-left:-8px;-webkit-box-shadow:0 0 3px rgba(0,0,0,.3);box-shadow:0 0 3px rgba(0,0,0,.3);display:none;background-color:#ff0;padding:0 6px;border-radius:2px;font-size:12px;line-height:16px;-webkit-filter:drop-shadow(0 0 3px rgba(0, 0, 0, .3));-moz-filter:drop-shadow(0 0 3px rgba(0, 0, 0, .3));-ms-filter:drop-shadow(0 0 3px rgba(0, 0, 0, .3));-o-filter:drop-shadow(0 0 3px rgba(0, 0, 0, .3));filter:drop-shadow(0 0 3px rgba(0, 0, 0, .3))}.date-picker-wrapper .date-range-length-tip:after{content:'';position:absolute;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #ff0;left:50%;margin-left:-4px;bottom:-4px}.date-picker-wrapper.two-months.no-gap .month1 .next,.date-picker-wrapper.two-months.no-gap .month2 .prev{display:none}.date-picker-wrapper .week-number{padding:5px 0;line-height:1;font-size:12px;margin-bottom:1px;color:#999;cursor:pointer}.date-picker-wrapper .week-number.week-number-selected{color:#49e;font-weight:700} \ No newline at end of file diff --git a/js/daterangepicker/jquery.daterangepicker.min.js b/js/daterangepicker/jquery.daterangepicker.min.js new file mode 100755 index 000000000..508ddcce6 --- /dev/null +++ b/js/daterangepicker/jquery.daterangepicker.min.js @@ -0,0 +1,7 @@ +/** + * jquery-date-range-picker + * @version v0.21.1 + * @link https://github.com/longbill/jquery-date-range-picker + * @license MIT + */ +!function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("jquery"),require("moment")):e(jQuery,moment)}(function(U,Q){"use strict";U.dateRangePickerLanguages={default:{selected:"Selected:",day:"Day",days:"Days",apply:"Close","week-1":"mo","week-2":"tu","week-3":"we","week-4":"th","week-5":"fr","week-6":"sa","week-7":"su","week-number":"W","month-name":["january","february","march","april","may","june","july","august","september","october","november","december"],shortcuts:"Shortcuts","custom-values":"Custom Values",past:"Past",following:"Following",previous:"Previous","prev-week":"Week","prev-month":"Month","prev-year":"Year",next:"Next","next-week":"Week","next-month":"Month","next-year":"Year","less-than":"Date range should not be more than %d days","more-than":"Date range should not be less than %d days","default-more":"Please select a date range longer than %d days","default-single":"Please select a date","default-less":"Please select a date range less than %d days","default-range":"Please select a date range between %d and %d days","default-default":"Please select a date range",time:"Time",hour:"Hour",minute:"Minute"},id:{selected:"Terpilih:",day:"Hari",days:"Hari",apply:"Tutup","week-1":"sen","week-2":"sel","week-3":"rab","week-4":"kam","week-5":"jum","week-6":"sab","week-7":"min","week-number":"W","month-name":["januari","februari","maret","april","mei","juni","juli","agustus","september","oktober","november","desember"],shortcuts:"Pintas","custom-values":"Nilai yang ditentukan",past:"Yang Lalu",following:"Mengikuti",previous:"Sebelumnya","prev-week":"Minggu","prev-month":"Bulan","prev-year":"Tahun",next:"Selanjutnya","next-week":"Minggu","next-month":"Bulan","next-year":"Tahun","less-than":"Tanggal harus lebih dari %d hari","more-than":"Tanggal harus kurang dari %d hari","default-more":"Jarak tanggal harus lebih lama dari %d hari","default-single":"Silakan pilih tanggal","default-less":"Jarak rentang tanggal tidak boleh lebih lama dari %d hari","default-range":"Rentang tanggal harus antara %d dan %d hari","default-default":"Silakan pilih rentang tanggal",time:"Waktu",hour:"Jam",minute:"Menit"},az:{selected:"Seçildi:",day:" gün",days:" gün",apply:"tətbiq","week-1":"1","week-2":"2","week-3":"3","week-4":"4","week-5":"5","week-6":"6","week-7":"7","month-name":["yanvar","fevral","mart","aprel","may","iyun","iyul","avqust","sentyabr","oktyabr","noyabr","dekabr"],shortcuts:"Qısayollar",past:"Keçmiş",following:"Növbəti",previous:"   ","prev-week":"Öncəki həftə","prev-month":"Öncəki ay","prev-year":"Öncəki il",next:"   ","next-week":"Növbəti həftə","next-month":"Növbəti ay","next-year":"Növbəti il","less-than":"Tarix aralığı %d gündən çox olmamalıdır","more-than":"Tarix aralığı %d gündən az olmamalıdır","default-more":"%d gündən çox bir tarix seçin","default-single":"Tarix seçin","default-less":"%d gündən az bir tarix seçin","default-range":"%d və %d gün aralığında tarixlər seçin","default-default":"Tarix aralığı seçin"},bg:{selected:"Избрано:",day:"Ден",days:"Дни",apply:"Затвори","week-1":"пн","week-2":"вт","week-3":"ср","week-4":"чт","week-5":"пт","week-6":"сб","week-7":"нд","week-number":"С","month-name":["януари","февруари","март","април","май","юни","юли","август","септември","октомври","ноември","декември"],shortcuts:"Преки пътища","custom-values":"Персонализирани стойности",past:"Минал",following:"Следващ",previous:"Предишен","prev-week":"Седмица","prev-month":"Месец","prev-year":"Година",next:"Следващ","next-week":"Седмица","next-month":"Месец","next-year":"Година","less-than":"Периодът от време не трябва да е повече от %d дни","more-than":"Периодът от време не трябва да е по-малко от %d дни","default-more":"Моля изберете период по-дълъг от %d дни","default-single":"Моля изберете дата","default-less":"Моля изберете период по-къс от %d дни","default-range":"Моля изберете период между %d и %d дни","default-default":"Моля изберете период",time:"Време",hour:"Час",minute:"Минута"},cn:{selected:"已选择:",day:"天",days:"天",apply:"确定","week-1":"一","week-2":"二","week-3":"三","week-4":"四","week-5":"五","week-6":"六","week-7":"日","week-number":"周","month-name":["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],shortcuts:"快捷选择",past:"过去",following:"将来",previous:"   ","prev-week":"上周","prev-month":"上个月","prev-year":"去年",next:"   ","next-week":"下周","next-month":"下个月","next-year":"明年","less-than":"所选日期范围不能大于%d天","more-than":"所选日期范围不能小于%d天","default-more":"请选择大于%d天的日期范围","default-less":"请选择小于%d天的日期范围","default-range":"请选择%d天到%d天的日期范围","default-single":"请选择一个日期","default-default":"请选择一个日期范围",time:"时间",hour:"小时",minute:"分钟"},cz:{selected:"Vybráno:",day:"Den",days:"Dny",apply:"Zavřít","week-1":"po","week-2":"út","week-3":"st","week-4":"čt","week-5":"pá","week-6":"so","week-7":"ne","month-name":["leden","únor","březen","duben","květen","červen","červenec","srpen","září","říjen","listopad","prosinec"],shortcuts:"Zkratky",past:"po",following:"následující",previous:"předchozí","prev-week":"týden","prev-month":"měsíc","prev-year":"rok",next:"další","next-week":"týden","next-month":"měsíc","next-year":"rok","less-than":"Rozsah data by neměl být větší než %d dnů","more-than":"Rozsah data by neměl být menší než %d dnů","default-more":"Prosím zvolte rozsah data větší než %d dnů","default-single":"Prosím zvolte datum","default-less":"Prosím zvolte rozsah data menší než %d dnů","default-range":"Prosím zvolte rozsah data mezi %d a %d dny","default-default":"Prosím zvolte rozsah data"},de:{selected:"Auswahl:",day:"Tag",days:"Tage",apply:"Schließen","week-1":"mo","week-2":"di","week-3":"mi","week-4":"do","week-5":"fr","week-6":"sa","week-7":"so","month-name":["januar","februar","märz","april","mai","juni","juli","august","september","oktober","november","dezember"],shortcuts:"Schnellwahl",past:"Vorherige",following:"Folgende",previous:"Vorherige","prev-week":"Woche","prev-month":"Monat","prev-year":"Jahr",next:"Nächste","next-week":"Woche","next-month":"Monat","next-year":"Jahr","less-than":"Datumsbereich darf nicht größer sein als %d Tage","more-than":"Datumsbereich darf nicht kleiner sein als %d Tage","default-more":"Bitte mindestens %d Tage auswählen","default-single":"Bitte ein Datum auswählen","default-less":"Bitte weniger als %d Tage auswählen","default-range":"Bitte einen Datumsbereich zwischen %d und %d Tagen auswählen","default-default":"Bitte ein Start- und Enddatum auswählen",Time:"Zeit",hour:"Stunde",minute:"Minute"},es:{selected:"Seleccionado:",day:"Día",days:"Días",apply:"Cerrar","week-1":"lu","week-2":"ma","week-3":"mi","week-4":"ju","week-5":"vi","week-6":"sa","week-7":"do","month-name":["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],shortcuts:"Accesos directos",past:"Pasado",following:"Siguiente",previous:"Anterior","prev-week":"Semana","prev-month":"Mes","prev-year":"Año",next:"Siguiente","next-week":"Semana","next-month":"Mes","next-year":"Año","less-than":"El rango no debería ser mayor de %d días","more-than":"El rango no debería ser menor de %d días","default-more":"Por favor selecciona un rango mayor a %d días","default-single":"Por favor selecciona un día","default-less":"Por favor selecciona un rango menor a %d días","default-range":"Por favor selecciona un rango entre %d y %d días","default-default":"Por favor selecciona un rango de fechas."},fr:{selected:"Sélection:",day:"Jour",days:"Jours",apply:"Fermer","week-1":"lu","week-2":"ma","week-3":"me","week-4":"je","week-5":"ve","week-6":"sa","week-7":"di","month-name":["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],shortcuts:"Raccourcis",past:"Passé",following:"Suivant",previous:"Précédent","prev-week":"Semaine","prev-month":"Mois","prev-year":"Année",next:"Suivant","next-week":"Semaine","next-month":"Mois","next-year":"Année","less-than":"L'intervalle ne doit pas être supérieure à %d jours","more-than":"L'intervalle ne doit pas être inférieure à %d jours","default-more":"Merci de choisir une intervalle supérieure à %d jours","default-single":"Merci de choisir une date","default-less":"Merci de choisir une intervalle inférieure %d jours","default-range":"Merci de choisir une intervalle comprise entre %d et %d jours","default-default":"Merci de choisir une date"},hu:{selected:"Kiválasztva:",day:"Nap",days:"Nap",apply:"Ok","week-1":"h","week-2":"k","week-3":"sz","week-4":"cs","week-5":"p","week-6":"sz","week-7":"v","month-name":["január","február","március","április","május","június","július","augusztus","szeptember","október","november","december"],shortcuts:"Gyorsválasztó",past:"Múlt",following:"Következő",previous:"Előző","prev-week":"Hét","prev-month":"Hónap","prev-year":"Év",next:"Következő","next-week":"Hét","next-month":"Hónap","next-year":"Év","less-than":"A kiválasztás nem lehet több %d napnál","more-than":"A kiválasztás nem lehet több %d napnál","default-more":"Válassz ki egy időszakot ami hosszabb mint %d nap","default-single":"Válassz egy napot","default-less":"Válassz ki egy időszakot ami rövidebb mint %d nap","default-range":"Válassz ki egy %d - %d nap hosszú időszakot","default-default":"Válassz ki egy időszakot"},it:{selected:"Selezionati:",day:"Giorno",days:"Giorni",apply:"Chiudi","week-1":"lu","week-2":"ma","week-3":"me","week-4":"gi","week-5":"ve","week-6":"sa","week-7":"do","month-name":["gennaio","febbraio","marzo","aprile","maggio","giugno","luglio","agosto","settembre","ottobre","novembre","dicembre"],shortcuts:"Scorciatoie",past:"Scorso",following:"Successivo",previous:"Precedente","prev-week":"Settimana","prev-month":"Mese","prev-year":"Anno",next:"Prossimo","next-week":"Settimana","next-month":"Mese","next-year":"Anno","less-than":"L'intervallo non dev'essere maggiore di %d giorni","more-than":"L'intervallo non dev'essere minore di %d giorni","default-more":"Seleziona un intervallo maggiore di %d giorni","default-single":"Seleziona una data","default-less":"Seleziona un intervallo minore di %d giorni","default-range":"Seleziona un intervallo compreso tra i %d e i %d giorni","default-default":"Seleziona un intervallo di date"},ko:{selected:"기간:",day:"일",days:"일간",apply:"닫기","week-1":"월","week-2":"화","week-3":"수","week-4":"목","week-5":"금","week-6":"토","week-7":"일","week-number":"주","month-name":["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],shortcuts:"단축키들",past:"지난(오늘기준)",following:"이후(오늘기준)",previous:"이전","prev-week":"1주","prev-month":"1달","prev-year":"1년",next:"다음","next-week":"1주","next-month":"1달","next-year":"1년","less-than":"날짜 범위는 %d 일보다 많을 수 없습니다","more-than":"날짜 범위는 %d 일보다 작을 수 없습니다","default-more":"날짜 범위를 %d 일보다 길게 선택해 주세요","default-single":"날짜를 선택해 주세요","default-less":"%d 일보다 작은 날짜를 선택해 주세요","default-range":"%d와 %d 일 사이의 날짜 범위를 선택해 주세요","default-default":"날짜 범위를 선택해 주세요",time:"시각",hour:"시",minute:"분"},no:{selected:"Valgt:",day:"Dag",days:"Dager",apply:"Lukk","week-1":"ma","week-2":"ti","week-3":"on","week-4":"to","week-5":"fr","week-6":"lø","week-7":"sø","month-name":["januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember"],shortcuts:"Snarveier","custom-values":"Egendefinerte Verdier",past:"Over",following:"Følger",previous:"Forrige","prev-week":"Uke","prev-month":"Måned","prev-year":"År",next:"Neste","next-week":"Uke","next-month":"Måned","next-year":"År","less-than":"Datoperioden skal ikkje være lengre enn %d dager","more-than":"Datoperioden skal ikkje være kortere enn %d dager","default-more":"Vennligst velg ein datoperiode lengre enn %d dager","default-single":"Vennligst velg ein dato","default-less":"Vennligst velg ein datoperiode mindre enn %d dager","default-range":"Vennligst velg ein datoperiode mellom %d og %d dager","default-default":"Vennligst velg ein datoperiode",time:"Tid",hour:"Time",minute:"Minutter"},nl:{selected:"Geselecteerd:",day:"Dag",days:"Dagen",apply:"Ok","week-1":"ma","week-2":"di","week-3":"wo","week-4":"do","week-5":"vr","week-6":"za","week-7":"zo","month-name":["januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],shortcuts:"Snelkoppelingen","custom-values":"Aangepaste waarden",past:"Verleden",following:"Komend",previous:"Vorige","prev-week":"Week","prev-month":"Maand","prev-year":"Jaar",next:"Volgende","next-week":"Week","next-month":"Maand","next-year":"Jaar","less-than":"Interval moet langer dan %d dagen zijn","more-than":"Interval mag niet minder dan %d dagen zijn","default-more":"Selecteer een interval langer dan %dagen","default-single":"Selecteer een datum","default-less":"Selecteer een interval minder dan %d dagen","default-range":"Selecteer een interval tussen %d en %d dagen","default-default":"Selecteer een interval",time:"Tijd",hour:"Uur",minute:"Minuut"},ru:{selected:"Выбрано:",day:"День",days:"Дней",apply:"Применить","week-1":"пн","week-2":"вт","week-3":"ср","week-4":"чт","week-5":"пт","week-6":"сб","week-7":"вс","month-name":["январь","февраль","март","апрель","май","июнь","июль","август","сентябрь","октябрь","ноябрь","декабрь"],shortcuts:"Быстрый выбор","custom-values":"Пользовательские значения",past:"Прошедшие",following:"Следующие",previous:"   ","prev-week":"Неделя","prev-month":"Месяц","prev-year":"Год",next:"   ","next-week":"Неделя","next-month":"Месяц","next-year":"Год","less-than":"Диапазон не может быть больше %d дней","more-than":"Диапазон не может быть меньше %d дней","default-more":"Пожалуйста выберите диапазон больше %d дней","default-single":"Пожалуйста выберите дату","default-less":"Пожалуйста выберите диапазон меньше %d дней","default-range":"Пожалуйста выберите диапазон между %d и %d днями","default-default":"Пожалуйста выберите диапазон",time:"Время",hour:"Часы",minute:"Минуты"},uk:{selected:"Вибрано:",day:"День",days:"Днів",apply:"Застосувати","week-1":"пн","week-2":"вт","week-3":"ср","week-4":"чт","week-5":"пт","week-6":"сб","week-7":"нд","month-name":["січень","лютий","березень","квітень","травень","червень","липень","серпень","вересень","жовтень","листопад","грудень"],shortcuts:"Швидкий вибір","custom-values":"Значення користувача",past:"Минулі",following:"Наступні",previous:"   ","prev-week":"Тиждень","prev-month":"Місяць","prev-year":"Рік",next:"   ","next-week":"Тиждень","next-month":"Місяць","next-year":"Рік","less-than":"Діапазон не може бути більш ніж %d днів","more-than":"Діапазон не може бути меньш ніж %d днів","default-more":"Будь ласка виберіть діапазон більше %d днів","default-single":"Будь ласка виберіть дату","default-less":"Будь ласка виберіть діапазон менше %d днів","default-range":"Будь ласка виберіть діапазон між %d та %d днями","default-default":"Будь ласка виберіть діапазон",time:"Час",hour:"Години",minute:"Хвилини"},pl:{selected:"Wybrany:",day:"Dzień",days:"Dni",apply:"Zamknij","week-1":"pon","week-2":"wt","week-3":"śr","week-4":"czw","week-5":"pt","week-6":"so","week-7":"nd","month-name":["styczeń","luty","marzec","kwiecień","maj","czerwiec","lipiec","sierpień","wrzesień","październik","listopad","grudzień"],shortcuts:"Skróty","custom-values":"Niestandardowe wartości",past:"Przeszłe",following:"Następne",previous:"Poprzednie","prev-week":"tydzień","prev-month":"miesiąc","prev-year":"rok",next:"Następny","next-week":"tydzień","next-month":"miesiąc","next-year":"rok","less-than":"Okres nie powinien być dłuższy niż %d dni","more-than":"Okres nie powinien być krótszy niż %d ni","default-more":"Wybierz okres dłuższy niż %d dni","default-single":"Wybierz datę","default-less":"Wybierz okres krótszy niż %d dni","default-range":"Wybierz okres trwający od %d do %d dni","default-default":"Wybierz okres",time:"Czas",hour:"Godzina",minute:"Minuta"},se:{selected:"Vald:",day:"dag",days:"dagar",apply:"godkänn","week-1":"ma","week-2":"ti","week-3":"on","week-4":"to","week-5":"fr","week-6":"lö","week-7":"sö","month-name":["januari","februari","mars","april","maj","juni","juli","augusti","september","oktober","november","december"],shortcuts:"genvägar","custom-values":"Anpassade värden",past:"över",following:"följande",previous:"förra","prev-week":"vecka","prev-month":"månad","prev-year":"år",next:"nästa","next-week":"vecka","next-month":"måned","next-year":"år","less-than":"Datumintervall bör inte vara mindre än %d dagar","more-than":"Datumintervall bör inte vara mer än %d dagar","default-more":"Välj ett datumintervall längre än %d dagar","default-single":"Välj ett datum","default-less":"Välj ett datumintervall mindre än %d dagar","default-range":"Välj ett datumintervall mellan %d och %d dagar","default-default":"Välj ett datumintervall",time:"tid",hour:"timme",minute:"minut"},pt:{selected:"Selecionado:",day:"Dia",days:"Dias",apply:"Fechar","week-1":"seg","week-2":"ter","week-3":"qua","week-4":"qui","week-5":"sex","week-6":"sab","week-7":"dom","week-number":"N","month-name":["janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"],shortcuts:"Atalhos","custom-values":"Valores Personalizados",past:"Passado",following:"Seguinte",previous:"Anterior","prev-week":"Semana","prev-month":"Mês","prev-year":"Ano",next:"Próximo","next-week":"Próxima Semana","next-month":"Próximo Mês","next-year":"Próximo Ano","less-than":"O período selecionado não deve ser maior que %d dias","more-than":"O período selecionado não deve ser menor que %d dias","default-more":"Selecione um período superior a %d dias","default-single":"Selecione uma data","default-less":"Selecione um período inferior a %d dias","default-range":"Selecione um período de %d a %d dias","default-default":"Selecione um período",time:"Tempo",hour:"Hora",minute:"Minuto"},tc:{selected:"已選擇:",day:"天",days:"天",apply:"確定","week-1":"一","week-2":"二","week-3":"三","week-4":"四","week-5":"五","week-6":"六","week-7":"日","week-number":"週","month-name":["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],shortcuts:"快速選擇",past:"過去",following:"將來",previous:"   ","prev-week":"上週","prev-month":"上個月","prev-year":"去年",next:"   ","next-week":"下週","next-month":"下個月","next-year":"明年","less-than":"所選日期範圍不能大於%d天","more-than":"所選日期範圍不能小於%d天","default-more":"請選擇大於%d天的日期範圍","default-less":"請選擇少於%d天的日期範圍","default-range":"請選擇%d天到%d天的日期範圍","default-single":"請選擇一個日期","default-default":"請選擇一個日期範圍",time:"日期",hour:"小時",minute:"分鐘"},ja:{selected:"選択しました:",day:"日",days:"日",apply:"確定","week-1":"月","week-2":"火","week-3":"水","week-4":"木","week-5":"金","week-6":"土","week-7":"日","month-name":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],shortcuts:"クイック選択",past:"過去",following:"将来",previous:"   ","prev-week":"先週","prev-month":"先月","prev-year":"昨年",next:"   ","next-week":"来週","next-month":"来月","next-year":"来年","less-than":"日付の範囲は%d日以上にすべきではありません","more-than":"日付の範囲は%d日を下回ってはいけません","default-more":"%d日よりも長い期間を選択してください","default-less":"%d日未満の期間を選択してください","default-range":"%d日と%d日の間の日付範囲を選択してください","default-single":"日付を選択してください","default-default":"日付範囲を選択してください",time:"時間",hour:"時間",minute:"分"},da:{selected:"Valgt:",day:"Dag",days:"Dage",apply:"Luk","week-1":"ma","week-2":"ti","week-3":"on","week-4":"to","week-5":"fr","week-6":"lø","week-7":"sø","month-name":["januar","februar","marts","april","maj","juni","juli","august","september","oktober","november","december"],shortcuts:"genveje","custom-values":"Brugerdefinerede værdier",past:"Forbi",following:"Følgende",previous:"Forrige","prev-week":"uge","prev-month":"måned","prev-year":"år",next:"Næste","next-week":"uge","next-month":"måned","next-year":"år","less-than":"Dato interval bør ikke være med end %d dage","more-than":"Dato interval bør ikke være mindre end %d dage","default-more":"Vælg datointerval længere end %d dage","default-single":"Vælg dato","default-less":"Vælg datointerval mindre end %d dage","default-range":"Vælg datointerval mellem %d og %d dage","default-default":"Vælg datointerval",time:"tid",hour:"time",minute:"minut"},fi:{selected:"Valittu:",day:"Päivä",days:"Päivää",apply:"Sulje","week-1":"ma","week-2":"ti","week-3":"ke","week-4":"to","week-5":"pe","week-6":"la","week-7":"su","week-number":"V","month-name":["tammikuu","helmikuu","maaliskuu","huhtikuu","toukokuu","kesäkuu","heinäkuu","elokuu","syyskuu","lokakuu","marraskuu","joulukuu"],shortcuts:"Pikavalinnat","custom-values":"Mukautetut Arvot",past:"Menneet",following:"Tulevat",previous:"Edellinen","prev-week":"Viikko","prev-month":"Kuukausi","prev-year":"Vuosi",next:"Seuraava","next-week":"Viikko","next-month":"Kuukausi","next-year":"Vuosi","less-than":"Aikajakson tulisi olla vähemmän kuin %d päivää","more-than":"Aikajakson ei tulisi olla vähempää kuin %d päivää","default-more":"Valitse pidempi aikajakso kuin %d päivää","default-single":"Valitse päivä","default-less":"Valitse lyhyempi aikajakso kuin %d päivää","default-range":"Valitse aikajakso %d ja %d päivän väliltä","default-default":"Valitse aikajakso",time:"Aika",hour:"Tunti",minute:"Minuutti"},cat:{selected:"Seleccionats:",day:"Dia",days:"Dies",apply:"Tanca","week-1":"Dl","week-2":"Dm","week-3":"Dc","week-4":"Dj","week-5":"Dv","week-6":"Ds","week-7":"Dg","week-number":"S","month-name":["gener","febrer","març","abril","maig","juny","juliol","agost","setembre","octubre","novembre","desembre"],shortcuts:"Dreçeres","custom-values":"Valors personalitzats",past:"Passat",following:"Futur",previous:"Anterior","prev-week":"Setmana","prev-month":"Mes","prev-year":"Any",next:"Següent","next-week":"Setmana","next-month":"Mes","next-year":"Any","less-than":"El període no hauria de ser de més de %d dies","more-than":"El període no hauria de ser de menys de %d dies","default-more":"Perfavor selecciona un període més gran de %d dies","default-single":"Perfavor selecciona una data","default-less":"Perfavor selecciona un període de menys de %d dies","default-range":"Perfavor selecciona un període d'entre %d i %d dies","default-default":"Perfavor selecciona un període",time:"Temps",hour:"Hora",minute:"Minut"},sk:{selected:"Vybrané:",day:"Deň",days:"Dni",apply:"Zavrieť","week-1":"po","week-2":"út","week-3":"st","week-4":"št","week-5":"pi","week-6":"so","week-7":"ne","week-number":"T","month-name":["január","február","marec","apríl","máj","jún","júl","august","september","október","november","december"],shortcuts:"Skratky","custom-values":"Vlastné hodnoty",past:"po",following:"nasledujúci",previous:"predchádzajúci","prev-week":"týždeň","prev-month":"mesiac","prev-year":"rok",next:"ďalší","next-week":"týždeň","next-month":"mesiac","next-year":"rok","less-than":"Rozsah dátumu by nemal byť väčší ako %d dní","more-than":"Rozsah dátumu by nemal byť menší ako %d dní","default-more":"Prosím zvoľte rozsah dlhší ako %d dní","default-single":"Prosím zvoľte dátum","default-less":"Prosím zvoľte rozsah menší ako %d dní","default-range":"Prosím zvoľte rozsah medzi %d a %d dňami","default-default":"Prosím zvoľte rozsah",time:"Čas",hour:"Hodina",minute:"Minúta"}},U.fn.dateRangePicker=function(f){f||(f={}),(f=U.extend(!0,{autoClose:!1,format:"YYYY-MM-DD",separator:" to ",language:"auto",startOfWeek:"sunday",getValue:function(){return U(this).val()},setValue:function(e){U(this).attr("readonly")||U(this).is(":disabled")||e==U(this).val()||U(this).val(e)},startDate:!1,endDate:!1,time:{enabled:!1},minDays:0,maxDays:0,showShortcuts:!1,shortcuts:{},customShortcuts:[],inline:!1,container:"body",alwaysOpen:!1,singleDate:!1,lookBehind:!1,batchMode:!1,duration:200,stickyMonths:!1,dayDivAttrs:[],dayTdAttrs:[],selectForward:!1,selectBackward:!1,applyBtnClass:"",singleMonth:"auto",hoveringTooltip:function(e,t,a){return 1'+f.customTopBar+"
"):(t+='
'+K("selected")+' ...',f.singleDate||(t+=' '+f.separator+' ... (3 '+K("days")+")"),t+="
",t+='
error
default
'),t+='',t+="");var a=f.showWeekNumbers?6:5,n="<";f.customArrowPrevSymbol&&(n=f.customArrowPrevSymbol);var r=">";if(f.customArrowNextSymbol&&(r=f.customArrowNextSymbol),t+='
'+E()+"
'+n+' '+(f.singleDate||!f.stickyMonths?''+r+"":"")+'
",f.singleMonth||(t+='
'+function(){for(var e=['
'],t=0;t<20;t++)e.push('
');return e.push("
"),e.join("")}()+'
'+E()+"
'+(f.stickyMonths?"":''+n+"")+' '+r+'
"),t+='
',f.singleDate||(t+='
'),t+='
',t+='")}().hide()).append('
'),U(f.container).append(m),f.inline?m.addClass("inline-wrapper"):i();f.alwaysOpen&&m.find(".apply-btn").hide();var e=_();if(G(e),f.time.enabled)if(f.startDate&&f.endDate||f.start&&f.end)Y(Q(f.start||f.startDate).toDate(),"time1"),Y(Q(f.end||f.endDate).toDate(),"time2");else{var a=f.defaultEndTime?f.defaultEndTime:e;Y(e,"time1"),Y(a,"time2")}var n="";n=f.singleDate?K("default-single"):f.minDays&&f.maxDays?K("default-range"):f.minDays?K("default-more"):f.maxDays?K("default-less"):K("default-default");m.find(".default-top").html(n.replace(/\%d/,f.minDays).replace(/\%d/,f.maxDays)),f.singleMonth?m.addClass("single-month"):m.addClass("two-months");setTimeout(function(){l(),s=!0},0),m.on("click",function(e){e.stopPropagation()}),U(document).on("click.datepicker",Z),m.find(".next").on("click",function(){f.stickyMonths?function(e){var t=F(f.month1),a=F(f.month2);if(q(a))return;if(!f.singleDate&&0<=L(t,a))return;O(t,"month1"),O(a,"month2"),C()}():function(e){var t=U(e).parents("table").hasClass("month2"),a=t?f.month2:f.month1;if(a=F(a),!f.singleMonth&&!f.singleDate&&!t&&0<=L(a,f.month2)||q(a))return;O(a,t?"month2":"month1"),I()}(this)}),m.find(".prev").on("click",function(){f.stickyMonths?function(e){var t=R(f.month1),a=R(f.month2);if(q(t))return;if(!f.singleDate&&L(a,t)<=0)return;O(a,"month2"),O(t,"month1"),C()}():function(e){var t=U(e).parents("table").hasClass("month2"),a=t?f.month2:f.month1;if(a=R(a),t&&L(a,f.month1)<=0||q(a))return;O(a,t?"month2":"month1"),I()}(this)}),m.attr("unselectable","on").css("user-select","none").on("selectstart",function(e){return e.preventDefault(),!1}),m.find(".apply-btn").on("click",function(){B();var e=W(new Date(f.start))+f.separator+W(new Date(f.end));U(t).trigger("datepicker-apply",{value:e,date1:new Date(f.start),date2:new Date(f.end)})}),m.find("[custom]").on("click",function(){var e=U(this).attr("custom");f.start=!1,f.end=!1,m.find(".day.checked").removeClass("checked"),f.setValue.call(o,e),M(),S(!0),C(),f.autoClose&&B()}),m.find("[shortcut]").on("click",function(){var e,t=U(this).attr("shortcut"),a=new Date,n=!1;if(-1!=t.indexOf("day")){var r=parseInt(t.split(",",2)[1],10);n=new Date((new Date).getTime()+864e5*r),a=new Date(a.getTime()+864e5*(0f.end){var a=f.end;f.end=v(f.start),f.start=p(a),f.time.enabled&&f.swapTime&&(u("time1",f.start),u("time2",f.end))}f.start=parseInt(f.start),f.end=parseInt(f.end),b(),f.start&&!f.end&&(U(n).trigger("datepicker-first-date-selected",{date1:new Date(f.start)}),y(e)),w(),M(),S(),C(),x()}}function g(e){if(e=parseInt(e,10),f.startDate&&H(e,f.startDate)<0)return!1;if(f.endDate&&0f.maxDays)return!1;if(0f.start)return!1;if(f.beforeShowDay&&"function"==typeof f.beforeShowDay){for(var t=!0,a=e;1f.start&&(a-=864e5),a'+e.attr("data-tooltip")+"";else if(!e.hasClass("invalid"))if(f.singleDate)m.find(".day.hovering").removeClass("hovering"),e.addClass("hovering");else if(m.find(".day").each(function(){var e=parseInt(U(this).attr("time"));f.start,f.end;e==t?U(this).addClass("hovering"):U(this).removeClass("hovering"),f.start&&!f.end&&(f.starte&&t<=e)?U(this).addClass("hovering"):U(this).removeClass("hovering")}),f.start&&!f.end){var n=z(t,f.start);f.hoveringTooltip&&("function"==typeof f.hoveringTooltip?a=f.hoveringTooltip(n,f.start,t):!0===f.hoveringTooltip&&1f.maxDays?(f.start=!1,f.end=!1,m.find(".day").removeClass("checked"),m.find(".drp_top-bar").removeClass("normal").addClass("error").find(".error-top").html(K("less-than").replace("%d",f.maxDays))):f.minDays&&et.getTime()){var n=t;t=e,e=n,n=null}var r=!0;if(f.startDate&&H(e,f.startDate)<0&&(r=!1),f.endDate&&0'+A(a)+"";if(!f.monthSelect)return n;var r=!!f.startDate&&Q(f.startDate).add(f.singleMonth||"month2"!==t?0:1,"month"),s=!!f.endDate&&Q(f.endDate).add(f.singleMonth||"month1"!==t?0:-1,"month"),o=r&&e.isSame(r,"year")?r.get("month"):0,i=s&&e.isSame(s,"year")?s.get("month"):11,d=Math.min(o,a),l=Math.max(i,a);if(d===l)return n;return P("month",V({minSelectable:o,maxSelectable:i,minVisible:d,maxVisible:l},a,function(e){return A(e)}))}(e=Q(e).toDate(),t),n=function(e,t){var a=(e=Q(e)).get("year"),n='
'+a+"
";if(!f.yearSelect)return n;var r=f.yearSelect&&"function"==typeof f.yearSelect,s=!!f.startDate&&Q(f.startDate).add(f.singleMonth||"month2"!==t?0:1,"month"),o=!!f.endDate&&Q(f.endDate).add(f.singleMonth||"month1"!==t?0:-1,"month"),i=r?f.yearSelect(a):f.yearSelect.slice(),d=s?Math.max(i[0],s.get("year")):Math.min(i[0],a),l=o?Math.min(i[1],o.get("year")):Math.max(i[1],a),u=Math.min(d,a),m=Math.max(l,a);if(u===m)return n;return P("year",V({minSelectable:d,maxSelectable:l,minVisible:u,maxVisible:m},a))}(e,t);m.find("."+t+" .month-name").html(a+" "+n),m.find("."+t+" tbody").html(function(e){var t=[];e.setDate(1);new Date(e.getTime()-864e5);var a,n,r=new Date,s=e.getDay();0===s&&"monday"===f.startOfWeek&&(s=7);if(0");for(var i=0;i<7;i++){var m="monday"==f.startOfWeek?i+1:i;a=t[7*u+m];var h=Q(a.time).format("L")==Q(r).format("L");if(a.extraClass="",a.tooltip="",a.valid&&f.beforeShowDay&&"function"==typeof f.beforeShowDay){var c=f.beforeShowDay(Q(a.time).toDate());a.valid=c[0],a.extraClass=c[1]||"",a.tooltip=c[2]||"",""!==a.tooltip&&(a.extraClass+=" has-tooltip ")}var p={time:a.time,"data-tooltip":a.tooltip,class:"day "+a.type+" "+a.extraClass+" "+(a.valid?"valid":"invalid")+" "+(h?"real-today":"")};0===i&&f.showWeekNumbers&&l.push('
'+f.getWeekNumber(a.date)+"
"),l.push("
"+J(a.time,a.day)+"
")}l.push("")}return l.join("")}(e)),f[t]=e,w(),m.find(".day").off("click").on("click",function(e){k(U(this))}),m.find(".day").off("mouseenter").on("mouseenter",function(e){y(U(this))}),m.find(".day").off("mouseleave").on("mouseleave",function(e){m.find(".date-range-length-tip").hide(),f.singleDate&&b()}),m.find(".week-number").off("click").on("click",function(e){var t,a,n,r;t=U(this),r=parseInt(t.attr("data-start-time"),10),f.startWeek?(m.find(".week-number-selected").removeClass("week-number-selected"),a=new Date(re.maxSelectable});return n}function P(e,t){for(var a,n='
"+a+"
"}function Y(e,t){m.find("."+t).append("
"+K("Time")+': 00:00
'),u(t,e)}function A(e){return K("month-name")[e]}function W(e){return Q(e).format(f.format)}function I(){C();var e=parseInt(Q(f.month1).format("YYYYMM")),t=parseInt(Q(f.month2).format("YYYYMM")),a=Math.abs(e-t);1"+K("week-number")+"":"";return"monday"==f.startOfWeek?e+""+K("week-1")+""+K("week-2")+""+K("week-3")+""+K("week-4")+""+K("week-5")+""+K("week-6")+""+K("week-7")+"":e+""+K("week-7")+""+K("week-1")+""+K("week-2")+""+K("week-3")+""+K("week-4")+""+K("week-5")+""+K("week-6")+""}function q(e){return e=Q(e),!(!f.startDate||!e.endOf("month").isBefore(f.startDate))||!(!f.endDate||!e.startOf("month").isAfter(f.endDate))}function $(e,t,r){var s=U.extend(!0,{},e);U.each(t,function(e,t){var a=t(r);for(var n in a)s.hasOwnProperty(n)?s[n]+=a[n]:s[n]=a[n]});var a="";for(var n in s)s.hasOwnProperty(n)&&(a+=n+'="'+s[n]+'" ');return a}function J(e,t){return f.showDateFilter&&"function"==typeof f.showDateFilter?f.showDateFilter(e,t):t}function K(e){var t=e.toLowerCase(),a=e in r?r[e]:t in r?r[t]:null,n=U.dateRangePickerLanguages.default;return null==a&&(a=e in n?n[e]:t in n?n[t]:""),a}function _(){var e=f.defaultTime?f.defaultTime:new Date;return f.lookBehind?(f.startDate&&L(e,f.startDate)<0&&(e=F(Q(f.startDate).toDate())),f.endDate&&0>>0,s=0;sAe(e)?(r=e+1,t-Ae(e)):(r=e,t);return{year:r,dayOfYear:n}}function qe(e,t,n){var s,i,r=ze(e.year(),t,n),r=Math.floor((e.dayOfYear()-r-1)/7)+1;return r<1?s=r+P(i=e.year()-1,t,n):r>P(e.year(),t,n)?(s=r-P(e.year(),t,n),i=e.year()+1):(i=e.year(),s=r),{week:s,year:i}}function P(e,t,n){var s=ze(e,t,n),t=ze(e+1,t,n);return(Ae(e)-s+t)/7}s("w",["ww",2],"wo","week"),s("W",["WW",2],"Wo","isoWeek"),t("week","w"),t("isoWeek","W"),n("week",5),n("isoWeek",5),v("w",p),v("ww",p,w),v("W",p),v("WW",p,w),Te(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=g(e)});function Be(e,t){return e.slice(t,7).concat(e.slice(0,t))}s("d",0,"do","day"),s("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),s("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),s("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),s("e",0,0,"weekday"),s("E",0,0,"isoWeekday"),t("day","d"),t("weekday","e"),t("isoWeekday","E"),n("day",11),n("weekday",11),n("isoWeekday",11),v("d",p),v("e",p),v("E",p),v("dd",function(e,t){return t.weekdaysMinRegex(e)}),v("ddd",function(e,t){return t.weekdaysShortRegex(e)}),v("dddd",function(e,t){return t.weekdaysRegex(e)}),Te(["dd","ddd","dddd"],function(e,t,n,s){s=n._locale.weekdaysParse(e,s,n._strict);null!=s?t.d=s:m(n).invalidWeekday=e}),Te(["d","e","E"],function(e,t,n,s){t[s]=g(e)});var Je="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Qe="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Xe="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),Ke=k,et=k,tt=k;function nt(){function e(e,t){return t.length-e.length}for(var t,n,s,i=[],r=[],a=[],o=[],u=0;u<7;u++)s=l([2e3,1]).day(u),t=M(this.weekdaysMin(s,"")),n=M(this.weekdaysShort(s,"")),s=M(this.weekdays(s,"")),i.push(t),r.push(n),a.push(s),o.push(t),o.push(n),o.push(s);i.sort(e),r.sort(e),a.sort(e),o.sort(e),this._weekdaysRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+r.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+i.join("|")+")","i")}function st(){return this.hours()%12||12}function it(e,t){s(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function rt(e,t){return t._meridiemParse}s("H",["HH",2],0,"hour"),s("h",["hh",2],0,st),s("k",["kk",2],0,function(){return this.hours()||24}),s("hmm",0,0,function(){return""+st.apply(this)+r(this.minutes(),2)}),s("hmmss",0,0,function(){return""+st.apply(this)+r(this.minutes(),2)+r(this.seconds(),2)}),s("Hmm",0,0,function(){return""+this.hours()+r(this.minutes(),2)}),s("Hmmss",0,0,function(){return""+this.hours()+r(this.minutes(),2)+r(this.seconds(),2)}),it("a",!0),it("A",!1),t("hour","h"),n("hour",13),v("a",rt),v("A",rt),v("H",p),v("h",p),v("k",p),v("HH",p,w),v("hh",p,w),v("kk",p,w),v("hmm",ge),v("hmmss",we),v("Hmm",ge),v("Hmmss",we),D(["H","HH"],x),D(["k","kk"],function(e,t,n){e=g(e);t[x]=24===e?0:e}),D(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),D(["h","hh"],function(e,t,n){t[x]=g(e),m(n).bigHour=!0}),D("hmm",function(e,t,n){var s=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s)),m(n).bigHour=!0}),D("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s,2)),t[N]=g(e.substr(i)),m(n).bigHour=!0}),D("Hmm",function(e,t,n){var s=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s))}),D("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[x]=g(e.substr(0,s)),t[T]=g(e.substr(s,2)),t[N]=g(e.substr(i))});k=de("Hours",!0);var at,ot={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Ce,monthsShort:Ue,week:{dow:0,doy:6},weekdays:Je,weekdaysMin:Xe,weekdaysShort:Qe,meridiemParse:/[ap]\.?m?\.?/i},R={},ut={};function lt(e){return e&&e.toLowerCase().replace("_","-")}function ht(e){for(var t,n,s,i,r=0;r=t&&function(e,t){for(var n=Math.min(e.length,t.length),s=0;s=t-1)break;t--}r++}return at}function dt(t){var e;if(void 0===R[t]&&"undefined"!=typeof module&&module&&module.exports&&null!=t.match("^[^/\\\\]*$"))try{e=at._abbr,require("./locale/"+t),ct(e)}catch(e){R[t]=null}return R[t]}function ct(e,t){return e&&((t=o(t)?mt(e):ft(e,t))?at=t:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),at._abbr}function ft(e,t){if(null===t)return delete R[e],null;var n,s=ot;if(t.abbr=e,null!=R[e])Q("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=R[e]._config;else if(null!=t.parentLocale)if(null!=R[t.parentLocale])s=R[t.parentLocale]._config;else{if(null==(n=dt(t.parentLocale)))return ut[t.parentLocale]||(ut[t.parentLocale]=[]),ut[t.parentLocale].push({name:e,config:t}),null;s=n._config}return R[e]=new K(X(s,t)),ut[e]&&ut[e].forEach(function(e){ft(e.name,e.config)}),ct(e),R[e]}function mt(e){var t;if(!(e=e&&e._locale&&e._locale._abbr?e._locale._abbr:e))return at;if(!a(e)){if(t=dt(e))return t;e=[e]}return ht(e)}function _t(e){var t=e._a;return t&&-2===m(e).overflow&&(t=t[O]<0||11We(t[Y],t[O])?b:t[x]<0||24P(r,u,l)?m(s)._overflowWeeks=!0:null!=h?m(s)._overflowWeekday=!0:(d=$e(r,a,o,u,l),s._a[Y]=d.year,s._dayOfYear=d.dayOfYear)),null!=e._dayOfYear&&(i=bt(e._a[Y],n[Y]),(e._dayOfYear>Ae(i)||0===e._dayOfYear)&&(m(e)._overflowDayOfYear=!0),h=Ze(i,0,e._dayOfYear),e._a[O]=h.getUTCMonth(),e._a[b]=h.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=c[t]=n[t];for(;t<7;t++)e._a[t]=c[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[x]&&0===e._a[T]&&0===e._a[N]&&0===e._a[Ne]&&(e._nextDay=!0,e._a[x]=0),e._d=(e._useUTC?Ze:je).apply(null,c),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[x]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(m(e).weekdayMismatch=!0)}}function Tt(e){if(e._f===f.ISO_8601)St(e);else if(e._f===f.RFC_2822)Ot(e);else{e._a=[],m(e).empty=!0;for(var t,n,s,i,r,a=""+e._i,o=a.length,u=0,l=ae(e._f,e._locale).match(te)||[],h=l.length,d=0;de.valueOf():e.valueOf()"}),i.toJSON=function(){return this.isValid()?this.toISOString():null},i.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},i.unix=function(){return Math.floor(this.valueOf()/1e3)},i.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},i.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},i.eraName=function(){for(var e,t=this.localeData().eras(),n=0,s=t.length;nthis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},i.isLocal=function(){return!!this.isValid()&&!this._isUTC},i.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},i.isUtc=At,i.isUTC=At,i.zoneAbbr=function(){return this._isUTC?"UTC":""},i.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},i.dates=e("dates accessor is deprecated. Use date instead.",ke),i.months=e("months accessor is deprecated. Use month instead",Ge),i.years=e("years accessor is deprecated. Use year instead",Ie),i.zone=e("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?(this.utcOffset(e="string"!=typeof e?-e:e,t),this):-this.utcOffset()}),i.isDSTShifted=e("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e,t={};return $(t,this),(t=Nt(t))._a?(e=(t._isUTC?l:W)(t._a),this._isDSTShifted=this.isValid()&&0 +* @copyright 2010-2022 Webkul IN +* @license https://store.webkul.com/license.html +*/ + +(function($) { + + var dateRangePickerOrg = $.fn.dateRangePicker; + $.fn.dateRangePicker = function(opt) { + if(typeof opt === "object") { + const custom_opt = { + format: 'DD-MM-YYYY', + showTopbar: false, + autoClose: true, + singleMonth: true, + language: lang_iso, + minDays: 2, + startOfWeek: 'monday', + hoveringTooltip: false, + container: '#page', + customArrowPrevSymbol: '', + customArrowNextSymbol: '', + getValue: function() + { + return $(this).find('span').html(); + }, + setValue: function(s, s1, s2) { + if (s) { + $(this).find('span').html(s.replace('to', '  ')); + } else { + $(this).find('span').html( + RangePickerCheckin + '    ' + RangePickerCheckout + ); + } + } + } + $.each(opt, function(index) { + delete custom_opt[index]; + }); + + $.extend(true, opt, custom_opt); + + } + + var args = Array.prototype.slice.call(arguments,0); + return dateRangePickerOrg.apply(this, args); + } +})(jQuery); \ No newline at end of file diff --git a/js/validate.js b/js/validate.js index a724f516b..79d7af124 100644 --- a/js/validate.js +++ b/js/validate.js @@ -184,9 +184,7 @@ function validate_field(that) var selector = '#id_country'; if ($(that).attr('name') == 'postcode_invoice') selector += '_invoice'; - var id_country = $(selector + ' option:selected').val(); - if (typeof(countriesNeedZipCode[id_country]) != 'undefined' && typeof(countries[id_country]) != 'undefined') var result = window['validate_'+$(that).attr('data-validate')]($(that).val(), countriesNeedZipCode[id_country], countries[id_country]['iso_code']); } diff --git a/mails/en/order_refund_received_customer.html b/mails/en/credit_slip_voucher.html old mode 100755 new mode 100644 similarity index 69% rename from mails/en/order_refund_received_customer.html rename to mails/en/credit_slip_voucher.html index 6a8da4180..1a9da8f38 --- a/mails/en/order_refund_received_customer.html +++ b/mails/en/credit_slip_voucher.html @@ -55,7 +55,7 @@ - + @@ -73,7 +73,6 @@ @@ -87,10 +86,14 @@ @@ -99,35 +102,6 @@
  Hi {firstname} {lastname},
- Your request for booking cancellation has been recieved.
  -

Order details

+

+ VOUCHER GENERATED +

- Order: {order_reference} Placed on {order_date}

- Refund Status: {status_name}

+ Your voucher has been generated successfully against your credit slip #{credit_slip_id}. +

+ Voucher Code: {voucher_code} for an amount of {voucher_amount}.

+ To use this voucher just copy and paste this code during payment process for your next order.
- - - -

Booking details

- - - - - - - - - {refundBookingHtml} - -
Room TypeHotelNum. RoomsDuration
-
- - - -   - - - - - - You can review your booking cancelation requests from the "Booking Refund Requests" section of your customer account by clicking "My account". - - -   diff --git a/mails/en/credit_slip_voucher.txt b/mails/en/credit_slip_voucher.txt new file mode 100644 index 000000000..b53903729 --- /dev/null +++ b/mails/en/credit_slip_voucher.txt @@ -0,0 +1,16 @@ + +[/{shop_url}] + +Hi {firstname} {lastname}, + +VOUCHER GENERATED + +Your voucher has been generated successfully against your credit slip #{credit_slip_id}. + +VOUCHER CODE: {voucher_code} for an amount of {voucher_amount}. + +To use this voucher just copy and paste this code during payment process for your next +order. + +{shop_name} [/{shop_url}] powered by Webkul(tm) +[https://www.webkul.com/] diff --git a/mails/en/order_refund_waiting_admin.html b/mails/en/order_refund_pending_admin.html similarity index 97% rename from mails/en/order_refund_waiting_admin.html rename to mails/en/order_refund_pending_admin.html index ae72fbae6..3c5a93547 100755 --- a/mails/en/order_refund_waiting_admin.html +++ b/mails/en/order_refund_pending_admin.html @@ -73,7 +73,7 @@ Hi {firstname} {lastname},
- A request has been generated for the booking cancellation. + A request has been generated for booking cancellations.
@@ -104,7 +104,7 @@ -

Booking details

+

Requested Booking Cancellations

diff --git a/mails/en/order_refund_waiting_admin.txt b/mails/en/order_refund_pending_admin.txt similarity index 71% rename from mails/en/order_refund_waiting_admin.txt rename to mails/en/order_refund_pending_admin.txt index 29b1acbbf..f520f617c 100755 --- a/mails/en/order_refund_waiting_admin.txt +++ b/mails/en/order_refund_pending_admin.txt @@ -1,7 +1,7 @@ Message from {shop_name} Hi {firstname} {lastname}, -A request has been generated for the booking cancellation. +A request has been generated for booking cancellations. Order details @@ -9,7 +9,7 @@ Order: {order_reference} Placed on {order_date} Refund Status: {status_name} Cancelation Reason: {cancelation_reason} -Booking details +Requested Booking Cancellations Room Num Room Type diff --git a/mails/en/order_refund_waiting_customer.html b/mails/en/order_refund_pending_customer.html similarity index 100% rename from mails/en/order_refund_waiting_customer.html rename to mails/en/order_refund_pending_customer.html diff --git a/mails/en/order_refund_waiting_customer.txt b/mails/en/order_refund_pending_customer.txt similarity index 100% rename from mails/en/order_refund_waiting_customer.txt rename to mails/en/order_refund_pending_customer.txt diff --git a/mails/en/order_refund_received_admin.html b/mails/en/order_refund_received_admin.html deleted file mode 100755 index 756fc11fd..000000000 --- a/mails/en/order_refund_received_admin.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - Message from {shop_name} - - - - - - -
- - - - - -
  - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - Hi {firstname} {lastname},
- A request has been recieved for the booking cancellation. -
-
- - - - - - -
  - -

Order details

- - Order: {order_reference} Placed on {order_date}

- Refund Status: {status_name}

-
-
-
 
-
- -

Booking details

- - - - - - - - - {refundBookingHtml} - -
Room NumRoom TypeHotelDuration
-
-
- - - - - - -
  - -

Customer details

- - Customer name: {customer_name}

- Customer email: {customer_email}

-
-
 
-
-
 
- - \ No newline at end of file diff --git a/mails/en/order_refund_received_admin.txt b/mails/en/order_refund_received_admin.txt deleted file mode 100755 index 11f19d0ef..000000000 --- a/mails/en/order_refund_received_admin.txt +++ /dev/null @@ -1,25 +0,0 @@ -Message from {shop_name} - -Hi {firstname} {lastname}, - -A request has been recieved for the booking cancellation. - -Order details - -Order: {order_reference} Placed on {order_date} -Refund Status: {status_name} - -Booking details -Room Num -Room Type -Hotel -Duration - -{refundBookingHtml} - -Customer details - -Customer name: {customer_name} -Customer email: {customer_email} - -{shop_url} {shop_name} powered by Webkul \ No newline at end of file diff --git a/mails/en/order_refund_received_customer.txt b/mails/en/order_refund_received_customer.txt deleted file mode 100755 index 9c54f50c9..000000000 --- a/mails/en/order_refund_received_customer.txt +++ /dev/null @@ -1,21 +0,0 @@ -Message from {shop_name} - -Hi {firstname} {lastname}, -Your request for booking cancellation has been recieved. - -Order details - -Order: {order_reference} Placed on {order_date} -Refund Status: {status_name} - -Booking details - -Room Type -Hotel -Num. Rooms -Duration - -{refundBookingHtml} -You can review your booking cancelation requests from the {refund_reqests_url} Booking Refund Requests section of your customer account by clicking {my_account_url} My account - -{shop_url} {shop_name} powered by Webkul \ No newline at end of file diff --git a/modules/blockcart/ajax-cart.js b/modules/blockcart/ajax-cart.js index 8043f19aa..d36f03166 100644 --- a/modules/blockcart/ajax-cart.js +++ b/modules/blockcart/ajax-cart.js @@ -134,26 +134,28 @@ var ajaxCart = { var dateFrom = $(this).attr('cat_rm_check_in'); var dateTo = $(this).attr('cat_rm_check_out'); - + var occupancy = getBookingOccupancyDetails($(this).closest('.booking_room_fields')); /* By Webkul * Note : In our case minimalQuantity is taken from Qty. field */ // var minimalQuantity = parseInt($(this).data('minimal_quantity')); - var minimalQuantity = parseInt($(this).data('minimal_quantity')); - var minimalQuantity = $("#cat_quantity_wanted_" + idProduct).val(); - if (!minimalQuantity) - minimalQuantity = 1; - if ($(this).prop('disabled') != 'disabled') - ajaxCart.add(idProduct, idProductAttribute, false, this, minimalQuantity, null, dateFrom, dateTo); + // var minimalQuantity = parseInt($(this).data('minimal_quantity')); + // var minimalQuantity = $("#quantity_wanted_" + idProduct).val(); + // if (!minimalQuantity) + // minimalQuantity = 1; + if ($(this).prop('disabled') != 'disabled' && occupancy) + ajaxCart.add(idProduct, idProductAttribute, false, this, occupancy, null, dateFrom, dateTo); }); //for product page 'add' button... if ($('.cart_block').length) { - $(document).off('click', '#add_to_cart button').on('click', '#add_to_cart button', function(e) { + $(document).off('click', '#add_to_cart button').on('click', '#add_to_cart button', function(e) { e.preventDefault(); - var date_from = $.datepicker.formatDate('yy-mm-dd', $.datepicker.parseDate(ajaxCart.dateFormat, $('#room_check_in').val())); - var date_to = $.datepicker.formatDate('yy-mm-dd', $.datepicker.parseDate(ajaxCart.dateFormat, $('#room_check_out').val())); - ajaxCart.add($('#product_page_product_id').val(), $('#idCombination').val(), true, null, $('#quantity_wanted').val(), null, date_from, date_to); - + var date_from = $('#room_check_in').val(); + var date_to = $('#room_check_out').val(); + var occupancy = getBookingOccupancyDetails($(this).closest('.booking_room_fields')); + if (occupancy) { + ajaxCart.add($('#product_page_product_id').val(), $('#idCombination').val(), true, null, occupancy, null, date_from, date_to); + } }); } @@ -214,8 +216,8 @@ var ajaxCart = { /*by webkul*/ if (pagename == 'product') { - dateFrom = $.datepicker.formatDate('yy-mm-dd', $.datepicker.parseDate(ajaxCart.dateFormat, $('#room_check_in').val())); - dateTo = $.datepicker.formatDate('yy-mm-dd', $.datepicker.parseDate(ajaxCart.dateFormat, $('#room_check_out').val())); + dateFrom = $('#room_check_in').val(); + dateTo = $('#room_check_out').val(); } else if (pagename == 'category') { dateFrom = $('#check_in_time').val(); dateTo = $('#check_out_time').val(); @@ -319,7 +321,7 @@ var ajaxCart = { // close fancybox updateFancyBox: function() {}, // add a product in the cart via ajax - add: function(idProduct, idCombination, addedFromProductPage, callerElement, quantity, whishlist, dateFrom, dateTo) { + add: function(idProduct, idCombination, addedFromProductPage, callerElement, occupancy, whishlist, dateFrom, dateTo) { if (addedFromProductPage && !checkCustomizations()) { if (contentOnly) { @@ -346,8 +348,9 @@ var ajaxCart = { if (addedFromProductPage) { $('#add_to_cart button').prop('disabled', 'disabled').addClass('disabled'); $('.filled').removeClass('filled'); - } else + } else { $(callerElement).prop('disabled', 'disabled'); + } if ($('.cart_block_list').hasClass('collapsed')) this.expand(); @@ -355,6 +358,7 @@ var ajaxCart = { // get the selected extra demands by customer var roomDemands = getRoomsExtraDemands(); + //send the ajax request to the server $.ajax({ type: 'POST', @@ -365,7 +369,7 @@ var ajaxCart = { async: true, cache: false, dataType: "json", - data: 'controller=cart&add=1&dateFrom=' + dateFrom + '&dateTo=' + dateTo + '&ajax=true&qty=' + ((quantity && quantity != null) ? quantity : '1') + '&id_product=' + idProduct + '&roomDemands=' + JSON.stringify(roomDemands) + '&token=' + static_token + ((parseInt(idCombination) && idCombination != null) ? '&ipa=' + parseInt(idCombination) : '' + '&id_customization=' + ((typeof customizationId !== 'undefined') ? customizationId : 0)), + data: 'controller=cart&add=1&dateFrom=' + dateFrom + '&dateTo=' + dateTo + '&ajax=true'+(occupancy_required_for_booking ? '&occupancy=' + JSON.stringify(occupancy) : '&qty=' + occupancy) + '&id_product=' + idProduct + '&roomDemands=' + JSON.stringify(roomDemands) + '&token=' + static_token + ((parseInt(idCombination) && idCombination != null) ? '&ipa=' + parseInt(idCombination) : '' + '&id_customization=' + ((typeof customizationId !== 'undefined') ? customizationId : 0)), success: function(jsonData, textStatus, jqXHR) { /*by webkul checking and setting availability of rooms*/ /*for product page add to cart quantity management*/ @@ -378,18 +382,19 @@ var ajaxCart = { if (pagename == 'category') { if (jsonData.avail_rooms <= room_warning_num) { - $(".cat_remain_rm_qty_" + idProduct).closest('.rm_left').show(); + $(callerElement).closest(".room_cont").find(".rm_left").show(); } else { - $("cat_remain_rm_qty_" + idProduct).closest('.rm_left').hide(); + $(callerElement).closest(".room_cont").find(".rm_left").hide(); } /*for category page add to cart quantity management*/ + $(callerElement).closest(".room_cont").find(".remain_rm_qty").text(jsonData.avail_rooms); + $(callerElement).closest(".room_cont").find('.max_avail_type_qty').val(jsonData.avail_rooms); - $(".cat_remain_rm_qty_" + idProduct).text(jsonData.avail_rooms); if (jsonData.avail_rooms == 0) { - $(".cat_remain_rm_qty_" + idProduct).closest('.room_cont').hide(); + $(callerElement).closest(".room_cont").hide(); } - //$('#cat_quantity_wanted_'+idProduct).val(1); + //$('#quantity_wanted_'+idProduct).val(1); } @@ -406,22 +411,27 @@ var ajaxCart = { if (jsonData.crossSelling) $('.crossseling').html(jsonData.crossSelling); - if (idCombination) - $(jsonData.products).each(function() { - if (this.id != undefined && this.id == parseInt(idProduct) && this.idCombination == parseInt(idCombination)) - if (contentOnly) - window.parent.ajaxCart.updateLayer(this); - else - ajaxCart.updateLayer(this); - }); - else - $(jsonData.products).each(function() { - if (this.id != undefined && this.id == parseInt(idProduct)) - if (contentOnly) - window.parent.ajaxCart.updateLayer(this); - else - ajaxCart.updateLayer(this); - }); + // if (idCombination) + // $(jsonData.products).each(function() { + // if (this.id != undefined && this.id == parseInt(idProduct) && this.idCombination == parseInt(idCombination)) + // if (contentOnly) + // window.parent.ajaxCart.updateLayer(this); + // else + // ajaxCart.updateLayer(this); + // }); + // else + // $(jsonData.products).each(function() { + // if (this.id != undefined && this.id == parseInt(idProduct)) + // if (contentOnly) + // window.parent.ajaxCart.updateLayer(this); + // else { + // ajaxCart.updateLayer(this); + // } + // }); + if (jsonData.last_added_product) { + ajaxCart.updateLayer(jsonData.last_added_product); + + } if (contentOnly) parent.$.fancybox.close(); } else { @@ -434,7 +444,9 @@ var ajaxCart = { else $(callerElement).removeProp('disabled'); } - + if (!addedFromProductPage) { + resetOccupancyField($(callerElement).closest('.booking_room_fields').find('.booking_occupancy_wrapper')); + } emptyCustomizations(); }, @@ -495,12 +507,11 @@ var ajaxCart = { /*by webkul checking and setting availability of rooms*/ /*for product page add to cart quantity management*/ if (pagename == 'product') { - var date_checkIn = $.datepicker.formatDate('yy-mm-dd', $.datepicker.parseDate(ajaxCart.dateFormat, $('#room_check_in').val())); - var date_checkOut = $.datepicker.formatDate('yy-mm-dd', $.datepicker.parseDate(ajaxCart.dateFormat, $('#room_check_out').val())); + var date_checkIn = $('#room_check_in').val(); + var date_checkOut = $('#room_check_out').val(); var product_page_id_product = $('#product_page_product_id').val(); if (idProduct == product_page_id_product && dateFrom < date_checkOut && dateTo >= date_checkIn) { - $("#max_avail_type_qty").val(jsonData.avail_rooms); - + $(".max_avail_type_qty").val(jsonData.avail_rooms); if (jsonData.avail_rooms == 0) { disableRoomTypeDemands(1); } @@ -510,18 +521,19 @@ var ajaxCart = { if (pagename == 'category') { if (jsonData.avail_rooms <= room_warning_num) { - $(".cat_remain_rm_qty_" + idProduct).closest('.rm_left').show(); + $(".room_cont").find('[data-id-product="'+idProduct+'"] .rm_left').show(); } else { - $("cat_remain_rm_qty_" + idProduct).closest('.rm_left').hide(); + $("room_cont").find('[data-id-product="'+idProduct+'"] .rm_left').hide(); } /*for category page add to cart quantity management*/ - $(".cat_remain_rm_qty_" + idProduct).text(jsonData.avail_rooms); + $(".room_cont").find('[data-id-product="'+idProduct+'"] .remain_rm_qty').text(jsonData.avail_rooms); + $(".room_cont").find('[data-id-product="'+idProduct+'"] .max_avail_type_qty').val(jsonData.avail_rooms); if (jsonData.avail_rooms == 0) { - $(".cat_remain_rm_qty_" + idProduct).closest('.room_cont').hide(); + $(".room_cont").find('[data-id-product="'+idProduct+'"]').hide(); } - //$('#cat_quantity_wanted_'+idProduct).val(1); + //$('#quantity_wanted_'+idProduct).val(1); } ajaxCart.updateCart(jsonData); @@ -615,14 +627,14 @@ var ajaxCart = { if (pagename == 'category') { // for category page.... - $(".cat_remain_rm_qty_" + idProduct).text(jsonData.avail_rooms); - - $(".cat_remain_rm_qty_" + idProduct).closest('.room_cont').show(0, function() { + $('.room_cont[data-id-product="'+idProduct+'"]').find(".remain_rm_qty").text(jsonData.avail_rooms); + $('.room_cont[data-id-product="'+idProduct+'"]').find('.max_avail_type_qty').val(jsonData.avail_rooms); + $('.room_cont[data-id-product="'+idProduct+'"]').show(0, function() { if (jsonData.avail_rooms <= room_warning_num) { - $(".cat_remain_rm_qty_" + idProduct).closest('.rm_left').show(); + $('.room_cont[data-id-product="'+idProduct+'"]').find('.rm_left').show(); } else { - $(".cat_remain_rm_qty_" + idProduct).closest('.rm_left').hide(); + $('.room_cont[data-id-product="'+idProduct+'"]').find('.rm_left').hide(); } }); } @@ -817,7 +829,7 @@ var ajaxCart = { content += '
'; content += '' + capacity_txt + ':'; - content += ' ' + cart_booking_data[key].adult + ' ' + adults_txt + ' & ' + cart_booking_data[key].children + ' ' + children_txt + '' + content += ' ' + cart_booking_data[key].adults + ' ' + adults_txt + ' & ' + cart_booking_data[key].children + ' ' + children_txt + '' content += '
'; if (this.hasAttributes) @@ -1010,17 +1022,25 @@ var ajaxCart = { $('#layer_cart_product_price').text(product.price); //by webkul has to work on it more.. - if (pagename == 'product') { - $('#layer_cart_product_time_duration').text($('#room_check_in').val() + ' - ' + $('#room_check_out').val()); - $('#layer_cart_product_quantity').text($('#quantity_wanted').val()); - $('#quantity_wanted').val(1); - } - if (pagename == 'category') { - $('#layer_cart_product_time_duration').text($('#check_in_time').val() + ' - ' + $('#check_out_time').val()); - $('#layer_cart_product_quantity').text($('#cat_quantity_wanted_' + product.id).val()); - $('#cat_quantity_wanted_' + product.id).val(1); + $('#layer_cart_product_time_duration').text( + product.date_from + ' - ' + product.date_to + ); + let rooms = 0; + let adults = 0; + let children = 0; + let room_quantity_text = ''; + if (occupancy_required_for_booking) { + $.each(product.occupancy, function(index, val) { + rooms++; + adults += parseInt(val.adults); + children += parseInt(val.children); + }); + room_quantity_text = getRoomTypeGuestOccupancyFormated(adults, children, rooms); + } else { + room_quantity_text = product.occupancy; } + $('#layer_cart_product_quantity').text(room_quantity_text); $('.layer_cart_img').html('' + product.name + ''); @@ -1189,4 +1209,48 @@ function getRoomsExtraDemands() }); return roomDemands; +} + +function getBookingOccupancyDetails(bookingform) +{ + let occupancy; + if (occupancy_required_for_booking) { + let selected_occupancy = $(bookingform).find(".occupancy_info_block.selected") + if (selected_occupancy.length) { + occupancy = []; + $(selected_occupancy).each(function(ind, element) { + if (parseInt($(element).find('.num_adults').val())) { + let child_ages = []; + $(element).find('.guest_child_age').each(function(index) { + if ($(this).val() > -1) { + child_ages.push($(this).val()); + } + }); + if ($(element).find('.num_children').val()) { + if (child_ages.length != $(element).find('.num_children').val()) { + $(bookingform).find('.booking_occupancy_wrapper').parent().addClass('open') + occupancy = false; + return false; + } + } + occupancy.push({ + 'adults': $(element).find('.num_adults').val(), + 'children': $(element).find('.num_children').val(), + 'child_ages': child_ages + }); + } else { + $(bookingform).find('.booking_occupancy_wrapper').parent().addClass('open') + occupancy = false; + return false; + } + }); + } else { + $(bookingform).find('.booking_occupancy_wrapper').parent().addClass('open') + occupancy = false; + } + } else { + occupancy = parseInt($(bookingform).find(".quantity_wanted").val()); + } + + return occupancy; } \ No newline at end of file diff --git a/modules/blockcart/blockcart.php b/modules/blockcart/blockcart.php index 2ad83363e..1571ec2f3 100644 --- a/modules/blockcart/blockcart.php +++ b/modules/blockcart/blockcart.php @@ -129,7 +129,7 @@ class Blockcart extends Module $wrappingCost = (float) ($params['cart']->getOrderTotal($useTax, Cart::ONLY_WRAPPING)); $totalToPay = $params['cart']->getOrderTotal($useTax); $tax_cost = 0; - if ($showTax) { + if ($useTax && $showTax) { $totalToPayWithoutTaxes = $params['cart']->getOrderTotal(false); $tax_cost = Tools::displayPrice($totalToPay - $totalToPayWithoutTaxes, $currency); } @@ -195,6 +195,52 @@ class Blockcart extends Module 1 ); + $addedProduct = false; + // get currently added product if exists + if (!empty($params['cookie']->currentAddedProduct)) { + $addedProduct = json_decode($params['cookie']->currentAddedProduct, true); + $objProduct = new Product($addedProduct['id_product'], false, $this->context->language->id); + + $addedProduct['name'] = $objProduct->name; + $addedProduct['link'] = $this->context->link->getProductLink( + $objProduct->id, + $objProduct->link_rewrite, + $objProduct->category, + null, + null, + $this->context->cart->id_shop + ); + $image = Product::getCover($objProduct->id); + $image['id_product'] = $objProduct->id; + // Product::defineProductImage(array(Product::getCover($objProduct->id)['id_image']/), $this->id_lang) + $addedProduct['image'] = $this->context->link->getImageLink( + $objProduct->link_rewrite, + Product::defineProductImage($image, $this->context->language->id), + 'home_default' + ); + $addedProduct['image_cart'] = $this->context->link->getImageLink( + $objProduct->link_rewrite, + Product::defineProductImage($image, $this->context->language->id), + 'cart_default' + ); + $price = $addedProduct['price'] = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( + $objProduct->id, + $addedProduct['date_from'], + $addedProduct['date_to'], + $addedProduct['req_rm'] + ); + + if ($priceDisplayMethod == PS_TAX_EXC) { + $addedProduct['price'] = Tools::displayPrice($price['total_price_tax_incl']); + } else { + $addedProduct['price'] = Tools::displayPrice($price['total_price_tax_excl']); + } + $addedProduct['date_from'] = Tools::displayDate($addedProduct['date_from']); + $addedProduct['date_to'] = Tools::displayDate($addedProduct['date_to']); + + unset($this->context->cookie->currentAddedProduct); + } + $response = array( 'products' => $products, 'customizedDatas' => Product::getAllCustomizedDatas((int) ($params['cart']->id)), @@ -206,6 +252,7 @@ class Blockcart extends Module 'shipping_cost_float' => $shipping_cost_float, 'show_wrapping' => $wrappingCost > 0 ? true : false, 'show_tax' => $showTax, + 'use_tax' => $useTax, 'tax_cost' => $tax_cost, 'wrapping_cost' => Tools::displayPrice($wrappingCost, $currency), 'product_total' => Tools::displayPrice($params['cart']->getOrderTotal($useTax, Cart::ONLY_PRODUCTS), $currency), @@ -218,7 +265,7 @@ class Blockcart extends Module 'static_token' => Tools::getToken(false), 'free_shipping' => Tools::displayPrice($total_free_shipping), 'free_shipping_float' => $total_free_shipping, - + 'last_added_product' => $addedProduct, 'cart_booking_data' => $htlCartData, 'total_rooms_in_cart' => $totalRooms, ); diff --git a/modules/blockcart/blockcart.tpl b/modules/blockcart/blockcart.tpl index 6192288d3..ab402c9ab 100644 --- a/modules/blockcart/blockcart.tpl +++ b/modules/blockcart/blockcart.tpl @@ -24,7 +24,7 @@ *} {if isset($blockcart_top) && $blockcart_top} -