From 2b1ef726db7382e04f4d51213418daae69d0fb05 Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Tue, 31 Mar 2026 18:03:35 +0530 Subject: [PATCH 01/30] added: Powered By webkul in frontend --- .../hotelreservationsystem/views/templates/hook/copyRight.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl b/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl index 790a4a52c..38c1cd38b 100644 --- a/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl +++ b/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl @@ -1,5 +1,5 @@
\ No newline at end of file From 17d8e63e31cd3f4e7587ba14b0107d1891b259e8 Mon Sep 17 00:00:00 2001 From: azhar bhat Date: Tue, 7 Apr 2026 17:30:01 +0530 Subject: [PATCH 02/30] composer file updated --- composer.json | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index 74ff00cb0..0e5523d61 100644 --- a/composer.json +++ b/composer.json @@ -1,22 +1,32 @@ { + "name": "qloapps/qloapps", + "description": "QloApps is an Open-source and Free hotel reservation system and booking engine. With the help of QloApps, you can launch your hotel booking website without any cost and take & manage online bookings.", + "type": "project", + "homepage": "https://qloapps.com", + "license": "OSL-3.0", + "authors": [ + { + "name": "QloApps", + "email": "support@qloapps.com" + }, + { + "name": "QloApps Community", + "homepage": "https://qloapps.com" + } + ], + "support": { + "forum": "https://forums.qloapps.com/", + "issues": "https://github.com/Qloapps/QloApps/issues", + "source": "https://github.com/Qloapps/QloApps" + }, "require": { - "php": ">=5.4 <8.0", + "php": ">=8.0 <8.5", "ext-simplexml": "*", "ext-curl": "*", "ext-gd": "*", "ext-zip": "*", "ext-soap": "*" }, - "name": "webkul/qloapps", - "description": "QloApps is an Open-source and Free hotel reservation system and booking engine. With the help of QloApps, you can launch your hotel booking website without any cost and take & manage online bookings.", - "type": "project", - "license": "OSL-3.0", - "authors": [ - { - "name": "webkul", - "email": "support@qloapps.com" - } - ], "minimum-stability": "dev", "prefer-stable": true } \ No newline at end of file From 5994798d8decb8fc4e7c40385f1390b6e7742020 Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Fri, 10 Apr 2026 12:28:23 +0530 Subject: [PATCH 03/30] chaned text from Webkul to Qloapps, and added qloapps hyperlink to it --- .../hotelreservationsystem/views/templates/hook/copyRight.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl b/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl index 38c1cd38b..15906d128 100644 --- a/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl +++ b/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl @@ -1,5 +1,5 @@
\ No newline at end of file From 9376843be4f919d168cfcdfb69939fa9b5680f88 Mon Sep 17 00:00:00 2001 From: azhar bhat Date: Wed, 29 Apr 2026 11:03:38 +0530 Subject: [PATCH 04/30] composer updated --- composer.json | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 0e5523d61..38b986fee 100644 --- a/composer.json +++ b/composer.json @@ -4,29 +4,53 @@ "type": "project", "homepage": "https://qloapps.com", "license": "OSL-3.0", + "keywords": [ + "hotel", + "hotel-management", + "booking", + "booking-engine", + "reservation", + "hospitality", + "property-management", + "qloapps", + "cms" + ], "authors": [ { "name": "QloApps", - "email": "support@qloapps.com" + "email": "support@qloapps.com", + "homepage": "https://qloapps.com" }, { "name": "QloApps Community", - "homepage": "https://qloapps.com" + "homepage": "https://github.com/Qloapps/QloApps/graphs/contributors" } ], "support": { + "docs": "https://qloapps.com/docs/", + "email": "support@qloapps.com", "forum": "https://forums.qloapps.com/", "issues": "https://github.com/Qloapps/QloApps/issues", "source": "https://github.com/Qloapps/QloApps" }, "require": { - "php": ">=8.0 <8.5", - "ext-simplexml": "*", + "php": ">8.0 <8.5", "ext-curl": "*", + "ext-dom": "*", "ext-gd": "*", - "ext-zip": "*", - "ext-soap": "*" + "ext-intl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-phar": "*", + "ext-simplexml": "*", + "ext-soap": "*", + "ext-zip": "*" }, - "minimum-stability": "dev", - "prefer-stable": true + "minimum-stability": "stable", + "prefer-stable": true, + "config": { + "sort-packages": true + } } \ No newline at end of file From ca6139bc506763ce596b733949cf32d0f1207098 Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Fri, 1 May 2026 12:12:31 +0530 Subject: [PATCH 07/30] Added feature: Booking Voucher PDF in order detail page Co-authored-by: Name --- classes/pdf/HTMLTemplateBookingVoucher.php | 846 ++++++++++++++++++ classes/pdf/PDF.php | 1 + controllers/admin/AdminOrdersController.php | 15 +- controllers/admin/AdminPdfController.php | 26 + pdf/booking-voucher.tpl | 428 +++++++++ themes/hotel-reservation-theme/css/global.css | 7 +- 6 files changed, 1315 insertions(+), 8 deletions(-) create mode 100644 classes/pdf/HTMLTemplateBookingVoucher.php create mode 100644 pdf/booking-voucher.tpl diff --git a/classes/pdf/HTMLTemplateBookingVoucher.php b/classes/pdf/HTMLTemplateBookingVoucher.php new file mode 100644 index 000000000..e1e4a8d7e --- /dev/null +++ b/classes/pdf/HTMLTemplateBookingVoucher.php @@ -0,0 +1,846 @@ +order = $order; + $this->smarty = $smarty; + $this->date = Tools::displayDate(date('Y-m-d')); + $this->title = sprintf(self::l('Booking Voucher - %s'), $this->order->reference); + $this->shop = new Shop((int)$this->order->id_shop); + } + + /** + * @return string HTML header + */ + public function getHeader() + { + $this->assignCommonHeaderData(); + $this->smarty->assign(array('header' => self::l('Booking Voucher'))); + + return $this->smarty->fetch($this->getTemplate('header')); + } + + /** + * Assembles all voucher data inline and renders the template. + * + * @return string HTML content + */ + public function getContent() + { + // --- Bookings --- + $objHotelBookingDetail = new HotelBookingDetail(); + $bookings = $objHotelBookingDetail->getBookingDataByOrderId((int)$this->order->id); + if (!is_array($bookings)) { + $bookings = array(); + } + + // --- Order state --- + $orderState = $this->order->getCurrentStateFull((int)$this->order->id_lang); + if (!is_array($orderState)) { + $orderState = array('name' => self::l('Confirmed')); + } elseif (empty($orderState['name'])) { + $orderState['name'] = self::l('Confirmed'); + } + + // --- Hotel / property --- + $hotelName = Configuration::get('PS_SHOP_NAME', null, null, (int)$this->order->id_shop); + $hotelPhone = Configuration::get('PS_SHOP_PHONE', null, null, (int)$this->order->id_shop); + $hotelEmail = Configuration::get('PS_SHOP_EMAIL', null, null, (int)$this->order->id_shop); + $hotelAddress = ''; + $checkInTime = '--'; + $checkOutTime = '--'; + $paymentPolicy = ''; + $cancellationPolicy = ''; + + if (!empty($bookings)) { + $firstBooking = reset($bookings); + if (!empty($firstBooking['id_hotel'])) { + $objHotel = new HotelBranchInformation((int)$firstBooking['id_hotel'], (int)$this->order->id_lang); + if (Validate::isLoadedObject($objHotel)) { + $idHotel = (int)$objHotel->id; + $hotelName = $objHotel->hotel_name ? $objHotel->hotel_name : $hotelName; + $hotelPhone = $objHotel->phone ? $objHotel->phone : $hotelPhone; + $hotelEmail = $objHotel->email ? $objHotel->email : $hotelEmail; + $checkInTime = $objHotel->check_in ?: '--'; + $checkOutTime = $objHotel->check_out ?: '--'; + $policies = is_array($objHotel->policies) ? '' : (string)$objHotel->policies; + if ($policies) { + $policyText = html_entity_decode($policies, ENT_QUOTES, 'UTF-8'); + $policyText = preg_replace('//i', "\n", $policyText); + $policyText = strip_tags($policyText); + $policyText = preg_replace("/[\r\n]+/", "\n", $policyText); + $paymentPolicy = nl2br(trim($policyText)); + } + $cancellationPolicy = $this->getFormattedCancellationPolicy($idHotel); + + if ($idHotelAddress = $objHotel->getHotelIdAddress()) { + $objHotelAddress = new Address((int)$idHotelAddress); + if (Validate::isLoadedObject($objHotelAddress)) { + $objHotelAddress->firstname = $hotelName; + $hotelAddress = AddressFormat::generateAddress( + $objHotelAddress, + array('avoid' => array('lastname')), + '
', + ' ' + ); + } + } + } + } + } + + if (!$hotelAddress) { + $shopAddress = $this->shop->getAddress(); + if ($shopAddress instanceof Address) { + $hotelAddress = AddressFormat::generateAddress($shopAddress, array(), '
', ' '); + } + } + + // --- Room and service data --- + $roomAndServiceData = $this->getRoomAndServiceData($bookings); + + // --- Guest details --- + $guestName = ''; + $guestEmail = ''; + $guestPhone = ''; + $guestAddress = ''; + + $customer = new Customer((int)$this->order->id_customer); + if (Validate::isLoadedObject($customer)) { + $guestName = trim($customer->firstname.' '.$customer->lastname); + $guestEmail = $customer->email; + $guestPhone = $customer->phone; + } + $addressId = (int)$this->order->id_address_invoice ?: (int)$this->order->id_address_delivery; + if ($addressId) { + $address = new Address($addressId); + if (Validate::isLoadedObject($address)) { + $guestPhone = $address->phone_mobile ? $address->phone_mobile : $address->phone; + $guestAddress = AddressFormat::generateAddress($address, array(), '
', ' '); + } + } + // Override with primary guest if this is a guest booking + if ($idCustomerGuestDetail = OrderCustomerGuestDetail::isCustomerGuestBooking((int)$this->order->id)) { + $traveler = new OrderCustomerGuestDetail((int)$idCustomerGuestDetail); + if (Validate::isLoadedObject($traveler)) { + $guestName = trim($traveler->firstname.' '.$traveler->lastname); + $guestEmail = $traveler->email; + $guestPhone = $traveler->phone; + } + } + + $footer = $this->getTotalsData($roomAndServiceData); + + // --- Payment --- + $paymentMethods = array(); + $payments = $this->order->getOrderPaymentDetail(); + if (is_array($payments) && $payments) { + foreach ($payments as $paymentDetail) { + $method = isset($paymentDetail['payment_method']) ? trim($paymentDetail['payment_method']) : ''; + if (!$method) { + $method = $this->order->payment; + } + $amount = isset($paymentDetail['real_paid_amount']) + ? (float)$paymentDetail['real_paid_amount'] + : (float)$paymentDetail['amount']; + $idCurrency = isset($paymentDetail['id_currency']) + ? (int)$paymentDetail['id_currency'] + : (int)$this->order->id_currency; + $paymentMethods[] = $method.' - '.Tools::displayPrice($amount, $idCurrency); + } + } + $paymentMethod = $paymentMethods ? implode('

', $paymentMethods) : $this->order->payment; + + $data = array( + 'order' => $this->order, + 'order_status' => $orderState['name'], + 'booking_date' => Tools::displayDate($this->order->date_add, null, false), + 'issue_date' => Tools::displayDate(date('Y-m-d'), null, false), + // hotel / property + 'hotel_name' => $hotelName, + 'hotel_phone' => $hotelPhone, + 'hotel_email' => $hotelEmail, + 'hotel_address' => $hotelAddress, + // guest + 'guest_name' => $guestName, + 'guest_email' => $guestEmail, + 'guest_phone' => $guestPhone, + 'guest_address' => $guestAddress, + 'total_guests' => (int)$roomAndServiceData['total_guests'], + // booking summary + 'total_rooms' => (int)$roomAndServiceData['total_rooms'], + 'total_room_nights' => (int)$roomAndServiceData['total_room_nights'], + // room and service tables + 'room_type_rows' => $roomAndServiceData['room_type_rows'], + 'service_rows' => $roomAndServiceData['service_rows'], + // payment + 'payment_method' => $paymentMethod, + 'payment_status' => $orderState['name'], + 'is_advance_payment' => (int)$this->order->is_advance_payment, + // totals + 'footer' => $footer, + // policies + 'check_in_time' => ($checkInTime && $checkInTime !== '--') + ? date('h:i a', strtotime($checkInTime)) : '--', + 'check_out_time' => ($checkOutTime && $checkOutTime !== '--') + ? date('h:i a', strtotime($checkOutTime)) : '--', + 'payment_policy' => $paymentPolicy + ? $paymentPolicy + : self::l('Please contact the property for policy details.'), + 'cancellation_policy' => $cancellationPolicy, + 'has_cancellation_policy' => (bool)trim(strip_tags($cancellationPolicy)), + // tax + 'tax_breakdown' => $this->getTaxBreakdownData(), + ); + + if (Tools::getValue('debug')) { + die(json_encode($data)); + } + $this->smarty->assign($data); + + return $this->smarty->fetch($this->getTemplate('booking-voucher')); + } + + /** + * Builds per-room and per-room-type rows, services, price breakdown, and occupancy totals. + * + * @param array $bookings + * @return array + */ + protected function getRoomAndServiceData($bookings) + { + $rooms = array(); + $serviceRows = array(); + $servicesByRoom = array(); + $priceBreakdown = array(); + $roomTypeRows = array(); + $totalRoomNights = 0; + $totalGuests = 0; + + foreach ($bookings as $booking) { + $nights = (int)HotelHelper::getNumberOfDays($booking['date_from'], $booking['date_to']); + if ($nights <= 0) { + $nights = 1; + } + + $roomReference = !empty($booking['room_num']) ? $booking['room_num'] : 'BK-'.(int)$booking['id']; + $adults = (int)$booking['adults']; + $children = (int)$booking['children']; + $totalGuests += $adults + $children; + $totalRoomNights += $nights; + + $serviceData = $this->getRoomServiceData($booking, $roomReference); + $servicesByRoom[$roomReference] = $serviceData['services']; + $serviceRows = array_merge($serviceRows, $serviceData['rows']); + + $roomTotalTaxExcl = (float)$booking['total_price_tax_excl']; + $roomTotalWithAutoServicesTaxExcl = $roomTotalTaxExcl + $serviceData['auto_added_room_total_tax_excl']; + $unitPriceTaxExcl = $nights ? Tools::ps_round( + $roomTotalWithAutoServicesTaxExcl / $nights, + _PS_PRICE_COMPUTE_PRECISION_, + $this->order->round_mode + ) : 0; + + // Group by room type + date range to aggregate multi-room rows + $roomTypeRowKey = implode('|', array( + (int)$booking['id_order_detail'], + isset($booking['room_type_name']) ? $booking['room_type_name'] : '', + isset($booking['date_from']) ? $booking['date_from'] : '', + isset($booking['date_to']) ? $booking['date_to'] : '', + )); + + if (!isset($roomTypeRows[$roomTypeRowKey])) { + $idOrderDetail = (int)$booking['id_order_detail']; + $detailTaxes = $idOrderDetail ? OrderDetail::getTaxListStatic($idOrderDetail) : null; + $orderDetailTaxLabel = self::l('No tax'); + if ($detailTaxes) { + $taxTemp = array(); + foreach ($detailTaxes as $tax) { + $objTax = new Tax($tax['id_tax']); + $taxTemp[] = sprintf(self::l('%1$s%2$s%%'), $objTax->rate + 0, ' '); + } + $orderDetailTaxLabel = implode(', ', $taxTemp); + } + + $roomTypeRows[$roomTypeRowKey] = array( + 'room_type_name' => isset($booking['room_type_name']) ? $booking['room_type_name'] : '', + 'order_detail_tax_label' => $orderDetailTaxLabel, + 'rooms' => 0, + 'adults' => 0, + 'children' => 0, + 'nights' => $nights, + 'check_in_date' => $this->formatDisplayDate($booking['date_from']), + 'check_out_date' => $this->formatDisplayDate($booking['date_to']), + 'unit_price_tax_excl' => 0, + 'total_price_tax_excl' => 0, + ); + } + + $roomTypeRows[$roomTypeRowKey]['rooms'] += 1; + $roomTypeRows[$roomTypeRowKey]['adults'] += $adults; + $roomTypeRows[$roomTypeRowKey]['children'] += $children; + $roomTypeRows[$roomTypeRowKey]['total_price_tax_excl'] += $roomTotalWithAutoServicesTaxExcl; + + $rooms[] = array( + 'room_ref' => $roomReference, + 'room_num' => isset($booking['room_num']) ? $booking['room_num'] : '', + 'room_type_name' => isset($booking['room_type_name']) ? $booking['room_type_name'] : '', + 'check_in_date' => $this->formatDisplayDate($booking['date_from']), + 'check_out_date' => $this->formatDisplayDate($booking['date_to']), + 'nights' => $nights, + 'adults' => $adults, + 'children' => $children, + 'unit_price_tax_excl' => $unitPriceTaxExcl, + 'total_price_tax_excl' => $roomTotalWithAutoServicesTaxExcl, + ); + + $priceBreakdown[] = array( + 'room_ref' => $roomReference, + 'room_type_name' => isset($booking['room_type_name']) ? $booking['room_type_name'] : '', + 'nights' => $nights, + 'unit_price_tax_excl' => $unitPriceTaxExcl, + 'room_total_tax_excl' => $roomTotalWithAutoServicesTaxExcl, + 'services_total_tax_excl' => $serviceData['total_price_tax_excl'], + 'subtotal_tax_excl' => $roomTotalWithAutoServicesTaxExcl + $serviceData['total_price_tax_excl'], + ); + } + + foreach ($roomTypeRows as &$roomTypeRow) { + $denominator = (int)$roomTypeRow['nights'] * (int)$roomTypeRow['rooms']; + if ($denominator > 0) { + $roomTypeRow['unit_price_tax_excl'] = Tools::ps_round( + $roomTypeRow['total_price_tax_excl'] / $denominator, + 6, + $this->order->round_mode + ); + } + } + unset($roomTypeRow); + + return array( + 'rooms' => $rooms, + 'room_type_rows' => array_values($roomTypeRows), + 'service_rows' => $serviceRows, + 'services_by_room' => $servicesByRoom, + 'price_breakdown' => $priceBreakdown, + 'total_rooms' => count($rooms), + 'total_room_nights' => $totalRoomNights, + 'total_guests' => $totalGuests, + ); + } + + protected function getRoomServiceData($booking, $roomReference) + { + $rows = array(); + $services = array(); + $totalPriceTaxExcl = 0; + + $objBookingDemand = new HotelBookingDemands(); + $extraDemands = $objBookingDemand->getRoomTypeBookingExtraDemands( + (int)$this->order->id, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (int)$booking['id'], + (int)$booking['id_order_detail'] + ); + + if (is_array($extraDemands)) { + foreach ($extraDemands as $demand) { + $services[] = array( + 'room_ref' => $roomReference, + 'check_in_out' => $this->formatDisplayDate($booking['date_from']).' - '.$this->formatDisplayDate($booking['date_to']), + 'name' => $demand['name'], + 'quantity' => 1, + 'total_price_tax_excl' => (float)$demand['total_price_tax_excl'], + ); + $rows[] = array( + 'room_ref' => $roomReference, + 'check_in_date' => $this->formatDisplayDate($booking['date_from']), + 'check_out_date' => $this->formatDisplayDate($booking['date_to']), + 'name' => $demand['name'], + 'quantity' => 1, + ); + $totalPriceTaxExcl += (float)$demand['total_price_tax_excl']; + } + } + + $objServiceProductOrderDetail = new ServiceProductOrderDetail(); + $autoAddedWithRoomPriceTaxExcl = (float)$objServiceProductOrderDetail->getRoomTypeServiceProducts( + (int)$this->order->id, + 0, + 0, + isset($booking['id_product']) ? (int)$booking['id_product'] : 0, + isset($booking['date_from']) ? $booking['date_from'] : 0, + isset($booking['date_to']) ? $booking['date_to'] : 0, + isset($booking['id_room']) ? (int)$booking['id_room'] : 0, + 1, + 0, + 1, + Product::PRICE_ADDITION_TYPE_WITH_ROOM + ); + + $additionalServices = $objServiceProductOrderDetail->getRoomTypeServiceProducts( + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, null, 0, (int)$booking['id'] + ); + + if (isset($additionalServices[(int)$booking['id']]['additional_services'])) { + foreach ($additionalServices[(int)$booking['id']]['additional_services'] as $service) { + $services[] = array( + 'room_ref' => $roomReference, + 'check_in_out' => $this->formatDisplayDate($booking['date_from']).' - '.$this->formatDisplayDate($booking['date_to']), + 'name' => $service['name'], + 'quantity' => (int)$service['quantity'], + 'total_price_tax_excl' => (float)$service['total_price_tax_excl'], + 'product_tax_label' => isset($service['product_tax_label']) ? $service['product_tax_label'] : self::l('No tax'), + ); + $rows[] = array( + 'room_ref' => $roomReference, + 'check_in_date' => $this->formatDisplayDate($booking['date_from']), + 'check_out_date' => $this->formatDisplayDate($booking['date_to']), + 'name' => $service['name'], + 'quantity' => (int)$service['quantity'], + 'total_price_tax_excl' => (float)$service['total_price_tax_excl'], + 'product_tax_label' => isset($service['product_tax_label']) ? $service['product_tax_label'] : self::l('No tax'), + ); + $totalPriceTaxExcl += (float)$service['total_price_tax_excl']; + } + } + + return array( + 'services' => $services, + 'rows' => $rows, + 'total_price_tax_excl' => $totalPriceTaxExcl, + 'auto_added_room_total_tax_excl' => $autoAddedWithRoomPriceTaxExcl, + ); + } + + /** + * Computes all cost totals using Order's existing methods. + * Mirrors the $footer calculation in HTMLTemplateInvoice::getContent(). + * + * @param array $roomAndServiceData + * @return array + */ + protected function getTotalsData($roomAndServiceData) + { + $objBookingDemand = new HotelBookingDemands(); + $totalDemandsPriceTE = (float)$objBookingDemand->getRoomTypeBookingExtraDemands( + (int)$this->order->id, 0, 0, 0, 0, 1, 1, 0 + ); + $totalDemandsPriceTI = (float)$objBookingDemand->getRoomTypeBookingExtraDemands( + (int)$this->order->id, 0, 0, 0, 0, 1, 1, 1 + ); + + $idsOrderDetail = $this->getOrderDetailIds(); + + $roomsCost = 0; + $roomsCostTaxIncl = 0; + $serviceProductsCost = 0; + $serviceProductsCostTaxIncl = 0; + $additionalServicesCost = 0; + $additionalServicesCostTaxIncl = 0; + $convenienceFee = 0; + $convenienceFeeTaxIncl = 0; + $productDiscountsTaxExcl = (float)$this->order->total_discounts_tax_excl; + $productDiscountsTaxIncl = (float)$this->order->total_discounts_tax_incl; + + foreach ($this->order->getCartRules() as $cartRule) { + if (!empty($cartRule['free_shipping'])) { + $productDiscountsTaxExcl -= (float)$this->order->total_shipping_tax_excl; + $productDiscountsTaxIncl -= (float)$this->order->total_shipping_tax_incl; + } + } + + if ($productDiscountsTaxExcl < 0) { + $productDiscountsTaxExcl = 0; + } + if ($productDiscountsTaxIncl < 0) { + $productDiscountsTaxIncl = 0; + } + + if (!empty($idsOrderDetail)) { + $roomsCost = (float)$this->order->getTotalProductsWithoutTaxes( + false, true, null, null, null, $idsOrderDetail + ) + (float)$this->order->getTotalProductsWithoutTaxes( + false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, + 1, Product::PRICE_ADDITION_TYPE_WITH_ROOM, $idsOrderDetail + ); + $roomsCostTaxIncl = (float)$this->order->getTotalProductsWithTaxes( + false, true, null, null, null, $idsOrderDetail + ) + (float)$this->order->getTotalProductsWithTaxes( + false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, + 1, Product::PRICE_ADDITION_TYPE_WITH_ROOM, $idsOrderDetail + ); + + $serviceProductsCost = (float)$this->order->getTotalProductsWithoutTaxes( + false, false, Product::SELLING_PREFERENCE_STANDALONE, false, false, $idsOrderDetail + ) + (float)$this->order->getTotalProductsWithoutTaxes( + false, false, Product::SELLING_PREFERENCE_HOTEL_STANDALONE, false, false, $idsOrderDetail + ); + $serviceProductsCostTaxIncl = (float)$this->order->getTotalProductsWithTaxes( + false, false, Product::SELLING_PREFERENCE_STANDALONE, false, false, $idsOrderDetail + ) + (float)$this->order->getTotalProductsWithTaxes( + false, false, Product::SELLING_PREFERENCE_HOTEL_STANDALONE, false, false, $idsOrderDetail + ); + + $additionalServicesCost = (float)$this->order->getTotalProductsWithoutTaxes( + false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 0, null, $idsOrderDetail + ) + $totalDemandsPriceTE; + $additionalServicesCostTaxIncl = (float)$this->order->getTotalProductsWithTaxes( + false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 0, null, $idsOrderDetail + ) + $totalDemandsPriceTI; + + $convenienceFee = (float)$this->order->getTotalProductsWithoutTaxes( + false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, + 1, Product::PRICE_ADDITION_TYPE_INDEPENDENT, $idsOrderDetail + ); + $convenienceFeeTaxIncl = (float)$this->order->getTotalProductsWithTaxes( + false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, + 1, Product::PRICE_ADDITION_TYPE_INDEPENDENT, $idsOrderDetail + ); + } + + $grandTotal = (float)$this->order->total_paid_tax_incl; + $totalPaidReal = (float)$this->order->getTotalPaid(); + $totalWithoutDiscountTaxExcl = $roomsCost + $serviceProductsCost + $additionalServicesCost + $convenienceFee; + $totalWithoutDiscountTaxIncl = $roomsCostTaxIncl + $serviceProductsCostTaxIncl + $additionalServicesCostTaxIncl + $convenienceFeeTaxIncl; + $totalTaxes = $totalWithoutDiscountTaxIncl - $totalWithoutDiscountTaxExcl; + if ($totalTaxes < 0) { + $totalTaxes = 0; + } + + $amountDue = $grandTotal - $totalPaidReal; + if ($amountDue < 0) { + $amountDue = 0; + } + + return array( + 'total_rooms' => (int)$roomAndServiceData['total_rooms'], + 'total_room_nights' => (int)$roomAndServiceData['total_room_nights'], + 'total_guests' => (int)$roomAndServiceData['total_guests'], + 'rooms_cost' => $roomsCost, + 'room_price_tax_excl' => $roomsCost, + 'room_price_tax_incl' => $roomsCostTaxIncl, + 'service_products_price_tax_excl' => $serviceProductsCost, + 'service_products_price_tax_incl' => $serviceProductsCostTaxIncl, + 'additional_service_price_tax_excl' => $additionalServicesCost, + 'additional_service_price_tax_incl' => $additionalServicesCostTaxIncl, + 'extra_services_cost' => $additionalServicesCost + $serviceProductsCost, + 'convenience_fee' => $convenienceFee, + 'total_convenience_fee_te' => $convenienceFee, + 'total_convenience_fee_ti' => $convenienceFeeTaxIncl, + 'total_paid_tax_excl' => (float)$this->order->total_paid_tax_excl, + 'total_taxes' => $totalTaxes, + 'total_rooms_tax' => $totalTaxes, + 'extra_services_tax' => 0, + 'total_without_discount_te' => $totalWithoutDiscountTaxExcl, + 'total_without_discount_ti' => $totalWithoutDiscountTaxIncl, + 'total_tax_without_discount' => $totalTaxes, + 'product_discounts_tax_excl' => $productDiscountsTaxExcl, + 'product_discounts_tax_incl' => $productDiscountsTaxIncl, + 'grand_total' => $grandTotal, + 'total_paid' => $totalPaidReal, + 'total_paid_real' => $totalPaidReal, + 'total_paid_tax_incl' => $grandTotal, + 'amount_due' => Tools::ps_round($amountDue, _PS_PRICE_COMPUTE_PRECISION_, $this->order->round_mode), + ); + } + + /** + * Builds tax breakdown by product type, handling both order detail taxes and extra demand taxes separately. + * Extracted: two-pass algorithm with per-type bucketing. + * + * @return array + */ + protected function getTaxBreakdownData() + { + $breakdowns = array( + 'room_tax' => array(), + 'additional_services_tax' => array(), + 'convenience_fee_tax' => array(), + 'extra_demands_tax' => array(), + 'service_products_tax' => array(), + ); + + $orderDetails = OrderDetail::getList((int)$this->order->id); + if (is_array($orderDetails)) { + foreach ($orderDetails as $orderDetail) { + $breakdownType = $this->getTaxBreakdownType($orderDetail); + if (!$breakdownType) { + continue; + } + + $taxes = OrderDetail::getTaxListStatic((int)$orderDetail['id_order_detail']); + if (!$taxes) { + $taxAmount = (float)$orderDetail['total_price_tax_incl'] - (float)$orderDetail['total_price_tax_excl']; + if ($taxAmount <= 0) { + continue; + } + $taxes = array(array( + 'id_tax' => 0, + 'rate' => (float)$orderDetail['total_price_tax_excl'] > 0 + ? (($taxAmount / (float)$orderDetail['total_price_tax_excl']) * 100) : 0, + 'total_amount' => $taxAmount, + )); + } + + foreach ($taxes as $taxDetail) { + $objTax = new Tax((int)$taxDetail['id_tax']); + $rate = Validate::isLoadedObject($objTax) + ? (float)$objTax->rate + : (isset($taxDetail['rate']) ? (float)$taxDetail['rate'] : 0); + $key = sprintf('%.3f', $rate); + + if (!isset($breakdowns[$breakdownType][$key])) { + $breakdowns[$breakdownType][$key] = array( + 'total_price_tax_excl' => 0, + 'total_tax_excl' => 0, + 'total_amount' => 0, + 'id_tax' => (int)$taxDetail['id_tax'], + 'rate' => sprintf('%.3f', $rate), + 'name' => Validate::isLoadedObject($objTax) + ? $objTax->name[(int)$this->order->id_lang] : '', + ); + } + + $breakdowns[$breakdownType][$key]['total_price_tax_excl'] += (float)$orderDetail['total_price_tax_excl']; + $breakdowns[$breakdownType][$key]['total_tax_excl'] += (float)$orderDetail['total_price_tax_excl']; + $breakdowns[$breakdownType][$key]['total_amount'] += (float)$taxDetail['total_amount']; + } + } + } + + $objBookingDemand = new HotelBookingDemands(); + $extraDemandTaxes = $objBookingDemand->getExtraDemandsTaxesDetails( + (int)$this->order->id, + $this->getOrderDetailIds() + ); + + if (is_array($extraDemandTaxes)) { + foreach ($extraDemandTaxes as $taxDetail) { + $rate = isset($taxDetail['rate']) ? (float)$taxDetail['rate'] : 0; + $key = sprintf('%.3f', $rate); + + if (!isset($breakdowns['extra_demands_tax'][$key])) { + $breakdowns['extra_demands_tax'][$key] = array( + 'total_price_tax_excl' => 0, + 'total_tax_excl' => 0, + 'total_amount' => 0, + 'id_tax' => (int)$taxDetail['id_tax'], + 'rate' => sprintf('%.3f', $rate), + 'name' => isset($taxDetail['name']) ? $taxDetail['name'] : '', + ); + } + + $taxBase = isset($taxDetail['total_tax_base']) + ? (float)$taxDetail['total_tax_base'] + : (float)$taxDetail['total_price_tax_excl']; + $breakdowns['extra_demands_tax'][$key]['total_price_tax_excl'] += $taxBase; + $breakdowns['extra_demands_tax'][$key]['total_tax_excl'] += $taxBase; + $breakdowns['extra_demands_tax'][$key]['total_amount'] += (float)$taxDetail['total_amount']; + } + } + + foreach ($breakdowns as $type => &$breakdown) { + if (empty($breakdown)) { + unset($breakdowns[$type]); + continue; + } + foreach ($breakdown as &$line) { + $line['total_price_tax_excl'] = Tools::ps_round( + $line['total_price_tax_excl'], + _PS_PRICE_COMPUTE_PRECISION_, + $this->order->round_mode + ); + $line['total_tax_excl'] = $line['total_price_tax_excl']; + $line['total_amount'] = Tools::ps_round( + $line['total_amount'], + _PS_PRICE_COMPUTE_PRECISION_, + $this->order->round_mode + ); + } + unset($line); + } + unset($breakdown); + + return $breakdowns; + } + + /** + * Classifies an OrderDetail row into a tax breakdown bucket. + * Extracted: complex multi-condition classification called inside a loop. + * + * @param array $orderDetail + * @return string|false + */ + protected function getTaxBreakdownType($orderDetail) + { + if (!empty($orderDetail['is_booking_product']) + || ((int)$orderDetail['product_auto_add'] + && (int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_WITH_ROOM_TYPE + && (int)$orderDetail['product_price_addition_type'] === (int)Product::PRICE_ADDITION_TYPE_WITH_ROOM) + ) { + return 'room_tax'; + } + + if (!(int)$orderDetail['is_booking_product'] + && !(int)$orderDetail['product_auto_add'] + && (int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_WITH_ROOM_TYPE + ) { + return 'additional_services_tax'; + } + + if (!(int)$orderDetail['is_booking_product'] + && (int)$orderDetail['product_auto_add'] + && (int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_WITH_ROOM_TYPE + && (int)$orderDetail['product_price_addition_type'] === (int)Product::PRICE_ADDITION_TYPE_INDEPENDENT + ) { + return 'convenience_fee_tax'; + } + + if (!(int)$orderDetail['is_booking_product'] + && ((int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_STANDALONE + || (int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_HOTEL_STANDALONE) + ) { + return 'service_products_tax'; + } + + return false; + } + + /** + * Returns order detail IDs for this order. + * Reused by getTotalsData() and getTaxBreakdownData(). + * + * @return array + */ + protected function getOrderDetailIds() + { + $orderDetails = OrderDetail::getList((int)$this->order->id); + + if (!is_array($orderDetails)) { + return array(); + } + + return array_unique(array_map('intval', array_column($orderDetails, 'id_order_detail'))); + } + + /** + * Formats a date for display; returns '--' for empty/zero dates. + * Reused across getRoomAndServiceData and getRoomServiceData loops. + * + * @param string $date + * @return string + */ + protected function formatDisplayDate($date) + { + if (!$date || $date === '0000-00-00 00:00:00') { + return '--'; + } + + return Tools::displayDate($date, null, false); + } + + /** + * Fetches and formats the hotel's cancellation/refund rules as HTML. + * + * @param int $idHotel + * @return string + */ + protected function getFormattedCancellationPolicy($idHotel) + { + if (!(int)$idHotel) { + return ''; + } + + $objBranchRefundRules = new HotelBranchRefundRules(); + $refundRules = $objBranchRefundRules->getHotelRefundRules( + (int)$idHotel, 0, 1, (int)$this->order->id_lang, 1 + ); + + if (!is_array($refundRules) || empty($refundRules)) { + return ''; + } + + $policyBlocks = array(); + foreach ($refundRules as $rule) { + $ruleName = isset($rule['name']) ? trim(strip_tags($rule['name'])) : ''; + $ruleDescription = isset($rule['description']) ? trim($rule['description']) : ''; + + if ($ruleName === '' && $ruleDescription === '') { + continue; + } + + $block = ''; + if ($ruleName !== '') { + $block .= ''.Tools::safeOutput($ruleName).''; + } + if ($ruleDescription !== '') { + if ($block !== '') { + $block .= ': '; + } + $block .= $ruleDescription; + } + $policyBlocks[] = $block; + } + + if (empty($policyBlocks)) { + return ''; + } + + return implode('

', $policyBlocks); + } + + /** + * @return string + */ + public function getBulkFilename() + { + return self::l('BookingVouchers').'.pdf'; + } + + /** + * @return string + */ + public function getFilename() + { + return sprintf(self::l('BOOKING VOUCHER - Refrence No. #%s.pdf'), $this->order->reference); + } +} diff --git a/classes/pdf/PDF.php b/classes/pdf/PDF.php index 626d3a51a..3ab61b01b 100644 --- a/classes/pdf/PDF.php +++ b/classes/pdf/PDF.php @@ -41,6 +41,7 @@ class PDFCore const TEMPLATE_ORDER_SLIP = 'OrderSlip'; const TEMPLATE_DELIVERY_SLIP = 'DeliverySlip'; const TEMPLATE_SUPPLY_ORDER_FORM = 'SupplyOrderForm'; + const TEMPLATE_BOOKING_VOUCHER = 'BookingVoucher'; /** * @param $objects diff --git a/controllers/admin/AdminOrdersController.php b/controllers/admin/AdminOrdersController.php index 30026bcca..b8095b2de 100644 --- a/controllers/admin/AdminOrdersController.php +++ b/controllers/admin/AdminOrdersController.php @@ -499,12 +499,15 @@ class AdminOrdersControllerCore extends AdminController ); } - $this->toolbar_btn['print'] = array( - 'short' => $this->l('Print'), - 'href' => 'javascript:window.print()', - 'desc' => $this->l('Print order'), - 'class' => 'icon-print', - ); + if (HotelBookingDetail::getIdHotelByIdOrder($order->id, false) && !$this->lite_display) { + $this->toolbar_btn['booking_voucher'] = array( + 'short' => $this->l('Booking Voucher'), + 'href' => $this->context->link->getAdminLink('AdminPdf').'&submitAction=generateBookingVoucherPDF&id_order='.$order->id, + 'desc' => $this->l('View booking voucher'), + 'class' => 'icon-file-text', + 'target' => true, + ); + } if ($this->tabAccess['edit'] === 1) { if (((int) $order->isReturnable()) diff --git a/controllers/admin/AdminPdfController.php b/controllers/admin/AdminPdfController.php index 7fba0f8e1..16eb82dc9 100644 --- a/controllers/admin/AdminPdfController.php +++ b/controllers/admin/AdminPdfController.php @@ -64,6 +64,15 @@ class AdminPdfControllerCore extends AdminController } } + public function processGenerateBookingVoucherPDF() + { + if (Tools::isSubmit('id_order')) { + $this->generateBookingVoucherPDFByIdOrder(Tools::getValue('id_order')); + } else { + die(Tools::displayError('The order ID is missing.')); + } + } + public function processGenerateOrderSlipPDF() { $order_slip = new OrderSlip((int)Tools::getValue('id_order_slip')); @@ -202,6 +211,23 @@ class AdminPdfControllerCore extends AdminController $this->generatePDF($order_invoice, PDF::TEMPLATE_INVOICE); } + public function generateBookingVoucherPDFByIdOrder($id_order) + { + $order = new Order((int)$id_order); + if (!Validate::isLoadedObject($order)) { + die(Tools::displayError('The order cannot be found within your database.')); + } + + $objHotelBookingDetail = new HotelBookingDetail(); + $bookingDetails = $objHotelBookingDetail->getBookingDataByOrderId((int)$order->id); + if (empty($bookingDetails)) { + die(Tools::displayError('No booking voucher is available for this order.')); + } + + $pdf = new PDF($order, PDF::TEMPLATE_BOOKING_VOUCHER, Context::getContext()->smarty); + $pdf->render('I'); + } + public function generatePDF($object, $template) { $pdf = new PDF($object, $template, Context::getContext()->smarty); diff --git a/pdf/booking-voucher.tpl b/pdf/booking-voucher.tpl new file mode 100644 index 000000000..c5b9ec805 --- /dev/null +++ b/pdf/booking-voucher.tpl @@ -0,0 +1,428 @@ +{** +* NOTICE OF LICENSE +* +* This source file is subject to the Open Software License version 3.0 +* that is bundled with this package in the file LICENSE.md +* It is also available through the world-wide-web at this URL: +* https://opensource.org/license/osl-3-0-php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to support@qloapps.com so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade this module to a newer +* versions in the future. If you wish to customize this module for your needs +* please refer to https://store.webkul.com/customisation-guidelines for more information. +* +* @author Webkul IN +* @copyright Since 2010 Webkul +* @license https://opensource.org/license/osl-3-0-php Open Software License version 3.0 +*} + +{assign var=pdf_style_template value=$smarty.const._PS_PDF_DIR_|cat:'invoice.style-tab.tpl'} +{include file=$pdf_style_template} + + + + + + + + + + + + + + + + + + + + + + + + + +
{l s='BOOKING INFORMATION' pdf='true'}
{l s='Booking Reference' pdf='true'}{$order->reference|escape:'html':'UTF-8'}{l s='Booking Date' pdf='true'}{$booking_date|escape:'html':'UTF-8'}{l s='Voucher Issue Date' pdf='true'}{$issue_date|escape:'html':'UTF-8'}
{l s='Status' pdf='true'}{$order_status|escape:'html':'UTF-8'}{l s='Total Rooms' pdf='true'}{$total_rooms|intval}{l s='Total Nights' pdf='true'}{$total_room_nights|intval}
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{l s='PROPERTY DETAILS' pdf='true'}{l s='GUEST INFORMATION' pdf='true'}
{l s='Property' pdf='true'}{$hotel_name|escape:'html':'UTF-8'}{l s='Primary Guest' pdf='true'}{$guest_name|escape:'html':'UTF-8'}
{l s='Email' pdf='true'}{$hotel_email|escape:'html':'UTF-8'}{l s='Email' pdf='true'}{$guest_email|escape:'html':'UTF-8'}
{l s='Phone' pdf='true'}{$hotel_phone|escape:'html':'UTF-8'}{l s='Phone' pdf='true'}{$guest_phone|escape:'html':'UTF-8'}
{l s='Address' pdf='true'}{if $hotel_address}{$hotel_address nofilter}{else}--{/if}{l s='Address' pdf='true'}{if $guest_address|strip_tags|trim}{$guest_address nofilter}{else}--{/if}
{l s='Total Guests' pdf='true'}{$total_guests|intval}
+ + + + + +
+ + + + + + + + + + + + + + + + + + {if $room_type_rows} + {foreach from=$room_type_rows item=room_type_row} + {cycle values="color_line_even,color_line_odd" assign=bgcolor_class} + + + + + + + + + + {/foreach} + {else} + + + + {/if} + +
{l s='ROOM DETAILS' pdf='true'}
{l s='Room Description' pdf='true'}{l s='Tax Rate(s)' pdf='true'}{l s='Unit Price' pdf='true'}
{l s='(Tax excl.)' pdf='true'}
{l s='Rooms' pdf='true'}{l s='Check-in Date' pdf='true'}{l s='Check-out Date' pdf='true'}{l s='Total' pdf='true'}
{l s='(Tax excl.)' pdf='true'}
{$room_type_row.room_type_name|escape:'html':'UTF-8'}{$room_type_row.order_detail_tax_label}{displayPrice currency=$order->id_currency price=$room_type_row.unit_price_tax_excl} + {if $room_type_row.adults <= 9}0{/if}{$room_type_row.adults|intval} {if $room_type_row.adults > 1}{l s='Adults' pdf='true'}{else}{l s='Adult' pdf='true'}{/if}{if $room_type_row.children}, {if $room_type_row.children <= 9}0{/if}{$room_type_row.children|intval} {if $room_type_row.children > 1}{l s='Children' pdf='true'}{else}{l s='Child' pdf='true'}{/if}{/if}
{if $room_type_row.rooms <= 9}0{/if}{$room_type_row.rooms|intval} {if $room_type_row.rooms > 1}{l s='Rooms' pdf='true'}{else}{l s='Room' pdf='true'}{/if} +
{$room_type_row.check_in_date|escape:'html':'UTF-8'}{$room_type_row.check_out_date|escape:'html':'UTF-8'}{displayPrice currency=$order->id_currency price=$room_type_row.total_price_tax_excl}
{l s='No room details available.' pdf='true'}
+ +{if $service_rows} + + + + +
+ + + + + + + + + + + + + + + {foreach from=$service_rows item=service} + {cycle values="color_line_even,color_line_odd" assign=bgcolor_class} + + + + + + + {/foreach} + +
{l s='SERVICES DETAILS' pdf='true'}
{l s='Room Ref' pdf='true'}{l s='Check-in / Check-out' pdf='true'}{l s='Service Name' pdf='true'}{l s='Qty' pdf='true'}
{$service.room_ref|escape:'html':'UTF-8'}{$service.check_in_date|escape:'html':'UTF-8'} - {$service.check_out_date|escape:'html':'UTF-8'}{$service.name|escape:'html':'UTF-8'}{$service.quantity|intval}
+{/if} + + + + + +
+ +{assign var=payment_method_rendered value=false} +{assign var=payment_status_rendered value=false} + + + + + + + + + + {if isset($footer.room_price_tax_excl) && $footer.room_price_tax_excl} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {/if} + {if isset($footer.service_products_price_tax_excl) && $footer.service_products_price_tax_excl} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {/if} + {if isset($footer.additional_service_price_tax_excl) && $footer.additional_service_price_tax_excl} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {/if} + {if isset($footer.total_convenience_fee_te) && $footer.total_convenience_fee_te} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {/if} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {if $footer.total_tax_without_discount > 0} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {/if} + {if isset($footer.product_discounts_tax_incl) && $footer.product_discounts_tax_incl > 0} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {/if} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {if $footer.amount_due > 0} + + {if !$payment_method_rendered} + + + {assign var=payment_method_rendered value=true} + {elseif !$payment_status_rendered} + + + {assign var=payment_status_rendered value=true} + {else} + + + {/if} + + + + + {/if} + +
{l s='PAYMENT INFORMATION' pdf='true'}{l s='TOTAL AMOUNT' pdf='true'}
{l s='Payment Method' pdf='true'}{$payment_method nofilter}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total Rooms Cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.room_price_tax_excl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total Products cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.service_products_price_tax_excl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Extra Services Cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.additional_service_price_tax_excl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Convenience Fee (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_convenience_fee_te}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total (Tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_without_discount_te}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total Tax' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_tax_without_discount}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total Discounts' pdf='true'}- {displayPrice currency=$order->id_currency price=$footer.product_discounts_tax_incl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Final Booking Amount' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_paid_tax_incl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Due Amount' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.amount_due}
+ +{if $has_cancellation_policy} + + + + +
+ + + + + + + + + + + + +
{l s='CANCELLATION POLICY' pdf='true'}
{$cancellation_policy nofilter}
+{/if} + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
{l s='PROPERTY POLICIES' pdf='true'}
{l s='Check-in time' pdf='true'}{$check_in_time|escape:'html':'UTF-8'}
{l s='Check-out time' pdf='true'}{$check_out_time|escape:'html':'UTF-8'}
{l s='Policies' pdf='true'}{$payment_policy nofilter}
+ +{if isset($HOOK_DISPLAY_PDF) && $HOOK_DISPLAY_PDF} + + + + + + + +
{$HOOK_DISPLAY_PDF}
+{/if} diff --git a/themes/hotel-reservation-theme/css/global.css b/themes/hotel-reservation-theme/css/global.css index 1c8ecf2e3..7fb667b55 100644 --- a/themes/hotel-reservation-theme/css/global.css +++ b/themes/hotel-reservation-theme/css/global.css @@ -5776,10 +5776,13 @@ textarea { background: #fff; } #columns { + padding: 0; +} +#columns:has(#center_column > *) { position: relative; padding-bottom: 50px; - padding-top: 15px; } - + padding-top: 15px; +} #index .tab-content { margin-top: 35px; margin-bottom: 34px; } From fa22d23f75b667dd4e0728dc15044b3c28f61dde Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Fri, 1 May 2026 13:09:48 +0530 Subject: [PATCH 08/30] removed print order button --- controllers/admin/AdminOrdersController.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/controllers/admin/AdminOrdersController.php b/controllers/admin/AdminOrdersController.php index 29bb57991..b28a6baa9 100644 --- a/controllers/admin/AdminOrdersController.php +++ b/controllers/admin/AdminOrdersController.php @@ -391,13 +391,6 @@ class AdminOrdersControllerCore extends AdminController ); } - $this->page_header_toolbar_btn['print'] = array( - 'short' => $this->l('Print'), - 'href' => 'javascript:window.print()', - 'desc' => $this->l('Print order'), - 'class' => 'icon-print', - ); - if ($this->tabAccess['edit'] === 1) { if (((int) $order->isReturnable()) && !$order->hasCompletelyRefunded(Order::ORDER_COMPLETE_CANCELLATION_OR_REFUND_REQUEST_FLAG, 0, 0) From 971df08c5f0ddf439789debf13cf866e2563df8d Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Fri, 1 May 2026 13:21:27 +0530 Subject: [PATCH 09/30] fixed: removed pdf button from inittoolbar function, and added in initPageHeaderToolbar Co-authored-by: Name --- controllers/admin/AdminOrdersController.php | 62 ++++----------------- 1 file changed, 10 insertions(+), 52 deletions(-) diff --git a/controllers/admin/AdminOrdersController.php b/controllers/admin/AdminOrdersController.php index b28a6baa9..e9be3b80a 100644 --- a/controllers/admin/AdminOrdersController.php +++ b/controllers/admin/AdminOrdersController.php @@ -391,6 +391,16 @@ class AdminOrdersControllerCore extends AdminController ); } + if (HotelBookingDetail::getIdHotelByIdOrder($order->id, false) && !$this->lite_display) { + $this->page_header_toolbar_btn['booking_voucher'] = array( + 'short' => $this->l('Booking Voucher'), + 'href' => $this->context->link->getAdminLink('AdminPdf').'&submitAction=generateBookingVoucherPDF&id_order='.$order->id, + 'desc' => $this->l('View booking voucher'), + 'class' => 'icon-file-text', + 'target' => true, + ); + } + if ($this->tabAccess['edit'] === 1) { if (((int) $order->isReturnable()) && !$order->hasCompletelyRefunded(Order::ORDER_COMPLETE_CANCELLATION_OR_REFUND_REQUEST_FLAG, 0, 0) @@ -518,58 +528,6 @@ class AdminOrdersControllerCore extends AdminController public function initToolbar() { - if ($this->display == 'view') { - /** @var Order $order */ - if (Validate::isLoadedObject($order = $this->loadObject())) { - // hotel link in header - if ($idHotel = HotelBookingDetail::getIdHotelByIdOrder($order->id)) { - $this->toolbar_btn['hotel'] = array( - 'href' => $this->context->link->getAdminLink('AdminAddHotel').'&id='.$idHotel.'&updatehtl_branch_info', - 'desc' => $this->l('View Hotel'), - 'class' => 'icon-building', - 'target' => true, - ); - } - - if (Configuration::get('PS_INVOICE') && $order->hasInvoice() && !$this->lite_display) { - $this->toolbar_btn['file'] = array( - 'short' => $this->l('Invoice'), - 'href' => $this->context->link->getAdminLink('AdminPdf').'&submitAction=generateInvoicePDF&id_order='.$order->id, - 'desc' => $this->l('View invoice'), - 'class' => 'icon-file-text', - 'target' => true, - ); - } - - if (HotelBookingDetail::getIdHotelByIdOrder($order->id, false) && !$this->lite_display) { - $this->toolbar_btn['booking_voucher'] = array( - 'short' => $this->l('Booking Voucher'), - 'href' => $this->context->link->getAdminLink('AdminPdf').'&submitAction=generateBookingVoucherPDF&id_order='.$order->id, - 'desc' => $this->l('View booking voucher'), - 'class' => 'icon-file-text', - 'target' => true, - ); - } - - if ($this->tabAccess['edit'] === 1) { - if (((int) $order->isReturnable()) - && !$order->hasCompletelyRefunded(Order::ORDER_COMPLETE_CANCELLATION_OR_REFUND_REQUEST_FLAG, 0, 0) - ) { - $orderTotalPaid = $order->getTotalPaid(); - $orderDiscounts = $order->getCartRules(); - $hasOrderDiscountOrPayment = ((float)$orderTotalPaid > 0 || $orderDiscounts) ? true : false; - $this->toolbar_btn['cancel'] = array( - 'short' => ($hasOrderDiscountOrPayment) ? $this->l('Refund') : $this->l('Cancel'), - 'id' => 'desc-order-standard_refund', - 'desc' => ($hasOrderDiscountOrPayment) ? $this->l('Initiate refund') : $this->l('Cancel bookings'), - 'class' => 'icon-exchange', - 'target' => true, - ); - } - } - } - } - $res = parent::initToolbar(); if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP && isset($this->toolbar_btn['new']) && Shop::isFeatureActive()) { unset($this->toolbar_btn['new']); From 0f76539a171ee1965b62f29a03b6e5d5b66d5830 Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Sat, 2 May 2026 13:56:21 +0530 Subject: [PATCH 10/30] combined code in getcontent funciton, and fixed pdf page change issue --- classes/pdf/HTMLTemplateBookingVoucher.php | 682 ++++++--------------- pdf/booking-voucher.tpl | 4 +- 2 files changed, 179 insertions(+), 507 deletions(-) diff --git a/classes/pdf/HTMLTemplateBookingVoucher.php b/classes/pdf/HTMLTemplateBookingVoucher.php index e1e4a8d7e..af465930f 100644 --- a/classes/pdf/HTMLTemplateBookingVoucher.php +++ b/classes/pdf/HTMLTemplateBookingVoucher.php @@ -22,6 +22,10 @@ * @license https://opensource.org/license/osl-3.0-php Open Software License version 3.0 */ +/** + * Builds and renders the Booking Voucher PDF for a given Order. + * Data assembly follows the same inline pattern as HTMLTemplateInvoice::getContent(). + */ class HTMLTemplateBookingVoucherCore extends HTMLTemplate { /** @var Order */ @@ -95,6 +99,7 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate $hotelEmail = $objHotel->email ? $objHotel->email : $hotelEmail; $checkInTime = $objHotel->check_in ?: '--'; $checkOutTime = $objHotel->check_out ?: '--'; + $policies = is_array($objHotel->policies) ? '' : (string)$objHotel->policies; if ($policies) { $policyText = html_entity_decode($policies, ENT_QUOTES, 'UTF-8'); @@ -103,7 +108,31 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate $policyText = preg_replace("/[\r\n]+/", "\n", $policyText); $paymentPolicy = nl2br(trim($policyText)); } - $cancellationPolicy = $this->getFormattedCancellationPolicy($idHotel); + + if ($idHotel) { + $objBranchRefundRules = new HotelBranchRefundRules(); + $refundRules = $objBranchRefundRules->getHotelRefundRules( + $idHotel, 0, 1, (int)$this->order->id_lang, 1 + ); + if (is_array($refundRules) && !empty($refundRules)) { + $policyBlocks = array(); + foreach ($refundRules as $rule) { + $ruleName = isset($rule['name']) ? trim(strip_tags($rule['name'])) : ''; + $ruleDescription = isset($rule['description']) ? trim($rule['description']) : ''; + if ($ruleName === '' && $ruleDescription === '') { + continue; + } + $block = $ruleName !== '' ? ''.Tools::safeOutput($ruleName).'' : ''; + if ($ruleDescription !== '') { + $block .= ($block !== '' ? ': ' : '').$ruleDescription; + } + $policyBlocks[] = $block; + } + if (!empty($policyBlocks)) { + $cancellationPolicy = implode('

', $policyBlocks); + } + } + } if ($idHotelAddress = $objHotel->getHotelIdAddress()) { $objHotelAddress = new Address((int)$idHotelAddress); @@ -128,8 +157,136 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate } } - // --- Room and service data --- - $roomAndServiceData = $this->getRoomAndServiceData($bookings); + // --- Rooms, services, occupancy --- + $totalRooms = 0; + $serviceRows = array(); + $roomTypeRows = array(); + $totalRoomNights = 0; + $totalGuests = 0; + + foreach ($bookings as $booking) { + $nights = (int)HotelHelper::getNumberOfDays($booking['date_from'], $booking['date_to']); + if ($nights <= 0) { + $nights = 1; + } + + $roomReference = !empty($booking['room_num']) ? $booking['room_num'] : 'BK-'.(int)$booking['id']; + $adults = (int)$booking['adults']; + $children = (int)$booking['children']; + $totalGuests += $adults + $children; + $totalRoomNights += $nights; + + $dateFrom = ($booking['date_from'] && $booking['date_from'] !== '0000-00-00 00:00:00') + ? Tools::displayDate($booking['date_from'], null, false) : '--'; + $dateTo = ($booking['date_to'] && $booking['date_to'] !== '0000-00-00 00:00:00') + ? Tools::displayDate($booking['date_to'], null, false) : '--'; + + // Extra demands for this room + $autoAddedWithRoomPriceTaxExcl = 0; + $roomServiceRows = array(); + + $objBookingDemand = new HotelBookingDemands(); + $extraDemands = $objBookingDemand->getRoomTypeBookingExtraDemands( + (int)$this->order->id, 0, 0, 0, 0, 0, 0, 0, + (int)$booking['id'], (int)$booking['id_order_detail'] + ); + if (is_array($extraDemands)) { + foreach ($extraDemands as $demand) { + $roomServiceRows[] = array( + 'room_ref' => $roomReference, + 'check_in_date' => $dateFrom, + 'check_out_date' => $dateTo, + 'name' => $demand['name'], + 'quantity' => 1, + ); + } + } + + // Auto-added services bundled with room price + $objServiceProductOrderDetail = new ServiceProductOrderDetail(); + $autoAddedWithRoomPriceTaxExcl = (float)$objServiceProductOrderDetail->getRoomTypeServiceProducts( + (int)$this->order->id, 0, 0, + isset($booking['id_product']) ? (int)$booking['id_product'] : 0, + isset($booking['date_from']) ? $booking['date_from'] : 0, + isset($booking['date_to']) ? $booking['date_to'] : 0, + isset($booking['id_room']) ? (int)$booking['id_room'] : 0, + 1, 0, 1, Product::PRICE_ADDITION_TYPE_WITH_ROOM + ); + + // Additional services chosen by guest + $additionalServices = $objServiceProductOrderDetail->getRoomTypeServiceProducts( + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, null, 0, (int)$booking['id'] + ); + if (isset($additionalServices[(int)$booking['id']]['additional_services'])) { + foreach ($additionalServices[(int)$booking['id']]['additional_services'] as $service) { + $roomServiceRows[] = array( + 'room_ref' => $roomReference, + 'check_in_date' => $dateFrom, + 'check_out_date' => $dateTo, + 'name' => $service['name'], + 'quantity' => (int)$service['quantity'], + ); + } + } + + $serviceRows = array_merge($serviceRows, $roomServiceRows); + + $roomTotalTaxExcl = (float)$booking['total_price_tax_excl']; + $roomTotalWithAutoServicesTaxExcl = $roomTotalTaxExcl + $autoAddedWithRoomPriceTaxExcl; + + // Aggregate per room type + date range + $roomTypeRowKey = implode('|', array( + (int)$booking['id_order_detail'], + isset($booking['room_type_name']) ? $booking['room_type_name'] : '', + isset($booking['date_from']) ? $booking['date_from'] : '', + isset($booking['date_to']) ? $booking['date_to'] : '', + )); + + if (!isset($roomTypeRows[$roomTypeRowKey])) { + $idOrderDetail = (int)$booking['id_order_detail']; + $detailTaxes = $idOrderDetail ? OrderDetail::getTaxListStatic($idOrderDetail) : null; + $orderDetailTaxLabel = self::l('No tax'); + if ($detailTaxes) { + $taxTemp = array(); + foreach ($detailTaxes as $tax) { + $objTax = new Tax($tax['id_tax']); + $taxTemp[] = sprintf(self::l('%1$s%2$s%%'), $objTax->rate + 0, ' '); + } + $orderDetailTaxLabel = implode(', ', $taxTemp); + } + + $roomTypeRows[$roomTypeRowKey] = array( + 'room_type_name' => isset($booking['room_type_name']) ? $booking['room_type_name'] : '', + 'order_detail_tax_label' => $orderDetailTaxLabel, + 'rooms' => 0, + 'adults' => 0, + 'children' => 0, + 'nights' => $nights, + 'check_in_date' => $dateFrom, + 'check_out_date' => $dateTo, + 'unit_price_tax_excl' => 0, + 'total_price_tax_excl' => 0, + ); + } + + $roomTypeRows[$roomTypeRowKey]['rooms'] += 1; + $roomTypeRows[$roomTypeRowKey]['adults'] += $adults; + $roomTypeRows[$roomTypeRowKey]['children'] += $children; + $roomTypeRows[$roomTypeRowKey]['total_price_tax_excl'] += $roomTotalWithAutoServicesTaxExcl; + $totalRooms++; + } + + foreach ($roomTypeRows as &$roomTypeRow) { + $denominator = (int)$roomTypeRow['nights'] * (int)$roomTypeRow['rooms']; + if ($denominator > 0) { + $roomTypeRow['unit_price_tax_excl'] = Tools::ps_round( + $roomTypeRow['total_price_tax_excl'] / $denominator, + 6, + $this->order->round_mode + ); + } + } + unset($roomTypeRow); // --- Guest details --- $guestName = ''; @@ -161,7 +318,8 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate } } - $footer = $this->getTotalsData($roomAndServiceData); + // --- Totals --- + $footer = $this->getTotalsData($totalRooms, $totalRoomNights, $totalGuests); // --- Payment --- $paymentMethods = array(); @@ -198,18 +356,17 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate 'guest_email' => $guestEmail, 'guest_phone' => $guestPhone, 'guest_address' => $guestAddress, - 'total_guests' => (int)$roomAndServiceData['total_guests'], + 'total_guests' => (int)$totalGuests, // booking summary - 'total_rooms' => (int)$roomAndServiceData['total_rooms'], - 'total_room_nights' => (int)$roomAndServiceData['total_room_nights'], + 'total_rooms' => (int)$totalRooms, + 'total_room_nights' => (int)$totalRoomNights, // room and service tables - 'room_type_rows' => $roomAndServiceData['room_type_rows'], - 'service_rows' => $roomAndServiceData['service_rows'], + 'room_type_rows' => array_values($roomTypeRows), + 'service_rows' => $serviceRows, // payment 'payment_method' => $paymentMethod, 'payment_status' => $orderState['name'], - 'is_advance_payment' => (int)$this->order->is_advance_payment, - // totals + // totals — key named $footer to match invoice convention 'footer' => $footer, // policies 'check_in_time' => ($checkInTime && $checkInTime !== '--') @@ -221,8 +378,6 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate : self::l('Please contact the property for policy details.'), 'cancellation_policy' => $cancellationPolicy, 'has_cancellation_policy' => (bool)trim(strip_tags($cancellationPolicy)), - // tax - 'tax_breakdown' => $this->getTaxBreakdownData(), ); if (Tools::getValue('debug')) { @@ -234,231 +389,16 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate } /** - * Builds per-room and per-room-type rows, services, price breakdown, and occupancy totals. - * - * @param array $bookings - * @return array - */ - protected function getRoomAndServiceData($bookings) - { - $rooms = array(); - $serviceRows = array(); - $servicesByRoom = array(); - $priceBreakdown = array(); - $roomTypeRows = array(); - $totalRoomNights = 0; - $totalGuests = 0; - - foreach ($bookings as $booking) { - $nights = (int)HotelHelper::getNumberOfDays($booking['date_from'], $booking['date_to']); - if ($nights <= 0) { - $nights = 1; - } - - $roomReference = !empty($booking['room_num']) ? $booking['room_num'] : 'BK-'.(int)$booking['id']; - $adults = (int)$booking['adults']; - $children = (int)$booking['children']; - $totalGuests += $adults + $children; - $totalRoomNights += $nights; - - $serviceData = $this->getRoomServiceData($booking, $roomReference); - $servicesByRoom[$roomReference] = $serviceData['services']; - $serviceRows = array_merge($serviceRows, $serviceData['rows']); - - $roomTotalTaxExcl = (float)$booking['total_price_tax_excl']; - $roomTotalWithAutoServicesTaxExcl = $roomTotalTaxExcl + $serviceData['auto_added_room_total_tax_excl']; - $unitPriceTaxExcl = $nights ? Tools::ps_round( - $roomTotalWithAutoServicesTaxExcl / $nights, - _PS_PRICE_COMPUTE_PRECISION_, - $this->order->round_mode - ) : 0; - - // Group by room type + date range to aggregate multi-room rows - $roomTypeRowKey = implode('|', array( - (int)$booking['id_order_detail'], - isset($booking['room_type_name']) ? $booking['room_type_name'] : '', - isset($booking['date_from']) ? $booking['date_from'] : '', - isset($booking['date_to']) ? $booking['date_to'] : '', - )); - - if (!isset($roomTypeRows[$roomTypeRowKey])) { - $idOrderDetail = (int)$booking['id_order_detail']; - $detailTaxes = $idOrderDetail ? OrderDetail::getTaxListStatic($idOrderDetail) : null; - $orderDetailTaxLabel = self::l('No tax'); - if ($detailTaxes) { - $taxTemp = array(); - foreach ($detailTaxes as $tax) { - $objTax = new Tax($tax['id_tax']); - $taxTemp[] = sprintf(self::l('%1$s%2$s%%'), $objTax->rate + 0, ' '); - } - $orderDetailTaxLabel = implode(', ', $taxTemp); - } - - $roomTypeRows[$roomTypeRowKey] = array( - 'room_type_name' => isset($booking['room_type_name']) ? $booking['room_type_name'] : '', - 'order_detail_tax_label' => $orderDetailTaxLabel, - 'rooms' => 0, - 'adults' => 0, - 'children' => 0, - 'nights' => $nights, - 'check_in_date' => $this->formatDisplayDate($booking['date_from']), - 'check_out_date' => $this->formatDisplayDate($booking['date_to']), - 'unit_price_tax_excl' => 0, - 'total_price_tax_excl' => 0, - ); - } - - $roomTypeRows[$roomTypeRowKey]['rooms'] += 1; - $roomTypeRows[$roomTypeRowKey]['adults'] += $adults; - $roomTypeRows[$roomTypeRowKey]['children'] += $children; - $roomTypeRows[$roomTypeRowKey]['total_price_tax_excl'] += $roomTotalWithAutoServicesTaxExcl; - - $rooms[] = array( - 'room_ref' => $roomReference, - 'room_num' => isset($booking['room_num']) ? $booking['room_num'] : '', - 'room_type_name' => isset($booking['room_type_name']) ? $booking['room_type_name'] : '', - 'check_in_date' => $this->formatDisplayDate($booking['date_from']), - 'check_out_date' => $this->formatDisplayDate($booking['date_to']), - 'nights' => $nights, - 'adults' => $adults, - 'children' => $children, - 'unit_price_tax_excl' => $unitPriceTaxExcl, - 'total_price_tax_excl' => $roomTotalWithAutoServicesTaxExcl, - ); - - $priceBreakdown[] = array( - 'room_ref' => $roomReference, - 'room_type_name' => isset($booking['room_type_name']) ? $booking['room_type_name'] : '', - 'nights' => $nights, - 'unit_price_tax_excl' => $unitPriceTaxExcl, - 'room_total_tax_excl' => $roomTotalWithAutoServicesTaxExcl, - 'services_total_tax_excl' => $serviceData['total_price_tax_excl'], - 'subtotal_tax_excl' => $roomTotalWithAutoServicesTaxExcl + $serviceData['total_price_tax_excl'], - ); - } - - foreach ($roomTypeRows as &$roomTypeRow) { - $denominator = (int)$roomTypeRow['nights'] * (int)$roomTypeRow['rooms']; - if ($denominator > 0) { - $roomTypeRow['unit_price_tax_excl'] = Tools::ps_round( - $roomTypeRow['total_price_tax_excl'] / $denominator, - 6, - $this->order->round_mode - ); - } - } - unset($roomTypeRow); - - return array( - 'rooms' => $rooms, - 'room_type_rows' => array_values($roomTypeRows), - 'service_rows' => $serviceRows, - 'services_by_room' => $servicesByRoom, - 'price_breakdown' => $priceBreakdown, - 'total_rooms' => count($rooms), - 'total_room_nights' => $totalRoomNights, - 'total_guests' => $totalGuests, - ); - } - - protected function getRoomServiceData($booking, $roomReference) - { - $rows = array(); - $services = array(); - $totalPriceTaxExcl = 0; - - $objBookingDemand = new HotelBookingDemands(); - $extraDemands = $objBookingDemand->getRoomTypeBookingExtraDemands( - (int)$this->order->id, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - (int)$booking['id'], - (int)$booking['id_order_detail'] - ); - - if (is_array($extraDemands)) { - foreach ($extraDemands as $demand) { - $services[] = array( - 'room_ref' => $roomReference, - 'check_in_out' => $this->formatDisplayDate($booking['date_from']).' - '.$this->formatDisplayDate($booking['date_to']), - 'name' => $demand['name'], - 'quantity' => 1, - 'total_price_tax_excl' => (float)$demand['total_price_tax_excl'], - ); - $rows[] = array( - 'room_ref' => $roomReference, - 'check_in_date' => $this->formatDisplayDate($booking['date_from']), - 'check_out_date' => $this->formatDisplayDate($booking['date_to']), - 'name' => $demand['name'], - 'quantity' => 1, - ); - $totalPriceTaxExcl += (float)$demand['total_price_tax_excl']; - } - } - - $objServiceProductOrderDetail = new ServiceProductOrderDetail(); - $autoAddedWithRoomPriceTaxExcl = (float)$objServiceProductOrderDetail->getRoomTypeServiceProducts( - (int)$this->order->id, - 0, - 0, - isset($booking['id_product']) ? (int)$booking['id_product'] : 0, - isset($booking['date_from']) ? $booking['date_from'] : 0, - isset($booking['date_to']) ? $booking['date_to'] : 0, - isset($booking['id_room']) ? (int)$booking['id_room'] : 0, - 1, - 0, - 1, - Product::PRICE_ADDITION_TYPE_WITH_ROOM - ); - - $additionalServices = $objServiceProductOrderDetail->getRoomTypeServiceProducts( - 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, null, 0, (int)$booking['id'] - ); - - if (isset($additionalServices[(int)$booking['id']]['additional_services'])) { - foreach ($additionalServices[(int)$booking['id']]['additional_services'] as $service) { - $services[] = array( - 'room_ref' => $roomReference, - 'check_in_out' => $this->formatDisplayDate($booking['date_from']).' - '.$this->formatDisplayDate($booking['date_to']), - 'name' => $service['name'], - 'quantity' => (int)$service['quantity'], - 'total_price_tax_excl' => (float)$service['total_price_tax_excl'], - 'product_tax_label' => isset($service['product_tax_label']) ? $service['product_tax_label'] : self::l('No tax'), - ); - $rows[] = array( - 'room_ref' => $roomReference, - 'check_in_date' => $this->formatDisplayDate($booking['date_from']), - 'check_out_date' => $this->formatDisplayDate($booking['date_to']), - 'name' => $service['name'], - 'quantity' => (int)$service['quantity'], - 'total_price_tax_excl' => (float)$service['total_price_tax_excl'], - 'product_tax_label' => isset($service['product_tax_label']) ? $service['product_tax_label'] : self::l('No tax'), - ); - $totalPriceTaxExcl += (float)$service['total_price_tax_excl']; - } - } - - return array( - 'services' => $services, - 'rows' => $rows, - 'total_price_tax_excl' => $totalPriceTaxExcl, - 'auto_added_room_total_tax_excl' => $autoAddedWithRoomPriceTaxExcl, - ); - } - - /** - * Computes all cost totals using Order's existing methods. + * Computes all cost totals using Order's own methods. * Mirrors the $footer calculation in HTMLTemplateInvoice::getContent(). + * Extracted because it is 100+ lines of calculation that would obscure the data assembly above. * - * @param array $roomAndServiceData + * @param int $totalRooms + * @param int $totalRoomNights + * @param int $totalGuests * @return array */ - protected function getTotalsData($roomAndServiceData) + protected function getTotalsData($totalRooms, $totalRoomNights, $totalGuests) { $objBookingDemand = new HotelBookingDemands(); $totalDemandsPriceTE = (float)$objBookingDemand->getRoomTypeBookingExtraDemands( @@ -468,7 +408,10 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate (int)$this->order->id, 0, 0, 0, 0, 1, 1, 1 ); - $idsOrderDetail = $this->getOrderDetailIds(); + $orderDetails = OrderDetail::getList((int)$this->order->id); + $idsOrderDetail = is_array($orderDetails) + ? array_unique(array_map('intval', array_column($orderDetails, 'id_order_detail'))) + : array(); $roomsCost = 0; $roomsCostTaxIncl = 0; @@ -478,19 +421,14 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate $additionalServicesCostTaxIncl = 0; $convenienceFee = 0; $convenienceFeeTaxIncl = 0; - $productDiscountsTaxExcl = (float)$this->order->total_discounts_tax_excl; $productDiscountsTaxIncl = (float)$this->order->total_discounts_tax_incl; foreach ($this->order->getCartRules() as $cartRule) { if (!empty($cartRule['free_shipping'])) { - $productDiscountsTaxExcl -= (float)$this->order->total_shipping_tax_excl; $productDiscountsTaxIncl -= (float)$this->order->total_shipping_tax_incl; } } - if ($productDiscountsTaxExcl < 0) { - $productDiscountsTaxExcl = 0; - } if ($productDiscountsTaxIncl < 0) { $productDiscountsTaxIncl = 0; } @@ -552,282 +490,18 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate } return array( - 'total_rooms' => (int)$roomAndServiceData['total_rooms'], - 'total_room_nights' => (int)$roomAndServiceData['total_room_nights'], - 'total_guests' => (int)$roomAndServiceData['total_guests'], - 'rooms_cost' => $roomsCost, 'room_price_tax_excl' => $roomsCost, - 'room_price_tax_incl' => $roomsCostTaxIncl, 'service_products_price_tax_excl' => $serviceProductsCost, - 'service_products_price_tax_incl' => $serviceProductsCostTaxIncl, 'additional_service_price_tax_excl' => $additionalServicesCost, - 'additional_service_price_tax_incl' => $additionalServicesCostTaxIncl, - 'extra_services_cost' => $additionalServicesCost + $serviceProductsCost, - 'convenience_fee' => $convenienceFee, 'total_convenience_fee_te' => $convenienceFee, - 'total_convenience_fee_ti' => $convenienceFeeTaxIncl, - 'total_paid_tax_excl' => (float)$this->order->total_paid_tax_excl, - 'total_taxes' => $totalTaxes, - 'total_rooms_tax' => $totalTaxes, - 'extra_services_tax' => 0, 'total_without_discount_te' => $totalWithoutDiscountTaxExcl, - 'total_without_discount_ti' => $totalWithoutDiscountTaxIncl, 'total_tax_without_discount' => $totalTaxes, - 'product_discounts_tax_excl' => $productDiscountsTaxExcl, 'product_discounts_tax_incl' => $productDiscountsTaxIncl, - 'grand_total' => $grandTotal, - 'total_paid' => $totalPaidReal, - 'total_paid_real' => $totalPaidReal, 'total_paid_tax_incl' => $grandTotal, 'amount_due' => Tools::ps_round($amountDue, _PS_PRICE_COMPUTE_PRECISION_, $this->order->round_mode), ); } - /** - * Builds tax breakdown by product type, handling both order detail taxes and extra demand taxes separately. - * Extracted: two-pass algorithm with per-type bucketing. - * - * @return array - */ - protected function getTaxBreakdownData() - { - $breakdowns = array( - 'room_tax' => array(), - 'additional_services_tax' => array(), - 'convenience_fee_tax' => array(), - 'extra_demands_tax' => array(), - 'service_products_tax' => array(), - ); - - $orderDetails = OrderDetail::getList((int)$this->order->id); - if (is_array($orderDetails)) { - foreach ($orderDetails as $orderDetail) { - $breakdownType = $this->getTaxBreakdownType($orderDetail); - if (!$breakdownType) { - continue; - } - - $taxes = OrderDetail::getTaxListStatic((int)$orderDetail['id_order_detail']); - if (!$taxes) { - $taxAmount = (float)$orderDetail['total_price_tax_incl'] - (float)$orderDetail['total_price_tax_excl']; - if ($taxAmount <= 0) { - continue; - } - $taxes = array(array( - 'id_tax' => 0, - 'rate' => (float)$orderDetail['total_price_tax_excl'] > 0 - ? (($taxAmount / (float)$orderDetail['total_price_tax_excl']) * 100) : 0, - 'total_amount' => $taxAmount, - )); - } - - foreach ($taxes as $taxDetail) { - $objTax = new Tax((int)$taxDetail['id_tax']); - $rate = Validate::isLoadedObject($objTax) - ? (float)$objTax->rate - : (isset($taxDetail['rate']) ? (float)$taxDetail['rate'] : 0); - $key = sprintf('%.3f', $rate); - - if (!isset($breakdowns[$breakdownType][$key])) { - $breakdowns[$breakdownType][$key] = array( - 'total_price_tax_excl' => 0, - 'total_tax_excl' => 0, - 'total_amount' => 0, - 'id_tax' => (int)$taxDetail['id_tax'], - 'rate' => sprintf('%.3f', $rate), - 'name' => Validate::isLoadedObject($objTax) - ? $objTax->name[(int)$this->order->id_lang] : '', - ); - } - - $breakdowns[$breakdownType][$key]['total_price_tax_excl'] += (float)$orderDetail['total_price_tax_excl']; - $breakdowns[$breakdownType][$key]['total_tax_excl'] += (float)$orderDetail['total_price_tax_excl']; - $breakdowns[$breakdownType][$key]['total_amount'] += (float)$taxDetail['total_amount']; - } - } - } - - $objBookingDemand = new HotelBookingDemands(); - $extraDemandTaxes = $objBookingDemand->getExtraDemandsTaxesDetails( - (int)$this->order->id, - $this->getOrderDetailIds() - ); - - if (is_array($extraDemandTaxes)) { - foreach ($extraDemandTaxes as $taxDetail) { - $rate = isset($taxDetail['rate']) ? (float)$taxDetail['rate'] : 0; - $key = sprintf('%.3f', $rate); - - if (!isset($breakdowns['extra_demands_tax'][$key])) { - $breakdowns['extra_demands_tax'][$key] = array( - 'total_price_tax_excl' => 0, - 'total_tax_excl' => 0, - 'total_amount' => 0, - 'id_tax' => (int)$taxDetail['id_tax'], - 'rate' => sprintf('%.3f', $rate), - 'name' => isset($taxDetail['name']) ? $taxDetail['name'] : '', - ); - } - - $taxBase = isset($taxDetail['total_tax_base']) - ? (float)$taxDetail['total_tax_base'] - : (float)$taxDetail['total_price_tax_excl']; - $breakdowns['extra_demands_tax'][$key]['total_price_tax_excl'] += $taxBase; - $breakdowns['extra_demands_tax'][$key]['total_tax_excl'] += $taxBase; - $breakdowns['extra_demands_tax'][$key]['total_amount'] += (float)$taxDetail['total_amount']; - } - } - - foreach ($breakdowns as $type => &$breakdown) { - if (empty($breakdown)) { - unset($breakdowns[$type]); - continue; - } - foreach ($breakdown as &$line) { - $line['total_price_tax_excl'] = Tools::ps_round( - $line['total_price_tax_excl'], - _PS_PRICE_COMPUTE_PRECISION_, - $this->order->round_mode - ); - $line['total_tax_excl'] = $line['total_price_tax_excl']; - $line['total_amount'] = Tools::ps_round( - $line['total_amount'], - _PS_PRICE_COMPUTE_PRECISION_, - $this->order->round_mode - ); - } - unset($line); - } - unset($breakdown); - - return $breakdowns; - } - - /** - * Classifies an OrderDetail row into a tax breakdown bucket. - * Extracted: complex multi-condition classification called inside a loop. - * - * @param array $orderDetail - * @return string|false - */ - protected function getTaxBreakdownType($orderDetail) - { - if (!empty($orderDetail['is_booking_product']) - || ((int)$orderDetail['product_auto_add'] - && (int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_WITH_ROOM_TYPE - && (int)$orderDetail['product_price_addition_type'] === (int)Product::PRICE_ADDITION_TYPE_WITH_ROOM) - ) { - return 'room_tax'; - } - - if (!(int)$orderDetail['is_booking_product'] - && !(int)$orderDetail['product_auto_add'] - && (int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_WITH_ROOM_TYPE - ) { - return 'additional_services_tax'; - } - - if (!(int)$orderDetail['is_booking_product'] - && (int)$orderDetail['product_auto_add'] - && (int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_WITH_ROOM_TYPE - && (int)$orderDetail['product_price_addition_type'] === (int)Product::PRICE_ADDITION_TYPE_INDEPENDENT - ) { - return 'convenience_fee_tax'; - } - - if (!(int)$orderDetail['is_booking_product'] - && ((int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_STANDALONE - || (int)$orderDetail['selling_preference_type'] === (int)Product::SELLING_PREFERENCE_HOTEL_STANDALONE) - ) { - return 'service_products_tax'; - } - - return false; - } - - /** - * Returns order detail IDs for this order. - * Reused by getTotalsData() and getTaxBreakdownData(). - * - * @return array - */ - protected function getOrderDetailIds() - { - $orderDetails = OrderDetail::getList((int)$this->order->id); - - if (!is_array($orderDetails)) { - return array(); - } - - return array_unique(array_map('intval', array_column($orderDetails, 'id_order_detail'))); - } - - /** - * Formats a date for display; returns '--' for empty/zero dates. - * Reused across getRoomAndServiceData and getRoomServiceData loops. - * - * @param string $date - * @return string - */ - protected function formatDisplayDate($date) - { - if (!$date || $date === '0000-00-00 00:00:00') { - return '--'; - } - - return Tools::displayDate($date, null, false); - } - - /** - * Fetches and formats the hotel's cancellation/refund rules as HTML. - * - * @param int $idHotel - * @return string - */ - protected function getFormattedCancellationPolicy($idHotel) - { - if (!(int)$idHotel) { - return ''; - } - - $objBranchRefundRules = new HotelBranchRefundRules(); - $refundRules = $objBranchRefundRules->getHotelRefundRules( - (int)$idHotel, 0, 1, (int)$this->order->id_lang, 1 - ); - - if (!is_array($refundRules) || empty($refundRules)) { - return ''; - } - - $policyBlocks = array(); - foreach ($refundRules as $rule) { - $ruleName = isset($rule['name']) ? trim(strip_tags($rule['name'])) : ''; - $ruleDescription = isset($rule['description']) ? trim($rule['description']) : ''; - - if ($ruleName === '' && $ruleDescription === '') { - continue; - } - - $block = ''; - if ($ruleName !== '') { - $block .= ''.Tools::safeOutput($ruleName).''; - } - if ($ruleDescription !== '') { - if ($block !== '') { - $block .= ': '; - } - $block .= $ruleDescription; - } - $policyBlocks[] = $block; - } - - if (empty($policyBlocks)) { - return ''; - } - - return implode('

', $policyBlocks); - } - /** * @return string */ diff --git a/pdf/booking-voucher.tpl b/pdf/booking-voucher.tpl index c5b9ec805..68aab5051 100644 --- a/pdf/booking-voucher.tpl +++ b/pdf/booking-voucher.tpl @@ -189,14 +189,12 @@ {assign var=payment_method_rendered value=false} {assign var=payment_status_rendered value=false} - + - - {if isset($footer.room_price_tax_excl) && $footer.room_price_tax_excl} {if !$payment_method_rendered} From fef0be689a24511950dd371622e7296eb2183f38 Mon Sep 17 00:00:00 2001 From: azhar bhat Date: Mon, 4 May 2026 12:56:56 +0530 Subject: [PATCH 11/30] composer updated --- composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composer.json b/composer.json index 38b986fee..0878ca817 100644 --- a/composer.json +++ b/composer.json @@ -48,6 +48,11 @@ "ext-soap": "*", "ext-zip": "*" }, + "require-dev": { + }, + "autoload": { + "classmap": ["classes/", "Core/", "Adapter/"] + }, "minimum-stability": "stable", "prefer-stable": true, "config": { From 860669d4a9e088dd25af8dcfdf703ec503811e3c Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Wed, 13 May 2026 17:21:07 +0530 Subject: [PATCH 12/30] Fixed: Fixed the table structure of booking voucher pdf, removed unnecessary varaible and function calls Co-authored-by: Name --- classes/pdf/HTMLTemplateBookingVoucher.php | 408 +++++--------- pdf/booking-voucher.tpl | 615 +++++++++------------ 2 files changed, 401 insertions(+), 622 deletions(-) diff --git a/classes/pdf/HTMLTemplateBookingVoucher.php b/classes/pdf/HTMLTemplateBookingVoucher.php index af465930f..c4e5d17df 100644 --- a/classes/pdf/HTMLTemplateBookingVoucher.php +++ b/classes/pdf/HTMLTemplateBookingVoucher.php @@ -72,97 +72,74 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate // --- Order state --- $orderState = $this->order->getCurrentStateFull((int)$this->order->id_lang); - if (!is_array($orderState)) { - $orderState = array('name' => self::l('Confirmed')); - } elseif (empty($orderState['name'])) { - $orderState['name'] = self::l('Confirmed'); - } // --- Hotel / property --- - $hotelName = Configuration::get('PS_SHOP_NAME', null, null, (int)$this->order->id_shop); - $hotelPhone = Configuration::get('PS_SHOP_PHONE', null, null, (int)$this->order->id_shop); - $hotelEmail = Configuration::get('PS_SHOP_EMAIL', null, null, (int)$this->order->id_shop); - $hotelAddress = ''; - $checkInTime = '--'; - $checkOutTime = '--'; - $paymentPolicy = ''; - $cancellationPolicy = ''; - - if (!empty($bookings)) { - $firstBooking = reset($bookings); - if (!empty($firstBooking['id_hotel'])) { - $objHotel = new HotelBranchInformation((int)$firstBooking['id_hotel'], (int)$this->order->id_lang); - if (Validate::isLoadedObject($objHotel)) { - $idHotel = (int)$objHotel->id; - $hotelName = $objHotel->hotel_name ? $objHotel->hotel_name : $hotelName; - $hotelPhone = $objHotel->phone ? $objHotel->phone : $hotelPhone; - $hotelEmail = $objHotel->email ? $objHotel->email : $hotelEmail; - $checkInTime = $objHotel->check_in ?: '--'; - $checkOutTime = $objHotel->check_out ?: '--'; - - $policies = is_array($objHotel->policies) ? '' : (string)$objHotel->policies; - if ($policies) { - $policyText = html_entity_decode($policies, ENT_QUOTES, 'UTF-8'); - $policyText = preg_replace('//i', "\n", $policyText); - $policyText = strip_tags($policyText); - $policyText = preg_replace("/[\r\n]+/", "\n", $policyText); - $paymentPolicy = nl2br(trim($policyText)); - } - - if ($idHotel) { - $objBranchRefundRules = new HotelBranchRefundRules(); - $refundRules = $objBranchRefundRules->getHotelRefundRules( - $idHotel, 0, 1, (int)$this->order->id_lang, 1 - ); - if (is_array($refundRules) && !empty($refundRules)) { - $policyBlocks = array(); - foreach ($refundRules as $rule) { - $ruleName = isset($rule['name']) ? trim(strip_tags($rule['name'])) : ''; - $ruleDescription = isset($rule['description']) ? trim($rule['description']) : ''; - if ($ruleName === '' && $ruleDescription === '') { - continue; - } - $block = $ruleName !== '' ? ''.Tools::safeOutput($ruleName).'' : ''; - if ($ruleDescription !== '') { - $block .= ($block !== '' ? ': ' : '').$ruleDescription; - } - $policyBlocks[] = $block; - } - if (!empty($policyBlocks)) { - $cancellationPolicy = implode('

', $policyBlocks); - } - } - } - - if ($idHotelAddress = $objHotel->getHotelIdAddress()) { - $objHotelAddress = new Address((int)$idHotelAddress); - if (Validate::isLoadedObject($objHotelAddress)) { - $objHotelAddress->firstname = $hotelName; - $hotelAddress = AddressFormat::generateAddress( - $objHotelAddress, - array('avoid' => array('lastname')), - '
', - ' ' - ); - } - } - } + $objHotel = null; + $idHotel = HotelBookingDetail::getIdHotelByIdOrder((int)$this->order->id); + if ($idHotel) { + $objHotel = new HotelBranchInformation((int)$idHotel, (int)$this->order->id_lang); + if (!Validate::isLoadedObject($objHotel)) { + $objHotel = null; } } - if (!$hotelAddress) { - $shopAddress = $this->shop->getAddress(); - if ($shopAddress instanceof Address) { - $hotelAddress = AddressFormat::generateAddress($shopAddress, array(), '
', ' '); + $hotelAddress = ''; + $paymentPolicy = ''; + $cancellationPolicy = ''; + + if ($objHotel) { + $policies = is_array($objHotel->policies) ? '' : (string)$objHotel->policies; + if ($policies) { + $policyText = html_entity_decode($policies, ENT_QUOTES, 'UTF-8'); + $policyText = preg_replace('//i', "\n", $policyText); + $policyText = strip_tags($policyText); + $policyText = preg_replace("/[\r\n]+/", "\n", $policyText); + $paymentPolicy = nl2br(trim($policyText)); + } + + $objBranchRefundRules = new HotelBranchRefundRules(); + $refundRules = $objBranchRefundRules->getHotelRefundRules( + (int)$objHotel->id, 0, 1, (int)$this->order->id_lang, 1 + ); + if (is_array($refundRules) && !empty($refundRules)) { + $policyBlocks = array(); + foreach ($refundRules as $rule) { + $ruleName = isset($rule['name']) ? trim(strip_tags($rule['name'])) : ''; + $ruleDescription = isset($rule['description']) ? trim($rule['description']) : ''; + if ($ruleName === '' && $ruleDescription === '') { + continue; + } + $block = $ruleName !== '' ? ''.Tools::safeOutput($ruleName).'' : ''; + if ($ruleDescription !== '') { + $block .= ($block !== '' ? ': ' : '').$ruleDescription; + } + $policyBlocks[] = $block; + } + if (!empty($policyBlocks)) { + $cancellationPolicy = implode('

', $policyBlocks); + } + } + + if ($idHotelAddress = $objHotel->getHotelIdAddress()) { + $objHotelAddress = new Address((int)$idHotelAddress); + if (Validate::isLoadedObject($objHotelAddress)) { + $objHotelAddress->firstname = $objHotel->hotel_name; + $hotelAddress = AddressFormat::generateAddress( + $objHotelAddress, + array('avoid' => array('lastname')), + '
', + ' ' + ); + } } } // --- Rooms, services, occupancy --- $totalRooms = 0; - $serviceRows = array(); - $roomTypeRows = array(); $totalRoomNights = 0; $totalGuests = 0; + $serviceGroups = array(); + $roomTypeRows = array(); foreach ($bookings as $booking) { $nights = (int)HotelHelper::getNumberOfDays($booking['date_from'], $booking['date_to']); @@ -170,20 +147,17 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate $nights = 1; } - $roomReference = !empty($booking['room_num']) ? $booking['room_num'] : 'BK-'.(int)$booking['id']; - $adults = (int)$booking['adults']; - $children = (int)$booking['children']; - $totalGuests += $adults + $children; + $totalGuests += (int)$booking['adults'] + (int)$booking['children']; $totalRoomNights += $nights; + $totalRooms++; $dateFrom = ($booking['date_from'] && $booking['date_from'] !== '0000-00-00 00:00:00') ? Tools::displayDate($booking['date_from'], null, false) : '--'; $dateTo = ($booking['date_to'] && $booking['date_to'] !== '0000-00-00 00:00:00') ? Tools::displayDate($booking['date_to'], null, false) : '--'; - // Extra demands for this room - $autoAddedWithRoomPriceTaxExcl = 0; - $roomServiceRows = array(); + // Services for this booking + $bookingServices = array(); $objBookingDemand = new HotelBookingDemands(); $extraDemands = $objBookingDemand->getRoomTypeBookingExtraDemands( @@ -192,17 +166,11 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate ); if (is_array($extraDemands)) { foreach ($extraDemands as $demand) { - $roomServiceRows[] = array( - 'room_ref' => $roomReference, - 'check_in_date' => $dateFrom, - 'check_out_date' => $dateTo, - 'name' => $demand['name'], - 'quantity' => 1, - ); + $bookingServices[] = array('name' => $demand['name'], 'quantity' => 1); } } - // Auto-added services bundled with room price + // Auto-added services bundled with room price (for cost calculation only) $objServiceProductOrderDetail = new ServiceProductOrderDetail(); $autoAddedWithRoomPriceTaxExcl = (float)$objServiceProductOrderDetail->getRoomTypeServiceProducts( (int)$this->order->id, 0, 0, @@ -219,20 +187,21 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate ); if (isset($additionalServices[(int)$booking['id']]['additional_services'])) { foreach ($additionalServices[(int)$booking['id']]['additional_services'] as $service) { - $roomServiceRows[] = array( - 'room_ref' => $roomReference, - 'check_in_date' => $dateFrom, - 'check_out_date' => $dateTo, - 'name' => $service['name'], - 'quantity' => (int)$service['quantity'], - ); + $bookingServices[] = array('name' => $service['name'], 'quantity' => (int)$service['quantity']); } } - $serviceRows = array_merge($serviceRows, $roomServiceRows); + if (!empty($bookingServices)) { + $serviceGroups[] = array( + 'room_type_name' => isset($booking['room_type_name']) ? $booking['room_type_name'] : '', + 'check_in_date' => $dateFrom, + 'check_out_date' => $dateTo, + 'room_count' => $totalRooms, + 'services' => $bookingServices, + ); + } - $roomTotalTaxExcl = (float)$booking['total_price_tax_excl']; - $roomTotalWithAutoServicesTaxExcl = $roomTotalTaxExcl + $autoAddedWithRoomPriceTaxExcl; + $roomTotalWithAutoServicesTaxExcl = (float)$booking['total_price_tax_excl'] + $autoAddedWithRoomPriceTaxExcl; // Aggregate per room type + date range $roomTypeRowKey = implode('|', array( @@ -270,10 +239,9 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate } $roomTypeRows[$roomTypeRowKey]['rooms'] += 1; - $roomTypeRows[$roomTypeRowKey]['adults'] += $adults; - $roomTypeRows[$roomTypeRowKey]['children'] += $children; + $roomTypeRows[$roomTypeRowKey]['adults'] += (int)$booking['adults']; + $roomTypeRows[$roomTypeRowKey]['children'] += (int)$booking['children']; $roomTypeRows[$roomTypeRowKey]['total_price_tax_excl'] += $roomTotalWithAutoServicesTaxExcl; - $totalRooms++; } foreach ($roomTypeRows as &$roomTypeRow) { @@ -288,40 +256,81 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate } unset($roomTypeRow); - // --- Guest details --- - $guestName = ''; - $guestEmail = ''; - $guestPhone = ''; $guestAddress = ''; + $guest = new Customer((int)$this->order->id_customer); - $customer = new Customer((int)$this->order->id_customer); - if (Validate::isLoadedObject($customer)) { - $guestName = trim($customer->firstname.' '.$customer->lastname); - $guestEmail = $customer->email; - $guestPhone = $customer->phone; - } $addressId = (int)$this->order->id_address_invoice ?: (int)$this->order->id_address_delivery; if ($addressId) { $address = new Address($addressId); if (Validate::isLoadedObject($address)) { - $guestPhone = $address->phone_mobile ? $address->phone_mobile : $address->phone; $guestAddress = AddressFormat::generateAddress($address, array(), '
', ' '); } } - // Override with primary guest if this is a guest booking + if ($idCustomerGuestDetail = OrderCustomerGuestDetail::isCustomerGuestBooking((int)$this->order->id)) { - $traveler = new OrderCustomerGuestDetail((int)$idCustomerGuestDetail); - if (Validate::isLoadedObject($traveler)) { - $guestName = trim($traveler->firstname.' '.$traveler->lastname); - $guestEmail = $traveler->email; - $guestPhone = $traveler->phone; + $objGuest = new OrderCustomerGuestDetail((int)$idCustomerGuestDetail); + if (Validate::isLoadedObject($objGuest)) { + $guest = $objGuest; } } - // --- Totals --- - $footer = $this->getTotalsData($totalRooms, $totalRoomNights, $totalGuests); + $objBookingDemand = new HotelBookingDemands(); + $totalDemandsPriceTE = (float)$objBookingDemand->getRoomTypeBookingExtraDemands( + (int)$this->order->id, 0, 0, 0, 0, 1, 1, 0 + ); + $totalDemandsPriceTI = (float)$objBookingDemand->getRoomTypeBookingExtraDemands( + (int)$this->order->id, 0, 0, 0, 0, 1, 1, 1 + ); + + $orderDetails = OrderDetail::getList((int)$this->order->id); + $idsOrderDetail = is_array($orderDetails) + ? array_unique(array_map('intval', array_column($orderDetails, 'id_order_detail'))) + : array(); + + $footer = array( + 'room_price_tax_excl' => 0, + 'service_products_price_tax_excl' => 0, + 'additional_service_price_tax_excl' => 0, + 'total_convenience_fee_te' => 0, + ); + $roomsCostTaxIncl = 0; + $serviceProductsCostTaxIncl = 0; + $additionalServicesCostTaxIncl = 0; + $convenienceFeeTaxIncl = 0; + + if (!empty($idsOrderDetail)) { + $footer['room_price_tax_excl'] = $this->order->getTotalProductsWithoutTaxes(false, true, null, null, null, $idsOrderDetail) + $this->order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 1, Product::PRICE_ADDITION_TYPE_WITH_ROOM, $idsOrderDetail); + $roomsCostTaxIncl = $this->order->getTotalProductsWithTaxes(false, true, null, null, null, $idsOrderDetail) + $this->order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 1, Product::PRICE_ADDITION_TYPE_WITH_ROOM, $idsOrderDetail); + $footer['service_products_price_tax_excl'] = $this->order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_STANDALONE, false, false, $idsOrderDetail) + $this->order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_HOTEL_STANDALONE, false, false, $idsOrderDetail); + $serviceProductsCostTaxIncl = $this->order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_STANDALONE, false, false, $idsOrderDetail) + $this->order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_HOTEL_STANDALONE, false, false, $idsOrderDetail); + $footer['additional_service_price_tax_excl'] = $this->order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 0, null, $idsOrderDetail) + $totalDemandsPriceTE; + $additionalServicesCostTaxIncl = $this->order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 0, null, $idsOrderDetail) + $totalDemandsPriceTI; + $footer['total_convenience_fee_te'] = $this->order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 1, Product::PRICE_ADDITION_TYPE_INDEPENDENT, $idsOrderDetail); + $convenienceFeeTaxIncl = $this->order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 1, Product::PRICE_ADDITION_TYPE_INDEPENDENT, $idsOrderDetail); + } + + $productDiscountsTaxIncl = (float)$this->order->total_discounts_tax_incl; + foreach ($this->order->getCartRules() as $cartRule) { + if (!empty($cartRule['free_shipping'])) { + $productDiscountsTaxIncl -= (float)$this->order->total_shipping_tax_incl; + } + } + if ($productDiscountsTaxIncl < 0) { + $productDiscountsTaxIncl = 0; + } + + $footer['total_without_discount_te'] = $footer['room_price_tax_excl'] + $footer['total_convenience_fee_te'] + $footer['additional_service_price_tax_excl'] + $footer['service_products_price_tax_excl']; + $totalWithoutDiscountTaxIncl = $roomsCostTaxIncl + $serviceProductsCostTaxIncl + $additionalServicesCostTaxIncl + $convenienceFeeTaxIncl; + $footer['total_tax_without_discount'] = max(0, $totalWithoutDiscountTaxIncl - $footer['total_without_discount_te']); + $grandTotal = max(0.0, $totalWithoutDiscountTaxIncl - $productDiscountsTaxIncl); + $footer['product_discounts_tax_incl'] = $productDiscountsTaxIncl; + $footer['total_paid_tax_incl'] = $grandTotal; + $footer['amount_due'] = Tools::ps_round( + max(0, $grandTotal - (float)$this->order->getTotalPaid()), + _PS_PRICE_COMPUTE_PRECISION_, + $this->order->round_mode + ); - // --- Payment --- $paymentMethods = array(); $payments = $this->order->getOrderPaymentDetail(); if (is_array($payments) && $payments) { @@ -336,48 +345,29 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate $idCurrency = isset($paymentDetail['id_currency']) ? (int)$paymentDetail['id_currency'] : (int)$this->order->id_currency; - $paymentMethods[] = $method.' - '.Tools::displayPrice($amount, $idCurrency); + $paymentMethods[] = $method.': '.Tools::displayPrice($amount, $idCurrency); } } $paymentMethod = $paymentMethods ? implode('

', $paymentMethods) : $this->order->payment; $data = array( 'order' => $this->order, - 'order_status' => $orderState['name'], + 'order_state' => $orderState, 'booking_date' => Tools::displayDate($this->order->date_add, null, false), 'issue_date' => Tools::displayDate(date('Y-m-d'), null, false), - // hotel / property - 'hotel_name' => $hotelName, - 'hotel_phone' => $hotelPhone, - 'hotel_email' => $hotelEmail, + 'hotel' => $objHotel, 'hotel_address' => $hotelAddress, - // guest - 'guest_name' => $guestName, - 'guest_email' => $guestEmail, - 'guest_phone' => $guestPhone, + 'guest' => $guest, 'guest_address' => $guestAddress, 'total_guests' => (int)$totalGuests, - // booking summary 'total_rooms' => (int)$totalRooms, 'total_room_nights' => (int)$totalRoomNights, - // room and service tables 'room_type_rows' => array_values($roomTypeRows), - 'service_rows' => $serviceRows, - // payment + 'service_groups' => $serviceGroups, 'payment_method' => $paymentMethod, - 'payment_status' => $orderState['name'], - // totals — key named $footer to match invoice convention 'footer' => $footer, - // policies - 'check_in_time' => ($checkInTime && $checkInTime !== '--') - ? date('h:i a', strtotime($checkInTime)) : '--', - 'check_out_time' => ($checkOutTime && $checkOutTime !== '--') - ? date('h:i a', strtotime($checkOutTime)) : '--', - 'payment_policy' => $paymentPolicy - ? $paymentPolicy - : self::l('Please contact the property for policy details.'), + 'payment_policy' => $paymentPolicy, 'cancellation_policy' => $cancellationPolicy, - 'has_cancellation_policy' => (bool)trim(strip_tags($cancellationPolicy)), ); if (Tools::getValue('debug')) { @@ -388,120 +378,6 @@ class HTMLTemplateBookingVoucherCore extends HTMLTemplate return $this->smarty->fetch($this->getTemplate('booking-voucher')); } - /** - * Computes all cost totals using Order's own methods. - * Mirrors the $footer calculation in HTMLTemplateInvoice::getContent(). - * Extracted because it is 100+ lines of calculation that would obscure the data assembly above. - * - * @param int $totalRooms - * @param int $totalRoomNights - * @param int $totalGuests - * @return array - */ - protected function getTotalsData($totalRooms, $totalRoomNights, $totalGuests) - { - $objBookingDemand = new HotelBookingDemands(); - $totalDemandsPriceTE = (float)$objBookingDemand->getRoomTypeBookingExtraDemands( - (int)$this->order->id, 0, 0, 0, 0, 1, 1, 0 - ); - $totalDemandsPriceTI = (float)$objBookingDemand->getRoomTypeBookingExtraDemands( - (int)$this->order->id, 0, 0, 0, 0, 1, 1, 1 - ); - - $orderDetails = OrderDetail::getList((int)$this->order->id); - $idsOrderDetail = is_array($orderDetails) - ? array_unique(array_map('intval', array_column($orderDetails, 'id_order_detail'))) - : array(); - - $roomsCost = 0; - $roomsCostTaxIncl = 0; - $serviceProductsCost = 0; - $serviceProductsCostTaxIncl = 0; - $additionalServicesCost = 0; - $additionalServicesCostTaxIncl = 0; - $convenienceFee = 0; - $convenienceFeeTaxIncl = 0; - $productDiscountsTaxIncl = (float)$this->order->total_discounts_tax_incl; - - foreach ($this->order->getCartRules() as $cartRule) { - if (!empty($cartRule['free_shipping'])) { - $productDiscountsTaxIncl -= (float)$this->order->total_shipping_tax_incl; - } - } - - if ($productDiscountsTaxIncl < 0) { - $productDiscountsTaxIncl = 0; - } - - if (!empty($idsOrderDetail)) { - $roomsCost = (float)$this->order->getTotalProductsWithoutTaxes( - false, true, null, null, null, $idsOrderDetail - ) + (float)$this->order->getTotalProductsWithoutTaxes( - false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, - 1, Product::PRICE_ADDITION_TYPE_WITH_ROOM, $idsOrderDetail - ); - $roomsCostTaxIncl = (float)$this->order->getTotalProductsWithTaxes( - false, true, null, null, null, $idsOrderDetail - ) + (float)$this->order->getTotalProductsWithTaxes( - false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, - 1, Product::PRICE_ADDITION_TYPE_WITH_ROOM, $idsOrderDetail - ); - - $serviceProductsCost = (float)$this->order->getTotalProductsWithoutTaxes( - false, false, Product::SELLING_PREFERENCE_STANDALONE, false, false, $idsOrderDetail - ) + (float)$this->order->getTotalProductsWithoutTaxes( - false, false, Product::SELLING_PREFERENCE_HOTEL_STANDALONE, false, false, $idsOrderDetail - ); - $serviceProductsCostTaxIncl = (float)$this->order->getTotalProductsWithTaxes( - false, false, Product::SELLING_PREFERENCE_STANDALONE, false, false, $idsOrderDetail - ) + (float)$this->order->getTotalProductsWithTaxes( - false, false, Product::SELLING_PREFERENCE_HOTEL_STANDALONE, false, false, $idsOrderDetail - ); - - $additionalServicesCost = (float)$this->order->getTotalProductsWithoutTaxes( - false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 0, null, $idsOrderDetail - ) + $totalDemandsPriceTE; - $additionalServicesCostTaxIncl = (float)$this->order->getTotalProductsWithTaxes( - false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, 0, null, $idsOrderDetail - ) + $totalDemandsPriceTI; - - $convenienceFee = (float)$this->order->getTotalProductsWithoutTaxes( - false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, - 1, Product::PRICE_ADDITION_TYPE_INDEPENDENT, $idsOrderDetail - ); - $convenienceFeeTaxIncl = (float)$this->order->getTotalProductsWithTaxes( - false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE, - 1, Product::PRICE_ADDITION_TYPE_INDEPENDENT, $idsOrderDetail - ); - } - - $grandTotal = (float)$this->order->total_paid_tax_incl; - $totalPaidReal = (float)$this->order->getTotalPaid(); - $totalWithoutDiscountTaxExcl = $roomsCost + $serviceProductsCost + $additionalServicesCost + $convenienceFee; - $totalWithoutDiscountTaxIncl = $roomsCostTaxIncl + $serviceProductsCostTaxIncl + $additionalServicesCostTaxIncl + $convenienceFeeTaxIncl; - $totalTaxes = $totalWithoutDiscountTaxIncl - $totalWithoutDiscountTaxExcl; - if ($totalTaxes < 0) { - $totalTaxes = 0; - } - - $amountDue = $grandTotal - $totalPaidReal; - if ($amountDue < 0) { - $amountDue = 0; - } - - return array( - 'room_price_tax_excl' => $roomsCost, - 'service_products_price_tax_excl' => $serviceProductsCost, - 'additional_service_price_tax_excl' => $additionalServicesCost, - 'total_convenience_fee_te' => $convenienceFee, - 'total_without_discount_te' => $totalWithoutDiscountTaxExcl, - 'total_tax_without_discount' => $totalTaxes, - 'product_discounts_tax_incl' => $productDiscountsTaxIncl, - 'total_paid_tax_incl' => $grandTotal, - 'amount_due' => Tools::ps_round($amountDue, _PS_PRICE_COMPUTE_PRECISION_, $this->order->round_mode), - ); - } - /** * @return string */ diff --git a/pdf/booking-voucher.tpl b/pdf/booking-voucher.tpl index 68aab5051..cf6d0b966 100644 --- a/pdf/booking-voucher.tpl +++ b/pdf/booking-voucher.tpl @@ -23,362 +23,273 @@ {assign var=pdf_style_template value=$smarty.const._PS_PDF_DIR_|cat:'invoice.style-tab.tpl'} {include file=$pdf_style_template} -
{l s='PAYMENT INFORMATION' pdf='true'} {l s='TOTAL AMOUNT' pdf='true'}
- +
+ + + + + + + + + + + + {* *} + + + + + + + + + + + + {if $service_groups} - - - - - - - - - - - + - - - - - - + - + {/if} +
+ + + + + + + + + + + + + + + + + + + + + + +
{l s='BOOKING INFORMATION' pdf='true'}
{l s='Booking Reference' pdf='true'}{$order->reference|escape:'html':'UTF-8'}{l s='Booking Date' pdf='true'}{$booking_date|escape:'html':'UTF-8'}{l s='Voucher Issue Date' pdf='true'}{$issue_date|escape:'html':'UTF-8'}
{l s='Status' pdf='true'}{if $order_state && isset($order_state.name)}{$order_state.name|escape:'html':'UTF-8'}{/if}{l s='Total Rooms' pdf='true'}{$total_rooms|intval}{l s='Total Nights' pdf='true'}{$total_room_nights|intval}
+
 
+ + + + + + + + + + + + + + + + + + + + + + +
{l s='PROPERTY DETAILS' pdf='true'}
{l s='Property' pdf='true'}{if $hotel}{$hotel->hotel_name|escape:'html':'UTF-8'}{/if}
{l s='Email' pdf='true'}{if $hotel}{$hotel->email|escape:'html':'UTF-8'}{/if}
{l s='Phone' pdf='true'}{if $hotel}{$hotel->phone|escape:'html':'UTF-8'}{/if}
{l s='Address' pdf='true'}{if $hotel_address}{$hotel_address nofilter}{else}--{/if}
+
  + + + + + + + + + + + + + + + + + + + + + + + + + + +
{l s='GUEST INFORMATION' pdf='true'}
{l s='Primary Guest' pdf='true'}{$guest->firstname|escape:'html':'UTF-8'} {$guest->lastname|escape:'html':'UTF-8'}
{l s='Email' pdf='true'}{$guest->email|escape:'html':'UTF-8'}
{l s='Phone' pdf='true'}{$guest->phone|escape:'html':'UTF-8'}
{l s='Address' pdf='true'}{if $guest_address|strip_tags|trim}{$guest_address nofilter}{else}--{/if}
{l s='Total Guests' pdf='true'}{$total_guests|intval}
+
 
+ + + + + + + + + + + + + + + + + {if $room_type_rows} + {foreach from=$room_type_rows item=room_type_row} + {cycle values="color_line_even,color_line_odd" assign=bgcolor_class} + + + + + + + + + + {/foreach} + {else} + + + + {/if} + +
{l s='ROOM DETAILS' pdf='true'}
{l s='Room Description' pdf='true'}{l s='Tax Rate(s)' pdf='true'}{l s='Unit Price' pdf='true'}
{l s='(Tax excl.)' pdf='true'}
{l s='Rooms' pdf='true'}{l s='Check-in Date' pdf='true'}{l s='Check-out Date' pdf='true'}{l s='Total' pdf='true'}
{l s='(Tax excl.)' pdf='true'}
{$room_type_row.room_type_name|escape:'html':'UTF-8'}{$room_type_row.order_detail_tax_label}{displayPrice currency=$order->id_currency price=$room_type_row.unit_price_tax_excl} + {if $room_type_row.adults <= 9}0{/if}{$room_type_row.adults|intval} {if $room_type_row.adults > 1}{l s='Adults' pdf='true'}{else}{l s='Adult' pdf='true'}{/if}{if $room_type_row.children}, {if $room_type_row.children <= 9}0{/if}{$room_type_row.children|intval} {if $room_type_row.children > 1}{l s='Children' pdf='true'}{else}{l s='Child' pdf='true'}{/if}{/if}
{if $room_type_row.rooms <= 9}0{/if}{$room_type_row.rooms|intval} {if $room_type_row.rooms > 1}{l s='Rooms' pdf='true'}{else}{l s='Room' pdf='true'}{/if} +
{$room_type_row.check_in_date|escape:'html':'UTF-8'}{$room_type_row.check_out_date|escape:'html':'UTF-8'}{displayPrice currency=$order->id_currency price=$room_type_row.total_price_tax_excl}
{l s='No room details available.' pdf='true'}
+
{l s='BOOKING INFORMATION' pdf='true'}
{l s='Booking Reference' pdf='true'}{$order->reference|escape:'html':'UTF-8'}{l s='Booking Date' pdf='true'}{$booking_date|escape:'html':'UTF-8'}{l s='Voucher Issue Date' pdf='true'}{$issue_date|escape:'html':'UTF-8'} 
{l s='Status' pdf='true'}{$order_status|escape:'html':'UTF-8'}{l s='Total Rooms' pdf='true'}{$total_rooms|intval}{l s='Total Nights' pdf='true'}{$total_room_nights|intval} + + + + + + + + + + + {foreach from=$service_groups item=group} + {foreach from=$group.services item=service name=service_loop} + + {if $smarty.foreach.service_loop.first} + + {/if} + + + + {/foreach} + {/foreach} + +
{l s='SERVICES DETAILS' pdf='true'}
{l s='Room Type' pdf='true'}{l s='Service Name' pdf='true'}{l s='Qty' pdf='true'}
{$group.room_type_name|escape:'html':'UTF-8'}
+ {$group.check_in_date|escape:'html':'UTF-8'} - {$group.check_out_date|escape:'html':'UTF-8'}
+ {l s='Room' pdf='true'} - {$group.room_count|intval} +
{$service.name|escape:'html':'UTF-8'}{$service.quantity|intval}
+
- +
 
+ + - + +
+ + + + + + + + + + + + + + +
{l s='PAYMENT INFORMATION' pdf='true'}
{l s='Payment Method' pdf='true'}{$payment_method nofilter}
{l s='Payment Status' pdf='true'}{if $order_state && isset($order_state.name)}{$order_state.name|escape:'html':'UTF-8'}{/if}
+
+ + + + + + {if isset($footer.room_price_tax_excl) && $footer.room_price_tax_excl} + + + + + {/if} + {if isset($footer.service_products_price_tax_excl) && $footer.service_products_price_tax_excl} + + + + + {/if} + {if isset($footer.additional_service_price_tax_excl) && $footer.additional_service_price_tax_excl} + + + + + {/if} + {if isset($footer.total_convenience_fee_te) && $footer.total_convenience_fee_te} + + + + + {/if} + + + + + {if $footer.total_tax_without_discount > 0} + + + + + {/if} + {if isset($footer.product_discounts_tax_incl) && $footer.product_discounts_tax_incl > 0} + + + + + {/if} + + + + + {if $footer.amount_due > 0} + + + + + {/if} + +
{l s='TOTAL AMOUNT' pdf='true'}
{l s='Total Rooms Cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.room_price_tax_excl}
{l s='Total Products cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.service_products_price_tax_excl}
{l s='Extra Services Cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.additional_service_price_tax_excl}
{l s='Convenience Fee (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_convenience_fee_te}
{l s='Total (Tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_without_discount_te}
{l s='Total Tax' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_tax_without_discount}
{l s='Total Discounts' pdf='true'}- {displayPrice currency=$order->id_currency price=$footer.product_discounts_tax_incl}
{l s='Final Booking Amount' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_paid_tax_incl}
{l s='Due Amount' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.amount_due}
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{l s='PROPERTY DETAILS' pdf='true'}{l s='GUEST INFORMATION' pdf='true'}
{l s='Property' pdf='true'}{$hotel_name|escape:'html':'UTF-8'}{l s='Primary Guest' pdf='true'}{$guest_name|escape:'html':'UTF-8'}
{l s='Email' pdf='true'}{$hotel_email|escape:'html':'UTF-8'}{l s='Email' pdf='true'}{$guest_email|escape:'html':'UTF-8'}
{l s='Phone' pdf='true'}{$hotel_phone|escape:'html':'UTF-8'}{l s='Phone' pdf='true'}{$guest_phone|escape:'html':'UTF-8'}
{l s='Address' pdf='true'}{if $hotel_address}{$hotel_address nofilter}{else}--{/if}{l s='Address' pdf='true'}{if $guest_address|strip_tags|trim}{$guest_address nofilter}{else}--{/if}
{l s='Total Guests' pdf='true'}{$total_guests|intval}
- - - - - -
- - - - - - - - - - - - - - - - - - {if $room_type_rows} - {foreach from=$room_type_rows item=room_type_row} - {cycle values="color_line_even,color_line_odd" assign=bgcolor_class} - - - - - - - - - - {/foreach} - {else} - - - - {/if} - -
{l s='ROOM DETAILS' pdf='true'}
{l s='Room Description' pdf='true'}{l s='Tax Rate(s)' pdf='true'}{l s='Unit Price' pdf='true'}
{l s='(Tax excl.)' pdf='true'}
{l s='Rooms' pdf='true'}{l s='Check-in Date' pdf='true'}{l s='Check-out Date' pdf='true'}{l s='Total' pdf='true'}
{l s='(Tax excl.)' pdf='true'}
{$room_type_row.room_type_name|escape:'html':'UTF-8'}{$room_type_row.order_detail_tax_label}{displayPrice currency=$order->id_currency price=$room_type_row.unit_price_tax_excl} - {if $room_type_row.adults <= 9}0{/if}{$room_type_row.adults|intval} {if $room_type_row.adults > 1}{l s='Adults' pdf='true'}{else}{l s='Adult' pdf='true'}{/if}{if $room_type_row.children}, {if $room_type_row.children <= 9}0{/if}{$room_type_row.children|intval} {if $room_type_row.children > 1}{l s='Children' pdf='true'}{else}{l s='Child' pdf='true'}{/if}{/if}
{if $room_type_row.rooms <= 9}0{/if}{$room_type_row.rooms|intval} {if $room_type_row.rooms > 1}{l s='Rooms' pdf='true'}{else}{l s='Room' pdf='true'}{/if} -
{$room_type_row.check_in_date|escape:'html':'UTF-8'}{$room_type_row.check_out_date|escape:'html':'UTF-8'}{displayPrice currency=$order->id_currency price=$room_type_row.total_price_tax_excl}
{l s='No room details available.' pdf='true'}
- -{if $service_rows} - - - - -
- +{if $cancellation_policy} +
 
- - - - - - - - - - - - {foreach from=$service_rows item=service} - {cycle values="color_line_even,color_line_odd" assign=bgcolor_class} - - - - - - - {/foreach} - -
{l s='SERVICES DETAILS' pdf='true'}
{l s='Room Ref' pdf='true'}{l s='Check-in / Check-out' pdf='true'}{l s='Service Name' pdf='true'}{l s='Qty' pdf='true'}
{$service.room_ref|escape:'html':'UTF-8'}{$service.check_in_date|escape:'html':'UTF-8'} - {$service.check_out_date|escape:'html':'UTF-8'}{$service.name|escape:'html':'UTF-8'}{$service.quantity|intval}
-{/if} - - - - - -
- -{assign var=payment_method_rendered value=false} -{assign var=payment_status_rendered value=false} - - - - - - - - {if isset($footer.room_price_tax_excl) && $footer.room_price_tax_excl} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {/if} - {if isset($footer.service_products_price_tax_excl) && $footer.service_products_price_tax_excl} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {/if} - {if isset($footer.additional_service_price_tax_excl) && $footer.additional_service_price_tax_excl} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {/if} - {if isset($footer.total_convenience_fee_te) && $footer.total_convenience_fee_te} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {/if} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {if $footer.total_tax_without_discount > 0} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {/if} - {if isset($footer.product_discounts_tax_incl) && $footer.product_discounts_tax_incl > 0} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {/if} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {if $footer.amount_due > 0} - - {if !$payment_method_rendered} - - - {assign var=payment_method_rendered value=true} - {elseif !$payment_status_rendered} - - - {assign var=payment_status_rendered value=true} - {else} - - - {/if} - - - - - {/if} - -
{l s='PAYMENT INFORMATION' pdf='true'}{l s='TOTAL AMOUNT' pdf='true'}
{l s='Payment Method' pdf='true'}{$payment_method nofilter}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total Rooms Cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.room_price_tax_excl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total Products cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.service_products_price_tax_excl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Extra Services Cost (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.additional_service_price_tax_excl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Convenience Fee (tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_convenience_fee_te}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total (Tax excl.)' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_without_discount_te}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total Tax' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_tax_without_discount}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Total Discounts' pdf='true'}- {displayPrice currency=$order->id_currency price=$footer.product_discounts_tax_incl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Final Booking Amount' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.total_paid_tax_incl}
{l s='Payment Method' pdf='true'}{$payment_method|escape:'html':'UTF-8'}{l s='Payment Status' pdf='true'}{$payment_status|escape:'html':'UTF-8'}{l s='Due Amount' pdf='true'}{displayPrice currency=$order->id_currency price=$footer.amount_due}
- -{if $has_cancellation_policy} - - - - -
- - - - - @@ -386,26 +297,20 @@
{l s='CANCELLATION POLICY' pdf='true'}
{$cancellation_policy nofilter}
{/if} - - - - -
+
 
- + - - - + - + @@ -414,11 +319,9 @@
{l s='PROPERTY POLICIES' pdf='true'}
{l s='Check-in time' pdf='true'}{$check_in_time|escape:'html':'UTF-8'}{if $hotel && $hotel->check_in}{$hotel->check_in|escape:'html':'UTF-8'}{/if}
{l s='Check-out time' pdf='true'}{$check_out_time|escape:'html':'UTF-8'}{if $hotel && $hotel->check_out}{$hotel->check_out|escape:'html':'UTF-8'}{/if}
{l s='Policies' pdf='true'}
-{if isset($HOOK_DISPLAY_PDF) && $HOOK_DISPLAY_PDF} - - - - +{if isset($HOOK_DISPLAY_PDF) && $HOOK_DISPLAY_PDF|strip_tags|trim} +
 
+ From 476d3b5756bd26983e3ad6ac7c2ebc54e6cb78fb Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Fri, 29 May 2026 20:06:53 +0530 Subject: [PATCH 13/30] Fixed: Show correct check-in and check-out time in order detail page and invoice based on date format selected from languages from back-office Co-authored-by: Name --- classes/pdf/HTMLTemplateInvoice.php | 15 +++++++++++++-- controllers/admin/AdminOrdersController.php | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/classes/pdf/HTMLTemplateInvoice.php b/classes/pdf/HTMLTemplateInvoice.php index c23fdd280..7fd8aefae 100644 --- a/classes/pdf/HTMLTemplateInvoice.php +++ b/classes/pdf/HTMLTemplateInvoice.php @@ -363,6 +363,17 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate $objBookingDemand = new HotelBookingDemands(); foreach ($order_bk_data as $data_k => $data_v) { $date_join = strtotime($data_v['date_from']).strtotime($data_v['date_to']); + $display_date_from = $data_v['date_from']; + $display_date_to = $data_v['date_to']; + $objHotelBranchInformation = new HotelBranchInformation($data_v['id_hotel']); + if ($objHotelBranchInformation->check_in) { + $display_date_from = date('Y-m-d', strtotime($data_v['date_from'])) + . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_in)); + } + if ($objHotelBranchInformation->check_out) { + $display_date_to = date('Y-m-d', strtotime($data_v['date_to'])) + . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_out)); + } /*Product price when order was created*/ $order_details_obj = new OrderDetail($data_v['id_order_detail']); @@ -474,8 +485,8 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate $cart_htl_data[$type_key]['date_diff'][$date_join]['num_rm'] = 1; $fullDate = (isset($context->controller->show_full_date) && $context->controller->show_full_date && (date('Y-m-d', strtotime($data_v['date_from'])) == date('Y-m-d', strtotime($data_v['date_to'])))) ? true : false; - $cart_htl_data[$type_key]['date_diff'][$date_join]['data_form'] = Tools::displayDate($data_v['date_from'], null, $fullDate); - $cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = Tools::displayDate($data_v['date_to'], null, $fullDate); + $cart_htl_data[$type_key]['date_diff'][$date_join]['data_form'] = Tools::displayDate($display_date_from, null, $fullDate); + $cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = Tools::displayDate($display_date_to, null, $fullDate); $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']; diff --git a/controllers/admin/AdminOrdersController.php b/controllers/admin/AdminOrdersController.php index 8a3722b84..affff25da 100644 --- a/controllers/admin/AdminOrdersController.php +++ b/controllers/admin/AdminOrdersController.php @@ -3395,6 +3395,16 @@ class AdminOrdersControllerCore extends AdminController $objBookingDemand = new HotelBookingDemands(); $objHotelRoomType = new HotelRoomType(); foreach ($order_detail_data as $key => $value) { + $objHotelBranchInformation = new HotelBranchInformation($value['id_hotel']); + if ($objHotelBranchInformation->check_in) { + $order_detail_data[$key]['date_from'] = date('Y-m-d', strtotime($value['date_from'])) + . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_in)); + } + if ($objHotelBranchInformation->check_out) { + $order_detail_data[$key]['date_to'] = date('Y-m-d', strtotime($value['date_to'])) + . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_out)); + } + $order_detail_data[$key]['total_room_price_te'] = $value['total_price_tax_excl']; $order_detail_data[$key]['total_room_price_ti'] = $value['total_price_tax_incl']; @@ -3624,6 +3634,16 @@ class AdminOrdersControllerCore extends AdminController // get booking information by order $bookingOrderInfo = $objBookingDetail->getBookingDataByOrderId($order->id); foreach ($bookingOrderInfo as &$bookingOrderRoomInfo) { + $objHotelBranchInformation = new HotelBranchInformation($bookingOrderRoomInfo['id_hotel']); + if ($objHotelBranchInformation->check_in) { + $bookingOrderRoomInfo['date_from'] = date('Y-m-d', strtotime($bookingOrderRoomInfo['date_from'])) + . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_in)); + } + if ($objHotelBranchInformation->check_out) { + $bookingOrderRoomInfo['date_to'] = date('Y-m-d', strtotime($bookingOrderRoomInfo['date_to'])) + . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_out)); + } + // Get last refund request for booking if ($bookingRefundDetail = OrderReturn::getOrdersReturnDetail($bookingOrderRoomInfo['id_order'], 0, $bookingOrderRoomInfo['id'])) { $bookingRefundDetail = reset($bookingRefundDetail); From c3abb0d067c7f69c8775db208f447f08f378ce78 Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Tue, 2 Jun 2026 16:30:34 +0530 Subject: [PATCH 14/30] Updated: spacing issue --- .../hotelreservationsystem/views/templates/hook/copyRight.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl b/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl index 15906d128..eb3541799 100644 --- a/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl +++ b/modules/hotelreservationsystem/views/templates/hook/copyRight.tpl @@ -1,5 +1,5 @@
\ No newline at end of file From d3d0ba26ee336871319cb47abe3ce7fc8d7a5c5e Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Tue, 2 Jun 2026 18:56:53 +0530 Subject: [PATCH 15/30] Fixed: Display dynamic check-in and check-out times in invoice PDF based on room booking status, and remove changes from order detail page Co-authored-by: Name --- classes/pdf/HTMLTemplateInvoice.php | 40 +++++++++++++-------- controllers/admin/AdminOrdersController.php | 20 ----------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/classes/pdf/HTMLTemplateInvoice.php b/classes/pdf/HTMLTemplateInvoice.php index 7fd8aefae..7c79514f7 100644 --- a/classes/pdf/HTMLTemplateInvoice.php +++ b/classes/pdf/HTMLTemplateInvoice.php @@ -363,17 +363,6 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate $objBookingDemand = new HotelBookingDemands(); foreach ($order_bk_data as $data_k => $data_v) { $date_join = strtotime($data_v['date_from']).strtotime($data_v['date_to']); - $display_date_from = $data_v['date_from']; - $display_date_to = $data_v['date_to']; - $objHotelBranchInformation = new HotelBranchInformation($data_v['id_hotel']); - if ($objHotelBranchInformation->check_in) { - $display_date_from = date('Y-m-d', strtotime($data_v['date_from'])) - . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_in)); - } - if ($objHotelBranchInformation->check_out) { - $display_date_to = date('Y-m-d', strtotime($data_v['date_to'])) - . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_out)); - } /*Product price when order was created*/ $order_details_obj = new OrderDetail($data_v['id_order_detail']); @@ -484,9 +473,32 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate $num_days = HotelHelper::getNumberOfDays($data_v['date_from'], $data_v['date_to']); $cart_htl_data[$type_key]['date_diff'][$date_join]['num_rm'] = 1; - $fullDate = (isset($context->controller->show_full_date) && $context->controller->show_full_date && (date('Y-m-d', strtotime($data_v['date_from'])) == date('Y-m-d', strtotime($data_v['date_to'])))) ? true : false; - $cart_htl_data[$type_key]['date_diff'][$date_join]['data_form'] = Tools::displayDate($display_date_from, null, $fullDate); - $cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = Tools::displayDate($display_date_to, null, $fullDate); + $display_date_from = $data_v['date_from']; + $display_date_to = $data_v['date_to']; + if ((int)$data_v['id_status'] === HotelBookingDetail::STATUS_CHECKED_OUT) { + if ($data_v['check_in']) { + $display_date_from = $data_v['check_in']; + } + if ($data_v['check_out']) { + $display_date_to = $data_v['check_out']; + } + } elseif ((int)$data_v['id_status'] === HotelBookingDetail::STATUS_CHECKED_IN) { + if ($data_v['check_in']) { + $display_date_from = $data_v['check_in']; + } + if ($data_v['check_out_time']) { + $display_date_to = date('Y-m-d', strtotime($data_v['date_to'])) . ' ' . date('H:i:s', strtotime($data_v['check_out_time'])); + } + } else { + if ($data_v['check_in_time']) { + $display_date_from = date('Y-m-d', strtotime($data_v['date_from'])) . ' ' . date('H:i:s', strtotime($data_v['check_in_time'])); + } + if ($data_v['check_out_time']) { + $display_date_to = date('Y-m-d', strtotime($data_v['date_to'])) . ' ' . date('H:i:s', strtotime($data_v['check_out_time'])); + } + } + ppp($cart_htl_data[$type_key]['date_diff'][$date_join]['data_form'] = Tools::displayDate($display_date_from, null, true)); + ppp($cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = Tools::displayDate($display_date_to, null, true)); $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']; diff --git a/controllers/admin/AdminOrdersController.php b/controllers/admin/AdminOrdersController.php index affff25da..8a3722b84 100644 --- a/controllers/admin/AdminOrdersController.php +++ b/controllers/admin/AdminOrdersController.php @@ -3395,16 +3395,6 @@ class AdminOrdersControllerCore extends AdminController $objBookingDemand = new HotelBookingDemands(); $objHotelRoomType = new HotelRoomType(); foreach ($order_detail_data as $key => $value) { - $objHotelBranchInformation = new HotelBranchInformation($value['id_hotel']); - if ($objHotelBranchInformation->check_in) { - $order_detail_data[$key]['date_from'] = date('Y-m-d', strtotime($value['date_from'])) - . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_in)); - } - if ($objHotelBranchInformation->check_out) { - $order_detail_data[$key]['date_to'] = date('Y-m-d', strtotime($value['date_to'])) - . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_out)); - } - $order_detail_data[$key]['total_room_price_te'] = $value['total_price_tax_excl']; $order_detail_data[$key]['total_room_price_ti'] = $value['total_price_tax_incl']; @@ -3634,16 +3624,6 @@ class AdminOrdersControllerCore extends AdminController // get booking information by order $bookingOrderInfo = $objBookingDetail->getBookingDataByOrderId($order->id); foreach ($bookingOrderInfo as &$bookingOrderRoomInfo) { - $objHotelBranchInformation = new HotelBranchInformation($bookingOrderRoomInfo['id_hotel']); - if ($objHotelBranchInformation->check_in) { - $bookingOrderRoomInfo['date_from'] = date('Y-m-d', strtotime($bookingOrderRoomInfo['date_from'])) - . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_in)); - } - if ($objHotelBranchInformation->check_out) { - $bookingOrderRoomInfo['date_to'] = date('Y-m-d', strtotime($bookingOrderRoomInfo['date_to'])) - . ' ' . date('H:i:s', strtotime($objHotelBranchInformation->check_out)); - } - // Get last refund request for booking if ($bookingRefundDetail = OrderReturn::getOrdersReturnDetail($bookingOrderRoomInfo['id_order'], 0, $bookingOrderRoomInfo['id'])) { $bookingRefundDetail = reset($bookingRefundDetail); From 757c72de09d6ab1c732dc75082468488586b0439 Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Tue, 2 Jun 2026 19:05:51 +0530 Subject: [PATCH 16/30] Removed: extra code for debugging Co-authored-by: Name --- classes/pdf/HTMLTemplateInvoice.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/classes/pdf/HTMLTemplateInvoice.php b/classes/pdf/HTMLTemplateInvoice.php index 7c79514f7..e5b36b518 100644 --- a/classes/pdf/HTMLTemplateInvoice.php +++ b/classes/pdf/HTMLTemplateInvoice.php @@ -473,6 +473,7 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate $num_days = HotelHelper::getNumberOfDays($data_v['date_from'], $data_v['date_to']); $cart_htl_data[$type_key]['date_diff'][$date_join]['num_rm'] = 1; + $fullDate = (isset($context->controller->show_full_date) && $context->controller->show_full_date && (date('Y-m-d', strtotime($data_v['date_from'])) == date('Y-m-d', strtotime($data_v['date_to'])))) ? true : false; $display_date_from = $data_v['date_from']; $display_date_to = $data_v['date_to']; if ((int)$data_v['id_status'] === HotelBookingDetail::STATUS_CHECKED_OUT) { @@ -497,8 +498,8 @@ class HTMLTemplateInvoiceCore extends HTMLTemplate $display_date_to = date('Y-m-d', strtotime($data_v['date_to'])) . ' ' . date('H:i:s', strtotime($data_v['check_out_time'])); } } - ppp($cart_htl_data[$type_key]['date_diff'][$date_join]['data_form'] = Tools::displayDate($display_date_from, null, true)); - ppp($cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = Tools::displayDate($display_date_to, null, true)); + $cart_htl_data[$type_key]['date_diff'][$date_join]['data_form'] = Tools::displayDate($display_date_from, null, true); + $cart_htl_data[$type_key]['date_diff'][$date_join]['data_to'] = Tools::displayDate($display_date_to, null, true); $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']; From 65cd0ff1edb22fb15f479ecf1697029c09340289 Mon Sep 17 00:00:00 2001 From: Amit sharma Date: Wed, 3 Jun 2026 19:38:26 +0530 Subject: [PATCH 17/30] fix: resolve invalid price display on product page for catalog rule base price --- classes/Cart.php | 2 +- classes/SpecificPrice.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/Cart.php b/classes/Cart.php index ad5ee4302..f12f41249 100644 --- a/classes/Cart.php +++ b/classes/Cart.php @@ -1833,7 +1833,7 @@ class CartCore extends ObjectModel $this->id, [], 0, - isset($product['id_hotel_cart_booking']) ? $product['id_hotel_cart_booking'] : null, + null, null, (int)$product['id_product'], null, diff --git a/classes/SpecificPrice.php b/classes/SpecificPrice.php index 1dd3829a2..79c63995e 100644 --- a/classes/SpecificPrice.php +++ b/classes/SpecificPrice.php @@ -367,12 +367,12 @@ class SpecificPriceCore extends ObjectModel `id_shop` '.self::formatIntInQuery(0, $id_shop).' AND `id_currency` '.self::formatIntInQuery(0, $id_currency).' AND `id_country` '.self::formatIntInQuery(0, $id_country). - ' AND `id_htl_cart_booking`='.(int)$id_htl_cart_booking.' '. + ' AND `id_htl_cart_booking` '.self::formatIntInQuery(0, $id_htl_cart_booking).' '. (Group::isFeatureActive() ? ' AND `id_group` '.self::formatIntInQuery(0, $id_group) : '').' '.$query_extra.' AND IF(`from_quantity` > 1, `from_quantity`, 0) <= '; $query .= (Configuration::get('PS_QTY_DISCOUNT_ON_COMBINATION') || !$id_cart || !$real_quantity) ? (int)$quantity : max(1, (int)$real_quantity); - $query .= ' ORDER BY `id_product_attribute` DESC, `from_quantity` DESC, `score` DESC, `to` DESC, `from` DESC'; + $query .= ' ORDER BY `id_htl_cart_booking` DESC, `id_product_attribute` DESC, `from_quantity` DESC, `score` DESC, `to` DESC, `from` DESC'; SpecificPrice::$_specificPriceCache[$key] = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow($query); } From 2bd11e0e99427a5836728b41c6030000cf9cfa0a Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Thu, 4 Jun 2026 16:23:01 +0530 Subject: [PATCH 18/30] Fixed: advance payment was added for the number of rooms added in cart in for loop which lead to Incorrect Total Amount Calculation Co-authored-by: Name --- classes/Cart.php | 90 +++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/classes/Cart.php b/classes/Cart.php index ad5ee4302..9cf5a1caf 100644 --- a/classes/Cart.php +++ b/classes/Cart.php @@ -1862,53 +1862,55 @@ class CartCore extends ObjectModel // by webkul to calculate rates of the product from hotelreservation syatem tables with feature prices.... $totalPriceByProduct = 0; $priceDisplay = Group::getPriceDisplayMethod(Group::getCurrent()->id); - foreach ($roomTypesByIdProduct as $key => $cartRoomInfo) { - $occupancy = array( - array( - 'adults' => $cartRoomInfo['adults'], - 'children' => $cartRoomInfo['children'], - 'child_ages' => json_decode($cartRoomInfo['child_ages']) - ) + + if ($type == Cart::ADVANCE_PAYMENT || $type == Cart::ADVANCE_PAYMENT_ONLY_PRODUCTS) { + // getProductMinAdvPaymentAmountByIdCart already aggregates all rooms for this product internally, + // so call it once and add once — not inside the per-room loop. + $advProductPrice = $objAdvPayment->getProductMinAdvPaymentAmountByIdCart( + $this->id, + $product['id_product'], + 0, + 0, + $with_taxes ); - // get the real price of the room type - $roomTotalPrice = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( - $cartRoomInfo['id_product'], - $cartRoomInfo['date_from'], - $cartRoomInfo['date_to'], - $occupancy, - Group::getCurrent()->id, - $cartRoomInfo['id_cart'], - $cartRoomInfo['id_guest'], - $cartRoomInfo['id_room'], - 0 - ); - if ($with_taxes) { - $totalPriceByProduct = $roomTotalPrice['total_price_tax_incl']; - } else { - $totalPriceByProduct = $roomTotalPrice['total_price_tax_excl']; - } - - // If customer has selected advance payment of the cart - if ($type == Cart::ADVANCE_PAYMENT || $type == Cart::ADVANCE_PAYMENT_ONLY_PRODUCTS) { - $advProductPrice = $objAdvPayment->getProductMinAdvPaymentAmountByIdCart( - $this->id, - $cartRoomInfo['id_product'], - 0, - 0, - $with_taxes - ); - - // set advance payment price only if it is greater than real price - if ($advProductPrice < $totalPriceByProduct) { - $totalPriceByProduct = $advProductPrice; - } - } - - // Rounding as per configurations if ($ps_round_type == Order::ROUND_TOTAL) { - $products_total[$id_tax_rules_group.'_'.$id_address] += Tools::processPriceRounding($totalPriceByProduct); + $products_total[$id_tax_rules_group.'_'.$id_address] += Tools::processPriceRounding($advProductPrice); } else { - $products_total[$id_tax_rules_group] += Tools::processPriceRounding($totalPriceByProduct); + $products_total[$id_tax_rules_group] += Tools::processPriceRounding($advProductPrice); + } + } else { + foreach ($roomTypesByIdProduct as $key => $cartRoomInfo) { + $occupancy = array( + array( + 'adults' => $cartRoomInfo['adults'], + 'children' => $cartRoomInfo['children'], + 'child_ages' => json_decode($cartRoomInfo['child_ages']) + ) + ); + // get the real price of the room type + $roomTotalPrice = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( + $cartRoomInfo['id_product'], + $cartRoomInfo['date_from'], + $cartRoomInfo['date_to'], + $occupancy, + Group::getCurrent()->id, + $cartRoomInfo['id_cart'], + $cartRoomInfo['id_guest'], + $cartRoomInfo['id_room'], + 0 + ); + if ($with_taxes) { + $totalPriceByProduct = $roomTotalPrice['total_price_tax_incl']; + } else { + $totalPriceByProduct = $roomTotalPrice['total_price_tax_excl']; + } + + // Rounding as per configurations + if ($ps_round_type == Order::ROUND_TOTAL) { + $products_total[$id_tax_rules_group.'_'.$id_address] += Tools::processPriceRounding($totalPriceByProduct); + } else { + $products_total[$id_tax_rules_group] += Tools::processPriceRounding($totalPriceByProduct); + } } } } From 0dc272fbff950b51f40f4b2db087d478290ac5e0 Mon Sep 17 00:00:00 2001 From: Amit sharma Date: Thu, 4 Jun 2026 17:00:19 +0530 Subject: [PATCH 19/30] fix: preserve service product price in cart total after occupancy update --- controllers/admin/AdminCartsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/admin/AdminCartsController.php b/controllers/admin/AdminCartsController.php index f76331422..8252c7fa7 100644 --- a/controllers/admin/AdminCartsController.php +++ b/controllers/admin/AdminCartsController.php @@ -1121,7 +1121,7 @@ class AdminCartsControllerCore extends AdminController 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']); + $bookingInfo['total_price'] = Tools::displayPrice($amtWithQty + $bookingInfo['demand_price'] + $bookingInfo['additional_service_price'] + $bookingInfo['additional_services_auto_add_price']); $response = array( 'curr_booking_info' => $bookingInfo, 'cart_info' => $this->ajaxReturnVars(), From 041fa626b0a9460401e698871f0f642f0905f1b8 Mon Sep 17 00:00:00 2001 From: varun kewlani Date: Thu, 4 Jun 2026 17:25:14 +0530 Subject: [PATCH 20/30] Fixed: show serial wise room number in Room Extra Services Detail in invoice pdf Co-authored-by: Name --- pdf/invoice.extra-demands-tab.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdf/invoice.extra-demands-tab.tpl b/pdf/invoice.extra-demands-tab.tpl index e7edf7fa4..cba5a8aed 100644 --- a/pdf/invoice.extra-demands-tab.tpl +++ b/pdf/invoice.extra-demands-tab.tpl @@ -114,8 +114,8 @@ {/foreach} {/if} + {assign var=roomCount value=$roomCount+1} {/foreach} - {assign var=roomCount value=$roomCount+1}
{$HOOK_DISPLAY_PDF}
From 4b1f340b319d7bd856a02cb9695de3410d7a2d0b Mon Sep 17 00:00:00 2001 From: Amit sharma Date: Thu, 4 Jun 2026 18:24:10 +0530 Subject: [PATCH 21/30] fix: make rating field optional for hotels --- .../hotelreservationsystem/classes/HotelBranchInformation.php | 4 ++-- .../controllers/admin/AdminAddHotelController.php | 4 ---- .../views/templates/admin/add_hotel/helpers/form/form.tpl | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/modules/hotelreservationsystem/classes/HotelBranchInformation.php b/modules/hotelreservationsystem/classes/HotelBranchInformation.php index b4fb0ada8..0797c7035 100644 --- a/modules/hotelreservationsystem/classes/HotelBranchInformation.php +++ b/modules/hotelreservationsystem/classes/HotelBranchInformation.php @@ -51,7 +51,7 @@ class HotelBranchInformation extends ObjectModel 'fields' => array( 'id_category' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), 'email' => array('type' => self::TYPE_STRING,'validate' => 'isEmail', 'size' => 255, 'required' => true), - 'rating' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true), + 'rating' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'), 'check_in' => array('type' => self::TYPE_STRING, 'required' => true), 'check_out' => array('type' => self::TYPE_STRING, 'required' => true), 'active' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool'), @@ -1160,7 +1160,7 @@ class HotelBranchInformation extends ObjectModel { // set additional hotel address and restriction fields validations here if (isset($this->webservice_validation) && $this->webservice_validation) { - if ($this->rating < 1 || $this->rating > 5) { + if ($this->rating && ($this->rating < 1 || $this->rating > 5)) { $message = Tools::displayError('Rating must be between 1 and 5.'); } elseif (!strtotime($this->check_in)) { $message = Tools::displayError('Check In time is invalid.'); diff --git a/modules/hotelreservationsystem/controllers/admin/AdminAddHotelController.php b/modules/hotelreservationsystem/controllers/admin/AdminAddHotelController.php index f804b11ba..868012630 100644 --- a/modules/hotelreservationsystem/controllers/admin/AdminAddHotelController.php +++ b/modules/hotelreservationsystem/controllers/admin/AdminAddHotelController.php @@ -391,10 +391,6 @@ class AdminAddHotelController extends ModuleAdminController $this->errors[] = $this->l('Check Out time must be before Check In time.'); } - if (!$rating) { - $this->errors[] = $this->l('Rating is required field.'); - } - if (!$address = trim($address)) { $this->errors[] = $this->l('Address is required field.'); } diff --git a/modules/hotelreservationsystem/views/templates/admin/add_hotel/helpers/form/form.tpl b/modules/hotelreservationsystem/views/templates/admin/add_hotel/helpers/form/form.tpl index 22ae63e61..978872548 100644 --- a/modules/hotelreservationsystem/views/templates/admin/add_hotel/helpers/form/form.tpl +++ b/modules/hotelreservationsystem/views/templates/admin/add_hotel/helpers/form/form.tpl @@ -214,11 +214,11 @@
- +
+ {if !empty($tab.has_kpi)} + +   » {l s='KPI'} + + + + + + + + + + + {/if} {foreach $tabs AS $child} {if $child.id_parent === $tab.id_tab} {if isset($access[$child.id_tab])} @@ -372,6 +439,32 @@ + {if !empty($child.has_kpi)} + +   » {l s='KPI'} + + + + + + + + + + + {/if} {/if} {/if} {/foreach} @@ -438,4 +531,4 @@
{/foreach} -
+
\ No newline at end of file diff --git a/admin/themes/default/template/controllers/customer_threads/helpers/view/view.tpl b/admin/themes/default/template/controllers/customer_threads/helpers/view/view.tpl index b42bea915..471eeb0ba 100644 --- a/admin/themes/default/template/controllers/customer_threads/helpers/view/view.tpl +++ b/admin/themes/default/template/controllers/customer_threads/helpers/view/view.tpl @@ -36,6 +36,7 @@ {/if} + {if $can_edit}
{foreach $actions as $action} @@ -48,6 +49,7 @@
+ {/if}
@@ -97,6 +99,7 @@ {/foreach}
+{if $can_edit}

{l s="Your answer to"} {if isset($customer->firstname)}{$customer->firstname|escape:'html':'UTF-8'} {$customer->lastname|escape:'html':'UTF-8'} {else} {$thread->email}{/if}

@@ -125,7 +128,7 @@
- +{/if} {if count($timeline_items)}

diff --git a/admin/themes/default/template/controllers/modules/js.tpl b/admin/themes/default/template/controllers/modules/js.tpl index d91c1b3bb..79097c46a 100644 --- a/admin/themes/default/template/controllers/modules/js.tpl +++ b/admin/themes/default/template/controllers/modules/js.tpl @@ -48,21 +48,65 @@ function modules_management(action) { var modules = document.getElementsByName('modules'); - var module_list = ''; + var module_list = []; for (var i = 0; i < modules.length; i++) { - if (modules[i].checked == true) + if (modules[i].checked) { - rel = modules[i].getAttribute('data-rel'); + var rel = modules[i].getAttribute('data-rel'); if (rel != "false" && action == "uninstall") { if (!confirm(rel)) return false; } - module_list += '|'+modules[i].value; + module_list.push(modules[i].value); } } - document.location.href=currentIndex+'&token='+token+'&'+action+'='+module_list.substring(1, module_list.length); + + module_list = encodeURIComponent(module_list.join('|')); + if (action == 'enable' || action == 'disable') { + var type = action == 'disable' ? 0 : 1; + document.location.href = currentIndex+'&token='+token+'&enable='+type+'&module_name='+module_list; + return false; + } + + document.location.href = currentIndex+'&token='+token+'&'+action+'='+module_list; + return false; + } + + function filter_modules_by_name(table, moduleName) + { + var moduleNames = moduleName.split('|'); + var filteredNames = []; + for (var i = 0; i < moduleNames.length; i++) { + moduleNames[i] = $.trim(moduleNames[i]); + if (moduleNames[i] !== '') { + filteredNames.push(moduleNames[i].toLowerCase()); + } + } + + if (!filteredNames.length) { + return; + } + + if (filteredNames.length === 1) { + $.uiTableFilter(table, filteredNames[0]); + return; + } + + table.find('tbody tr').each(function() { + var rowText = $(this).text().toLowerCase(); + var isMatch = false; + + for (var i = 0; i < filteredNames.length; i++) { + if (rowText.indexOf(filteredNames[i]) !== -1) { + isMatch = true; + break; + } + } + + $(this).toggle(isMatch); + }); } $('document').ready( function() { @@ -71,7 +115,7 @@ $.uiTableFilter($('#moduleContainer').find('table'), anchor); if (module_name != '') - $.uiTableFilter($('#moduleContainer').find('table'), module_name); + filter_modules_by_name($('#moduleContainer').find('table'), module_name); $('#moduleQuicksearch').on('keyup', function(){ val = this.value; diff --git a/admin/themes/default/template/controllers/modules/list.tpl b/admin/themes/default/template/controllers/modules/list.tpl index 6166962a4..9fab8630b 100644 --- a/admin/themes/default/template/controllers/modules/list.tpl +++ b/admin/themes/default/template/controllers/modules/list.tpl @@ -190,6 +190,19 @@

@@ -44,7 +44,7 @@
{include file="controllers/products/multishop/checkbox.tpl" field="meta_description" type="default" multilang="true"}
@@ -53,7 +53,7 @@ languages=$languages input_name='meta_description' input_value=$product->meta_description - maxchar=160 + maxchar=255 } @@ -91,16 +91,11 @@ -
-
- {foreach from=$languages item=language} -
- {l s='The product link will look like this:'}
- {if isset($rewritten_links[$language.id_lang][0])}{$rewritten_links[$language.id_lang][0]|escape:'html':'UTF-8'}{/if}{if isset($product->id)}{$product->link_rewrite[$language.id_lang]|escape:'html':'UTF-8'}{/if}{if isset($rewritten_links[$language.id_lang][1])}{$rewritten_links[$language.id_lang][1]|escape:'html':'UTF-8'}{/if} -
- {/foreach} -
-
+ {include file="seo_preview.tpl" + languages = $languages + preview_link = $rewritten_links|default:'' + inputs = ['meta_title'=>$product->meta_title ,'meta_description' => $product->meta_description,'link_rewrite' => $product->link_rewrite, 'name' => $product->name, 'description_short' => $product->description_short] + }
@@ -61,6 +61,10 @@ {/foreach} }); {/if} - $(".textarea-autosize").autosize(); + $(".textarea-autosize").each(function() { + var $ta = $(this), val = $ta.val(); + $ta.val('').autosize(); + if (val) { $ta.val(val).trigger('input'); } + }); diff --git a/admin/themes/default/template/controllers/orders/_cart_booking_demands.tpl b/admin/themes/default/template/controllers/orders/_cart_booking_demands.tpl index c07968f9d..fad6ca46e 100644 --- a/admin/themes/default/template/controllers/orders/_cart_booking_demands.tpl +++ b/admin/themes/default/template/controllers/orders/_cart_booking_demands.tpl @@ -32,9 +32,7 @@ {if isset($selectedRoomDemands) && $selectedRoomDemands} {/if} - {if isset($serviceProducts) && $serviceProducts} -
  • {l s='Services'}
  • - {/if} +
  • {l s='Services'}
  • {if isset($selectedRoomDemands) && $selectedRoomDemands} diff --git a/admin/themes/default/template/controllers/orders/form.tpl b/admin/themes/default/template/controllers/orders/form.tpl index 0451cd31d..1482462e9 100644 --- a/admin/themes/default/template/controllers/orders/form.tpl +++ b/admin/themes/default/template/controllers/orders/form.tpl @@ -1572,6 +1572,7 @@ id_room: idRoom, id_cart: idCart, id_product: idProduct, + id_customer: id_customer, id_hotel_cart_booking: idHotelCartBooking, action: 'getRoomTypeCartDemands', ajax: true @@ -1877,7 +1878,7 @@ {l s='Customer'}
    -
    +
    id_customer) && $cart->id_customer && isset($customer) && $customer }style="display:none;"{/if} class="form-group">
    -
    +
    + {if isset($cart->id_customer) && $cart->id_customer && isset($customer) && $customer } +
    +
    +
    + {$customer->firstname} {$customer->lastname} #{$customer->id} +
    + {$customer->email}
    +
    + +
    +
    + {/if} +
    {*
    {include file='controllers/orders/_current_cart_details_data.tpl'} - @@ -451,7 +456,7 @@ {l s='Transaction ID'} {l s='Amount'} {l s='Invoice'} - + @@ -463,6 +468,11 @@ {$payment['transaction_id']|escape:'html':'UTF-8'} {displayPrice price=$payment['real_paid_amount'] currency=$payment['id_currency']} {if isset($payment['invoice_number'])}{$payment['invoice_number']}{else}--{/if} + {if isset($receipt_management_active) && $receipt_management_active} + + {l s='Receipt'} + + {/if} {l s='Details'} @@ -707,7 +717,7 @@ {if isset($messages) && $messages}
    -  {l s='Order Private Notes'} +  {l s='Order Notes'}
    {foreach from=$messages item=message name=customerMessage} @@ -720,11 +730,11 @@ {$message['elastname']|escape:'html':'UTF-8'}{else}{$message['cfirstname']|escape:'html':'UTF-8'} {$message['clastname']|escape:'html':'UTF-8'} {/if},   - {dateFormat date=$message['date_add']} + {dateFormat date=$message.date_add full=1} - {if ($message['private'] == 1)} + {* {if ($message['private'] == 1)} {l s='Private'} - {/if} + {/if} *}

    {if !$smarty.foreach.customerMessage.last}
    {/if} @@ -1302,7 +1312,30 @@ {/if}
    - + {* Css for handling order disable status modal *} + + {strip} {addJsDefL name=no_rm_avail_txt}{l s='No room available.' js=1}{/addJsDefL} {addJsDefL name=no_realloc_rm_avail_txt}{l s='No room available for reallocation.' js=1}{/addJsDefL} @@ -1335,7 +1368,6 @@ {addJsDefL name='invalid_occupancy_txt'}{l s='Invalid occupancy(adults/children) found.' js=1}{/addJsDefL} {addJsDefL name='select_room_txt'}{l s='Select room' 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'} {addJsDef ROOM_STATUS_CHECKED_IN=$ROOM_STATUS_CHECKED_IN|escape:'quotes':'UTF-8'} {addJsDef ROOM_STATUS_CHECKED_OUT=$ROOM_STATUS_CHECKED_OUT|escape:'quotes':'UTF-8'} {addJsDef ALLOTMENT_MANUAL=$ALLOTMENT_MANUAL|escape:'quotes':'UTF-8'} @@ -1346,4 +1378,4 @@ {addJsDefL name=txt_booking_document_delete_success}{l s='Document deleted successfully.' js=1}{/addJsDefL} {addJsDefL name='error_found_txt'}{l s='Errors found' js=1}{/addJsDefL} {/strip} -{/block} +{/block} \ No newline at end of file diff --git a/admin/themes/default/template/controllers/orders/modals/_add_room_booking.tpl b/admin/themes/default/template/controllers/orders/modals/_add_room_booking.tpl index b9e6524cf..c9d897d84 100644 --- a/admin/themes/default/template/controllers/orders/modals/_add_room_booking.tpl +++ b/admin/themes/default/template/controllers/orders/modals/_add_room_booking.tpl @@ -128,6 +128,12 @@ {/if} +
    +
    + + +
    +
    {if isset($invoices_collection) && sizeof($invoices_collection)} +
    +
    + + +
    +
    + +
    +
    + + +
    +
    - \ No newline at end of file diff --git a/admin/themes/default/template/controllers/products/helpers/form/form.tpl b/admin/themes/default/template/controllers/products/helpers/form/form.tpl index 5a97a1cec..6b1030882 100644 --- a/admin/themes/default/template/controllers/products/helpers/form/form.tpl +++ b/admin/themes/default/template/controllers/products/helpers/form/form.tpl @@ -218,6 +218,7 @@ $('.product-tab-content').on('displayed', function(e) { $('#toolbar-footer').appendTo($(this).children('.product-tab')); + $(window).trigger('resize.autosize'); }); }); diff --git a/admin/themes/default/template/controllers/products/informations.tpl b/admin/themes/default/template/controllers/products/informations.tpl index cb97d2652..c8716ea36 100644 --- a/admin/themes/default/template/controllers/products/informations.tpl +++ b/admin/themes/default/template/controllers/products/informations.tpl @@ -104,7 +104,7 @@
    {include file="controllers/products/multishop/checkbox.tpl" field="name" type="default" multilang="true"}
    diff --git a/admin/themes/default/template/controllers/products/input_text_lang.tpl b/admin/themes/default/template/controllers/products/input_text_lang.tpl index 81f63038a..7f92a362c 100644 --- a/admin/themes/default/template/controllers/products/input_text_lang.tpl +++ b/admin/themes/default/template/controllers/products/input_text_lang.tpl @@ -39,7 +39,6 @@ class="form-control {if isset($input_class)}{$input_class} {/if}" name="{$input_name}_{$language.id_lang}" value="{$input_value[$language.id_lang]|default:''|escape:'html'}" - onkeyup="if (isArrowKey(event)) return ;updateFriendlyURL();" onblur="updateLinkRewrite();" {if isset($required)} required="required"{/if} {if isset($maxchar)} data-maxchar="{$maxchar}"{/if} diff --git a/admin/themes/default/template/controllers/products/seo.tpl b/admin/themes/default/template/controllers/products/seo.tpl index 8544f75a1..062d3ee5a 100644 --- a/admin/themes/default/template/controllers/products/seo.tpl +++ b/admin/themes/default/template/controllers/products/seo.tpl @@ -30,7 +30,7 @@
    {include file="controllers/products/multishop/checkbox.tpl" field="meta_title" type="default" multilang="true"}
    @@ -39,7 +39,7 @@ languages=$languages input_name='meta_title' input_value=$product->meta_title - maxchar=70 + maxchar=128 }
    @@ -47,7 +47,7 @@
    {include file="controllers/products/multishop/checkbox.tpl" field="meta_description" type="default" multilang="true"}
    @@ -56,7 +56,7 @@ languages=$languages input_name='meta_description' input_value=$product->meta_description - maxchar=160 + maxchar=255 } @@ -94,16 +94,11 @@ -
    -
    - {foreach from=$languages item=language} -
    - {l s='The product link will look like this:'}
    - {if isset($rewritten_links[$language.id_lang][0])}{$rewritten_links[$language.id_lang][0]|escape:'html':'UTF-8'}{/if}{if isset($product->id)}{$product->link_rewrite[$language.id_lang]|escape:'html':'UTF-8'}{/if}{if isset($rewritten_links[$language.id_lang][1])}{$rewritten_links[$language.id_lang][1]|escape:'html':'UTF-8'}{/if} -
    - {/foreach} -
    -
    + {include file="seo_preview.tpl" + languages = $languages + preview_link = $rewritten_links|default:'' + inputs = ['meta_title'=>$product->meta_title ,'meta_description' => $product->meta_description,'link_rewrite' => $product->link_rewrite, 'name' => $product->name, 'description_short' => $product->description_short] + } @@ -64,6 +64,10 @@ {/foreach} }); {/if} - $(".textarea-autosize").autosize(); + $(".textarea-autosize").each(function() { + var $ta = $(this), val = $ta.val(); + $ta.val('').autosize(); + if (val) { $ta.val(val).trigger('input'); } + }); diff --git a/admin/themes/default/template/header.tpl b/admin/themes/default/template/header.tpl index 148be94a4..bb6f6f97f 100644 --- a/admin/themes/default/template/header.tpl +++ b/admin/themes/default/template/header.tpl @@ -298,6 +298,18 @@ {/if}