diff --git a/admin/themes/default/template/controllers/normal_products/prices.tpl b/admin/themes/default/template/controllers/normal_products/prices.tpl index 115c78469..375f291a4 100644 --- a/admin/themes/default/template/controllers/normal_products/prices.tpl +++ b/admin/themes/default/template/controllers/normal_products/prices.tpl @@ -187,6 +187,7 @@ $(document).ready(function () { {/if} + {* Code For eco-tax working
+ *} + {include file="controllers/products/_tourism_tax_rule_field.tpl"} + {*| + + | ++ + | ++ + | +-- | +
|---|---|---|---|
| + + | ++ + | +
+
+
+
+
+ |
+ + + + + + | +
| + + | ++ + | ++ + | +-- | +
|---|---|---|---|
| + + | ++ + | +
+
+
+
+
+ |
+ + + + + + | +
| + | + | -- | +
|---|---|---|
|
+
+
+
+
+ |
+
+
+
+
+
+ |
+ + + + + + | +
' + . $this->l('Uncheck days when the tax should not apply within the date range.') + . '
', + 'form_group_class' => 'tourism-tax-field', + ), ), + 'submit' => array('title' => $this->l('Save')), 'buttons' => array( 'save-and-stay' => array( 'title' => $this->l('Save and stay'), - 'name' => 'submitAdd'.$this->table.'AndStay', + 'name' => 'submitAdd' . $this->table . 'AndStay', 'type' => 'submit', 'class' => 'btn btn-default pull-right', 'icon' => 'process-icon-save', @@ -248,25 +816,72 @@ class AdminTaxesControllerCore extends AdminController return parent::renderForm(); } + /** + * List column callback: display a Yes/No badge for is_tourism_tax. + * + * @param mixed $value 1 or 0 + * @param array $row + * @return string + */ + public function displayTourismTaxBadge($value, $row) + { + if ($value) { + return '' . $this->l('Yes') . ''; + } + return '' . $this->l('No') . ''; + } + + /** + * List column callback: display tax rate with the correct symbol. + * Tourism-fixed taxes show currency sign; tourism-percent and regular taxes show %. + * + * @param mixed $value ps_tax.rate + * @param array $row Full list row (includes tourism_tax_calc_type, tourism_tax_value from join) + * @return string + */ + public function displayRate($value, $row) + { + return TaxConfiguration::getFormattedRateForDisplay($value, $row, $this->context->currency); + } + public function postProcess() { if ($this->action == 'save') { + if ((int) Tools::getValue('is_tourism_tax')) { + $_POST['rate'] = '0'; + } /* Checking fields validity */ $this->validateRules(); + $this->validateTourismTaxFields(); if (!count($this->errors)) { - $id = (int)(Tools::getValue('id_'.$this->table)); + $existingTaxId = (int)(Tools::getValue('id_'.$this->table)); + $isTourismTax = (int) Tools::getValue('is_tourism_tax'); /* Object update */ - if (isset($id) && !empty($id)) { + if (isset($existingTaxId) && !empty($existingTaxId)) { /** @var Tax $object */ - $object = new $this->className($id); + $object = new $this->className($existingTaxId); if (Validate::isLoadedObject($object)) { + $wasTourismTax = (int) $object->is_tourism_tax; $this->copyFromPost($object, $this->table); $result = $object->update(false, false); if (!$result) { $this->errors[] = Tools::displayError('An error occurred while updating an object.').' '.$this->table.''; } elseif ($this->postImage($object->id)) { + if ($isTourismTax) { + self::saveTourismTaxSubtype($object->id); + } + if ($wasTourismTax && !$isTourismTax) { + $this->cleanTaxFromTourismTaxRulesGroups($object->id); + $tourismTax = TaxConfiguration::getByTaxId($object->id); + if ($tourismTax) { + $tourismTax->delete(); + } + } + if (!$wasTourismTax && $isTourismTax) { + $this->cleanTaxFromVatTaxRulesGroups($object->id); + } if (Tools::isSubmit('submitAdd'.$this->table.'AndStay')) { Tools::redirectAdmin(self::$currentIndex.'&update'.$this->table.'&id_'.$this->table.'='.$object->id.'&conf=4'.'&token='.$this->token); } else { @@ -286,12 +901,14 @@ class AdminTaxesControllerCore extends AdminController if (!$object->add()) { $this->errors[] = Tools::displayError('An error occurred while creating an object.').' '.$this->table.''; } elseif ($this->postImage($object->id)) { + if ($isTourismTax) { + self::saveTourismTaxSubtype($object->id); + } if (Tools::isSubmit('submitAdd'.$this->table.'AndStay')) { Tools::redirectAdmin(self::$currentIndex.'&update'.$this->table.'&id_'.$this->table.'='.$object->id.'&conf=3'.'&token='.$this->token); } else { Tools::redirectAdmin(self::$currentIndex.'&conf=3&token='.$this->token); } - Tools::redirectAdmin(self::$currentIndex.'&id_'.$this->table.'='.$object->id.'&conf=3'.'&token='.$this->token); } } } else { @@ -302,6 +919,317 @@ class AdminTaxesControllerCore extends AdminController } } + /** + * Validate tourism-tax-specific fields and append to $this->errors. + */ + protected function validateTourismTaxFields() + { + if (!(int) Tools::getValue('is_tourism_tax')) { + return; + } + + $taxValue = Tools::getValue('tax_value', ''); + if ($taxValue === '') { + $this->errors[] = $this->l('Tax value is required.'); + $this->invalidCoreFields['tax_value'] = true; + } else { + if (!is_numeric($taxValue)) { + $this->errors[] = $this->l('Tax value must be a valid number.'); + $this->invalidCoreFields['tax_value'] = true; + } elseif ((float) $taxValue < 0) { + $this->errors[] = $this->l('Tax value cannot be negative.'); + $this->invalidCoreFields['tax_value'] = true; + } + } + + if ((int) Tools::getValue('has_multiple_valid_ranges')) { + $submittedRangeFroms = Tools::getValue('validity_from', array()); + $submittedRangeTos = Tools::getValue('validity_to', array()); + $filledRanges = array(); + foreach ($submittedRangeFroms as $rowIndex => $fromRaw) { + $toRaw = isset($submittedRangeTos[$rowIndex]) ? $submittedRangeTos[$rowIndex] : ''; + $fromRaw = trim($fromRaw); + $toRaw = trim($toRaw); + if ($fromRaw === '0000-00-00') { $fromRaw = ''; } + if ($toRaw === '0000-00-00') { $toRaw = ''; } + $rowNum = (int) $rowIndex + 1; + if ($fromRaw === '' && $toRaw === '') { + continue; + } + if ($fromRaw !== '' && !Validate::isDate($fromRaw)) { + $this->errors[] = sprintf($this->l('Valid range #%d: "From" is not a valid date.'), $rowNum); + $this->invalidValidityFields[$rowIndex]['from'] = true; + } + if ($toRaw !== '' && !Validate::isDate($toRaw)) { + $this->errors[] = sprintf($this->l('Valid range #%d: "To" is not a valid date.'), $rowNum); + $this->invalidValidityFields[$rowIndex]['to'] = true; + } + if ($fromRaw !== '' && $toRaw !== '' && Validate::isDate($fromRaw) && Validate::isDate($toRaw) + && strtotime($toRaw) < strtotime($fromRaw) + ) { + $this->errors[] = sprintf($this->l('Valid range #%d: "To" must be on or after "From".'), $rowNum); + $this->invalidValidityFields[$rowIndex]['from'] = true; + $this->invalidValidityFields[$rowIndex]['to'] = true; + } + $filledRanges[] = array( + 'from' => ($fromRaw !== '' && Validate::isDate($fromRaw)) ? strtotime($fromRaw) : null, + 'to' => ($toRaw !== '' && Validate::isDate($toRaw)) ? strtotime($toRaw) : null, + 'rowNum' => $rowNum, + 'rowIndex' => $rowIndex, + ); + } + if (empty($filledRanges)) { + $this->errors[] = $this->l('Add at least one range or disable "Set valid date ranges".'); + } + $rangeCount = count($filledRanges); + for ($outerIdx = 0; $outerIdx < $rangeCount; $outerIdx++) { + for ($innerIdx = $outerIdx + 1; $innerIdx < $rangeCount; $innerIdx++) { + $outerRange = $filledRanges[$outerIdx]; + $innerRange = $filledRanges[$innerIdx]; + $outerMinEffective = ($outerRange['from'] === null) ? -PHP_INT_MAX : $outerRange['from']; + $outerMaxEffective = ($outerRange['to'] === null) ? PHP_INT_MAX : $outerRange['to']; + $innerMinEffective = ($innerRange['from'] === null) ? -PHP_INT_MAX : $innerRange['from']; + $innerMaxEffective = ($innerRange['to'] === null) ? PHP_INT_MAX : $innerRange['to']; + if ($outerMinEffective <= $innerMaxEffective && $outerMaxEffective >= $innerMinEffective) { + $this->errors[] = sprintf( + $this->l('Valid ranges #%d and #%d overlap. Each range must cover a distinct period.'), + $outerRange['rowNum'], + $innerRange['rowNum'] + ); + $this->invalidValidityFields[$outerRange['rowIndex']]['from'] = true; + $this->invalidValidityFields[$outerRange['rowIndex']]['to'] = true; + $this->invalidValidityFields[$innerRange['rowIndex']]['from'] = true; + $this->invalidValidityFields[$innerRange['rowIndex']]['to'] = true; + break 2; + } + } + } + } + + if ((int) Tools::getValue('has_child_rate') && (int) Tools::getValue('has_child_age_range')) { + $infantMaxAge = (int) Configuration::get('QLO_GLOBAL_MAX_INFANT_AGE'); + $childMaxAge = (int) Configuration::get('WK_GLOBAL_CHILD_MAX_AGE'); + $submittedBandMins = Tools::getValue('child_min', array()); + $submittedBandMaxs = Tools::getValue('child_max', array()); + $submittedBandValues = Tools::getValue('child_value', array()); + if (empty($submittedBandValues)) { + $this->errors[] = $this->l('Add at least one range or disable "Set child age range".'); + } + $filledBands = array(); + foreach ($submittedBandValues as $rowIndex => $taxValue) { + $bandMinRaw = isset($submittedBandMins[$rowIndex]) ? $submittedBandMins[$rowIndex] : ''; + $bandMaxRaw = isset($submittedBandMaxs[$rowIndex]) ? $submittedBandMaxs[$rowIndex] : ''; + $rowNum = (int) $rowIndex + 1; + if ($taxValue === '' || $taxValue === false) { + $this->errors[] = sprintf($this->l('Child range #%d: tax value is required.'), $rowNum); + $this->invalidChildFields[$rowIndex]['value'] = true; + continue; + } + $bandMin = ($bandMinRaw !== '') ? (int) $bandMinRaw : 0; + $bandMax = ($bandMaxRaw !== '') ? (int) $bandMaxRaw : 0; + if (!is_numeric($taxValue)) { + $this->errors[] = sprintf($this->l('Child range #%d: tax value must be a valid number.'), $rowNum); + $this->invalidChildFields[$rowIndex]['value'] = true; + } elseif ((float) $taxValue < 0) { + $this->errors[] = sprintf($this->l('Child range #%d: tax value cannot be negative.'), $rowNum); + $this->invalidChildFields[$rowIndex]['value'] = true; + } + if ($bandMax > 0 && $bandMin > $bandMax) { + $this->errors[] = sprintf($this->l('Child range #%d: min age cannot be greater than max age.'), $rowNum); + $this->invalidChildFields[$rowIndex]['min'] = true; + $this->invalidChildFields[$rowIndex]['max'] = true; + } + if ($bandMin > 0 && $bandMin < $infantMaxAge) { + $this->errors[] = sprintf($this->l('Child range #%d: min age cannot be below the infant age (%d).'), $rowNum, $infantMaxAge); + $this->invalidChildFields[$rowIndex]['min'] = true; + } + if ($bandMax > 0 && $bandMax >= $childMaxAge) { + $this->errors[] = sprintf($this->l('Child range #%d: max age cannot be %d or above (the general child age setting).'), $rowNum, $childMaxAge); + $this->invalidChildFields[$rowIndex]['max'] = true; + } + $filledBands[] = array('minAge' => $bandMin, 'maxAge' => $bandMax, 'rowNum' => $rowNum, 'rowIndex' => $rowIndex); + } + $bandCount = count($filledBands); + for ($outerIdx = 0; $outerIdx < $bandCount; $outerIdx++) { + for ($innerIdx = $outerIdx + 1; $innerIdx < $bandCount; $innerIdx++) { + $outerBand = $filledBands[$outerIdx]; + $innerBand = $filledBands[$innerIdx]; + $outerMaxEffective = ($outerBand['maxAge'] == 0) ? PHP_INT_MAX : $outerBand['maxAge']; + $innerMaxEffective = ($innerBand['maxAge'] == 0) ? PHP_INT_MAX : $innerBand['maxAge']; + if ($outerBand['minAge'] <= $innerMaxEffective && $outerMaxEffective >= $innerBand['minAge']) { + $this->errors[] = sprintf( + $this->l('Child age ranges #%d and #%d overlap. Each range must cover a distinct age range.'), + $outerBand['rowNum'], + $innerBand['rowNum'] + ); + $this->invalidChildFields[$outerBand['rowIndex']]['min'] = true; + $this->invalidChildFields[$outerBand['rowIndex']]['max'] = true; + $this->invalidChildFields[$innerBand['rowIndex']]['min'] = true; + $this->invalidChildFields[$innerBand['rowIndex']]['max'] = true; + break 2; + } + } + } + } + + if ((int) Tools::getValue('is_tiered')) { + $submittedTierMins = Tools::getValue('tier_min', array()); + $submittedTierMaxs = Tools::getValue('tier_max', array()); + $submittedTierValues = Tools::getValue('tier_value', array()); + if (empty($submittedTierValues)) { + $this->errors[] = $this->l('Add at least one range or disable "Tiered pricing".'); + } + $filledTiers = array(); + foreach ($submittedTierValues as $rowIndex => $taxValue) { + $tierMinRaw = isset($submittedTierMins[$rowIndex]) ? $submittedTierMins[$rowIndex] : ''; + $tierMaxRaw = isset($submittedTierMaxs[$rowIndex]) ? $submittedTierMaxs[$rowIndex] : ''; + $rowNum = (int) $rowIndex + 1; + if ($taxValue === '' || $taxValue === false) { + $this->errors[] = sprintf($this->l('Tier #%d: tax value is required.'), $rowNum); + $this->invalidTierFields[$rowIndex]['value'] = true; + continue; + } + $tierMin = ($tierMinRaw !== '') ? (float) $tierMinRaw : 0.0; + $tierMax = ($tierMaxRaw !== '') ? (float) $tierMaxRaw : 0.0; + if (!is_numeric($taxValue)) { + $this->errors[] = sprintf($this->l('Tier #%d: tax value must be a valid number.'), $rowNum); + $this->invalidTierFields[$rowIndex]['value'] = true; + } elseif ((float) $taxValue < 0) { + $this->errors[] = sprintf($this->l('Tier #%d: tax value cannot be negative.'), $rowNum); + $this->invalidTierFields[$rowIndex]['value'] = true; + } + if ($tierMax > 0 && $tierMin > $tierMax) { + $this->errors[] = sprintf($this->l('Tier #%d: min price cannot be greater than max price.'), $rowNum); + $this->invalidTierFields[$rowIndex]['min'] = true; + $this->invalidTierFields[$rowIndex]['max'] = true; + } + $filledTiers[] = array('minPrice' => $tierMin, 'maxPrice' => $tierMax, 'rowNum' => $rowNum, 'rowIndex' => $rowIndex); + } + $tierCount = count($filledTiers); + for ($outerIdx = 0; $outerIdx < $tierCount; $outerIdx++) { + for ($innerIdx = $outerIdx + 1; $innerIdx < $tierCount; $innerIdx++) { + $outerTier = $filledTiers[$outerIdx]; + $innerTier = $filledTiers[$innerIdx]; + $outerMaxEffective = ($outerTier['maxPrice'] == 0) ? PHP_FLOAT_MAX : $outerTier['maxPrice']; + $innerMaxEffective = ($innerTier['maxPrice'] == 0) ? PHP_FLOAT_MAX : $innerTier['maxPrice']; + if ($outerTier['minPrice'] < $innerMaxEffective && $outerMaxEffective > $innerTier['minPrice']) { + $this->errors[] = sprintf( + $this->l('Price ranges #%d and #%d overlap. Each range must cover a distinct price range.'), + $outerTier['rowNum'], + $innerTier['rowNum'] + ); + $this->invalidTierFields[$outerTier['rowIndex']]['min'] = true; + $this->invalidTierFields[$outerTier['rowIndex']]['max'] = true; + $this->invalidTierFields[$innerTier['rowIndex']]['min'] = true; + $this->invalidTierFields[$innerTier['rowIndex']]['max'] = true; + break 2; + } + } + } + } + } + + public function setMedia() + { + parent::setMedia(); + if ($this->display == 'edit' || $this->display == 'add') { + $this->addCSS(_MODULE_DIR_ . 'hotelreservationsystem/views/css/HotelReservationAdmin.css'); + $this->addJs(_MODULE_DIR_ . 'hotelreservationsystem/views/js/TourismTaxForm.js'); + } + } + + /** + * @param int $idTax + */ + protected static function saveTourismTaxSubtype($idTax) + { + $idTax = (int) $idTax; + $tourismTax = TaxConfiguration::getByTaxId($idTax); + $tourismTaxExists = (bool) $tourismTax; + if (!$tourismTax) { + $tourismTax = new TaxConfiguration(); + $tourismTax->id = $idTax; + } + $tourismTax->calculation_type = (int) Tools::getValue('tax_calc_type', 0); + $tourismTax->per_night = (int) Tools::getValue('is_per_night', 1); + $tourismTax->per_person = (int) Tools::getValue('is_per_person', 0); + $tourismTax->tax_value = (float) Tools::getValue('tax_value', 0); + $tourismTax->has_tiered_pricing = (int) Tools::getValue('is_tiered', 0); + $tourismTax->apply_on_child = (int) Tools::getValue('has_child_rate', 0); + $tourismTax->has_child_age_range = (int) Tools::getValue('has_child_age_range', 0); + $tourismTax->child_calculation_type = (int) Tools::getValue('child_calc_type', 0); + $tourismTax->has_multiple_valid_ranges = (int) Tools::getValue('has_multiple_valid_ranges', 0); + + $submittedDays = Tools::getValue('valid_days', array()); + if (!is_array($submittedDays)) { + $submittedDays = array(); + } + $submittedDays = array_values(array_intersect(TaxConfiguration::DAY_KEYS, $submittedDays)); + if (empty($submittedDays) || count($submittedDays) === 7) { + $tourismTax->special_days = null; + } else { + $tourismTax->special_days = json_encode($submittedDays); + } + + if ($tourismTaxExists) { + $tourismTax->update(); + } else { + $tourismTax->add(); + } + + TaxPriceTier::saveAll( + $idTax, + Tools::getValue('tier_min', array()), + Tools::getValue('tier_max', array()), + Tools::getValue('tier_value', array()) + ); + TaxChildRange::saveAll( + $idTax, + Tools::getValue('child_min', array()), + Tools::getValue('child_max', array()), + Tools::getValue('child_value', array()) + ); + TaxValidityRange::saveAll( + $idTax, + Tools::getValue('validity_from', array()), + Tools::getValue('validity_to', array()) + ); + } + + /** + * Remove this tax from all tourism tax rules groups when it is demoted from tourism to regular. + * Flashes a warning listing how many rule rows were deleted. + * + * @param int $idTax + */ + protected function cleanTaxFromTourismTaxRulesGroups($idTax) + { + $count = TaxConfiguration::cleanFromTourismTaxRulesGroups($idTax); + if ($count) { + $this->warnings[] = sprintf( + $this->l('This tax was removed from %d tourism tax rules group(s) because it is no longer marked as a tourism tax.'), + $count + ); + } + } + + /** + * Remove this tax from all standard (non-tourism) tax rules groups when it is promoted to a + * tourism tax. Flashes a warning listing how many rule rows were deleted. + * + * @param int $idTax + */ + protected function cleanTaxFromVatTaxRulesGroups($idTax) + { + $count = TaxConfiguration::cleanFromVatTaxRulesGroups($idTax); + if ($count) { + $this->warnings[] = sprintf( + $this->l('This tax was removed from %d standard tax rules group(s) because it is now marked as a tourism tax.'), + $count + ); + } + } + public function updateOptionPsUseEcotax($value) { $old_value = (int)Configuration::get('PS_USE_ECOTAX'); diff --git a/controllers/front/GuestTrackingController.php b/controllers/front/GuestTrackingController.php index 9d0ec86a6..2bd9908fe 100644 --- a/controllers/front/GuestTrackingController.php +++ b/controllers/front/GuestTrackingController.php @@ -249,6 +249,7 @@ class GuestTrackingControllerCore extends FrontController if ($orderProducts = $order->getProducts()) { $total_convenience_fee_te = 0; $total_convenience_fee_ti = 0; + $total_tourism_tax = OrderTaxDetail::getOrderTourismTaxTotal($idOrder); $objOrderReturn = new OrderReturn(); $refundReqBookings = $objOrderReturn->getOrderRefundRequestedBookings($order->id, 0, 1); @@ -555,6 +556,7 @@ class GuestTrackingControllerCore extends FrontController 'use_tax' => Configuration::get('PS_TAX'), 'guestInformations' => (array)$customer, 'view_on_map' => Configuration::get('WK_GOOGLE_ACTIVE_MAP'), + 'total_tourism_tax' => isset($total_tourism_tax) ? $total_tourism_tax : 0, )); } diff --git a/controllers/front/OrderConfirmationController.php b/controllers/front/OrderConfirmationController.php index 539c69f58..77c17f6fa 100644 --- a/controllers/front/OrderConfirmationController.php +++ b/controllers/front/OrderConfirmationController.php @@ -133,6 +133,7 @@ class OrderConfirmationControllerCore extends FrontController $orderTotalInfo['total_paid_real'] = 0; $orderTotalInfo['total_wrapping'] = 0; $orderTotalInfo['total_order_amount'] = 0; + $orderTotalInfo['total_tourism_tax'] = 0.0; $orders_has_invoice = 1; if ($cartOrders = Order::getAllOrdersByCartId($order->id_cart)) { @@ -151,6 +152,7 @@ class OrderConfirmationControllerCore extends FrontController $idOrder = $cartOrder['id_order']; $objCartOrder = new Order($idOrder); $orderProducts = $objCartOrder->getProducts(); + $roomTourismTaxByBooking = OrderTaxDetail::getAppliedTourismTaxTotals($idOrder, OrderTaxDetail::SCOPE_ROOM); if (!empty($orderProducts)) { foreach ($orderProducts as $type_key => $type_value) { @@ -186,6 +188,7 @@ class OrderConfirmationControllerCore extends FrontController $order_details_obj = new OrderDetail($data_v['id_order_detail']); $cart_htl_data[$type_key]['name'] = $order_details_obj->product_name; $stage_name = ''; + $roomTotalPriceTaxIncl = (float) $data_v['total_price_tax_incl']; if (isset($cart_htl_data[$type_key]['date_diff'][$date_join])) { $cart_htl_data[$type_key]['date_diff'][$date_join]['num_rm'] += 1; @@ -197,8 +200,8 @@ class OrderConfirmationControllerCore extends FrontController //// By webkul New way to calculate product prices with feature Prices $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; - $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; - $cart_htl_data[$type_key]['date_diff'][$date_join]['amount_tax_incl'] += $data_v['total_price_tax_incl']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $roomTotalPriceTaxIncl/$num_days; + $cart_htl_data[$type_key]['date_diff'][$date_join]['amount_tax_incl'] += $roomTotalPriceTaxIncl; $cart_htl_data[$type_key]['date_diff'][$date_join]['amount_tax_excl'] += $data_v['total_price_tax_excl']; $cart_htl_data[$type_key]['date_diff'][$date_join]['is_backorder'] = $data_v['is_back_order']; if ($data_v['is_back_order']) { @@ -220,8 +223,8 @@ class OrderConfirmationControllerCore extends FrontController // By webkul New way to calculate product prices with feature Prices $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; - $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; - $cart_htl_data[$type_key]['date_diff'][$date_join]['amount_tax_incl'] = $data_v['total_price_tax_incl']; + $cart_htl_data[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $roomTotalPriceTaxIncl/$num_days; + $cart_htl_data[$type_key]['date_diff'][$date_join]['amount_tax_incl'] = $roomTotalPriceTaxIncl; $cart_htl_data[$type_key]['date_diff'][$date_join]['amount_tax_excl'] = $data_v['total_price_tax_excl']; if ($data_v['is_back_order']) { $any_back_order = 1; @@ -392,13 +395,18 @@ class OrderConfirmationControllerCore extends FrontController $orderTotalInfo['total_tax'] += $objCartOrder->total_paid_tax_incl - $objCartOrder->total_paid_tax_excl; $orderTotalInfo['total_paid'] += $objCartOrder->total_paid; $orderTotalInfo['total_paid_real'] += $objCartOrder->total_paid_real; + + $orderTotalInfo['total_tourism_tax'] += OrderTaxDetail::getOrderTourismTaxTotal($idOrder); } $totalTaxIncl = $orderTotalInfo['total_rooms_ti'] + $orderTotalInfo['total_services_ti'] + $orderTotalInfo['total_convenience_fee_ti'] + $orderTotalInfo['total_auto_add_services_ti'] + $orderTotalInfo['total_standalone_products_ti']; $totalTaxExcl = $orderTotalInfo['total_rooms_te'] + $orderTotalInfo['total_services_te'] + $orderTotalInfo['total_convenience_fee_te'] + $orderTotalInfo['total_auto_add_services_te'] + $orderTotalInfo['total_standalone_products_te']; - $orderTotalInfo['total_tax_without_discount'] = $totalTaxIncl - $totalTaxExcl; + $orderTotalInfo['total_tax_without_discount'] = ($totalTaxIncl - $totalTaxExcl) - $orderTotalInfo['total_tourism_tax']; + if ($orderTotalInfo['total_tax_without_discount'] < 0) { + $orderTotalInfo['total_tax_without_discount'] = 0; + } } $this->context->smarty->assign('orderTotalInfo', $orderTotalInfo); diff --git a/controllers/front/OrderDetailController.php b/controllers/front/OrderDetailController.php index b4f597c96..85220ba0b 100644 --- a/controllers/front/OrderDetailController.php +++ b/controllers/front/OrderDetailController.php @@ -88,6 +88,7 @@ class OrderDetailControllerCore extends FrontController $objBookingDetail = new HotelBookingDetail(); $objRoomType = new HotelRoomType(); $objServiceProductOrderDetail = new ServiceProductOrderDetail(); + $roomTourismTaxByBooking = OrderTaxDetail::getAppliedTourismTaxTotals($order->id, OrderTaxDetail::SCOPE_ROOM); $anyBackOrder = 0; $processedProducts = array(); $cartHotelData = array(); @@ -145,6 +146,8 @@ class OrderDetailControllerCore extends FrontController $bookingRefundDetail = reset($bookingRefundDetail); } + $roomTotalPriceTaxIncl = (float) $data_v['total_price_tax_incl']; + if (isset($cartHotelData[$type_key]['date_diff'][$date_join])) { $cartHotelData[$type_key]['date_diff'][$date_join]['num_rm'] += 1; @@ -155,10 +158,10 @@ class OrderDetailControllerCore extends FrontController $cartHotelData[$type_key]['date_diff'][$date_join]['children'] += $data_v['children']; $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; - $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; + $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $roomTotalPriceTaxIncl/$num_days; $cartHotelData[$type_key]['date_diff'][$date_join]['avg_paid_unit_price_tax_excl'] += $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl']; $cartHotelData[$type_key]['date_diff'][$date_join]['avg_paid_unit_price_tax_incl'] += $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl']; - $cartHotelData[$type_key]['date_diff'][$date_join]['amount_tax_incl'] += $data_v['total_price_tax_incl']; + $cartHotelData[$type_key]['date_diff'][$date_join]['amount_tax_incl'] += $roomTotalPriceTaxIncl; $cartHotelData[$type_key]['date_diff'][$date_join]['amount_tax_excl'] += $data_v['total_price_tax_excl']; $cartHotelData[$type_key]['date_diff'][$date_join]['is_backorder'] = $data_v['is_back_order']; if ($data_v['is_back_order']) { @@ -182,10 +185,10 @@ class OrderDetailControllerCore extends FrontController $cartHotelData[$type_key]['date_diff'][$date_join]['children'] = $data_v['children']; $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl'] = $data_v['total_price_tax_excl']/$num_days; - $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $data_v['total_price_tax_incl']/$num_days; + $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl'] = $roomTotalPriceTaxIncl/$num_days; $cartHotelData[$type_key]['date_diff'][$date_join]['avg_paid_unit_price_tax_excl'] = $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_excl']; $cartHotelData[$type_key]['date_diff'][$date_join]['avg_paid_unit_price_tax_incl'] = $cartHotelData[$type_key]['date_diff'][$date_join]['paid_unit_price_tax_incl']; - $cartHotelData[$type_key]['date_diff'][$date_join]['amount_tax_incl'] = $data_v['total_price_tax_incl']; + $cartHotelData[$type_key]['date_diff'][$date_join]['amount_tax_incl'] = $roomTotalPriceTaxIncl; $cartHotelData[$type_key]['date_diff'][$date_join]['amount_tax_excl'] = $data_v['total_price_tax_excl']; $cartHotelData[$type_key]['date_diff'][$date_join]['is_backorder'] = $data_v['is_back_order']; if ($data_v['is_back_order']) { @@ -382,6 +385,7 @@ class OrderDetailControllerCore extends FrontController } } + $totalTourismTax = OrderTaxDetail::getOrderTourismTaxTotal((int) $id_order); $this->context->smarty->assign( array( @@ -389,6 +393,7 @@ class OrderDetailControllerCore extends FrontController 'THEME_DIR' => _THEME_DIR_, 'total_convenience_fee_ti' => $total_convenience_fee_ti, 'total_convenience_fee_te' => $total_convenience_fee_te, + 'total_tourism_tax' => $totalTourismTax, 'any_back_order' => $anyBackOrder, 'shw_bo_msg' => Configuration::get('WK_SHOW_MSG_ON_BO'), 'back_ord_msg' => Configuration::get('WK_BO_MESSAGE'), diff --git a/controllers/front/OrderOpcController.php b/controllers/front/OrderOpcController.php index dc2816cd1..6a0f3b4bb 100644 --- a/controllers/front/OrderOpcController.php +++ b/controllers/front/OrderOpcController.php @@ -950,6 +950,14 @@ class OrderOpcControllerCore extends ParentOrderController $objRoomTypeServiceProduct = new RoomTypeServiceProduct(); $roomTypeServiceProducts = $objRoomTypeServiceProduct->getServiceProductsData($idProduct, 1, 0, true, 1); $cartBookings = $objCartBookingData->getHotelCartRoomsInfoByRoomType($this->context->cart->id, $idProduct,$dateFrom, $dateTo); + $availableServicePricing = $objRoomTypeServiceProduct->getServiceProductsPricingForCartBookings( + $roomTypeServiceProducts, + $cartBookings, + $idProduct, + $dateFrom, + $dateTo + ); + foreach($cartBookings as &$cartBookingData) { $cartBookingData['selected_service'] = $objServiceProductCartDetail->getServiceProductsInCart( $cartBookingData['id_cart'], @@ -965,7 +973,22 @@ class OrderOpcControllerCore extends ParentOrderController null, 1 ); + foreach ($cartBookingData['selected_service'] as &$selectedService) { + if ($selectedService['tourism_tax'] > 0 && TaxConfiguration::isGrossedUp($selectedService['tourism_tax'])) { + $selectedService['total_price_tax_incl'] += $selectedService['tourism_tax']; + $quantity = $selectedService['quantity'] ?: 1; + $impliedNumDays = $selectedService['unit_price_tax_excl'] > 0 ? $selectedService['total_price_tax_excl'] / ($selectedService['unit_price_tax_excl'] * $quantity) : 1; + $unitDivisor = $impliedNumDays * $quantity; + $selectedService['unit_price_tax_incl'] += $unitDivisor > 0 ? ($selectedService['tourism_tax'] / $unitDivisor) : 0; + } + } + unset($selectedService); + + $cartBookingData['available_service_pricing'] = isset($availableServicePricing[$cartBookingData['id']]) + ? $availableServicePricing[$cartBookingData['id']] + : array(); } + unset($cartBookingData); $this->context->smarty->assign(array( 'roomTypeServiceProducts' => $roomTypeServiceProducts, 'cartRooms' => $cartBookings diff --git a/controllers/front/ParentOrderController.php b/controllers/front/ParentOrderController.php index fe384389a..d445b6e4c 100644 --- a/controllers/front/ParentOrderController.php +++ b/controllers/front/ParentOrderController.php @@ -364,6 +364,14 @@ class ParentOrderControllerCore extends FrontController 0 ); + foreach ($standaloneProducts as &$standaloneProduct) { + if (TaxConfiguration::isGrossedUp($standaloneProduct['tourism_tax_amount'])) { + $standaloneProduct['unit_price_tax_incl'] += $standaloneProduct['tourism_tax_amount'] / max(1, (int) $standaloneProduct['quantity']); + $standaloneProduct['total_price_tax_incl'] += $standaloneProduct['tourism_tax_amount']; + } + } + unset($standaloneProduct); + if (count($standaloneProducts)) { $this->context->smarty->assign('standalone_products', $standaloneProducts); } diff --git a/controllers/front/ProductController.php b/controllers/front/ProductController.php index d7259646b..170cc40a6 100644 --- a/controllers/front/ProductController.php +++ b/controllers/front/ProductController.php @@ -472,7 +472,6 @@ class ProductControllerCore extends FrontController ); } } - $this->assignBookingFormVars($this->product->id, $date_from, $date_to, $occupancy_value); $this->assignRoomServiceProductVars(); @@ -485,6 +484,36 @@ class ProductControllerCore extends FrontController $feature_price = HotelRoomTypeFeaturePricing::getRoomTypeFeaturePricesPerDay($this->product->id, $date_from, $date_to, false, 0, 0, 0, 0, 1, 1, $occupancy_value); } $productPriceWithoutReduction = $this->product->getPriceWithoutReduct(!$useTax); + if ($useTax && (bool) Configuration::get('QLO_TOURISM_TAX_GROSSED_UP')) { + $totalExclTourismTax = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( + $this->product->id, + $date_from, + $date_to, + $occupancy_value, + 0, + 0, + 0, + 0, + 1, + 0 + ); + $totalInclTourismTax = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( + $this->product->id, + $date_from, + $date_to, + $occupancy_value, + 0, + 0, + 0, + 0, + 1, + 0, + true + ); + $tourismTaxTotal = $totalInclTourismTax['total_price_tax_incl'] - $totalExclTourismTax['total_price_tax_incl']; + $numDaysInDuration = HotelHelper::getNumberOfDays($date_from, $date_to); + $productPriceWithoutReduction += $tourismTaxTotal / $numDaysInDuration; + } $feature_price_diff = (float)($productPriceWithoutReduction - $feature_price); $this->context->smarty->assign('feature_price', $feature_price); $this->context->smarty->assign('feature_price_diff', $feature_price_diff); @@ -716,6 +745,7 @@ class ProductControllerCore extends FrontController // calculate room type price first $useTax = HotelBookingDetail::useTax(); $totalPrice = 0; + $includeTourismTax = $useTax && Configuration::get('QLO_TOURISM_TAX_GROSSED_UP'); $priceWithoutDiscount = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( $idProduct, $dateFrom, @@ -726,13 +756,21 @@ class ProductControllerCore extends FrontController 0, 0, 1, - 0 + 0, + $includeTourismTax ); $roomTypeDateRangePrice = HotelRoomTypeFeaturePricing::getRoomTypeTotalPrice( $idProduct, $dateFrom, $dateTo, - $occupancy + $occupancy, + 0, + 0, + 0, + 0, + 1, + 1, + $includeTourismTax ); $featurePrice = 0; @@ -750,9 +788,8 @@ class ProductControllerCore extends FrontController 1, $occupancy ); - $roomTypeDateRangePrice = $roomTypeDateRangePrice['total_price_tax_incl']; $totalPriceWithoutDiscount = $priceWithoutDiscount['total_price_tax_incl']; - + $roomTypeDateRangePrice = $roomTypeDateRangePrice['total_price_tax_incl']; } else { $featurePrice = HotelRoomTypeFeaturePricing::getRoomTypeFeaturePricesPerDay( $idProduct, @@ -780,6 +817,10 @@ class ProductControllerCore extends FrontController if ($roomServiceProducts = json_decode($roomServiceProducts, true)) { $objRoomTypeServiceProductPrice = new RoomTypeServiceProductPrice(); $objRoomTypeServiceProduct = new RoomTypeServiceProduct(); + $serviceOccupancy = (is_array($occupancy) && isset($occupancy[0])) ? $occupancy[0] : array('adults' => 1, 'child_ages' => array()); + $serviceNumAdults = isset($serviceOccupancy['adults']) ? (int) $serviceOccupancy['adults'] : 1; + $serviceChildAges = !empty($serviceOccupancy['child_ages']) ? (array) $serviceOccupancy['child_ages'] : array(); + foreach ($roomServiceProducts as &$product) { if (!$objRoomTypeServiceProduct->isRoomTypeLinkedWithProduct($idProduct, $product['id_product'])) { unset($product); @@ -804,8 +845,17 @@ class ProductControllerCore extends FrontController $useTax, $product['quantity'], $dateFrom, - $dateTo + $dateTo, + false, + null, + 1, + null, + 0, + $useTax && Configuration::get('QLO_TOURISM_TAX_GROSSED_UP'), + $serviceNumAdults, + $serviceChildAges ); + $product['price'] = $productPrice; $serviceProductsPrice += $productPrice; } @@ -871,6 +921,11 @@ class ProductControllerCore extends FrontController } } $smartyVars['associated_hotels'] = $associatedHotels; + if (!$idHotel) { + // no hotel picked yet (e.g. initial page load) — price/tourism tax must match + // the hotel the dropdown defaults to, which is simply its first option + $idHotel = reset($associatedHotels)['id_hotel']; + } } } if ($idHotel) { @@ -880,6 +935,7 @@ class ProductControllerCore extends FrontController $smartyVars['buying_option'] = $buying_option; } $useTax = HotelBookingDetail::useTax(); + $includeTourismTax = $useTax && Configuration::get('QLO_TOURISM_TAX_GROSSED_UP'); $objServiceProductOption = new ServiceProductOption(); if ($serviceProductOptions = $objServiceProductOption->getProductOptions($this->product->id)) { foreach ($serviceProductOptions as &$serviceProductOption) { @@ -892,7 +948,15 @@ class ProductControllerCore extends FrontController $idHotel, false, $useTax, - 1 + 1, + null, + null, + false, + null, + 1, + null, + 0, + $includeTourismTax ); } @@ -904,7 +968,15 @@ class ProductControllerCore extends FrontController $idHotel, false, $useTax, - $quantity + $quantity, + null, + null, + false, + null, + 1, + null, + 0, + $includeTourismTax ); $smartyVars['service_price_without_reduction'] = Product::getServiceProductPrice( $this->product->id, @@ -917,7 +989,10 @@ class ProductControllerCore extends FrontController null, false, null, - false + false, + null, + 0, + $includeTourismTax ); if ($quantity) { $smartyVars['quantity'] = $quantity; diff --git a/install/data/db_structure.sql b/install/data/db_structure.sql index 07a270b34..69e4c3bef 100644 --- a/install/data/db_structure.sql +++ b/install/data/db_structure.sql @@ -1182,6 +1182,7 @@ CREATE TABLE `PREFIX_orders` ( `invoice_date` datetime NOT NULL, `delivery_date` datetime NOT NULL, `source` varchar(255) DEFAULT NULL, + `id_source` int(10) unsigned DEFAULT NULL, `valid` int(1) unsigned NOT NULL DEFAULT '0', `is_advance_payment` tinyint(1) NOT NULL DEFAULT '0', `advance_paid_amount` decimal(20,6) NOT NULL DEFAULT '0.00', @@ -1201,16 +1202,83 @@ CREATE TABLE `PREFIX_orders` ( KEY `id_shop_group` (`id_shop_group`), KEY (`current_state`), KEY `id_shop` (`id_shop`), + KEY `id_source` (`id_source`), INDEX `date_add`(`date_add`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; -CREATE TABLE `PREFIX_order_detail_tax` ( - `id_order_detail` int(11) NOT NULL, - `id_tax` int(11) NOT NULL, - `unit_amount` DECIMAL(16, 6) NOT NULL DEFAULT '0.00', - `total_amount` DECIMAL(16, 6) NOT NULL DEFAULT '0.00', - KEY (`id_order_detail`), - KEY `id_tax` (`id_tax`) +CREATE TABLE `PREFIX_tax_configuration` ( + `id_tax` int(11) unsigned NOT NULL, + `tax_value` decimal(20,6) NOT NULL DEFAULT '0.000000', + `calculation_type` tinyint(1) NOT NULL DEFAULT '0', + `per_night` tinyint(1) NOT NULL DEFAULT '1', + `per_person` tinyint(1) NOT NULL DEFAULT '0', + `has_tiered_pricing` tinyint(1) NOT NULL DEFAULT '0', + `apply_on_child` tinyint(1) NOT NULL DEFAULT '0', + `has_child_age_range` tinyint(1) NOT NULL DEFAULT '0', + `child_calculation_type` tinyint(1) NOT NULL DEFAULT '0', + `has_multiple_valid_ranges` tinyint(1) NOT NULL DEFAULT '0', + `special_days` text, + PRIMARY KEY (`id_tax`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + +CREATE TABLE `PREFIX_tax_price_tier` ( + `id_tax_price_tier` int(11) unsigned NOT NULL AUTO_INCREMENT, + `id_tax` int(11) unsigned NOT NULL, + `min_amount` decimal(20,6) NOT NULL DEFAULT '0.000000', + `max_amount` decimal(20,6) NOT NULL DEFAULT '0.000000', + `tax_value` decimal(20,6) NOT NULL DEFAULT '0.000000', + PRIMARY KEY (`id_tax_price_tier`), + KEY `id_tax` (`id_tax`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + +CREATE TABLE `PREFIX_tax_child_range` ( + `id_tax_child_range` int(11) unsigned NOT NULL AUTO_INCREMENT, + `id_tax` int(11) unsigned NOT NULL, + `min_age` tinyint(3) unsigned NOT NULL DEFAULT '0', + `max_age` tinyint(3) unsigned NOT NULL DEFAULT '17', + `tax_value` decimal(20,6) NOT NULL DEFAULT '0.000000', + PRIMARY KEY (`id_tax_child_range`), + KEY `id_tax` (`id_tax`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + +CREATE TABLE `PREFIX_tax_validity_range` ( + `id_tax_validity_range` int(11) unsigned NOT NULL AUTO_INCREMENT, + `id_tax` int(11) unsigned NOT NULL, + `valid_from` date DEFAULT NULL, + `valid_to` date DEFAULT NULL, + PRIMARY KEY (`id_tax_validity_range`), + KEY `id_tax` (`id_tax`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + +CREATE TABLE `PREFIX_order_tax_detail` ( + `id_order_tax_detail` int(11) unsigned NOT NULL AUTO_INCREMENT, + `id_order` int(11) unsigned NOT NULL, + `id_order_detail` int(11) unsigned NOT NULL, + `id_htl_booking` int(11) unsigned NOT NULL, + `id_service_product_order_detail` int(11) unsigned NOT NULL DEFAULT '0', + `id_tax` int(11) unsigned NOT NULL, + `unit_amount` decimal(20,6) NOT NULL DEFAULT '0.000000', + `total_amount` decimal(20,6) NOT NULL DEFAULT '0.000000', + `date_add` datetime NOT NULL, + PRIMARY KEY (`id_order_tax_detail`), + KEY `id_order` (`id_order`), + KEY `id_order_detail` (`id_order_detail`), + KEY `id_htl_booking` (`id_htl_booking`), + KEY `id_service_product_order_detail` (`id_service_product_order_detail`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + +CREATE TABLE `PREFIX_order_tax_exemption` ( + `id_order_tax_exemption` int(11) unsigned NOT NULL AUTO_INCREMENT, + `id_htl_booking` int(11) unsigned NOT NULL DEFAULT '0', + `id_service_product_order_detail` int(11) unsigned NOT NULL DEFAULT '0', + `id_order` int(11) unsigned NOT NULL, + `id_employee` int(11) unsigned NOT NULL, + `note` text, + `date_add` datetime NOT NULL, + PRIMARY KEY (`id_order_tax_exemption`), + KEY `id_htl_booking` (`id_htl_booking`), + KEY `id_service_product_order_detail` (`id_service_product_order_detail`), + KEY `id_order` (`id_order`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; CREATE TABLE `PREFIX_order_invoice` ( @@ -1280,6 +1348,7 @@ CREATE TABLE `PREFIX_order_detail` ( `product_supplier_reference` varchar(32) DEFAULT NULL, `product_weight` DECIMAL(20,6) NOT NULL, `id_tax_rules_group` INT(11) UNSIGNED DEFAULT '0', + `id_tourism_tax_rule_group` INT(11) UNSIGNED DEFAULT '0', `tax_computation_method` tinyint(1) unsigned NOT NULL DEFAULT '0', `tax_name` varchar(16) NOT NULL, `tax_rate` DECIMAL(10,3) NOT NULL DEFAULT '0.000', @@ -1536,6 +1605,7 @@ CREATE TABLE `PREFIX_product` ( `id_category_default` int(10) unsigned DEFAULT NULL, `id_shop_default` int(10) unsigned NOT NULL DEFAULT 1, `id_tax_rules_group` INT(11) UNSIGNED NOT NULL, + `id_tourism_tax_rules_group` INT(11) UNSIGNED NOT NULL DEFAULT '0', `on_sale` tinyint(1) unsigned NOT NULL DEFAULT '0', `online_only` tinyint(1) unsigned NOT NULL DEFAULT '0', `ean13` varchar(13) DEFAULT NULL, @@ -1599,6 +1669,7 @@ CREATE TABLE IF NOT EXISTS `PREFIX_product_shop` ( `id_shop` int(10) unsigned NOT NULL, `id_category_default` int(10) unsigned DEFAULT NULL, `id_tax_rules_group` INT(11) UNSIGNED NOT NULL, + `id_tourism_tax_rules_group` INT(11) UNSIGNED NOT NULL DEFAULT '0', `on_sale` tinyint(1) unsigned NOT NULL DEFAULT '0', `online_only` tinyint(1) unsigned NOT NULL DEFAULT '0', `ecotax` decimal(17,6) NOT NULL DEFAULT '0.000000', @@ -1888,6 +1959,48 @@ CREATE TABLE `PREFIX_search_word` ( UNIQUE KEY `id_lang` (`id_lang`,`id_shop`, `word`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; +CREATE TABLE `PREFIX_business_source` ( + `id_business_source` int(10) unsigned NOT NULL auto_increment, + `code` varchar(64) NOT NULL, + `position` int(10) unsigned NOT NULL DEFAULT '0', + `unremovable` tinyint(1) UNSIGNED NOT NULL DEFAULT '0', + `active` tinyint(1) UNSIGNED NOT NULL DEFAULT '1', + `deleted` tinyint(1) UNSIGNED NOT NULL DEFAULT '0', + `date_add` datetime NOT NULL, + `date_upd` datetime NOT NULL, + PRIMARY KEY (`id_business_source`), + UNIQUE KEY `code` (`code`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + +CREATE TABLE `PREFIX_business_source_lang` ( + `id_business_source` int(10) unsigned NOT NULL, + `id_lang` int(10) unsigned NOT NULL, + `name` varchar(64) NOT NULL, + PRIMARY KEY (`id_business_source`,`id_lang`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + +CREATE TABLE `PREFIX_source` ( + `id_source` int(10) unsigned NOT NULL auto_increment, + `id_business_source` int(10) unsigned NOT NULL, + `code` varchar(64) NOT NULL, + `position` int(10) unsigned NOT NULL DEFAULT '0', + `unremovable` tinyint(1) UNSIGNED NOT NULL DEFAULT '0', + `active` tinyint(1) UNSIGNED NOT NULL DEFAULT '1', + `deleted` tinyint(1) UNSIGNED NOT NULL DEFAULT '0', + `date_add` datetime NOT NULL, + `date_upd` datetime NOT NULL, + PRIMARY KEY (`id_source`), + UNIQUE KEY `code` (`code`), + KEY `id_business_source` (`id_business_source`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + +CREATE TABLE `PREFIX_source_lang` ( + `id_source` int(10) unsigned NOT NULL, + `id_lang` int(10) unsigned NOT NULL, + `name` varchar(64) NOT NULL, + PRIMARY KEY (`id_source`,`id_lang`) +) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; + CREATE TABLE `PREFIX_specific_price` ( `id_specific_price` INT UNSIGNED NOT NULL AUTO_INCREMENT, `id_specific_price_rule` INT(11) UNSIGNED NOT NULL, @@ -1997,6 +2110,7 @@ CREATE TABLE `PREFIX_tag_count` ( CREATE TABLE `PREFIX_tax` ( `id_tax` int(10) unsigned NOT NULL auto_increment, `rate` DECIMAL(10, 3) NOT NULL, + `is_tourism_tax` TINYINT(1) NOT NULL DEFAULT '0', `active` tinyint(1) unsigned NOT NULL DEFAULT '1', `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id_tax`) @@ -2122,6 +2236,7 @@ CREATE TABLE `PREFIX_tax_rules_group` ( `id_tax_rules_group` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR( 50 ) NOT NULL, `active` INT NOT NULL, +`is_tourism_tax_rule_group` TINYINT(1) NOT NULL DEFAULT '0', `deleted` TINYINT(1) UNSIGNED NOT NULL, `date_add` DATETIME NOT NULL, `date_upd` DATETIME NOT NULL diff --git a/install/data/xml/business_source.xml b/install/data/xml/business_source.xml new file mode 100644 index 000000000..ec7355d81 --- /dev/null +++ b/install/data/xml/business_source.xml @@ -0,0 +1,23 @@ + +- {l s='Total tax'} + {l s='Room and Service Tax'} {displayPrice price=($total_tax_without_discount)}
{/if} + {if $show_taxes && isset($total_tourism_tax) && $total_tourism_tax > 0} ++ {l s='Total Tourism Tax'} + {displayPrice price=$total_tourism_tax} +
+ {/if}
{if $display_tax_label}
diff --git a/themes/hotel-reservation-theme/css/modules/blockcart/blockcart.css b/themes/hotel-reservation-theme/css/modules/blockcart/blockcart.css
index 62651c4c9..fbc3166f9 100644
--- a/themes/hotel-reservation-theme/css/modules/blockcart/blockcart.css
+++ b/themes/hotel-reservation-theme/css/modules/blockcart/blockcart.css
@@ -228,6 +228,7 @@
.cart_block .cart_block_shipping_cost,
.cart_block .cart_block_tax_cost,
+.cart_block .cart_block_tourism_tax_cost,
.cart_block .cart_block_total,
.cart_block .cart_block_wrapping_cost,
.cart_block .cart_block_convenience_fee {
diff --git a/themes/hotel-reservation-theme/js/modules/blockcart/ajax-cart.js b/themes/hotel-reservation-theme/js/modules/blockcart/ajax-cart.js
index 342d20974..103892bee 100644
--- a/themes/hotel-reservation-theme/js/modules/blockcart/ajax-cart.js
+++ b/themes/hotel-reservation-theme/js/modules/blockcart/ajax-cart.js
@@ -1320,6 +1320,12 @@ var ajaxCart = {
$('.ajax_cart_shipping_cost').parent().find('.unvisible').show();
$('.ajax_cart_tax_cost').text(jsonData.tax_cost);
+ $('.ajax_cart_tourism_tax_cost').text(jsonData.tourism_tax_cost);
+ if (parseFloat(jsonData.tourism_tax) > 0) {
+ $('.ajax_cart_tourism_tax_line').show();
+ } else {
+ $('.ajax_cart_tourism_tax_line').hide();
+ }
$('.cart_block_wrapping_cost').text(jsonData.wrapping_cost);
$('.ajax_block_cart_total').text(jsonData.total);
diff --git a/themes/hotel-reservation-theme/modules/blockcart/blockcart.tpl b/themes/hotel-reservation-theme/modules/blockcart/blockcart.tpl
index 75ce91bfc..694f6a1df 100644
--- a/themes/hotel-reservation-theme/modules/blockcart/blockcart.tpl
+++ b/themes/hotel-reservation-theme/modules/blockcart/blockcart.tpl
@@ -139,7 +139,15 @@
{if $show_tax && $use_tax}
+ {if isset($orderTotalInfo['total_tourism_tax']) && $orderTotalInfo['total_tourism_tax'] > 0}
+
- {l s='Total Tax'}
+ {l s='Room and Service Tax'}
{displayWtPriceWithCurrency price=$orderTotalInfo['total_tax_without_discount'] currency=$objOrderCurrency convert=1}
+
+ {/if}
{if $order->total_discounts > 0}
+ {l s='Total Tourism Tax'}
+
+
+ {displayWtPriceWithCurrency price=$orderTotalInfo['total_tourism_tax'] currency=$objOrderCurrency}
+
+
diff --git a/themes/hotel-reservation-theme/order-detail.tpl b/themes/hotel-reservation-theme/order-detail.tpl
index 72f129f6d..ec701631c 100644
--- a/themes/hotel-reservation-theme/order-detail.tpl
+++ b/themes/hotel-reservation-theme/order-detail.tpl
@@ -331,7 +331,7 @@
{assign var=room_services_price_tax_excl value=($order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE))}
{assign var=room_services_price_tax_incl value=($order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_WITH_ROOM_TYPE))}
- {assign var=total_tax_without_discount value=(($room_price_tax_incl - $room_price_tax_excl) + ($room_services_price_tax_incl - $room_services_price_tax_excl ))}
+ {assign var=total_tax_without_discount value=(($room_price_tax_incl - $room_price_tax_excl) + ($room_services_price_tax_incl - $room_services_price_tax_excl) - ($total_tourism_tax|default:0))}
{assign var=total_standard_products_tax_incl value=($order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_WITH_STANDALONE) + $order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_WITH_HOTEL))}
{assign var=total_standard_products_tax_excl value=($order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_WITH_STANDALONE) + $order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_WITH_HOTEL))}
@@ -374,12 +374,21 @@
{/if}
-
+ {if isset($total_tourism_tax) && $total_tourism_tax > 0}
+ {l s='Total Tax'}
+ {l s='Room and Service Tax'}
{displayWtPriceWithCurrency price=($total_tax_without_discount) currency=$currency}
+
+ {/if}
+
{if $order->total_discounts > 0}
{l s='Total Tourism Tax'}
+
+ {displayWtPriceWithCurrency price=$total_tourism_tax currency=$currency}
+
+
{l s='Total Vouchers'}
@@ -752,7 +761,7 @@
{assign var=total_standard_products_tax_incl value=($order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_WITH_STANDALONE) + $order->getTotalProductsWithTaxes(false, false, Product::SELLING_PREFERENCE_WITH_HOTEL))}
{assign var=total_standard_products_tax_excl value=($order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_WITH_STANDALONE) + $order->getTotalProductsWithoutTaxes(false, false, Product::SELLING_PREFERENCE_WITH_HOTEL))}
- {assign var=total_tax_without_discount value=(($room_price_tax_incl - $room_price_tax_excl) + ($room_services_price_tax_incl - $room_services_price_tax_excl) + ($total_standard_products_tax_incl - $total_standard_products_tax_excl))}
+ {assign var=total_tax_without_discount value=(($room_price_tax_incl - $room_price_tax_excl) + ($room_services_price_tax_incl - $room_services_price_tax_excl) + ($total_standard_products_tax_incl - $total_standard_products_tax_excl) - ($total_tourism_tax|default:0))}
{if isset($cart_htl_data) && $cart_htl_data}
@@ -793,11 +802,19 @@
{/if}
-
+ {if isset($total_tourism_tax) && $total_tourism_tax > 0}
+ {l s='Total Tax'}
+ {l s='Room and Service Tax'}
{displayWtPriceWithCurrency price=($total_tax_without_discount) currency=$currency}
+
+ {/if}
{if $order->total_discounts > 0}
{l s='Total Tourism Tax'}
+
+ {displayWtPriceWithCurrency price=$total_tourism_tax currency=$currency}
+
+
{l s='Total Vouchers'}