diff --git a/classes/CartRule.php b/classes/CartRule.php index 0600ec990..6118deda4 100644 --- a/classes/CartRule.php +++ b/classes/CartRule.php @@ -445,6 +445,7 @@ class CartRuleCore extends ObjectModel if ($this->minimum_amount) { + // Minimum amount is converted to the default currency $minimum_amount = $this->minimum_amount; if ($this->minimum_amount_currency != Configuration::get('PS_CURRENCY_DEFAULT')) { @@ -454,9 +455,32 @@ class CartRuleCore extends ObjectModel else $minimum_amount = $this->minimum_amount / $minimum_amount_currency->conversion_rate; } + $cartTotal = $context->cart->getOrderTotal($this->minimum_amount_tax, Cart::ONLY_PRODUCTS); if ($this->minimum_amount_shipping) $cartTotal += $context->cart->getOrderTotal($this->minimum_amount_tax, Cart::ONLY_SHIPPING); + + // If a product is given for free in this rule and already in the cart, the price is subtracted + if ($this->gift_product) + { + $in_cart = (bool)Db::getInstance()->getValue('SELECT id_product FROM '._DB_PREFIX_.'cart_product WHERE id_product = '.(int)$this->gift_product.' AND id_cart = '.(int)$context->cart->id); + if ($in_cart) + { + $ref = false; + $product_price = Product::getPriceStatic( + $this->gift_product, + $this->minimum_amount_tax, + null, null, null, null, null, null, null, + $context->cart->id_customer ? $context->cart->id_customer : null, + $context->cart->id, + (int)$context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')} ? (int)$context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')} : null, + $ref, null, null, + $context, null + ); + $cartTotal -= $product_price; + } + } + if ($cartTotal < $minimum_amount) return Tools::displayError('You do not reach the minimum amount required to use this voucher'); } diff --git a/controllers/admin/AdminCartRulesController.php b/controllers/admin/AdminCartRulesController.php index e7a7e04cc..f5672d9f9 100644 --- a/controllers/admin/AdminCartRulesController.php +++ b/controllers/admin/AdminCartRulesController.php @@ -239,7 +239,7 @@ class AdminCartRulesControllerCore extends AdminController foreach ($results as $row) $attributes[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row; Context::getContext()->smarty->assign('product_rule_itemlist', $attributes); - $choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch(); + $choose_content = $this->createTemplate('controllers/cart_rules/product_rule_itemlist.tpl')->fetch(); Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content); break; case 'products': @@ -253,7 +253,7 @@ class AdminCartRulesControllerCore extends AdminController foreach ($results as $row) $products[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row; Context::getContext()->smarty->assign('product_rule_itemlist', $products); - $choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch(); + $choose_content = $this->createTemplate('controllers/cart_rules/product_rule_itemlist.tpl')->fetch(); Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content); break; case 'manufacturers': @@ -265,7 +265,7 @@ class AdminCartRulesControllerCore extends AdminController foreach ($results as $row) $products[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row; Context::getContext()->smarty->assign('product_rule_itemlist', $products); - $choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch(); + $choose_content = $this->createTemplate('controllers/cart_rules/product_rule_itemlist.tpl')->fetch(); Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content); break; case 'suppliers': @@ -277,7 +277,7 @@ class AdminCartRulesControllerCore extends AdminController foreach ($results as $row) $products[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row; Context::getContext()->smarty->assign('product_rule_itemlist', $products); - $choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch(); + $choose_content = $this->createTemplate('controllers/cart_rules/product_rule_itemlist.tpl')->fetch(); Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content); break; case 'categories': @@ -291,12 +291,12 @@ class AdminCartRulesControllerCore extends AdminController foreach ($results as $row) $categories[in_array($row['id'], $selected) ? 'selected' : 'unselected'][] = $row; Context::getContext()->smarty->assign('product_rule_itemlist', $categories); - $choose_content = $this->createTemplate('product_rule_itemlist.tpl')->fetch(); + $choose_content = $this->createTemplate('controllers/cart_rules/product_rule_itemlist.tpl')->fetch(); Context::getContext()->smarty->assign('product_rule_choose_content', $choose_content); break; } - return $this->createTemplate('product_rule.tpl')->fetch(); + return $this->createTemplate('controllers/cart_rules/product_rule.tpl')->fetch(); } public function ajaxProcess() diff --git a/override/classes/_Hook.php b/override/classes/_Hook.php new file mode 100644 index 000000000..7a9bd9238 --- /dev/null +++ b/override/classes/_Hook.php @@ -0,0 +1,56 @@ + +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision: 7310 $ +* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +// +// IMPORTANT : don't forget to delete the underscore _ in the file name if you want to use it ! +// + +class Hook extends HookCore +{ + private static $hookTime = array(); + private static $hookMemoryUsage = array(); + + public static function getHookTime() + { + return self::$hookTime; + } + + public static function getHookMemoryUsage() + { + return self::$hookMemoryUsage; + } + + public static function exec($hook_name, $hookArgs = array(), $id_module = null) + { + $memoryUsage = memory_get_usage(); + $t0 = microtime(true); + $result = parent::exec($hook_name, $hookArgs, $id_module); + self::$hookTime[$hook_name] = microtime(true) - $t0; + self::$hookMemoryUsage[$hook_name] = memory_get_usage() - $memoryUsage; + return $result; + } +} diff --git a/override/classes/controller/_Controller.php b/override/classes/controller/_Controller.php new file mode 100644 index 000000000..b399c2240 --- /dev/null +++ b/override/classes/controller/_Controller.php @@ -0,0 +1,419 @@ + +* @copyright 2007-2011 PrestaShop SA +* @version Release: $Revision: 10840 $ +* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) +* International Registered Trademark & Property of PrestaShop SA +*/ + +// +// IMPORTANT : don't forget to delete the underscore _ in the file name if you want to use it ! +// + +function developpementErrorHandler($errno, $errstr, $errfile, $errline) +{ + if (!(error_reporting() & $errno)) + return; + switch($errno) + { + case E_ERROR: + echo '[PHP Error #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_WARNING: + echo '[PHP Warning #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_PARSE: + echo '[PHP Parse #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_NOTICE: + echo '[PHP Notice #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_CORE_ERROR: + echo '[PHP Core #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_CORE_WARNING: + echo '[PHP Core warning #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_COMPILE_ERROR: + echo '[PHP Compile #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_COMPILE_WARNING: + echo '[PHP Compile warning #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_USER_ERROR: + echo '[PHP Error #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_USER_WARNING: + echo '[PHP User warning #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_USER_NOTICE: + echo '[PHP User notice #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_STRICT: + echo '[PHP Strict #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + case E_RECOVERABLE_ERROR: + echo '[PHP Recoverable error #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + break; + default: + echo '[PHP Unknown error #'.$errno.'] '.$errstr.' ('.$errfile.', line '.$errline.')'; + } + die; + return true; +} + +abstract class Controller extends ControllerCore +{ + public $_memory = array(); + public $_time = array(); + private static $_footer = true; + + public static function disableParentCalls() + { + self::$_footer = false; + } + + private function displayMemoryColor($n) + { + $n /= 1048576; + if ($n > 3) + return ''.round($n, 2).' Mb'; + if ($n > 1) + return ''.round($n, 2).' Mb'; + return ''.round($n, 2).' Mb'; + } + + private function displaySQLQueries($n) + { + if ($n > 150) + return ''.$n.' queries'; + if ($n > 100) + return ''.$n.' queries'; + return ''.$n.' quer'.($n == 1 ? 'y' : 'ies').''; + } + + private function displayLoadTimeColor($n, $kikoo = false) + { + if ($n > 1) + return ''.round($n, 3).'s'.($kikoo ? '
You\'d better run your shop on a toaster' : ''); + if ($n > 0.5) + return ''.round($n * 1000).'ms'.($kikoo ? '
I hope it is a shared hosting' : ''); + return ''.round($n * 1000).'ms'.($kikoo ? '
Good boy! That\'s what I call a webserver!' : ''); + } + + private function getTimeColor($n) + { + if ($n > 4) + return 'style="color:red"'; + if ($n > 2) + return 'style="color:orange"'; + return 'style="color:green"'; + } + + private function getQueryColor($n) + { + if ($n > 5) + return 'style="color:red"'; + if ($n > 2) + return 'style="color:orange"'; + return 'style="color:green"'; + } + + private function getTableColor($n) + { + if ($n > 30) + return 'style="color:red"'; + if ($n > 20) + return 'style="color:orange"'; + return 'style="color:green"'; + } + + private function getObjectModelColor($n) + { + if ($n > 50) + return 'style="color:red"'; + if ($n > 10) + return 'style="color:orange"'; + return 'style="color:green"'; + } + + public function __construct() + { + parent::__construct(); + + // error management + set_error_handler('developpementErrorHandler'); + ini_set('html_errors', 'on'); + ini_set('display_errors', 'on'); + error_reporting(E_ALL | E_STRICT); + + if (!self::$_footer) + return; + + $this->_memory['config'] = memory_get_usage(); + $this->_time['config'] = microtime(true); + + parent::__construct(); + $this->_memory['constructor'] = memory_get_usage(); + $this->_time['constructor'] = microtime(true); + } + + public function run() + { + $this->init(); + $this->_memory['init'] = memory_get_usage(); + $this->_time['init'] = microtime(true); + + if ($this->checkAccess()) + { + $this->_memory['checkAccess'] = memory_get_usage(); + $this->_time['checkAccess'] = microtime(true); + + if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className))) + $this->setMedia(); + $this->_memory['setMedia'] = memory_get_usage(); + $this->_time['setMedia'] = microtime(true); + + // postProcess handles ajaxProcess + $this->postProcess(); + $this->_memory['postProcess'] = memory_get_usage(); + $this->_time['postProcess'] = microtime(true); + + if (!empty($this->redirect_after)) + $this->redirect(); + + if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className))) + $this->initHeader(); + $this->_memory['initHeader'] = memory_get_usage(); + $this->_time['initHeader'] = microtime(true); + + $this->initContent(); + $this->_memory['initContent'] = memory_get_usage(); + $this->_time['initContent'] = microtime(true); + + if (!$this->content_only && ($this->display_footer || (isset($this->className) && $this->className))) + $this->initFooter(); + $this->_memory['initFooter'] = memory_get_usage(); + $this->_time['initFooter'] = microtime(true); + + // default behavior for ajax process is to use $_POST[action] or $_GET[action] + // then using displayAjax[action] + if ($this->ajax) + { + $action = Tools::getValue('action'); + if (!empty($action) && method_exists($this, 'displayAjax'.Tools::toCamelCase($action))) + $this->{'displayAjax'.$action}(); + elseif (method_exists($this, 'displayAjax')) + $this->displayAjax(); + } + else + $this->displayDebug(); + } + else + { + $this->initCursedPage(); + $this->displayDebug(); + } + } + + function ini_get_display_errors() + { + $a = 'display_errors'; + $b = ini_get($a); + switch (strtolower($b)) + { + case 'on': + case 'yes': + case 'true': + return 'assert.active' !== $a; + case 'stdout': + case 'stderr': + return 'display_errors' === $a; + default: + return (bool)(int)$b; + } + } + + private function sizeofvar($var) + { + $start_memory = memory_get_usage(); + $tmp = unserialize(serialize($var)); + $size = memory_get_usage() - $start_memory; + return $size; + } + + public function displayDebug() + { + global $start_time; + + $this->display(); + $this->_memory['display'] = memory_get_usage(); + $this->_time['display'] = microtime(true); + + if (!$this->ini_get_display_errors()) + return; + + $hr = '
'; + + $totalSize = 0; + foreach (get_included_files() as $file) + $totalSize += filesize($file); + + $totalQueryTime = 0; + foreach (Db::getInstance()->queries as $data) + $totalQueryTime += $data['time']; + + $hooktime = Hook::getHookTime(); + arsort($hooktime); + $totalHookTime = 0; + foreach ($hooktime as $time) + $totalHookTime += $time; + + $hookMemoryUsage = Hook::getHookMemoryUsage(); + arsort($hookMemoryUsage); + $totalHookMemoryUsage = 0; + foreach ($hookMemoryUsage as $usage) + $totalHookMemoryUsage += $usage; + + $globalSize = array(); + $totalGlobalSize = 0; + foreach ($GLOBALS as $key => $value) + if ($key != 'GLOBALS') + { + $totalGlobalSize += ($size = $this->sizeofvar($value)); + if ($size > 1024) + $globalSize[$key] = round($size / 1024, 1); + } + arsort($globalSize); + + echo '

+
+ Load time: '.$this->displayLoadTimeColor($this->_time['display'] - $start_time, true).''; + if (self::$_footer) + echo ''; + echo '
+
+ Hook processing: '.$this->displayLoadTimeColor($totalHookTime).' / '.$this->displayMemoryColor($totalHookMemoryUsage).' + +
+
+ Memory peak usage: '.$this->displayMemoryColor(memory_get_peak_usage()).''; + if (self::$_footer) + { + echo ''; + } + echo '
'; + + echo ' +
+ DB type: '.get_class(Db::getInstance()).' +
SQL Queries: '.$this->displaySQLQueries(count(Db::getInstance()->queries)).' +
Time spent querying: '.$this->displayLoadTimeColor($totalQueryTime).' +
+
+ Included files: '.sizeof(get_included_files()).'
+ Size of included files: '.$this->displayMemoryColor($totalSize).' +
+
+ Globals (> 1 Ko only): '.round($totalGlobalSize / 1024).' Ko + +
'; + + echo ' +
+ +
+
+

Stopwatch (with SQL_NO_CACHE) (total = '.count(Db::getInstance()->queries).')

'; + $queries = Db::getInstance()->queries; + uasort($queries, 'prestashop_querytime_sort'); + foreach ($queries as $data) + echo $hr.'getTimeColor($data['time'] * 1000).'>'.round($data['time'] * 1000, 3).' ms '.$data['query'].'
in '.$data['file'].':'.$data['line']; + echo '
+
+

Doubles (IDs replaced by "XX") (total = '.count(Db::getInstance()->uniqQueries).')

'; + $queries = Db::getInstance()->uniqQueries; + arsort($queries); + foreach ($queries as $q => $nb) + echo $hr.'getQueryColor($nb).'>'.$nb.' '.$q; + echo '
+
+

Tables stress

'; + $tables = Db::getInstance()->tables; + arsort($tables); + foreach ($tables as $table => $nb) + echo $hr.'getTableColor($nb).'>'.$nb.' '.$table; + echo '
'; + + if (isset(ObjectModel::$debug_list)) + { + echo '
+

ObjectModel instances

'; + $list = ObjectModel::$debug_list; + uasort($list, create_function('$a,$b', 'return (count($a) < count($b)) ? 1 : -1;')); + $i = 0; + foreach ($list as $class => $info) + { + echo $hr.'getObjectModelColor(count($info)).'>'.count($info).' '; + echo ''.$class.''; + echo ''; + $i++; + } + echo '
'; + } + } +} + +function prestashop_querytime_sort($a, $b) +{ + if ($a['time'] == $b['time']) + return 0; + return ($a['time'] > $b['time']) ? -1 : 1; +} diff --git a/override/classes/db/_Db.php b/override/classes/db/_Db.php index e0c672afa..7d96d5b3e 100644 --- a/override/classes/db/_Db.php +++ b/override/classes/db/_Db.php @@ -92,9 +92,17 @@ abstract class Db extends DbCore $result = parent::query($sql); $end = microtime(true); + // Save details + $timeSpent = $end - $start; + $trace = debug_backtrace(false); + while (preg_match('@[/\\\\]classes[/\\\\]db[/\\\\]@i', $trace[0]['file'])) + array_shift($trace); + $this->queries[] = array( - 'query' => $sql, - 'time' => ($end - $start) + 'query' => $sql, + 'time' => $timeSpent, + 'file' => $trace[0]['file'], + 'line' => $trace[0]['line'], ); return $result;