* @copyright 2007-2017 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ /** * @property TaxRulesGroup $object */ class AdminTaxRulesGroupControllerCore extends AdminController { public $tax_rule; public $selected_countries = array(); public $selected_states = array(); public $errors_tax_rule; public function __construct() { $this->bootstrap = true; $this->table = 'tax_rules_group'; $this->className = 'TaxRulesGroup'; $this->lang = false; $this->context = Context::getContext(); $this->fields_list = array( 'id_tax_rules_group' => array( 'title' => $this->l('ID'), 'align' => 'center', 'class' => 'fixed-width-xs' ), 'name' => array( 'title' => $this->l('Name') ), 'is_tourism_tax_rule_group' => array( 'title' => $this->l('Tourism tax'), 'callback' => 'tourismTaxAsLabel', 'badge_success' => true, 'badge_danger' => true, 'type' => 'bool', 'align' => 'center', 'class' => 'fixed-width-sm', 'optional' => true, 'visible_default' => true, 'filter_key' => 'a!is_tourism_tax_rule_group', 'orderby' => false, ), 'active' => array( 'title' => $this->l('Enabled'), 'active' => 'status', 'type' => 'bool', 'orderby' => false, 'align' => 'center', 'class' => 'fixed-width-sm' ) ); $this->bulk_actions = array( 'delete' => array( 'text' => $this->l('Delete selected'), 'confirm' => $this->l('Delete selected items?'), 'icon' => 'icon-trash' ) ); $this->_select = 'a.`is_tourism_tax_rule_group` AS `badge_success`, IF(a.`is_tourism_tax_rule_group`, 0, 1) AS `badge_danger`'; $this->_where .= ' AND a.deleted = 0'; parent::__construct(); } public function tourismTaxAsLabel($value, $row) { return $value ? $this->l('Yes') : $this->l('No'); } public function initPageHeaderToolbar() { if (empty($this->display)) { $this->page_header_toolbar_btn['new_tax_rules_group'] = array( 'href' => self::$currentIndex.'&addtax_rules_group&token='.$this->token, 'desc' => $this->l('Add new tax rules group', null, null, false), 'icon' => 'process-icon-new' ); } parent::initPageHeaderToolbar(); } public function renderList() { $this->addRowAction('edit'); $this->addRowAction('delete'); return parent::renderList(); } /** * Return taxes for the rule-form dropdown, filtered by whether this TRG is a tourism group. * * @return array */ protected function getTaxOptionsForGroup() { $idTaxRulesGroup = (int) Tools::getValue('id_tax_rules_group'); $isTourism = false; if ($idTaxRulesGroup) { $taxRulesGroup = new TaxRulesGroup($idTaxRulesGroup); if (Validate::isLoadedObject($taxRulesGroup)) { $isTourism = (bool) $taxRulesGroup->is_tourism_tax_rule_group; } } $idLang = (int) $this->context->language->id; $sql = new DbQuery(); if ($isTourism) { $sql->select( 't.`id_tax`, tl.`name`,' . ' IFNULL(tourism_tax.`calculation_type`, 0) AS tourism_tax_calc_type,' . ' IFNULL(tourism_tax.`tax_value`, 0) AS tourism_tax_value' ); } else { $sql->select('t.`id_tax`, tl.`name`, t.`rate`'); } $sql->from('tax', 't'); $sql->leftJoin('tax_lang', 'tl', 't.`id_tax` = tl.`id_tax` AND tl.`id_lang` = ' . $idLang); if ($isTourism) { $sql->leftJoin('tax_configuration', 'tourism_tax', 'tourism_tax.`id_tax` = t.`id_tax`'); } $sql->where('t.`deleted` != 1 AND t.`active` = 1'); $sql->where('t.`is_tourism_tax` = ' . ($isTourism ? 1 : 0)); $sql->orderBy('tl.`name` ASC'); $rows = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql); if ($isTourism && is_array($rows)) { $currency = $this->context->currency; $currencySign = Tools::safeOutput(trim($currency->prefix . $currency->suffix)); foreach ($rows as &$row) { $typeLabel = ((int) $row['tourism_tax_calc_type'] === TaxConfiguration::CALCULATION_TYPE_PERCENTAGE) ? (float) $row['tourism_tax_value'] . '%' : $currencySign . ' ' . (float) $row['tourism_tax_value']; $row['name'] = $row['name'] . ' (' . $typeLabel . ')'; } unset($row); } return $rows; } public function initRulesList($id_group) { $this->table = 'tax_rule'; $this->list_id = 'tax_rule'; $this->identifier = 'id_tax_rule'; $this->className = 'TaxRule'; $this->lang = false; $this->list_simple_header = false; $this->toolbar_btn = null; $this->list_no_link = true; $this->bulk_actions = array( 'delete' => array('text' => $this->l('Delete selected'), 'confirm' => $this->l('Delete selected items?'), 'icon' => 'icon-trash') ); $this->fields_list = array( 'country_name' => array( 'title' => $this->l('Country') ), 'state_name' => array( 'title' => $this->l('State') ), 'zipcode' => array( 'title' => $this->l('Zip/Postal code'), 'class' => 'fixed-width-md' ), 'behavior' => array( 'title' => $this->l('Behavior') ), 'rate' => array( 'title' => $this->l('Tax'), 'class' => 'fixed-width-sm', 'callback' => 'displayTaxRuleRate', 'orderby' => false, 'search' => false, ), 'description' => array( 'title' => $this->l('Description') ) ); $this->addRowAction('edit'); $this->addRowAction('delete'); $this->_select = ' c.`name` AS country_name, s.`name` AS state_name, CONCAT_WS(" - ", a.`zipcode_from`, a.`zipcode_to`) AS zipcode, t.`rate`, t.`is_tourism_tax`, IFNULL(tourism_tax.`calculation_type`, 0) AS tourism_tax_calc_type, IFNULL(tourism_tax.`tax_value`, 0) AS tourism_tax_value'; $this->_join = ' LEFT JOIN `'._DB_PREFIX_.'country_lang` c ON (a.`id_country` = c.`id_country` AND id_lang = '.(int)$this->context->language->id.') LEFT JOIN `'._DB_PREFIX_.'state` s ON (a.`id_state` = s.`id_state`) LEFT JOIN `'._DB_PREFIX_.'tax` t ON (a.`id_tax` = t.`id_tax`) LEFT JOIN `'._DB_PREFIX_.'tax_configuration` tourism_tax ON (tourism_tax.`id_tax` = t.`id_tax`)'; $this->_where = 'AND `id_tax_rules_group` = '.(int)$id_group; $this->_use_found_rows = false; $this->show_toolbar = false; $this->tpl_list_vars = array('id_tax_rules_group' => (int)$id_group); $this->_filter = false; return parent::renderList(); } public function renderForm() { $this->fields_form = array( 'legend' => array( 'title' => $this->l('Tax Rules'), 'icon' => 'icon-money' ), 'input' => array( array( 'type' => 'text', 'label' => $this->l('Name'), 'name' => 'name', 'required' => true, 'hint' => $this->l('Invalid characters:').' <>;=#{}' ), array( 'type' => 'switch', 'label' => $this->l('Enable'), 'name' => 'active', 'required' => false, 'is_bool' => true, 'values' => array( array( 'id' => 'active_on', 'value' => 1, 'label' => $this->l('Enabled') ), array( 'id' => 'active_off', 'value' => 0, 'label' => $this->l('Disabled') ) ) ), array( 'type' => 'switch', 'label' => $this->l('Tourism tax rule group'), 'name' => 'is_tourism_tax_rule_group', 'required' => false, 'is_bool' => true, 'hint' => $this->l('Mark this group as tourism-only. All tax rules added here must reference tourism taxes.'), 'values' => array( array('id' => 'is_tourism_tax_rule_on', 'value' => 1, 'label' => $this->l('Yes')), array('id' => 'is_tourism_tax_rule_off', 'value' => 0, 'label' => $this->l('No')), ) ), ), 'submit' => array( 'title' => $this->l('Save and stay'), 'stay' => true ) ); if (Shop::isFeatureActive()) { $this->fields_form['input'][] = array( 'type' => 'shop', 'label' => $this->l('Shop association'), 'name' => 'checkBoxShopAsso', ); } if (!($obj = $this->loadObject(true))) { return; } if (!isset($obj->id)) { $content = parent::renderForm(); } else { $this->page_header_toolbar_btn['new'] = array( 'href' => '#', 'desc' => $this->l('Add a new tax rule') ); $content = parent::renderForm(); $this->tpl_folder = 'tax_rules/'; $content .= $this->initRuleForm(); // We change the variable $ tpl_folder to avoid the overhead calling the file in list_action_edit.tpl in intList (); $content .= $this->initRulesList((int)$obj->id); } return $content; } public function initRuleForm() { $this->fields_form[0]['form'] = array( 'legend' => array( 'title' => $this->l('New tax rule'), 'icon' => 'icon-money' ), 'input' => array( array( 'type' => 'select', 'label' => $this->l('Country'), 'name' => 'country', 'id' => 'country', 'options' => array( 'query' => Country::getCountries($this->context->language->id), 'id' => 'id_country', 'name' => 'name', 'default' => array( 'value' => 0, 'label' => $this->l('All', 'AdminTaxRulesGroupController') ) ) ), array( 'type' => 'select', 'label' => $this->l('State'), 'name' => 'states[]', 'id' => 'states', 'multiple' => true, 'options' => array( 'query' => array(), 'id' => 'id_state', 'name' => 'name', 'default' => array( 'value' => 0, 'label' => $this->l('All', 'AdminTaxRulesGroupController') ) ) ), array( 'type' => 'hidden', 'name' => 'action' ), array( 'type' => 'text', 'label' => $this->l('Zip/postal code range'), 'name' => 'zipcode', 'required' => false, 'hint' => $this->l('You can define a range of Zip/postal codes (e.g., 75000-75015) or simply use one Zip/postal code.') ), array( 'type' => 'select', 'label' => $this->l('Behavior'), 'name' => 'behavior', 'required' => false, 'options' => array( 'query' => array( array( 'id' => 0, 'name' => $this->l('This tax only') ), array( 'id' => 1, 'name' => $this->l('Combine') ), array( 'id' => 2, 'name' => $this->l('One after another') ) ), 'id' => 'id', 'name' => 'name' ), 'hint' => array( $this->l('You must define the behavior if an address matches multiple rules:').'
', $this->l('- This tax only: Will apply only this tax').'
', $this->l('- Combine: Combine taxes (e.g.: 10% + 5% = 15%)').'
', $this->l('- One after another: Apply taxes one after another (e.g.: 0 + 10% = 0 + 5% = 5.5)') ), ), array( 'type' => 'select', 'label' => $this->l('Tax'), 'name' => 'id_tax', 'required' => false, 'options' => array( 'query' => $this->getTaxOptionsForGroup(), 'id' => 'id_tax', 'name' => 'name', 'default' => array( 'value' => 0, 'label' => $this->l('No Tax') ) ), 'hint' => $this->l('Select the tax to apply for this rule.') ), array( 'type' => 'text', 'label' => $this->l('Description'), 'name' => 'description', ) ), 'submit' => array( 'title' => $this->l('Save and stay'), 'stay' => true ) ); if (!($obj = $this->loadObject(true))) { return; } $this->fields_value = array( 'action' => 'create_rule', 'id_tax_rules_group' => $obj->id, 'id_tax_rule' => '' ); $this->getlanguages(); $helper = new HelperForm(); $helper->override_folder = $this->tpl_folder; $helper->currentIndex = self::$currentIndex; $helper->token = $this->token; $helper->table = 'tax_rule'; $helper->identifier = 'id_tax_rule'; $helper->id = $obj->id; $helper->toolbar_scroll = true; $helper->show_toolbar = true; $helper->languages = $this->_languages; $helper->default_form_language = $this->default_form_language; $helper->allow_employee_form_lang = $this->allow_employee_form_lang; $helper->fields_value = $this->getFieldsValue($this->object); $helper->toolbar_btn['save_new_rule'] = array( 'href' => self::$currentIndex.'&id_tax_rules_group='.$obj->id.'&action=create_rule&token='.$this->token, 'desc' => 'Save tax rule', 'class' => 'process-icon-save' ); $helper->submit_action = 'create_rule'; return $helper->generateForm($this->fields_form); } /** * List column callback: render the Tax column in the tax rules list. * Tourism-fixed taxes show the currency sign; tourism-percent and regular VAT taxes show %. * * @param mixed $value t.rate (always 0 for tourism taxes) * @param array $row Full list row — includes is_tourism_tax, tourism_tax_calc_type, tourism_tax_value * @return string */ public function displayTaxRuleRate($value, $row) { return TaxConfiguration::getFormattedRateForDisplay($value, $row, $this->context->currency); } public function postProcess() { if ($this->action == 'save') { $idGroup = (int) Tools::getValue('id_tax_rules_group'); $newIsTourism = (int) Tools::getValue('is_tourism_tax_rule_group'); if ($idGroup) { $taxRulesGroup = new TaxRulesGroup($idGroup); if (Validate::isLoadedObject($taxRulesGroup)) { $oldIsTourism = (int) $taxRulesGroup->is_tourism_tax_rule_group; if ($oldIsTourism && !$newIsTourism) { if ($taxRulesGroup->hasTourismTaxRules()) { $this->errors[] = $this->l( 'Cannot convert this to a VAT group: it still contains tourism taxes. Remove the tourism tax rules first.' ); $_POST['is_tourism_tax_rule_group'] = $oldIsTourism; $this->display = 'edit'; return; } if ($taxRulesGroup->hasProductsUsingAsTourismGroup()) { $this->errors[] = $this->l( 'Cannot convert this to a VAT group: one or more products still use it as their tourism tax rule. Reassign them first.' ); $_POST['is_tourism_tax_rule_group'] = $oldIsTourism; $this->display = 'edit'; return; } } if (!$oldIsTourism && $newIsTourism) { if ($taxRulesGroup->hasVatTaxRules()) { $this->errors[] = $this->l( 'Cannot convert this to a tourism group: it still contains VAT taxes. Remove the VAT tax rules first.' ); $_POST['is_tourism_tax_rule_group'] = $oldIsTourism; $this->display = 'edit'; return; } if ($taxRulesGroup->hasProductsUsingAsVatGroup()) { $this->errors[] = $this->l( 'Cannot convert this to a tourism group: one or more products or services still use it as their regular tax rule. Reassign them first.' ); $_POST['is_tourism_tax_rule_group'] = $oldIsTourism; $this->display = 'edit'; return; } } } } } parent::postProcess(); } public function initProcess() { if (Tools::isSubmit('deletetax_rule')) { if ($this->tabAccess['delete'] === 1) { $this->action = 'delete_tax_rule'; } else { $this->errors[] = Tools::displayError('You do not have permission to delete this.'); } } elseif (Tools::isSubmit('submitBulkdeletetax_rule')) { if ($this->tabAccess['delete'] === 1) { $this->action = 'bulk_delete_tax_rules'; } else { $this->errors[] = Tools::displayError('You do not have permission to delete this.'); } } elseif (Tools::getValue('action') == 'create_rule') { if ($this->tabAccess['add'] === 1) { $this->action = 'create_rule'; } else { $this->errors[] = Tools::displayError('You do not have permission to add this.'); } } else { parent::initProcess(); } } protected function processCreateRule() { $zip_code = Tools::getValue('zipcode'); $id_rule = (int)Tools::getValue('id_tax_rule'); $id_tax = (int)Tools::getValue('id_tax'); $id_tax_rules_group = (int)Tools::getValue('id_tax_rules_group'); $behavior = (int)Tools::getValue('behavior'); $description = pSQL(Tools::getValue('description')); if ((int)($id_country = Tools::getValue('country')) == 0) { $countries = Country::getCountries($this->context->language->id); $this->selected_countries = array(); foreach ($countries as $country) { $this->selected_countries[] = (int)$country['id_country']; } } else { $this->selected_countries = array($id_country); } $this->selected_states = Tools::getValue('states'); if (empty($this->selected_states) || count($this->selected_states) == 0) { $this->selected_states = array(0); } $tax_rules_group = new TaxRulesGroup((int)$id_tax_rules_group); foreach ($this->selected_countries as $id_country) { $first = true; foreach ($this->selected_states as $id_state) { if ($tax_rules_group->hasUniqueTaxRuleForCountry($id_country, $id_state, $id_rule)) { $this->errors[] = Tools::displayError('A tax rule already exists for this country/state with tax only behavior.'); continue; } $taxRule = new TaxRule(); // update or creation? if (isset($id_rule) && $first) { $taxRule->id = $id_rule; $first = false; } $taxRule->id_tax = $id_tax; $tax_rules_group = new TaxRulesGroup((int)$id_tax_rules_group); $taxRule->id_tax_rules_group = (int)$tax_rules_group->id; $taxRule->id_country = (int)$id_country; $taxRule->id_state = (int)$id_state; list($taxRule->zipcode_from, $taxRule->zipcode_to) = $taxRule->breakDownZipCode($zip_code); // Construct Object Country $country = new Country((int)$id_country, (int)$this->context->language->id); if ($zip_code && $country->need_zip_code) { if ($country->zip_code_format) { foreach (array($taxRule->zipcode_from, $taxRule->zipcode_to) as $zip_code) { if ($zip_code) { if (!$country->checkZipCode($zip_code)) { $this->errors[] = sprintf( Tools::displayError('The Zip/postal code is invalid. It must be typed as follows: %s for %s.'), str_replace('C', $country->iso_code, str_replace('N', '0', str_replace('L', 'A', $country->zip_code_format))), $country->name ); } } } } } $taxRule->behavior = (int)$behavior; $taxRule->description = $description; $this->tax_rule = $taxRule; $_POST['id_state'] = $taxRule->id_state; $this->errors = array_merge($this->errors, $this->validateTaxRule($taxRule)); if (count($this->errors) == 0) { $tax_rules_group = $this->updateTaxRulesGroup($tax_rules_group); $taxRule->id = (int)$tax_rules_group->getIdTaxRuleGroupFromHistorizedId((int)$taxRule->id); $taxRule->id_tax_rules_group = (int)$tax_rules_group->id; if (!$taxRule->save()) { $this->errors[] = Tools::displayError('An error has occurred: Cannot save the current tax rule.'); } } } } if (count($this->errors) == 0) { Tools::redirectAdmin( self::$currentIndex.'&'.$this->identifier.'='.(int)$tax_rules_group->id.'&conf=4&update'.$this->table.'&token='.$this->token ); } else { $this->display = 'edit'; } } protected function processBulkDeleteTaxRules() { $this->deleteTaxRule(Tools::getValue('tax_ruleBox')); } protected function processDeleteTaxRule() { $this->deleteTaxRule(array(Tools::getValue('id_tax_rule'))); } protected function deleteTaxRule(array $id_tax_rule_list) { $result = true; foreach ($id_tax_rule_list as $id_tax_rule) { $tax_rule = new TaxRule((int)$id_tax_rule); if (Validate::isLoadedObject($tax_rule)) { $tax_rules_group = new TaxRulesGroup((int)$tax_rule->id_tax_rules_group); $tax_rules_group = $this->updateTaxRulesGroup($tax_rules_group); $tax_rule = new TaxRule($tax_rules_group->getIdTaxRuleGroupFromHistorizedId((int)$id_tax_rule)); if (Validate::isLoadedObject($tax_rule)) { $result &= $tax_rule->delete(); } } } Tools::redirectAdmin( self::$currentIndex.'&'.$this->identifier.'='.(int)$tax_rules_group->id.'&conf=4&update'.$this->table.'&token='.$this->token ); } /** * Check if the tax rule could be added in the database * * @param TaxRule $taxRule * * @return array */ protected function validateTaxRule(TaxRule $taxRule) { // @TODO: check if the rule already exists return $taxRule->validateController(); } protected function displayAjaxUpdateTaxRule() { if ($this->tabAccess['view'] === 1) { $id_tax_rule = Tools::getValue('id_tax_rule'); $tax_rules = new TaxRule((int)$id_tax_rule); $output = array(); foreach ($tax_rules as $key => $result) { $output[$key] = $result; } die(json_encode($output)); } } /** * @param TaxRulesGroup $object * * @return TaxRulesGroup */ protected function updateTaxRulesGroup($object) { static $tax_rules_group = null; if ($tax_rules_group === null) { $object->update(); $tax_rules_group = $object; } return $tax_rules_group; } }