Skip to content
This repository has been archived by the owner on Jun 10, 2019. It is now read-only.

Commit

Permalink
Topological sorting implemented, XML fix for sorting removed.
Browse files Browse the repository at this point in the history
  • Loading branch information
icurdinj committed Dec 23, 2016
1 parent f89f23f commit 1c1c5e4
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 15 deletions.
16 changes: 1 addition & 15 deletions app/code/local/Inchoo/PHP7/etc/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,11 @@
<config>
<modules>
<Inchoo_PHP7>
<version>2.0.0</version>
<version>2.0.1</version>
</Inchoo_PHP7>
</modules>

<global>
<!-- Fix for incorrect sorting in the calculation of the discount -->
<sales>
<quote>
<totals>
<msrp>
<before>grand_total</before>
</msrp>
<shipping>
<after>subtotal,freeshipping,tax_subtotal,msrp</after>
</shipping>
</totals>
</quote>
</sales>

<helpers>
<core>
<rewrite>
Expand Down
250 changes: 250 additions & 0 deletions app/code/local/Mage/Sales/Model/Config/Ordered.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
<?php
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magento.com for more information.
*
* @category Mage
* @package Mage_Sales
* @copyright Copyright (c) 2006-2015 X.commerce, Inc. (http://www.magento.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/

/**
* Configuration class for ordered items
*
* @category Mage
* @package Mage_Sales
* @author Magento Core Team <[email protected]>
*/
abstract class Mage_Sales_Model_Config_Ordered extends Mage_Core_Model_Config_Base
{
/**
* Cache key for collectors
*
* @var string|null
*/
protected $_collectorsCacheKey = null;

/**
* Configuration path where to collect registered totals
*
* @var string|null
*/
protected $_totalsConfigNode = null;

/**
* Prepared models
*
* @var array
*/
protected $_models = array();

/**
* Models configuration
*
* @var array
*/
protected $_modelsConfig = array();

/**
* Sorted models
*
* @var array
*/
protected $_collectors = array();

/**
* Initialize total models configuration and objects
*
* @return Mage_Sales_Model_Config_Ordered
*/
protected function _initModels()
{
$totalsConfig = $this->getNode($this->_totalsConfigNode);

foreach ($totalsConfig->children() as $totalCode => $totalConfig) {
$class = $totalConfig->getClassName();
if (!empty($class)) {
$this->_models[$totalCode] = $this->_initModelInstance($class, $totalCode, $totalConfig);
}
}
return $this;
}

/**
* Init model class by configuration
*
* @abstract
* @param string $class
* @param string $totalCode
* @param array $totalConfig
* @return mixed
*/
abstract protected function _initModelInstance($class, $totalCode, $totalConfig);

/**
* Prepare configuration array for total model
*
* @param string $code
* @param Mage_Core_Model_Config_Element $totalConfig
* @return array
*/
protected function _prepareConfigArray($code, $totalConfig)
{
$totalConfig = (array)$totalConfig;
if (isset($totalConfig['before'])) {
$totalConfig['before'] = explode(',', $totalConfig['before']);
} else {
$totalConfig['before'] = array();
}
if (isset($totalConfig['after'])) {
$totalConfig['after'] = explode(',', $totalConfig['after']);
} else {
$totalConfig['after'] = array();
}
$totalConfig['_code'] = $code;
return $totalConfig;
}

/**
* Aggregate before/after information from all items and sort totals based on this data
*
* @return array
*/
protected function _getSortedCollectorCodes()
{
if (Mage::app()->useCache('config')) {
$cachedData = Mage::app()->loadCache($this->_collectorsCacheKey);
if ($cachedData) {
return unserialize($cachedData);
}
}
$configArray = $this->_modelsConfig;
// invoke simple sorting if the first element contains the "sort_order" key
reset($configArray);
$element = current($configArray);
if (isset($element['sort_order'])) {
uasort($configArray, array($this, '_compareSortOrder'));
$sortedCollectors = array_keys($configArray);
} else {
$sortedCollectors = array_keys($configArray);
// Move all totals with before specification in front of related total

foreach ($configArray as $code => &$data) {
foreach ($data['before'] as $positionCode) {
if (!isset($configArray[$positionCode])) {
continue;
}
if (!in_array($code, $configArray[$positionCode]['after'], true)) {
// Also add additional after condition for related total,
// to keep it always after total with before value specified
$configArray[$positionCode]['after'][] = $code;
}
$currentPosition = array_search($code, $sortedCollectors, true);
$desiredPosition = array_search($positionCode, $sortedCollectors, true);
if ($currentPosition > $desiredPosition) {
// Only if current position is not corresponding to before condition
array_splice($sortedCollectors, $currentPosition, 1); // Removes existent
array_splice($sortedCollectors, $desiredPosition, 0, $code); // Add at new position
}
}
}
// Sort out totals with after position specified
foreach ($configArray as $code => &$data) {
$maxAfter = null;
$currentPosition = array_search($code, $sortedCollectors, true);

foreach ($data['after'] as $positionCode) {
$maxAfter = max($maxAfter, array_search($positionCode, $sortedCollectors, true));
}

if ($maxAfter !== null && $maxAfter > $currentPosition) {
// Moves only if it is in front of after total
array_splice($sortedCollectors, $maxAfter + 1, 0, $code); // Add at new position
array_splice($sortedCollectors, $currentPosition, 1); // Removes existent
}
}
}
if (Mage::app()->useCache('config')) {
Mage::app()->saveCache(serialize($sortedCollectors), $this->_collectorsCacheKey, array(
Mage_Core_Model_Config::CACHE_TAG
)
);
}
return $sortedCollectors;
}

/**
* Initialize collectors array.
* Collectors array is array of total models ordered based on configuration settings
*
* @return Mage_Sales_Model_Config_Ordered
*/
protected function _initCollectors()
{
$sortedCodes = $this->_getSortedCollectorCodes();
foreach ($sortedCodes as $code) {
$this->_collectors[$code] = $this->_models[$code];
}

return $this;
}

/**
* Callback that uses after/before for comparison
*
* @param array $a
* @param array $b
* @return int
*/
protected function _compareTotals($a, $b)
{
$aCode = $a['_code'];
$bCode = $b['_code'];
if (in_array($aCode, $b['after']) || in_array($bCode, $a['before'])) {
$res = -1;
} elseif (in_array($bCode, $a['after']) || in_array($aCode, $b['before'])) {
$res = 1;
} else {
$res = 0;
}
return $res;
}

/**
* Callback that uses sort_order for comparison
*
* @param array $a
* @param array $b
* @return int
*/
protected function _compareSortOrder($a, $b)
{
if (!isset($a['sort_order']) || !isset($b['sort_order'])) {
return 0;
}
if ($a['sort_order'] > $b['sort_order']) {
$res = 1;
} elseif ($a['sort_order'] < $b['sort_order']) {
$res = -1;
} else {
$res = 0;
}
return $res;
}
}
7 changes: 7 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Change Log
## [unreleased]
### Removed
- XML totals sort order fix from config.xml

### Added
- topological sort implemented in Mage_Sales_Model_Config_Ordered override (this should fix not only Magento Core problems with totals sorting, but also ensure 3rd party extensions which add something to totals work properly with PHP 7.)

## [2.0.0] 2016-10-18
### Changed
- this is the release for Magento core 1.9.3; older versions are supported from _1.9.2.4_ branch and 1.* releases.
Expand Down

0 comments on commit 1c1c5e4

Please sign in to comment.