PageRenderTime 9ms CodeModel.GetById 627ms app.highlight 1239ms RepoModel.GetById 274ms app.codeStats 1ms

/avalaratax/avalaratax.php

https://github.com/DaveBenNoah/PrestaShop-modules
PHP | 1491 lines | 1254 code | 123 blank | 114 comment | 200 complexity | e51b25ab6ebf947b42584f07fb5c2883 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2/*
   3* 2007-2011 PrestaShop
   4*
   5* NOTICE OF LICENSE
   6*
   7* This source file is subject to the Academic Free License (AFL 3.0)
   8* that is bundled with this package in the file LICENSE.txt.
   9* It is also available through the world-wide-web at this URL:
  10* http://opensource.org/licenses/afl-3.0.php
  11* If you did not receive a copy of the license and are unable to
  12* obtain it through the world-wide-web, please send an email
  13* to license@prestashop.com so we can send you a copy immediately.
  14*
  15* DISCLAIMER
  16*
  17* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
  18* versions in the future. If you wish to customize PrestaShop for your
  19* needs please refer to http://www.prestashop.com for more information.
  20*
  21*  @author PrestaShop SA <contact@prestashop.com>
  22*  @copyright  2007-2014 PrestaShop SA
  23*  @license    http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
  24*  International Registered Trademark & Property of PrestaShop SA
  25*/
  26
  27// Security
  28if (!defined('_PS_VERSION_'))
  29	exit;
  30
  31spl_autoload_register('avalaraAutoload');
  32
  33class AvalaraTax extends Module
  34{
  35	/**
  36	 * @brief Constructor
  37	 */
  38	public function __construct()
  39	{
  40		$this->name = 'avalaratax';
  41		$this->tab = 'billing_invoicing';
  42		$this->version = '3.4.10';
  43		$this->author = 'PrestaShop';
  44		parent::__construct();
  45
  46		$this->displayName = $this->l('Avalara - AvaTax');
  47		$this->description = $this->l('Sales Tax is complicated. AvaTax makes it easy.');
  48		
  49		/** Backward compatibility */
  50		require(_PS_MODULE_DIR_.$this->name.'/backward_compatibility/backward.php');
  51		
  52		if (!extension_loaded('soap') || !class_exists('SoapClient'))
  53			$this->warning = $this->l('SOAP extension should be enabled on your server to use this module.');
  54	}
  55
  56	/**
  57	 * @brief Installation method
  58	 */
  59	public function install()
  60	{
  61		Configuration::updateValue('AVALARATAX_URL', 'https://avatax.avalara.net');
  62		Configuration::updateValue('AVALARATAX_ADDRESS_VALIDATION', 1);
  63		Configuration::updateValue('AVALARATAX_TAX_CALCULATION', 1);
  64		Configuration::updateValue('AVALARATAX_TIMEOUT', 300);
  65
  66		// Value possible : Development / Production
  67		Configuration::updateValue('AVALARATAX_MODE', 'Production');
  68		Configuration::updateValue('AVALARATAX_ADDRESS_NORMALIZATION', 1);
  69		Configuration::updateValue('AVALARATAX_COMMIT_ID', (int)Configuration::get('PS_OS_DELIVERED')); 
  70		Configuration::updateValue('AVALARATAX_CANCEL_ID', (int)Configuration::get('PS_OS_CANCELED'));
  71		Configuration::updateValue('AVALARATAX_REFUND_ID', (int)Configuration::get('PS_OS_REFUND'));
  72		Configuration::updateValue('AVALARATAX_POST_ID', (int)Configuration::get('PS_OS_PAYMENT')); 
  73		Configuration::updateValue('AVALARATAX_STATE', 1);
  74		Configuration::updateValue('PS_TAX_DISPLAY', 1);
  75		Configuration::updateValue('AVALARATAX_COUNTRY', 0);
  76		Configuration::updateValue('AVALARA_CACHE_MAX_LIMIT', 3600); /* The values in cache will be refreshed every 1 minute by default */
  77
  78		// Make sure Avalara Tables don't exist before installation
  79		Db::getInstance()->Execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'avalara_product_cache`');
  80		Db::getInstance()->Execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'avalara_carrier_cache`');
  81		Db::getInstance()->Execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'avalara_address_validation_cache`');
  82		Db::getInstance()->Execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'avalara_returned_products`');
  83		Db::getInstance()->Execute('DROP TABLE IF EXISTS `'._DB_PREFIX_.'avalara_temp`');
  84
  85		if (!Db::getInstance()->Execute('
  86		CREATE TABLE `'._DB_PREFIX_.'avalara_product_cache` (
  87		`id_cache` int(10) unsigned NOT NULL auto_increment,
  88		`id_product` int(10) unsigned NOT NULL,
  89		`tax_rate` float(8, 2) unsigned NOT NULL,
  90		`region` varchar(2) NOT NULL,
  91		`id_address` int(10) unsigned NOT NULL,
  92		`update_date` datetime,
  93		PRIMARY KEY (`id_cache`),
  94		UNIQUE (`id_product`, `region`),
  95		KEY `id_product2` (`id_product`,`region`,`id_address`))
  96		ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8') ||
  97			!Db::getInstance()->Execute('
  98		CREATE TABLE `'._DB_PREFIX_.'avalara_carrier_cache` (
  99		`id_cache` int(10) unsigned NOT NULL auto_increment,
 100		`id_carrier` int(10) unsigned NOT NULL,
 101		`tax_rate` float(8, 2) unsigned NOT NULL,
 102		`region` varchar(2) NOT NULL,
 103		`amount` float(8, 2) unsigned NOT NULL,
 104		`update_date` datetime,
 105		`id_cart` int(10) unsigned NOT NULL,
 106		`cart_hash` varchar(32) DEFAULT NULL,
 107		PRIMARY KEY (`id_cache`),
 108		KEY `cart_hash` (`cart_hash`))
 109		ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8') ||
 110			!Db::getInstance()->Execute('
 111		CREATE TABLE `'._DB_PREFIX_.'avalara_address_validation_cache` (
 112		`id_avalara_address_validation_cache` int(10) unsigned NOT NULL auto_increment,
 113		`id_address` int(10) unsigned NOT NULL,
 114		`date_add` datetime,
 115		PRIMARY KEY (`id_avalara_address_validation_cache`),
 116		UNIQUE (`id_address`))
 117		ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8') ||
 118			!Db::getInstance()->Execute('
 119		CREATE TABLE `'._DB_PREFIX_.'avalara_returned_products` (
 120		`id_returned_product` int(10) unsigned NOT NULL auto_increment,
 121		`id_order` int(10) unsigned NOT NULL,
 122		`id_product` int(10) unsigned NOT NULL,
 123		`total` float(8, 2) unsigned NOT NULL,
 124		`quantity` int(10) unsigned NOT NULL,
 125		`name` varchar(255) NOT NULL,
 126		`description_short` varchar(255) NULL,
 127		`tax_code` varchar(255) NULL,
 128		PRIMARY KEY (`id_returned_product`))
 129		ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8') ||
 130			!Db::getInstance()->Execute('
 131		CREATE TABLE `'._DB_PREFIX_.'avalara_temp` (
 132		`id_order` int(10) unsigned NOT NULL,
 133		`id_order_detail` int(10) unsigned NOT NULL)
 134		ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8') ||
 135			!Db::getInstance()->Execute('
 136		CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'avalara_taxcodes` (
 137		`id_taxcode` int(10) unsigned NOT NULL auto_increment,
 138		`id_product` int(10) unsigned NOT NULL,
 139		`tax_code`  varchar(30) NOT NULL,
 140		`taxable` int(2) unsigned  NOT NULL DEFAULT 1,
 141		PRIMARY KEY (`id_taxcode`),
 142		UNIQUE (`id_product`))
 143		ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8'))
 144			return false;
 145
 146		if (!parent::install() || !$this->registerHook('leftColumn') || !$this->registerHook('updateOrderStatus') ||
 147			!$this->registerHook('cancelProduct') || !$this->registerHook('adminOrder') || !$this->registerHook('backOfficeTop') ||
 148			!$this->registerHook('header') || !$this->overrideFiles())
 149			return false;
 150		return true;
 151	}
 152
 153	public function uninstall()
 154	{
 155		if (!$this->removeOverrideFiles() || !parent::uninstall() ||
 156			!Configuration::deleteByName('AVALARATAX_URL') ||
 157			!Configuration::deleteByName('AVALARATAX_ADDRESS_VALIDATION') ||
 158			!Configuration::deleteByName('AVALARATAX_TAX_CALCULATION') ||
 159			!Configuration::deleteByName('AVALARATAX_TIMEOUT') ||
 160			!Configuration::deleteByName('AVALARATAX_MODE') ||
 161			!Configuration::deleteByName('AVALARATAX_ACCOUNT_NUMBER') ||
 162			!Configuration::deleteByName('AVALARATAX_COMPANY_CODE') ||
 163			!Configuration::deleteByName('AVALARATAX_LICENSE_KEY') ||
 164			!Configuration::deleteByName('AVALARATAX_ADDRESS_NORMALIZATION') ||
 165			!Configuration::deleteByName('AVALARATAX_ADDRESS_LINE1') ||
 166			!Configuration::deleteByName('AVALARATAX_ADDRESS_LINE2') ||
 167			!Configuration::deleteByName('AVALARATAX_CITY') ||
 168			!Configuration::deleteByName('AVALARATAX_STATE') ||
 169			!Configuration::deleteByName('AVALARATAX_ZIP_CODE') ||
 170			!Configuration::deleteByName('AVALARATAX_COUNTRY') ||
 171			!Configuration::deleteByName('AVALARATAX_COMMIT_ID') ||
 172			!Configuration::deleteByName('AVALARATAX_CANCEL_ID') ||
 173			!Configuration::deleteByName('AVALARATAX_REFUND_ID') ||
 174			!Configuration::deleteByName('AVALARA_CACHE_MAX_LIMIT') ||
 175			!Configuration::deleteByName('AVALARATAX_POST_ID') ||
 176			!Configuration::deleteByName('AVALARATAX_CONFIGURATION_OK') ||			
 177			!Db::getInstance()->Execute('DROP TABLE `'._DB_PREFIX_.'avalara_product_cache`') ||
 178			!Db::getInstance()->Execute('DROP TABLE `'._DB_PREFIX_.'avalara_carrier_cache`') ||
 179			!Db::getInstance()->Execute('DROP TABLE `'._DB_PREFIX_.'avalara_address_validation_cache`') ||
 180			!Db::getInstance()->Execute('DROP TABLE `'._DB_PREFIX_.'avalara_returned_products`') ||
 181			!Db::getInstance()->Execute('DROP TABLE `'._DB_PREFIX_.'avalara_temp`'))
 182			return false;
 183		// Do not remove taxcode table
 184		return true;
 185	}
 186
 187	/**
 188	 * @brief Describe the override schema
 189	 */
 190	protected static function getOverrideInfo()
 191	{
 192		return array(
 193			'Tax.php' => array(
 194				'source' => 'override/classes/tax/Tax.php',
 195				'dest' => 'override/classes/Tax.php',
 196				'md5' => array(
 197					'1.1' => '5d9e318d673bfa723b02f14f952e0a7a',
 198					'2.3' => '86c900cd6fff286aa6a52df2ff72228a',
 199					'3.0.2' => 'c558c0b15877980134e301af34e42c3e',
 200				)
 201			),
 202			'Cart.php' => array(
 203				'source' => 'override/classes/Cart.php',
 204				'dest' => 'override/classes/Cart.php',
 205				'md5' => array(
 206					'3.0.2' => 'e4b05425b6dc61f75aad434265f3cac8',
 207					'3.0.3' => 'f7388cb50fbfd300c9f81cc407b7be83',
 208				)
 209			),
 210			'AddressController.php' => array(
 211				'source' => 'override/controllers/front/AddressController.php',
 212				'dest' => 'override/controllers/AddressController.php',
 213				'md5' => array(
 214					'1.1' => 'ebc4f31298395c4b113c7e2d7cc41b4a',
 215					'3.0.2' => 'ff3d9cb2956c35f4229d5277cb2e92e6',
 216					'3.2.1' => 'bc34c1150f7170d3ec7912eb383cd04b',
 217				)
 218			),
 219			'AuthController.php' => array(
 220				'source' => 'override/controllers/front/AuthController.php',
 221				'dest' => 'override/controllers/AuthController.php',
 222				'md5' => array(
 223					'1.1' => '7304d7af971b30f2dcd401b80bbdf805',
 224					'3.0.2' => '3eb86260a7c8d6cfa1d209fb3e8f8bd6',
 225				)
 226			),
 227		);
 228	}
 229
 230	protected function removeOverrideFiles()
 231	{
 232		/** In v1.5, we do not remove override files */
 233		if (version_compare(_PS_VERSION_, '1.5', '<'))
 234			foreach (self::getOverrideInfo() as $key => $params)
 235			{
 236				if (!file_exists(_PS_ROOT_DIR_.'/'.$params['dest']))
 237					continue;
 238
 239				$md5 = md5_file(_PS_ROOT_DIR_.'/'.$params['dest']);
 240				$removed = false;
 241				foreach ($params['md5'] as $hash)
 242					if ($md5 == $hash)
 243					{
 244						if (unlink(_PS_ROOT_DIR_.'/'.$params['dest']))
 245							$removed = true;
 246						break;
 247					}
 248				if (!$removed)
 249					$this->_errors[] = $this->l('Error while removing override: ').$key;
 250			}
 251
 252		return !isset($this->_errors) || !$this->_errors || !count($this->_errors);
 253	}
 254
 255	protected function overrideFiles()
 256	{
 257		/** In v1.5, we do not copy the override files */
 258		if (version_compare(_PS_VERSION_, '1.5', '<') && $this->removeOverrideFiles())
 259		{
 260			/** Check if the override directories exists */
 261			if (!is_dir(_PS_ROOT_DIR_.'/override/classes/'))
 262				mkdir(_PS_ROOT_DIR_.'/override/classes/', 0777, true);
 263			if (!is_dir(_PS_ROOT_DIR_.'/override/controllers/'))
 264				mkdir(_PS_ROOT_DIR_.'/override/controllers/', 0777, true);
 265
 266			foreach (self::getOverrideInfo() as $key => $params)
 267				if (file_exists(_PS_ROOT_DIR_.'/'.$params['dest']))
 268					$this->_errors[] = $this->l('This override file already exists, please merge it manually: ').$key;
 269				elseif (!copy(_PS_MODULE_DIR_.'avalaratax/'.$params['source'], _PS_ROOT_DIR_.'/'.$params['dest']))
 270						$this->_erroors[] = $this->l('Error while copying the override file: ').$key;
 271		}
 272		return !isset($this->_errors) || !$this->_errors || !count($this->_errors);
 273	}
 274
 275	/******************************************************************/
 276	/** Hook Methods **************************************************/
 277	/******************************************************************/
 278
 279	public function hookAdminOrder($params)
 280	{
 281		$this->purgeTempTable();
 282	}
 283
 284	public function hookCancelProduct($params)
 285	{
 286		if (isset($_POST['cancelProduct']))
 287		{
 288			$order = new Order((int)$_POST['id_order']);
 289			if (!Validate::isLoadedObject($order))
 290				return false;
 291			if ($order->invoice_number)
 292			{
 293				// Get all the cancel product's IDs
 294				$cancelledIdsOrderDetail = array();
 295				foreach ($_POST['cancelQuantity'] as $idOrderDetail => $qty)
 296					if ($qty > 0)
 297						$cancelledIdsOrderDetail[] = (int)$idOrderDetail;
 298				$cancelledIdsOrderDetail = implode(', ', $cancelledIdsOrderDetail);
 299
 300				// Fill temp table
 301				Db::getInstance()->Execute('INSERT INTO '._DB_PREFIX_.'avalara_temp (`id_order`, `id_order_detail`)
 302										VALUES ('.(int)$_POST['id_order'].', '.(int)$params['id_order_detail'].')');
 303				// Check if we are at the end of the loop
 304				$totalLoop = Db::getInstance()->ExecuteS('SELECT COUNT(`id_order`) as totalLines
 305														FROM `'._DB_PREFIX_.'avalara_temp`
 306														WHERE `id_order_detail` IN ('.pSQL($cancelledIdsOrderDetail).')');
 307
 308				if ($totalLoop[0]['totalLines'] != count(array_filter($_POST['cancelQuantity'])))
 309					return false;
 310
 311				// Clean the temp table because we are at the end of the loop
 312				$this->purgeTempTable();
 313
 314				// Get details for cancelledIdsOrderDetail (Grab the info to post to Avalara in English.)
 315				$cancelledProdIdsDetails = Db::getInstance()->ExecuteS('SELECT od.`product_id` as id_product, od.`id_order_detail`, pl.`name`,
 316																		pl.`description_short`, od.`product_price` as price, od.`reduction_percent`,
 317																		od.`reduction_amount`, od.`product_quantity` as quantity, atc.`tax_code`
 318																		FROM '._DB_PREFIX_.'order_detail od
 319																		LEFT JOIN '._DB_PREFIX_.'product p ON (p.id_product = od.product_id)
 320																		LEFT JOIN '._DB_PREFIX_.'product_lang pl ON (pl.id_product = p.id_product)
 321																		LEFT JOIN '._DB_PREFIX_.'avalara_taxcodes atc ON (atc.id_product = p.id_product)
 322																		WHERE pl.`id_lang` = '.(int)Configuration::get('PS_LANG_DEFAULT').' AND od.`id_order` = '.(int)$_POST['id_order'].'
 323																		AND od.`id_order_detail` IN ('.pSQL($cancelledIdsOrderDetail).')');
 324				// Build the product list
 325				$products = array();
 326				foreach ($cancelledProdIdsDetails as $cancelProd)
 327					$products[] = array('id_product' => (int)$cancelProd['id_product'],
 328												'quantity' => (int)$_POST['cancelQuantity'][$cancelProd['id_order_detail']],
 329												'total' => pSQL($_POST['cancelQuantity'][$cancelProd['id_order_detail']] * ($cancelProd['price'] - ($cancelProd['price'] * ($cancelProd['reduction_percent'] / 100)) - $cancelProd['reduction_amount'])), // Including those product with discounts
 330												'name' => pSQL(Tools::safeOutput($cancelProd['name'])),
 331												'description_short' => pSQL(Tools::safeOutput($cancelProd['description_short']), true),
 332												'tax_code' => pSQL(Tools::safeOutput($cancelProd['tax_code'])));
 333				// Send to Avalara
 334				$commitResult = $this->getTax($products, array('type' => 'ReturnInvoice', 'DocCode' => (int)$_POST['id_order']));
 335				if ($commitResult['ResultCode'] == 'Warning' || $commitResult['ResultCode'] == 'Error' || $commitResult['ResultCode'] == 'Exception')
 336					echo $this->_displayConfirmation($this->l('The following error was generated while cancelling the orders you selected. <br /> - '.
 337							Tools::safeOutput($commitResult['Messages']['Summary'])), 'error');
 338				else
 339				{
 340					$this->commitToAvalara(array('id_order' => (int)$_POST['id_order']));
 341					echo $this->_displayConfirmation($this->l('The products you selected were cancelled.'));
 342				}
 343			}
 344		}
 345	}
 346
 347	protected function getDestinationAddress($id_order)
 348	{
 349		$order = new Order((int)$id_order);
 350		if (!Validate::isLoadedObject($order))
 351			return false;
 352
 353		$address = new Address((int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
 354		if (!Validate::isLoadedObject($address))
 355			return false;
 356
 357		$state = null;
 358		if (!empty($address->id_state))
 359		{
 360			$state = new State((int)$address->id_state);
 361			if (!Validate::isLoadedObject($state))
 362				return false;
 363		}
 364
 365		return array($address, $state, $order);
 366	}
 367
 368	public function hookUpdateOrderStatus($params)
 369	{
 370		list($params['address'], $params['state'], $params['order']) = self::getDestinationAddress((int)$params['id_order']);
 371
 372		if ($params['newOrderStatus']->id == (int)Configuration::get('AVALARATAX_COMMIT_ID'))
 373			return $this->commitToAvalara($params);
 374		elseif ($params['newOrderStatus']->id == (int)Configuration::get('AVALARATAX_CANCEL_ID'))
 375		{
 376			$params['CancelCode'] = 'V';
 377			$this->cancelFromAvalara($params);
 378			return $this->cancelFromAvalara($params);
 379		}
 380		elseif ($params['newOrderStatus']->id == (int)Configuration::get('AVALARATAX_POST_ID'))
 381			return $this->postToAvalara($params);
 382		elseif ($params['newOrderStatus']->id == (int)Configuration::get('AVALARATAX_REFUND_ID'))
 383			return $this->commitToAvalara($params);
 384
 385		return false;
 386	}
 387
 388	public function hookBackOfficeTop()
 389	{
 390		if (Tools::isSubmit('submitAddproduct') || Tools::isSubmit('submitAddproductAndStay'))
 391			Db::getInstance()->Execute('REPLACE INTO `'._DB_PREFIX_.'avalara_taxcodes` (`id_product`, `tax_code`)
 392				VALUES ('.(isset($_GET['id_product']) ? (int)$_GET['id_product'] : 0).', \''.pSQL(Tools::safeOutput($_POST['tax_code'])).'\')');
 393
 394
 395		if ((isset($_GET['updateproduct']) || isset($_GET['addproduct'])) && isset($_GET['id_product']) && (int)$_GET['id_product'])
 396		{
 397			$r = Db::getInstance()->getRow('
 398			SELECT `tax_code`
 399			FROM `'._DB_PREFIX_.'avalara_taxcodes` atc
 400			WHERE atc.`id_product` = '.(int)Tools::getValue('id_product'));
 401
 402			if (version_compare(_PS_VERSION_, '1.5', '<')) /* v1.4.x an older */
 403			{
 404				return '
 405				<script type="text/javascript">
 406					$(function() {
 407						// Add the Tax Code field
 408						$(\'<tr><td class="col-left">'.$this->l('Tax Code (Avalara)').':</td><td style="padding-bottom:5px;"><input type="text" style="width: 130px; margin-right: 5px;" value="'.
 409							($r ? Tools::safeOutput($r['tax_code']) : '').'" name="tax_code" maxlength="13" size="55"></td></tr>\').appendTo(\'#product #step1 table:eq(0) tbody\');
 410
 411						// override original tax rules
 412						$(\'span #id_tax_rules_group\').parent().html(\'Avalara\');
 413					});
 414				</script>';
 415			}
 416			elseif (version_compare(_PS_VERSION_, '1.6', '<')) /* v1.5.x */
 417			{
 418				return '
 419				<script type="text/javascript">
 420					$(function() {
 421						var done = false;
 422						// Add the Tax Code field
 423						$(\'#link-Informations\').click(function() {
 424										if (done == false) {
 425														done = true;
 426														$(\'<tr><td class="col-left"><label for="tax_code">'.$this->l('Tax Code:').'</label></td><td style="padding-bottom:5px;"><input type="text" style="width: 130px; margin-right: 5px;" value="'.
 427														($r ? Tools::safeOutput($r['tax_code']) : '').'" name="tax_code" maxlength="13" size="55"> <span class="small">(Avalara)</span></td></tr>\').appendTo(\'#step1 table:first tbody\');
 428										}
 429						});
 430
 431						// override original tax rules
 432						$(\'#link-Prices\').click(function() {
 433										$(\'span #id_tax_rules_group\').parent().html(\'Avalara\');
 434						});
 435					});
 436				</script>';
 437			}
 438			else /* v1.6.x and newer */
 439			{
 440				return '
 441				<script type="text/javascript">
 442					$(function() {
 443						var done = false;
 444						// Add the Tax Code field
 445						$(\'#link-Prices\').click(function() {
 446							if (done == false) {
 447								done = true;
 448								$(\'#id_tax_rules_group\').parent().parent().parent().parent().html(\'<div class="form-group"><label class="control-label col-lg-3" for="tax_code"><span class="label-tooltip" data-toggle="tooltip" title="" data-original-title="'.$this->l('Tax rules will be handled by Avalara').'">'.$this->l('Tax Code (Avalara):').'</span></label><div class="input-group col-lg-4"><input type="text" value="'.($r ? Tools::safeOutput($r['tax_code']) : '').'" name="tax_code" maxlength="13" /><div class="alert alert-info" style="margin-top: 40px;">'.$this->l('Tax rules will be handled by Avalara').'</div></div>\');
 449							}
 450						});
 451					});
 452				</script>';
 453			}
 454		}
 455		elseif ((Tools::isSubmit('updatecarrier') || Tools::isSubmit('addcarrier')) && Tools::getValue('id_carrier'))
 456			return '<script type="text/javascript">
 457			$(function() {
 458				// override original tax rules
 459				$(\'div #id_tax_rules_group\').parent().html(\'<label class="t">Avalara</label>\');
 460			});
 461		</script>';
 462
 463		if (Tools::getValue('tab') == 'AdminTaxes' || Tools::getValue('tab') == 'AdminTaxRulesGroup' || Tools::getValue('controller') == 'admintaxes' || Tools::getValue('controller') == 'admintaxrulesgroup')
 464		{
 465			// JS for 1.5
 466			if (version_compare(_PS_VERSION_, '1.5', '>'))
 467				return '<script type="text/javascript">
 468				$(function() {
 469								$(\'#content form\').hide();
 470								$(\'#desc-tax-new\').hide();
 471								$(\'#desc-tax-save\').hide();
 472								$(\'#content div:first\').append(\'<div class="warn">'.$this->l('Tax rules are overwritten by Avalara Tax Module.').'</div>\');
 473				});
 474				</script>';
 475			// JS for 1.4
 476			return '<script type="text/javascript">
 477				$(function() {
 478				if ($(\'#Taxes\').size() || $(\'#submitFiltertax_rules_group\').size())
 479					$(\'#content\').prepend(\'<div class="warn"><img src="../img/admin/warn2.png">'.
 480			$this->l('Tax rules are overwritten by Avalara Tax Module.').'</div>\');
 481				});
 482				</script>';
 483		}
 484		return '';
 485	}
 486
 487	public function hookHeader()
 488	{
 489		if (!$this->context->cart || ((int)$this->context->cart->id_customer && !(int)$this->context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}))
 490			$id_address = (int)(Db::getInstance()->getValue('SELECT `id_address` FROM `'._DB_PREFIX_.'address` WHERE `id_customer` = '.(int)($this->context->cart->id_customer).' AND `deleted` = 0 ORDER BY `id_address`'));
 491		else
 492			$id_address = $this->context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')};
 493
 494		if (!(int)$id_address)
 495			return ;
 496		return '<script type="text/javascript">
 497				function refresh_taxes(count)
 498				{
 499					$.ajax({
 500						type : \'POST\',
 501						url : \''.$this->_path.'\' + \'ajax.php\',
 502						data : {
 503							\'id_cart\': "'.(int)$this->context->cart->id.'",
 504							\'id_lang\': "'.(int)$this->context->cookie->id_lang.'",
 505							\'id_address\': "'.(int)$id_address.'",
 506							\'ajax\': "getProductTaxRate",
 507							\'token\': "'.md5(_COOKIE_KEY_.Configuration::get('PS_SHOP_NAME')).'",
 508						},
 509						dataType: \'json\',
 510						success : function(d) {
 511							if (d.hasError == false && d.cached_tax == true)
 512											$(\'#total_tax\').html($(\'body\').data(\'total_tax\'));
 513							else if (d.hasError == false && d.cached_tax == false)
 514											$(\'#total_tax\').html(d.total_tax);
 515							else
 516											$(\'#total_tax\').html(\''.$this->l('Error while calculating taxes').'\');
 517						}
 518					});
 519				}
 520
 521		$(function() {
 522			/* ajax call to cache taxes product taxes that exist in the current cart */
 523			$(\'body\').data(\'total_tax\', $(\'#total_tax\').text());
 524			$(\'#total_tax\').html(\'<img src="img/loader.gif" alt="" />\');
 525			refresh_taxes(1);
 526		});
 527		</script>';
 528	}
 529
 530	/******************************************************************/
 531	/** Main Form Methods *********************************************/
 532	/******************************************************************/
 533	public function getContent()
 534	{
 535		$buffer = '';
 536		
 537		if (version_compare(_PS_VERSION_,'1.5','>'))
 538			$this->context->controller->addJQueryPlugin('fancybox');
 539		else
 540			$buffer .= '<script type="text/javascript" src="'.__PS_BASE_URI__.'js/jquery/jquery.fancybox-1.3.4.js"></script>
 541		  	<link type="text/css" rel="stylesheet" href="'.__PS_BASE_URI__.'css/jquery.fancybox-1.3.4.css" />';
 542
 543		if (Tools::isSubmit('SubmitAvalaraTaxSettings')) 
 544		{
 545			Configuration::updateValue('AVALARATAX_ACCOUNT_NUMBER', Tools::getValue('avalaratax_account_number'));
 546			Configuration::updateValue('AVALARATAX_LICENSE_KEY', Tools::getValue('avalaratax_license_key'));
 547			Configuration::updateValue('AVALARATAX_URL', Tools::getValue('avalaratax_url'));
 548			Configuration::updateValue('AVALARATAX_COMPANY_CODE', Tools::getValue('avalaratax_company_code'));
 549
 550			$connectionTestResult = $this->_testConnection();
 551			if (strpos($connectionTestResult[0], 'Error') === false)
 552			{
 553				Configuration::updateValue('AVALARATAX_CONFIGURATION_OK', true); 
 554				$buffer .= $this->_displayConfirmation();
 555			}
 556		}
 557		elseif (Tools::isSubmit('SubmitAvalaraTaxOptions'))
 558		{
 559			Configuration::updateValue('AVALARATAX_ADDRESS_VALIDATION', Tools::getValue('avalaratax_address_validation'));
 560			Configuration::updateValue('AVALARATAX_TAX_CALCULATION', Tools::getValue('avalaratax_tax_calculation'));
 561			Configuration::updateValue('AVALARATAX_TIMEOUT', (int)Tools::getValue('avalaratax_timeout'));
 562			Configuration::updateValue('AVALARATAX_ADDRESS_NORMALIZATION', Tools::getValue('avalaratax_address_normalization'));
 563			Configuration::updateValue('AVALARATAX_TAX_OUTSIDE', Tools::getValue('avalaratax_tax_outside'));
 564			Configuration::updateValue('AVALARA_CACHE_MAX_LIMIT', (int)Tools::getValue('avalara_cache_max_limit'));
 565
 566			$buffer .= $this->_displayConfirmation();
 567		}
 568		elseif (Tools::isSubmit('SubmitAvalaraTestConnection'))
 569			$connectionTestResult = $this->_testConnection();
 570		elseif (Tools::isSubmit('SubmitAvalaraAddressOptions'))
 571		{
 572			/* Validate address*/
 573			$address = new Address();
 574			$address->address1 = Tools::getValue('avalaratax_address_line1');
 575			$address->address2 = Tools::getValue('avalaratax_address_line2');
 576			$address->city = Tools::getValue('avalaratax_city');
 577			$address->id_state = State::getIdByIso(Tools::getValue('avalaratax_state'));
 578			$address->id_country = Tools::getValue('avalaratax_country');
 579			$address->postcode = Tools::getValue('avalaratax_zip_code');
 580
 581			$normalizedAddress = $this->validateAddress($address);
 582			if (isset($normalizedAddress['ResultCode']) && $normalizedAddress['ResultCode'] == 'Success')
 583			{
 584				$buffer .= $this->_displayConfirmation($this->l('The address you submitted has been validated.'));
 585				Configuration::updateValue('AVALARATAX_ADDRESS_LINE1', $normalizedAddress['Normalized']['Line1']);
 586				Configuration::updateValue('AVALARATAX_ADDRESS_LINE2', $normalizedAddress['Normalized']['Line2']);
 587				Configuration::updateValue('AVALARATAX_CITY', $normalizedAddress['Normalized']['City']);
 588				Configuration::updateValue('AVALARATAX_STATE', $normalizedAddress['Normalized']['Region']);
 589				Configuration::updateValue('AVALARATAX_COUNTRY', $normalizedAddress['Normalized']['Country']);
 590				Configuration::updateValue('AVALARATAX_ZIP_CODE', $normalizedAddress['Normalized']['PostalCode']);
 591			}
 592			else
 593			{
 594				$message = $this->l('The following error was generated while validating your address:');
 595				if (isset($normalizedAddress['Exception']['FaultString']))
 596					$message .= '<br /> - '.Tools::safeOutput($normalizedAddress['Exception']['FaultString']);
 597				if (isset($normalizedAddress['Messages']['Summary']))
 598					foreach ($normalizedAddress['Messages']['Summary'] as $summary)
 599						$message .= '<br /> - '.Tools::safeOutput($summary);
 600				$buffer .= $this->_displayConfirmation($message, 'error');
 601
 602				Configuration::updateValue('AVALARATAX_ADDRESS_LINE1', Tools::getValue('avalaratax_address_line1'));
 603				Configuration::updateValue('AVALARATAX_ADDRESS_LINE2', Tools::getValue('avalaratax_address_line2'));
 604				Configuration::updateValue('AVALARATAX_CITY', Tools::getValue('avalaratax_city'));
 605				Configuration::updateValue('AVALARATAX_STATE', Tools::getValue('avalaratax_state'));
 606				Configuration::updateValue('AVALARATAX_ZIP_CODE', Tools::getValue('avalaratax_zip_code'));
 607			}
 608		}
 609		elseif (Tools::isSubmit('SubmitAvalaraTaxClearCache'))
 610		{
 611			Db::getInstance()->Execute('TRUNCATE TABLE `'._DB_PREFIX_.'avalara_product_cache`');
 612			Db::getInstance()->Execute('TRUNCATE TABLE `'._DB_PREFIX_.'avalara_carrier_cache`');
 613
 614			$buffer .= $this->_displayConfirmation('Cache cleared!');
 615		}
 616
 617		$confValues = Configuration::getMultiple(array(
 618										// Configuration
 619										'AVALARATAX_ACCOUNT_NUMBER', 'AVALARATAX_LICENSE_KEY', 'AVALARATAX_URL', 'AVALARATAX_COMPANY_CODE',
 620										// Options
 621										'AVALARATAX_ADDRESS_VALIDATION', 'AVALARATAX_TAX_CALCULATION', 'AVALARATAX_TIMEOUT',
 622										'AVALARATAX_ADDRESS_NORMALIZATION', 'AVALARATAX_TAX_OUTSIDE', 'AVALARATAX_COMMIT_ID', 'AVALARATAX_CANCEL_ID',
 623										'AVALARATAX_REFUND_ID', 'AVALARATAX_POST_ID', 'AVALARA_CACHE_MAX_LIMIT',
 624										// Default Address
 625										'AVALARATAX_ADDRESS_LINE1', 'AVALARATAX_ADDRESS_LINE2', 'AVALARATAX_CITY', 'AVALARATAX_STATE',
 626										'AVALARATAX_ZIP_CODE', 'AVALARATAX_COUNTRY'));
 627
 628		$stateList = array();
 629		$stateList[] = array('id' => '0', 'name' => $this->l('Choose your state (if applicable)'), 'iso_code' => '--');
 630		foreach (State::getStates((int)$this->context->cookie->id_lang) as $state)
 631			$stateList[] = array('id' => $state['id_state'], 'name' => $state['name'], 'iso_code' => $state['iso_code']);
 632
 633		$countryList = array();
 634		$countryList[] = array('id' => '0', 'name' => $this->l('Choose your country'), 'iso_code' => '--');
 635		foreach (Country::getCountries((int)$this->context->cookie->id_lang, false, null, false) as $country)
 636			$countryList[] = array('id' => $country['id_country'], 'name' => $country['name'], 'iso_code' => $country['iso_code']);
 637
 638		$buffer .= '<link href="'.$this->_path.'css/avalara.css" rel="stylesheet" type="text/css">
 639		<script type="text/javascript">
 640			/* Fancybox */
 641			$(\'a.avalara-video-btn\').live(\'click\', function(){
 642			    $.fancybox({
 643			        \'type\' : \'iframe\',
 644			        \'href\' : this.href.replace(new RegExp("watch\\?v=", "i"), \'embed\') + \'?rel=0&autoplay=1\',
 645			        \'swf\': {\'allowfullscreen\':\'true\', \'wmode\':\'transparent\'},
 646			        \'overlayShow\' : true,
 647			        \'centerOnScroll\' : true,
 648			        \'speedIn\' : 100,
 649			        \'speedOut\' : 50,
 650			        \'width\' : 853,
 651			        \'height\' : 480
 652			    });
 653			    return false;
 654			});
 655		</script>
 656		<script type="text/javascript">
 657			$(document).ready(function(){
 658			    var height1 = 0;
 659			    var height = 0;
 660			     $(\'.field-height1\').each(function(){
 661				if (height1 < $(this).height())
 662				    height1 = $(this).height();
 663			    });
 664			    
 665			    $(\'.field-height\').each(function(){
 666				if (height < $(this).height())
 667				    height = $(this).height();
 668			    });
 669			
 670				$(\'.field-height1\').css({\'height\' : $(\'.field-height1\').css(\'height\', height1+\'px\')});
 671			    $(\'.field-height\').css({\'height\' : $(\'.field-height\').css(\'height\', height+\'px\')});
 672
 673updateAvalaraTaxState($(\'#avalaratax_country\').val());
 674
 675    $(\'#avalaratax_country\').change(function(){
 676updateAvalaraTaxState($(this).val());
 677    });
 678
 679
 680			});
 681function updateAvalaraTaxState(iso_code)
 682{
 683
 684var default_state = "'.$confValues['AVALARATAX_STATE'].'";
 685
 686$(\'#avalaratax_state\').html(\'\');
 687	$.ajax({
 688	    type : \'GET\',
 689	    url : \'../modules/avalaratax/states.php?country_iso_code=\'+iso_code,
 690	    dataType: \'JSON\',
 691	    success: function(data)
 692	    {
 693		if (data != 0)
 694		{
 695		    $.each(data[iso_code], function(i, item){
 696if (default_state == item.state_iso_code)
 697			$(\'#avalaratax_state\').append(\'<option  selected="selected" value="\'+item.state_iso_code+\'">\'+item.name+\'</option>\');
 698else
 699			$(\'#avalaratax_state\').append(\'<option  value="\'+item.state_iso_code+\'">\'+item.name+\'</option>\');
 700			$(\'#avalaratax_state\').show();
 701			$(\'#avalaratax_label_state\').show();
 702		    });
 703		}
 704		else
 705		{
 706		    $(\'#avalaratax_state\').hide();
 707		    $(\'#avalaratax_label_state\').hide();
 708		}
 709	    }
 710	});
 711
 712}
 713
 714		</script>
 715		<div class="avalara-wrap">
 716			<p class="avalara-intro"><a href="http://www.info.avalara.com/prestashop" class="avalara-logo" target="_blank"><img src="'.$this->_path.'img/avalara_logo.png" alt="Avalara" border="0" /></a><a href="http://www.info.avalara.com/prestashop" class="avalara-link" target="_blank">'.$this->l('Create an account').'</a>'.$this->l('Avalara and PrestaShop have partnered to provide the easiest way for you to accurately calculate and file sales tax.').'</p>
 717			<div class="clear"></div>
 718			<div class="avalara-content">
 719				<div class="avalara-video">
 720					<a href="http://www.youtube.com/embed/tm1tENVdcQ8" class="avalara-video-btn"><img src="'.$this->_path.'img/avalara-video-screen.jpg" alt="Avalara Video" /><img src="'.$this->_path.'img/btn-video.png" alt="" class="video-icon" /></a>
 721				</div>
 722				<h3>'.$this->l('Doing sales tax right is simple with Avalara.').'</h3>
 723				<p>'.$this->l('We do all of the research and automate the process for you, ensuring that the system is up-to-date with the most recent sales tax and VAT rates and rules in every state and country, so you don’t have to.  As a cloud-based service, AvaTax eliminates ongoing maintenance and support.  It provides you with a complete solution to manage your sales tax needs.').'</p>
 724				<img src="'.$this->_path.'img/avatax_badge.png" alt="AvaTax Certified" class="avatax-badge" />
 725				<ul>
 726					<li>'.$this->l('Address Validation included').'</li>
 727					<li>'.$this->l('Rooftop Accurate Calculations').'</li>
 728					<li>'.$this->l('Product and Service Taxability Rules').'</li>
 729					<li>'.$this->l('Exemption Certificate Management').'</li>
 730					<li>'.$this->l('Out-of-the-Box Sales Tax Reporting').'</li>
 731				</ul>
 732				<a href="http://www.info.avalara.com/prestashop" class="avalara-link" target="_blank">'.$this->l('Create an account').'</a>
 733				<p class="contact-avalara"><a href="http://www.info.avalara.com/prestashop" target="_blank">'.$this->l('Contact Avalara Today to Start Your Service').'</a></p>
 734			</div>
 735			<fieldset class="field-height1 right-fieldset">
 736			<legend><img src="'.$this->_path.'img/icon-console.gif" alt="" />'.$this->l('AvaTax Admin Console').'</legend>
 737				<p><a href="https://admin-avatax.avalara.net/" target="_blank">'.$this->l('Log-in to AvaTax Admin Console').'</a></p>
 738				<a href="https://admin-avatax.avalara.net/" target="_blank"><img src="'.$this->_path.'img/avatax-logo.png" alt="AvaTax" class="avatax-logo" /></a>
 739			</fieldset>
 740			<form action="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'" method="post" class="left-form">
 741				<fieldset class="field-height1">
 742					<legend><img src="'.$this->_path.'img/icon-config.gif" alt="" />'.$this->l('Configuration').'</legend>
 743					<h4>'.$this->l('AvaTax Credentials').'</h4>';	
 744					if (isset($connectionTestResult))
 745						$buffer .= '<div id="test_connection" style="background: '.Tools::safeOutput($connectionTestResult[1]).';">'.$connectionTestResult[0].'</div>';
 746			
 747					$buffer .= '<label>'.$this->l('Account Number').'</label>
 748					<div class="margin-form">
 749						<input type="text" name="avalaratax_account_number" value="'.(isset($confValues['AVALARATAX_ACCOUNT_NUMBER']) ? Tools::safeOutput($confValues['AVALARATAX_ACCOUNT_NUMBER']) : '').'" />
 750					</div>
 751					<label>'.$this->l('License Key').'</label>
 752					<div class="margin-form">
 753						<input type="text" name="avalaratax_license_key" value="'.(isset($confValues['AVALARATAX_LICENSE_KEY']) ? Tools::safeOutput($confValues['AVALARATAX_LICENSE_KEY']) : '').'" />
 754					</div>
 755					<label>'.$this->l('URL').'</label>
 756					<div class="margin-form">
 757						<input type="text" name="avalaratax_url" value="'.(isset($confValues['AVALARATAX_URL']) ? Tools::safeOutput($confValues['AVALARATAX_URL']) : '').'" />
 758					</div>
 759					<label>'.$this->l('Company Code').'</label>
 760					<div class="margin-form">
 761						<input type="text" name="avalaratax_company_code" value="'.(isset($confValues['AVALARATAX_COMPANY_CODE']) ? Tools::safeOutput($confValues['AVALARATAX_COMPANY_CODE']) : '').'" /> '.$this->l('Located in the top-right corner of your AvaTax Admin Console').'
 762					</div>
 763					<div class="margin-form">
 764						<input type="submit" class="button" name="SubmitAvalaraTaxSettings" value="'.$this->l('Save Settings').'" /><img src="'.$this->_path.'img/icon-connection.gif" alt="" class="icon-connection" /><input type="submit" id="avalaratax_test_connection" class="button" name="SubmitAvalaraTestConnection" value="'.$this->l('Click here to Test Connection').'" />
 765					</div>
 766				</fieldset>
 767			</form>
 768			<form action="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'" method="post" class="form-half reset-label">
 769				<fieldset class="field-height MR7">
 770					<legend><img src="'.$this->_path.'img/icon-options.gif" alt="" />'.$this->l('Options').'</legend>
 771					<label>'.$this->l('Enable address validation').'</label>
 772					<div class="margin-form">
 773						<input type="checkbox" name="avalaratax_address_validation" value="1"'.(isset($confValues['AVALARATAX_ADDRESS_VALIDATION']) && $confValues['AVALARATAX_ADDRESS_VALIDATION'] ? ' checked="checked"' : '').' />
 774						('.$this->l('Not compatible with One Page Checkout').')
 775					</div>
 776					<label>'.$this->l('Enable tax calculation').'</label>
 777					<div class="margin-form">
 778						<input type="checkbox" name="avalaratax_tax_calculation" value="1" '.(isset($confValues['AVALARATAX_TAX_CALCULATION']) && $confValues['AVALARATAX_TAX_CALCULATION'] ? ' checked="checked"' : '').' />
 779					</div>
 780					<label>'.$this->l('Enable address normalization in uppercase').'</label>
 781					<div class="margin-form">
 782						<input type="checkbox" name="avalaratax_address_normalization" value="1" '.(isset($confValues['AVALARATAX_ADDRESS_NORMALIZATION']) && $confValues['AVALARATAX_ADDRESS_NORMALIZATION'] ? ' checked="checked"' : '').' />
 783					</div>
 784					<label>'.$this->l('Enable tax calculation outside of your state').'</label>
 785					<div class="margin-form">
 786						<input type="checkbox" name="avalaratax_tax_outside" value="1" '.(isset($confValues['AVALARATAX_TAX_OUTSIDE']) && $confValues['AVALARATAX_TAX_OUTSIDE'] ? ' checked="checked"' : '').' />
 787					</div>
 788					<label>'.$this->l('Request timeout').'</label>
 789					<div class="margin-form">
 790						<input type="text" name="avalaratax_timeout" value="'.(isset($confValues['AVALARATAX_TIMEOUT']) ? Tools::safeOutput($confValues['AVALARATAX_TIMEOUT']) : '').'" style="width: 40px;" /> '.$this->l('seconds').'
 791					</div>
 792					<label>'.$this->l('Refresh tax rate cache every x seconds (default 3600):').'</label>
 793					<div class="margin-form">
 794						<input type="text" name="avalara_cache_max_limit" value="'.(isset($confValues['AVALARA_CACHE_MAX_LIMIT']) ? (int)Tools::safeOutput($confValues['AVALARA_CACHE_MAX_LIMIT']) : '').'" style="width: 40px;" /> '.$this->l('seconds').'
 795					</div>
 796					<div class="margin-form">
 797						<input type="submit" class="button avalaratax_button" name="SubmitAvalaraTaxOptions" value="'.$this->l('Save Settings').'" />
 798						<input type="submit" class="button avalaratax_button" name="SubmitAvalaraTaxClearCache" value="'.$this->l('Clear Cache').'" style="display: none;"/>
 799					</div>
 800					<div class="sep"></div>
 801					<h4>'.$this->l('Default Post/Commit/Cancel/Refund Options').'</h4>
 802					<span class="avalara-info">'.$this->l('When an order\'s status is updated, the following options will be used to update Avalara\'s records.').'</span>';
 803	
 804			// Check if the order status exist
 805			$orderStatusList = array();
 806			foreach (Db::getInstance()->ExecuteS('SELECT `id_order_state`, `name` FROM `'._DB_PREFIX_.'order_state_lang` WHERE `id_lang` = '.(int)$this->context->cookie->id_lang) as $v)
 807				$orderStatusList[$v['id_order_state']] = Tools::safeOutput($v['name']);
 808			$buffer .= '<table class="avalara-table" cellspacing="0" cellpadding="0" width="100%">
 809						<th>'.$this->l('Action').'</th>
 810						<th>'.$this->l('Order status in your store').'</th>
 811						<tr>
 812							<td class="avalaratax_column">'.$this->l('Post order to Avalara').':</td>
 813							<td>'.(isset($orderStatusList[Configuration::get('AVALARATAX_POST_ID')]) ? html_entity_decode(Tools::safeOutput($orderStatusList[Configuration::get('AVALARATAX_POST_ID')])) :
 814								'<div style="color: red">'.$this->l('[ERROR] A default value was not found. Please, restore PrestaShop\'s default statuses.').'</div>').'
 815							</td>
 816						</tr>
 817						<tr>
 818							<td class="avalaratax_column">'.$this->l('Commit order to Avalara').':</td>
 819							<td>'.(isset($orderStatusList[Configuration::get('AVALARATAX_COMMIT_ID')]) ? html_entity_decode(Tools::safeOutput($orderStatusList[Configuration::get('AVALARATAX_COMMIT_ID')])) :
 820								'<div style="color: red">'.$this->l('[ERROR] A default value was not found. Please, restore PrestaShop\'s default statuses.').'</div>').'
 821							</td>
 822						</tr>
 823						<tr>
 824							<td class="avalaratax_column">'.$this->l('Delete order from Avalara').':</td>
 825							<td>'.(isset($orderStatusList[Configuration::get('AVALARATAX_CANCEL_ID')]) ? html_entity_decode(Tools::safeOutput($orderStatusList[Configuration::get('AVALARATAX_CANCEL_ID')])) :
 826								'<div style="color: red">'.$this->l('[ERROR] A default value was not found. Please, restore PrestaShop\'s default statuses.').'</div>').'
 827							</td>
 828						</tr>
 829						<tr>
 830							<td class="avalaratax_column last">'.$this->l('Void order in Avalara').':</td>
 831							<td class="last">'.(isset($orderStatusList[Configuration::get('AVALARATAX_REFUND_ID')]) ? html_entity_decode(Tools::safeOutput($orderStatusList[Configuration::get('AVALARATAX_REFUND_ID')])) :
 832								'<div style="color: red">'.$this->l('[ERROR] A default value was not found. Please, restore PrestaShop\'s default statuses.').'</div>').'
 833							</td>
 834						</tr>
 835					</table>
 836				</fieldset>
 837			</form>
 838			<form action="'.Tools::safeOutput($_SERVER['REQUEST_URI']).'" method="post" class="form-half">
 839				<fieldset class="field-height ML7">
 840					<legend><img src="'.$this->_path.'img/icon-address.gif" alt="" />'.$this->l('Default Origin Address and Tax Information').'</legend>
 841					<label>'.$this->l('Address Line 1').'</label>
 842					<div class="margin-form">
 843						<input type="text" name="avalaratax_address_line1" value="'.(isset($confValues['AVALARATAX_ADDRESS_LINE1']) ? Tools::safeOutput($confValues['AVALARATAX_ADDRESS_LINE1']) : '').'" />
 844					</div>
 845					<label>'.$this->l('Address Line 2').'</label>
 846					<div class="margin-form">
 847						<input type="text" name="avalaratax_address_line2" value="'.(isset($confValues['AVALARATAX_ADDRESS_LINE2']) ? Tools::safeOutput($confValues['AVALARATAX_ADDRESS_LINE2']) : '').'" />
 848					</div>
 849					<label>'.$this->l('City').'</label>
 850					<div class="margin-form">
 851						<input type="text" name="avalaratax_city" value="'.(isset($confValues['AVALARATAX_CITY']) ? Tools::safeOutput($confValues['AVALARATAX_CITY']) : '').'" />
 852					</div>
 853					<label>'.$this->l('Zip Code').'</label>
 854					<div class="margin-form">
 855						<input type="text" name="avalaratax_zip_code" value="'.(isset($confValues['AVALARATAX_ZIP_CODE']) ? Tools::safeOutput($confValues['AVALARATAX_ZIP_CODE']) : '').'" />
 856					</div>
 857					<label>'.$this->l('Country').'</label>
 858					<div class="margin-form">
 859						<select name="avalaratax_country" id="avalaratax_country">';
 860			foreach ($countryList as $country)
 861				$buffer .= '<option value="'.substr(strtoupper($country['iso_code']), 0, 2).'" '.($country['iso_code'] == $confValues['AVALARATAX_COUNTRY'] ? ' selected="selected"' : '').'>'.Tools::safeOutput($country['name']).'</option>';
 862			$buffer .= '</select>
 863					</div>
 864					<label id="avalaratax_label_state" >'.$this->l('State').'</label>
 865					<div class="margin-form">
 866						<select name="avalaratax_state" id="avalaratax_state">';
 867			foreach ($stateList as $state)
 868				$buffer .= '<option value="'.substr(strtoupper($state['iso_code']), 0, 2).'" '.($state['iso_code'] == $confValues['AVALARATAX_STATE'] ? ' selected="selected"' : '').'>'.Tools::safeOutput($state['name']).'</option>';
 869			
 870			$buffer .= '</select>
 871					</div>
 872					<div class="margin-form">
 873						<input type="submit" class="button" name="SubmitAvalaraAddressOptions" value="'.$this->l('Save Settings').'" />
 874					</div>
 875				</fieldset>
 876			</form>
 877			<div class="clear"></div>
 878		</div>';
 879		
 880		return $buffer;
 881	}
 882
 883	/*
 884	** Display a custom message for settings update
 885	** $text string Text to be displayed in the message
 886	** $type string (confirm|warn|error) Decides what color will the message have (green|yellow)
 887	*/
 888	private function _displayConfirmation($text = '', $type = 'confirm')
 889	{
 890		if ($type == 'confirm')
 891			$img = 'ok.gif';
 892		elseif ($type == 'warn')
 893			$img = 'warn2.png';
 894		elseif ($type == 'error')
 895			$img = 'disabled.gif';
 896		else
 897			die('Invalid type.');
 898
 899		return '<div class="conf '.Tools::safeOutput($type).'">
 900			<img src="../img/admin/'.$img.'" alt="" title="" />
 901			'.(empty($text) ? $this->l('Settings updated') : $text).
 902			'<img src="http://www.prestashop.com/modules/avalaratax.png?sid='.urlencode(Configuration::get('AVALARATAX_ACCOUNT_NUMBER')).'" style="float: right;" />
 903		</div>';
 904	}
 905
 906	/**
 907	 * @brief init the Avatax SDK
 908	 */
 909	private function _connectToAvalara()
 910	{
 911		$timeout = Configuration::get('AVALARATAX_TIMEOUT');
 912		if ((int)$timeout > 0)
 913			ini_set('max_execution_time', (int)$timeout);
 914
 915		include_once(dirname(__FILE__).'/sdk/AvaTax.php');
 916
 917		/* Just instantiate the ATConfig class to init the settings (mandatory...) */
 918		new ATConfig(Configuration::get('AVALARATAX_MODE'), array('url' => Configuration::get('AVALARATAX_URL'), 'account' => Configuration::get('AVALARATAX_ACCOUNT_NUMBER'),
 919				'license' => Configuration::get('AVALARATAX_LICENSE_KEY'), 'trace' => false));
 920	}
 921
 922	/**
 923	 * @brief Connect to Avalara to make sure everything is OK
 924	 */
 925	private function _testConnection()
 926	{
 927		$this->_connectToAvalara();
 928		try
 929		{
 930			$client = new TaxServiceSoap(Configuration::get('AVALARATAX_MODE'));
 931			$connectionTest = $client->ping();
 932			if ($connectionTest->getResultCode() == SeverityLevel::$Success)
 933			{
 934				try
 935				{
 936					$authorizedTest = $client->isAuthorized('GetTax');
 937					if ($authorizedTest->getResultCode() == SeverityLevel::$Success)
 938						$expirationDate = $authorizedTest->getexpires();
 939				}
 940				catch (SoapFault $exception)
 941				{
 942				}
 943
 944				return array('<img src="../img/admin/ok.gif" alt="" /><strong style="color: green;">'.$this->l('Connection Test performed successfully.').'</strong><br /><br />'.$this->l('Ping version is:').' '.Tools::safeOutput($connectionTest->getVersion()).(isset($expirationDate) ? '<br /><br />'.$this->l('License Expiration Date:').' '.Tools::safeOutput($expirationDate) : ''), '#D6F5D6');
 945			}
 946		}
 947		catch (SoapFault $exception)
 948		{
 949			return array('<img src="../img/admin/forbbiden.gif" alt="" /><b style="color: #CC0000;">'.$this->l('Connection Test Failed.').'</b><br /><br />'.$this->l('Either the Account or License Key is incorrect. Please confirm the Account and License Key before testing the connection again.').'<br /><br /><strong style="color: #CC0000;">'.$this->l('Error(s):').' '.Tools::safeOutput($exception->faultstring).'</strong>', '#FFD8D8');
 950		}
 951	}
 952
 953	/**
 954	 * @brief Validates a given address
 955	 */
 956	public function validateAddress(Address $address)
 957	{
 958		$this->_connectToAvalara();
 959		$client = new AddressServiceSoap(Configuration::get('AVALARATAX_MODE'));
 960
 961		if (!empty($address->id_state))
 962			$state = new State((int)$address->id_state);
 963		if (!empty($address->id_country))
 964			$country = new Country((int)$address->id_country);
 965
 966		$avalaraAddress = new AvalaraAddress($address->address1, $address->address2, null, $address->city,
 967											(isset($state) ? $state->iso_code : null), $address->postcode, (isset($country) ? $country->iso_code : null), 0);
 968
 969		$buffer = array();
 970		try
 971		{
 972			$request = new ValidateRequest($avalaraAddress, TextCase::$Upper, false);
 973			$result = $client->Validate($request);
 974			$addresses = $result->ValidAddresses;
 975
 976			$buffer['ResultCode'] = Tools::safeOutput($result->getResultCode());
 977			if ($result->getResultCode() != SeverityLevel::$Success)
 978				foreach ($result->getMessages() as $msg)
 979				{
 980					$buffer['Messages']['Name'][] = Tools::safeOutput($msg->getName());
 981					$buffer['Messages']['Summary'][] = Tools::safeOutput($msg->getSummary());
 982				}
 983			else
 984				foreach ($result->getvalidAddresses() as $valid)
 985				{
 986					$buffer['Normalized']['Line1'] = Tools::safeOutput($valid->getline1());
 987					$buffer['Normalized']['Line2'] = Tools::safeOutput($valid->getline2());
 988					$buffer['Normalized']['City']= Tools::safeOutput($valid->getcity());
 989					$buffer['Normalized']['Region'] = Tools::safeOutput($valid->getregion());
 990					$buffer['Normalized']['PostalCode'] = Tools::safeOutput($valid->getpostalCode());
 991					$buffer['Normalized']['Country'] = Tools::safeOutput($valid->getcountry());
 992					$buffer['Normalized']['County'] = Tools::safeOutput($valid->getcounty());
 993					$buffer['Normalized']['FIPS'] = Tools::safeOutput($valid->getfipsCode());
 994					$buffer['Normalized']['PostNet'] = Tools::safeOutput($valid->getpostNet());
 995					$buffer['Normalized']['CarrierRoute'] = Tools::safeOutput($valid->getcarrierRoute());
 996					$buffer['Normalized']['AddressType'] = Tools::safeOutput($valid->getaddressType());
 997				}
 998		}
 999		catch (SoapFault $exception)
1000		{
1001			$buffer['Exception']['FaultString'] = Tools::safeOutput($exception->faultstring);
1002			$buffer['Exception']['LastRequest'] = Tools::safeOutput($client->__getLastRequest());
1003			$buffer['Exception']['LastResponse'] = Tools::safeOutput($client->__getLastResponse());
1004		}
1005		return $buffer;
1006	}
1007
1008	/**
1009	 * @brief Executes tax actions on documents
1010	 *
1011	 * @param Array $products Array of Product for which taxes need to be calculated
1012	 * @param Array $params
1013	 * 		type : (default SalesOrder) SalesOrder|SalesInvoice|ReturnInvoice
1014	 * 		cart : (required for SalesOrder and SalesInvoice) Cart object
1015	 * 		DocC…

Large files files are truncated, but you can click here to view the full file