排序算法:Magento 结帐总额排序错误导致运费计算错误

Sort algorithm: Magento checkout totals sorted wrongly causing wrong shipping tax calculation(排序算法:Magento 结帐总额排序错误导致运费计算错误)
本文介绍了排序算法:Magento 结帐总额排序错误导致运费计算错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

在 Magento 中有一个功能,您可以通过指定运行总计之前和之后的总计来定义总计计算的顺序.

我添加了一个自定义总数,如果我将以下几行添加到 config.xml,排序是错误的.错误的意思是:tax_shipping before shipping.这会导致运费税金增加两次.

但这违反了条件

tax_shipping之后:航运

我的猜测:在全套规则中肯定有一些矛盾.但是我怎么才能找到它呢?

这是我添加的唯一规则.如果没有此规则,tax_shipping 将排在 shipping 之后.

<class>n98_shippingprotection/quote_address_total_shippingprotectionTax</class><after>小计,折扣,运费,税</after><before>grand_total</before></shippingprotectiontax>

下面我将 usort 调用返回的排序数组粘贴到 Mage_Sales_Model_Quote_Address_Total_Collector::_getSortedCollectorCodes()对于没有安装Magento的,代码是这样的:

/*** uasort回调函数** @param 数组 $a* @param 数组 $b* @return 整数*/受保护的函数 _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;} 别的 {$res = 0;}返回 $res;}受保护的函数 _getSortedCollectorCodes(){...uasort($configArray, array($this, '_compareTotals'));Mage::log('排序:');//这会产生下面的输出$loginfo = "";foreach($configArray as $code=>$data) {$loginfo .= "$code
";$loginfo .= "after: ".implode(',',$data['after'])."
";$loginfo .= "before: ".implode(',',$data['before'])."
";$loginfo .= "
";}法师::日志($loginfo);...

日志输出:

名义后:之前:小计,grand_total小计之后:名义上之前:总计,运费,免运费,税小计,折扣,税,小,礼品包装,现金交付,现金交付_税,运输保护,运输保护税免运费之后:小计,名义之前:tax_subtotal,shipping,grand_total,tax,discounttax_shipping之后:运费,小计,免运费,tax_subtotal,名义之前:税收,折扣,grand_total,grand_total礼品包装之后:小计,名义前:tax_小计之后:免费送货,小计,小计,名义之前:税收,折扣,运费,grand_total,weee,customerbalance,giftcardaccount,reward呜呜呜之后:小计,tax_subtotal,名义,免费送货,小计,小计,名义之前:税收,折扣,grand_total,grand_total,税收运输之后:小计,免运费,tax_subtotal,名义之前:grand_total,discount,tax_shipping,tax,cashondelivery,cashondelivery_tax,shippingprotection,shippingprotectiontax折扣之后:小计,运费,名义,免费送货,tax_subtotal,tax_shipping,weee之前:grand_total,tax,customerbalance,giftcardaccount,reward,cashondelivery,cashondelivery_tax,shippingprotection,shippingprotectiontax货到付款之后:小计,折扣,运费,名义,小计,运费,名义,免运费,tax_subtotal,tax_shipping,小,小计,免运费,tax_subtotal,名义之前:税,总金额,总金额,客户余额,礼品卡帐户,税金包装,奖励,客户余额,礼品卡帐户,奖励运输保护之后:小计,折扣,运费,名义,小计,运费,名义,免运费,tax_subtotal,tax_shipping,小,小计,免运费,tax_subtotal,名义之前:税,总金额,总金额,客户余额,礼品卡帐户,税金礼品包装,奖励,现金交付_税,客户余额,礼品卡帐户,奖励税之后:小计,运费,折扣,tax_subtotal,免费送货,tax_shipping,名义,小,现金交付,shippingprotection之前:grand_total,customerbalance,giftcardaccount,tax_giftwrapping,reward,cashondelivery_tax,shippingprotectiontax运输保护税之后:小计,折扣,运费,税,名义,小计,运费,名义,免费送货,tax_subtotal,tax_shipping,小,小计,免费送货,tax_subtotal,名义,小计,运费,折扣,tax_subtotal,免费送货,tax_shipping,名义,weee,货到付款,运输保障之前:grand_total,customerbalance,giftcardaccount,rewardcashondelivery_tax之后:小计,折扣,运费,税,名义,小计,运费,名义,免费送货,tax_subtotal,tax_shipping,小,小计,免费送货,tax_subtotal,名义,小计,运费,折扣,tax_subtotal,免费送货,tax_shipping,名义,weee,货到付款之前:grand_total,customerbalance,giftcardaccount,rewardtax_giftwrapping后:税,小计,运费,折扣,tax_subtotal,免费送货,tax_shipping,名义,小之前:grand_total、customerbalance、giftcardaccount累计之后:小计、名义、运费、免运费、tax_subtotal、折扣、税、tax_giftwrapping、cashondelivery、cashondelivery_tax、shippingprotection、shippingprotectiontax之前:客户余额,礼品卡帐户,奖励报酬之后:wee,discount,tax,tax_subtotal,grand_total,subtotal,shipping,nominal,freeshipping,tax_subtotal,tax_shipping,weee,subtotal,shipping,discount,tax_subtotal,freeshipping,tax_shipping,nominal,weee,freeshipping,subtotal,subtotal,nominal小计,名义,运费,免运费,tax_subtotal,折扣,tax,tax_giftwrapping之前:礼品卡帐户,客户余额,客户余额礼品卡账户之后:小,折扣,税收,tax_subtotal,grand_total,奖励,小计,运费,名义,免费送货,tax_shipping,weee之前:客户余额客户余额之后:wee,discount,tax,tax_subtotal,grand_total,reward,giftcardaccount,subtotal,shipping,nominal,freeshipping,tax_shipping,weee前:

在 Vinai 的回答之后,我添加了更多调试代码

$fp = fopen('/tmp/dotfile','w');fwrite($fp,"digraph TotalOrder
");fwrite($fp,"{
");foreach($configArray as $code=>$data) {$_code = $data['_code'];foreach($data['before'] 作为 $beforeCode) {fwrite($fp,"$beforeCode -> $_code;
");}foreach($data['after'] 作为 $afterCode) {fwrite($fp,"$_code -> $afterCode;
");}}fwrite($fp,"}
");fclose($fp);

并使用graphviz对其进行可视化:dot -Tpng dotfile >即.png.这是第一次尝试的结果.排序后调用.

编辑 2:

我觉得这没什么用.

所以我在合并之后/之前条目之前对数组进行了可视化.(紧跟在 $configArray = $this->_modelsConfig; 之后)

这是没有我的shippingprotectiontax条目:

这是我的shippingprotectiontax条目:

我没有看到任何明显的矛盾.

编辑 3:

uasort 之前的配置数组:

<前>大批 ('名义' =>大批 ('class' => 'sales/quote_address_total_nominal','之前' =>大批 (0 => '小计',1 => 'grand_total',),'renderer' => 'checkout/total_nominal','之后' =>大批 (),'_code' => '名义',),'小计' =>大批 ('class' => 'sales/quote_address_total_subtotal','之后' =>大批 (0 => '名义',),'之前' =>大批 (0 => 'grand_total',1 => '运输',2 => '免费送货',3 => 'tax_subtotal',4 => '折扣',5 => '税',6 => '小',7 => '礼品包装',8 => '现金交付',9 => 'cashondelivery_tax',10 => '运输保护',11 => 'shippingprotectiontax',),'renderer' => 'tax/checkout_subtotal','admin_renderer' => 'adminhtml/sales_order_create_totals_subtotal','_code' => '小计',),'运输' =>大批 ('class' => 'sales/quote_address_total_shipping','之后' =>大批 (0 => '小计',1 => '免费送货',2 => 'tax_subtotal',3 => '名义',),'之前' =>大批 (0 => 'grand_total',1 => '折扣',2 => 'tax_shipping',3 => '税',4 => '现金交付',5 => 'cashondelivery_tax',6 => '运输保护',7 => 'shippingprotectiontax',),'renderer' => 'tax/checkout_shipping','admin_renderer' => 'adminhtml/sales_order_create_totals_shipping','_code' => '运输',),'grand_total' =>大批 ('class' => 'sales/quote_address_total_grand','之后' =>大批 (0 => '小计',1 => '名义',2 => '运输',3 => '免费送货',4 => 'tax_subtotal',5 => '折扣',6 => '税',7 => 'tax_giftwrapping',8 => '现金交付',9 => 'cashondelivery_tax',10 => '运输保护',11 => 'shippingprotectiontax',),'renderer' => 'tax/checkout_grandtotal','admin_renderer' => 'adminhtml/sales_order_create_totals_grandtotal','之前' =>大批 (0 => '客户余额',1 => '礼品卡账户',2 => '奖励',),'_code' => 'grand_total',),'免费送货' =>大批 ('class' => 'salesrule/quote_freeshipping','之后' =>大批 (0 => '小计',1 => '名义',),'之前' =>大批 (0 => 'tax_subtotal',1 => '运输',2 => 'grand_total',3 => '税',4 => '折扣',),'_code' => '免费送货',),'折扣' =>大批 ('class' => 'salesrule/quote_discount','之后' =>大批 (0 => '小计',1 => '运输',2 => '名义',3 => '免费送货',4 => 'tax_subtotal',5 => 'tax_shipping',6 => '小',),'之前' =>大批 (0 => 'grand_total',1 => '税',2 => '客户余额',3 => '礼品卡账户',4 => '奖励',5 => '现金交付',6 => 'cashondelivery_tax',7 => '运输保护',8 => 'shippingprotectiontax',),'renderer' => 'tax/checkout_discount','admin_renderer' => 'adminhtml/sales_order_create_totals_discount','_code' => '折扣',),'tax_subtotal' =>大批 ('class' => 'tax/sales_total_quote_subtotal','之后' =>大批 (0 => '免费送货',1 => '小计',2 => '小计',3 => '名义',),'之前' =>大批 (0 => '税',1 => '折扣',2 => '运输',3 => 'grand_total',4 => '小',5 => '客户余额',6 => '礼品卡账户',7 => '奖励',),'_code' => 'tax_subtotal',),'tax_shipping' =>大批 ('class' => 'tax/sales_total_quote_shipping','之后' =>大批 (0 => '运输',1 => '小计',2 => '免费送货',3 => 'tax_subtotal',4 => '名义',),'之前' =>大批 (0 => '税',1 => '折扣',2 => 'grand_total',3 => 'grand_total',),'_code' => 'tax_shipping',),'税' =>大批 ('class' => 'tax/sales_total_quote_tax','之后' =>大批 (0 => '小计',1 => '运输',2 => '折扣',3 => 'tax_subtotal',4 => '免费送货',5 => 'tax_shipping',6 => '名义',7 => '小',8 => '现金交付',9 => '运输保护',),'之前' =>大批 (0 => 'grand_total',1 => '客户余额',2 => '礼品卡账户',3 => 'tax_giftwrapping',4 => '奖励',5 => 'cashondelivery_tax',6 => 'shippingprotectiontax',),'renderer' => 'tax/checkout_tax','admin_renderer' => 'adminhtml/sales_order_create_totals_tax','_code' => '税',),'小' =>大批 ('class' => 'weee/total_quote_weee','之后' =>大批 (0 => '小计',1 => 'tax_subtotal',2 => '名义',3 => '免费送货',4 => '小计',5 => '小计',6 => '名义',),'之前' =>大批 (0 => '税',1 => '折扣',2 => 'grand_total',3 => 'grand_total',4 => '税',),'_code' => 'weee',),'客户余额' =>大批 ('class' => 'enterprise_customerbalance/total_quote_customerbalance','之后' =>大批 (0 => '小',1 => '折扣',2 => '税',3 => 'tax_subtotal',4 => 'grand_total',5 => '奖励',6 => '礼品卡账户',7 => '小计',8 => '运输',9 => '名义',10 => '免费送货',11 => 'tax_shipping',12 => '小',),'renderer' => 'enterprise_customerbalance/checkout_total','之前' =>大批 (),'_code' => '客户余额',),'礼品卡帐户' =>大批 ('class' => 'enterprise_giftcardaccount/total_quote_giftcardaccount','之后' =>大批 (0 => '小',1 => '折扣',2 => '税',3 => 'tax_subtotal',4 => 'grand_total',5 => '奖励',6 => '小计',7 => '运输',8 => '名义',9 => '免费送货',11 => 'tax_shipping',12 => '小',),'之前' =>大批 (0 => '客户余额',),'renderer' => 'enterprise_giftcardaccount/checkout_cart_total','_code' => 'giftcardaccount',),'礼品包装' =>大批 ('class' => 'enterprise_giftwrapping/total_quote_giftwrapping','之后' =>大批 (0 => '小计',1 => '名义',),'renderer' => 'enterprise_giftwrapping/checkout_totals','之前' =>大批 (),'_code' => '礼品包装',),'tax_giftwrapping' =>大批 ('class' => 'enterprise_giftwrapping/total_quote_tax_giftwrapping','之后' =>大批 (0 => '税',1 => '小计',2 => '运输',3 => '折扣',4 => 'tax_subtotal',5 => '免费送货',6 => 'tax_shipping',7 => '名义',8 => '小',),'之前' =>大批 (0 => 'grand_total',1 => '客户余额',2 => '礼品卡账户',),'_code' => 'tax_giftwrapping',),'奖励' =>大批 ('class' => 'enterprise_reward/total_quote_reward','之后' =>大批 (0 => '小',1 => '折扣',2 => '税',3 => 'tax_subtotal',4 => 'grand_total',5 => '小计',6 => '运输',7 => '名义',8 => '免费送货',9 => 'tax_subtotal',10 => 'tax_shipping',11 => '小',12 => '小计',13 => '运输',14 => '折扣',15 => 'tax_subtotal',16 => '免费送货',17 => 'tax_shipping',18 => '名义',19 => '小',20 => '免费送货',21 => '小计',22 => '小计',23 => '名义',24 => '小计',25 => '名义',26 => '运输',27 => '免费送货',28 => 'tax_subtotal',29 => '折扣',30 => '税',31 => 'tax_giftwrapping',),'之前' =>大批 (0 => '礼品卡账户',1 => '客户余额',2 => '客户余额',),'renderer' => 'enterprise_reward/checkout_total','_code' => '奖励',),'现金交付' =>大批 ('class' => 'cashondelivery/quote_total','之后' =>大批 (0 => '小计',1 => '折扣',2 => '运输',3 => '名义',4 => '小计',5 => '运输',6 => '名义',7 => '免费送货',8 => 'tax_subtotal',9 => 'tax_shipping',10 => '小',11 => '小计',12 => '免费送货',13 => 'tax_subtotal',14 => '名义',),'之前' =>大批 (0 => '税',1 => 'grand_total',2 => 'grand_total',3 => '客户余额',4 => '礼品卡账户',5 => 'tax_giftwrapping',6 => '奖励',7 => '客户余额',8 => '礼品卡账户',9 => '奖励',),'renderer' => 'cashondelivery/checkout_cod','admin_renderer' => 'cashondelivery/adminhtml_sales_order_create_totals_cod','_code' => '现金交货',),'cashondelivery_tax' =>大批 ('class' => 'cashondelivery/quote_taxTotal','之后' =>大批 (0 => '小计',1 => '折扣',2 => '运输',3 => '税',4 => '名义',5 => '小计',6 => '运输',7 => '名义',8 => '免费送货',9 => 'tax_subtotal',10 => 'tax_shipping',11 => '小',12 => '小计',13 => '免费送货',14 => 'tax_subtotal',15 => '名义',16 => '小计',17 => '运输',18 => '折扣',19 => 'tax_subtotal',20 => '免费送货',21 => 'tax_shipping',22 => '名义',23 => '小',24 => '现金交付',),'之前' =>大批 (0 => 'grand_total',1 => '客户余额',2 => '礼品卡账户',3 => '奖励',),'_code' => 'cashondelivery_tax',),'运输保护' =>大批 ('class' => 'n98_shippingprotection/quote_address_total_shippingprotection','之后' =>大批 (0 => '小计',1 => '折扣',2 => '运输',3 => '名义',4 => '小计',5 => '运输',6 => '名义',7 => '免费送货',8 => 'tax_subtotal',9 => 'tax_shipping',10 => '小',11 => '小计',12 => '免费送货',13 => 'tax_subtotal',14 => '名义',),'之前' =>大批 (0 => '税',1 => 'grand_total',2 => 'grand_total',3 => '客户余额',4 => '礼品卡账户',5 => 'tax_giftwrapping',6 => '奖励',7 => 'cashondelivery_tax',8 => '客户余额',9 => '礼品卡账户',10 => '奖励',),'_code' => 'shippingprotection',),'运输保护税' =>大批 ('class' => 'n98_shippingprotection/quote_address_total_shippingprotectionTax','之后' =>大批 (0 => '小计',1 => '折扣',2 => '运输',3 => '税',4 => '名义',5 => '小计',6 => '运输',7 => '名义',8 => '免费送货',9 => 'tax_subtotal',10 => 'tax_shipping',11 => '小',12 => '小计',13 => '免费送货',14 => 'tax_subtotal',15 => '名义',16 => '小计',17 => '运输',18 => '折扣',19 => 'tax_subtotal',20 => '免费送货',21 => 'tax_shipping',22 => '名义',23 => '小',24 => '现金交付',25 => '运输保护',),'之前' =>大批 (0 => 'grand_total',1 => '客户余额',2 => '礼品卡账户',3 => '奖励',),'_code' => 'shippingprotectiontax',),)

<小时>

更新: Magento 错误票证:https://jira.magento.com/browse/MCACE-129

解决方案

最后,这是我针对这个问题的补丁.

它实现了 Vinai 建议的拓扑排序.

  1. app/code/core/Mage/Sales/Model/Config/Ordered.php复制到app/code/local/Mage/Sales/Model/Config/Ordered.php
  2. 将补丁的内容保存到文件 total-sorting.patch 并调用 patch -p0 app/code/local/Mage/Sales/Model/Config/Ordered.php

如果升级,请确保重新应用这些步骤.

该补丁经过测试可与 Magento 1.7.0.2 配合使用

<前>--- app/code/core/Mage/Sales/Model/Config/Ordered.php 2012-08-14 14:19:50.306504947 +0200+++ app/code/local/Mage/Sales/Model/Config/Ordered.php 2012-08-15 10:00:47.027003404 +0200@@ -121,6 +121,78 @@返回 $totalConfig;}+//[补丁代码开始]++/**+ * 拓扑排序+ *+ * 版权所有:http://www.calcatraz.com/blog/php-topological-sort-function-384+ * 并修复见 http://stackoverflow.com/questions/11953021/topological-sorting-in-php 上的评论+ *+ * @param $nodeids 节点 ID+ * @param $edges 边数组.每条边被指定为一个包含两个元素的数组:边的源节点和目标节点+ * @return 数组|null+ */+ 函数拓扑排序($nodeids,$edges){+ $L = $S = $nodes = array();+ foreach($nodeids 作为 $id) {+ $nodes[$id] = array('in'=>array(), 'out'=>array());+ foreach($edges as $e) {+ if ($id==$e[0]) { $nodes[$id]['out'][]=$e[1];}+ if ($id==$e[1]) { $nodes[$id]['in'][]=$e[0];}+ }+ }+ foreach ($nodes as $id=>$n) { if (empty($n['in'])) $S[]=$id;}+ while ($id = array_shift($S)) {+ if (!in_array($id, $L)) {+ $L[] = $id;+ foreach($nodes[$id]['out'] as $m) {+ $nodes[$m]['in'] = array_diff($nodes[$m]['in'], array($id));+ if (empty($nodes[$m]['in'])) { $S[] = $m;}+ }+ $nodes[$id]['out'] = array();+ }+ }+ foreach($nodes as $n) {+ if (!empty($n['in']) 或 !empty($n['out'])) {+ 返回空;//不可排序,因为图是循环的+ }+ }+ 返回 $L;+ }++/**+ * 排序配置数组+ *+ * public 可以通过测试轻松访问+ *+ * @param $configArray+ * @return 数组+ */+ 公共函数 _topSortConfigArray($configArray)+ {+ $nodes = array_keys($configArray);+ $edges = 数组();++ foreach ($configArray as $code => $data) {+ $_code = $data['_code'];+ if (!isset($configArray[$_code])) 继续;+ foreach ($data['before'] as $beforeCode) {+ if (!isset($configArray[$beforeCode])) 继续;+ $edges[] = array($_code, $beforeCode);+ }++ foreach ($data['after'] as $afterCode) {+ if (!isset($configArray[$afterCode])) 继续;+ $edges[] = array($afterCode, $_code);+ }+ }+ 返回 $this->topological_sort($nodes, $edges);+ }++//[补丁代码结束]++/*** 汇总所有项目的前后信息并根据此数据对总数进行排序*@@ -138,38 +210,16 @@//如果第一个元素包含sort_order"键,则调用简单排序重置($configArray);$element = current($configArray);+//[补丁代码开始]如果 (isset($element['sort_order'])) {uasort($configArray, array($this, '_compareSortOrder'));+ $sortedCollectors = array_keys($configArray);+} 别的 {- foreach ($configArray as $code => $data) {- foreach ($data['before'] as $beforeCode) {- 如果 (!isset($configArray[$beforeCode])) {- 继续;- }- $configArray[$code]['before'] = array_unique(array_merge(- $configArray[$code]['before'], $configArray[$beforeCode]['before']- ));- $configArray[$beforeCode]['after'] = array_merge(- $configArray[$beforeCode]['after'], array($code), $data['after']- );- $configArray[$beforeCode]['after'] = array_unique($configArray[$beforeCode]['after']);- }- foreach ($data['after'] as $afterCode) {- 如果 (!isset($configArray[$afterCode])) {- 继续;- }- $configArray[$code]['after'] = array_unique(array_merge(- $configArray[$code]['after'], $configArray[$afterCode]['after']- ));- $configArray[$afterCode]['before'] = array_merge(- $configArray[$afterCode]['before'], array($code), $data['before']- );- $configArray[$afterCode]['before'] = array_unique($configArray[$afterCode]['before']);- }- }- uasort($configArray, array($this, '_compareTotals'));+ $sortedCollectors = $this->_topSortConfigArray($configArray);}- $sortedCollectors = array_keys($configArray);+//[补丁代码结束]+if (Mage::app()->useCache('config')) {Mage::app()->saveCache(serialize($sortedCollectors), $this->_collectorsCacheKey, array(Mage_Core_Model_Config::CACHE_TAG@@ -196,27 +246,6 @@}/**- * 使用 after/before 进行比较的回调- *- * @param 数组 $a- * @param 数组 $b- * @return int- */- 受保护的函数 _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;- } 别的 {- $res = 0;- }- 返回 $res;- }——-/*** 使用 sort_order 进行比较的回调** @param 数组 $a

编辑:还有另一个建议的更改(对于 Magento 2):https://github.com/magento/magento2/pull/49

In Magento there is a functionality where you can define the order of total calculation by specifing before and after which totals a total should be run.

I added a custom total and if I add the following lines to the config.xml, the sorting is wrong. Wrong means: tax_shipping comes before shipping. This causes the tax for the shipping cost to be added twice.

But this violates the condition

tax_shipping
after: shipping

My guess: There must be some contradiction in the full set of rules. But how can I find it?

This is the only rule I add. Without this rule, tax_shipping is sorted after shipping.

<shippingprotectiontax>
    <class>n98_shippingprotection/quote_address_total_shippingprotectionTax</class>
    <after>subtotal,discount,shipping,tax</after>
    <before>grand_total</before>
</shippingprotectiontax>

Below I paste the sorted array that is returned by the usort call in Mage_Sales_Model_Quote_Address_Total_Collector::_getSortedCollectorCodes() For those who do not have a Magento installation, the code is like this:

/**
 * uasort callback function
 *
 * @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;
}

protected function _getSortedCollectorCodes()
{

    ...

    uasort($configArray, array($this, '_compareTotals'));
    Mage::log('Sorted:');

    // this produces the output below
    $loginfo = "";
    foreach($configArray as $code=>$data) {
        $loginfo .= "$code
";
        $loginfo .= "after: ".implode(',',$data['after'])."
";
        $loginfo .= "before: ".implode(',',$data['before'])."
";
        $loginfo .= "
";
    }
    Mage::log($loginfo);

    ...

Log output:

nominal
after: 
before: subtotal,grand_total

subtotal
after: nominal
before: grand_total,shipping,freeshipping,tax_subtotal,discount,tax,weee,giftwrapping,cashondelivery,cashondelivery_tax,shippingprotection,shippingprotectiontax

freeshipping
after: subtotal,nominal
before: tax_subtotal,shipping,grand_total,tax,discount

tax_shipping
after: shipping,subtotal,freeshipping,tax_subtotal,nominal
before: tax,discount,grand_total,grand_total

giftwrapping
after: subtotal,nominal
before: 

tax_subtotal
after: freeshipping,subtotal,subtotal,nominal
before: tax,discount,shipping,grand_total,weee,customerbalance,giftcardaccount,reward

weee
after: subtotal,tax_subtotal,nominal,freeshipping,subtotal,subtotal,nominal
before: tax,discount,grand_total,grand_total,tax

shipping
after: subtotal,freeshipping,tax_subtotal,nominal
before: grand_total,discount,tax_shipping,tax,cashondelivery,cashondelivery_tax,shippingprotection,shippingprotectiontax

discount
after: subtotal,shipping,nominal,freeshipping,tax_subtotal,tax_shipping,weee
before: grand_total,tax,customerbalance,giftcardaccount,reward,cashondelivery,cashondelivery_tax,shippingprotection,shippingprotectiontax

cashondelivery
after: subtotal,discount,shipping,nominal,subtotal,shipping,nominal,freeshipping,tax_subtotal,tax_shipping,weee,subtotal,freeshipping,tax_subtotal,nominal
before: tax,grand_total,grand_total,customerbalance,giftcardaccount,tax_giftwrapping,reward,customerbalance,giftcardaccount,reward

shippingprotection
after: subtotal,discount,shipping,nominal,subtotal,shipping,nominal,freeshipping,tax_subtotal,tax_shipping,weee,subtotal,freeshipping,tax_subtotal,nominal
before: tax,grand_total,grand_total,customerbalance,giftcardaccount,tax_giftwrapping,reward,cashondelivery_tax,customerbalance,giftcardaccount,reward

tax
after: subtotal,shipping,discount,tax_subtotal,freeshipping,tax_shipping,nominal,weee,cashondelivery,shippingprotection
before: grand_total,customerbalance,giftcardaccount,tax_giftwrapping,reward,cashondelivery_tax,shippingprotectiontax

shippingprotectiontax
after: subtotal,discount,shipping,tax,nominal,subtotal,shipping,nominal,freeshipping,tax_subtotal,tax_shipping,weee,subtotal,freeshipping,tax_subtotal,nominal,subtotal,shipping,discount,tax_subtotal,freeshipping,tax_shipping,nominal,weee,cashondelivery,shippingprotection
before: grand_total,customerbalance,giftcardaccount,reward

cashondelivery_tax
after: subtotal,discount,shipping,tax,nominal,subtotal,shipping,nominal,freeshipping,tax_subtotal,tax_shipping,weee,subtotal,freeshipping,tax_subtotal,nominal,subtotal,shipping,discount,tax_subtotal,freeshipping,tax_shipping,nominal,weee,cashondelivery
before: grand_total,customerbalance,giftcardaccount,reward

tax_giftwrapping
after: tax,subtotal,shipping,discount,tax_subtotal,freeshipping,tax_shipping,nominal,weee
before: grand_total,customerbalance,giftcardaccount

grand_total
after: subtotal,nominal,shipping,freeshipping,tax_subtotal,discount,tax,tax_giftwrapping,cashondelivery,cashondelivery_tax,shippingprotection,shippingprotectiontax
before: customerbalance,giftcardaccount,reward

reward
after: wee,discount,tax,tax_subtotal,grand_total,subtotal,shipping,nominal,freeshipping,tax_subtotal,tax_shipping,weee,subtotal,shipping,discount,tax_subtotal,freeshipping,tax_shipping,nominal,weee,freeshipping,subtotal,subtotal,nominal,subtotal,nominal,shipping,freeshipping,tax_subtotal,discount,tax,tax_giftwrapping
before: giftcardaccount,customerbalance,customerbalance

giftcardaccount
after: wee,discount,tax,tax_subtotal,grand_total,reward,subtotal,shipping,nominal,freeshipping,tax_shipping,weee
before: customerbalance

customerbalance
after: wee,discount,tax,tax_subtotal,grand_total,reward,giftcardaccount,subtotal,shipping,nominal,freeshipping,tax_shipping,weee
before: 

EDIT:

After Vinai's answer I added more debug code

$fp = fopen('/tmp/dotfile','w');
fwrite($fp,"digraph TotalOrder
");
fwrite($fp,"{
");
foreach($configArray as $code=>$data) {
    $_code = $data['_code'];
    foreach($data['before'] as $beforeCode) {
        fwrite($fp,"$beforeCode -> $_code;
");
    }
    foreach($data['after'] as $afterCode) {
        fwrite($fp,"$_code -> $afterCode;
");
    }
}
fwrite($fp,"}
");
fclose($fp);

And visualized it with graphviz: dot -Tpng dotfile > viz.png. That's the result of the first try. Called after the sorting.

EDIT2:

I think this is pretty useless.

So I made a visualization of the array before merging the after/before entries. (right after $configArray = $this->_modelsConfig;)

This is it without my shippingprotectiontax entry:

This is it with my shippingprotectiontax entry:

I do not see any clear contradictions.

EDIT3:

Config array just before uasort:

array (
  'nominal' => 
  array (
    'class' => 'sales/quote_address_total_nominal',
    'before' => 
    array (
      0 => 'subtotal',
      1 => 'grand_total',
    ),
    'renderer' => 'checkout/total_nominal',
    'after' => 
    array (
    ),
    '_code' => 'nominal',
  ),
  'subtotal' => 
  array (
    'class' => 'sales/quote_address_total_subtotal',
    'after' => 
    array (
      0 => 'nominal',
    ),
    'before' => 
    array (
      0 => 'grand_total',
      1 => 'shipping',
      2 => 'freeshipping',
      3 => 'tax_subtotal',
      4 => 'discount',
      5 => 'tax',
      6 => 'weee',
      7 => 'giftwrapping',
      8 => 'cashondelivery',
      9 => 'cashondelivery_tax',
      10 => 'shippingprotection',
      11 => 'shippingprotectiontax',
    ),
    'renderer' => 'tax/checkout_subtotal',
    'admin_renderer' => 'adminhtml/sales_order_create_totals_subtotal',
    '_code' => 'subtotal',
  ),
  'shipping' => 
  array (
    'class' => 'sales/quote_address_total_shipping',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'freeshipping',
      2 => 'tax_subtotal',
      3 => 'nominal',
    ),
    'before' => 
    array (
      0 => 'grand_total',
      1 => 'discount',
      2 => 'tax_shipping',
      3 => 'tax',
      4 => 'cashondelivery',
      5 => 'cashondelivery_tax',
      6 => 'shippingprotection',
      7 => 'shippingprotectiontax',
    ),
    'renderer' => 'tax/checkout_shipping',
    'admin_renderer' => 'adminhtml/sales_order_create_totals_shipping',
    '_code' => 'shipping',
  ),
  'grand_total' => 
  array (
    'class' => 'sales/quote_address_total_grand',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'nominal',
      2 => 'shipping',
      3 => 'freeshipping',
      4 => 'tax_subtotal',
      5 => 'discount',
      6 => 'tax',
      7 => 'tax_giftwrapping',
      8 => 'cashondelivery',
      9 => 'cashondelivery_tax',
      10 => 'shippingprotection',
      11 => 'shippingprotectiontax',
    ),
    'renderer' => 'tax/checkout_grandtotal',
    'admin_renderer' => 'adminhtml/sales_order_create_totals_grandtotal',
    'before' => 
    array (
      0 => 'customerbalance',
      1 => 'giftcardaccount',
      2 => 'reward',
    ),
    '_code' => 'grand_total',
  ),
  'freeshipping' => 
  array (
    'class' => 'salesrule/quote_freeshipping',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'nominal',
    ),
    'before' => 
    array (
      0 => 'tax_subtotal',
      1 => 'shipping',
      2 => 'grand_total',
      3 => 'tax',
      4 => 'discount',
    ),
    '_code' => 'freeshipping',
  ),
  'discount' => 
  array (
    'class' => 'salesrule/quote_discount',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'shipping',
      2 => 'nominal',
      3 => 'freeshipping',
      4 => 'tax_subtotal',
      5 => 'tax_shipping',
      6 => 'weee',
    ),
    'before' => 
    array (
      0 => 'grand_total',
      1 => 'tax',
      2 => 'customerbalance',
      3 => 'giftcardaccount',
      4 => 'reward',
      5 => 'cashondelivery',
      6 => 'cashondelivery_tax',
      7 => 'shippingprotection',
      8 => 'shippingprotectiontax',
    ),
    'renderer' => 'tax/checkout_discount',
    'admin_renderer' => 'adminhtml/sales_order_create_totals_discount',
    '_code' => 'discount',
  ),
  'tax_subtotal' => 
  array (
    'class' => 'tax/sales_total_quote_subtotal',
    'after' => 
    array (
      0 => 'freeshipping',
      1 => 'subtotal',
      2 => 'subtotal',
      3 => 'nominal',
    ),
    'before' => 
    array (
      0 => 'tax',
      1 => 'discount',
      2 => 'shipping',
      3 => 'grand_total',
      4 => 'weee',
      5 => 'customerbalance',
      6 => 'giftcardaccount',
      7 => 'reward',
    ),
    '_code' => 'tax_subtotal',
  ),
  'tax_shipping' => 
  array (
    'class' => 'tax/sales_total_quote_shipping',
    'after' => 
    array (
      0 => 'shipping',
      1 => 'subtotal',
      2 => 'freeshipping',
      3 => 'tax_subtotal',
      4 => 'nominal',
    ),
    'before' => 
    array (
      0 => 'tax',
      1 => 'discount',
      2 => 'grand_total',
      3 => 'grand_total',
    ),
    '_code' => 'tax_shipping',
  ),
  'tax' => 
  array (
    'class' => 'tax/sales_total_quote_tax',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'shipping',
      2 => 'discount',
      3 => 'tax_subtotal',
      4 => 'freeshipping',
      5 => 'tax_shipping',
      6 => 'nominal',
      7 => 'weee',
      8 => 'cashondelivery',
      9 => 'shippingprotection',
    ),
    'before' => 
    array (
      0 => 'grand_total',
      1 => 'customerbalance',
      2 => 'giftcardaccount',
      3 => 'tax_giftwrapping',
      4 => 'reward',
      5 => 'cashondelivery_tax',
      6 => 'shippingprotectiontax',
    ),
    'renderer' => 'tax/checkout_tax',
    'admin_renderer' => 'adminhtml/sales_order_create_totals_tax',
    '_code' => 'tax',
  ),
  'weee' => 
  array (
    'class' => 'weee/total_quote_weee',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'tax_subtotal',
      2 => 'nominal',
      3 => 'freeshipping',
      4 => 'subtotal',
      5 => 'subtotal',
      6 => 'nominal',
    ),
    'before' => 
    array (
      0 => 'tax',
      1 => 'discount',
      2 => 'grand_total',
      3 => 'grand_total',
      4 => 'tax',
    ),
    '_code' => 'weee',
  ),
  'customerbalance' => 
  array (
    'class' => 'enterprise_customerbalance/total_quote_customerbalance',
    'after' => 
    array (
      0 => 'wee',
      1 => 'discount',
      2 => 'tax',
      3 => 'tax_subtotal',
      4 => 'grand_total',
      5 => 'reward',
      6 => 'giftcardaccount',
      7 => 'subtotal',
      8 => 'shipping',
      9 => 'nominal',
      10 => 'freeshipping',
      11 => 'tax_shipping',
      12 => 'weee',
    ),
    'renderer' => 'enterprise_customerbalance/checkout_total',
    'before' => 
    array (
    ),
    '_code' => 'customerbalance',
  ),
  'giftcardaccount' => 
  array (
    'class' => 'enterprise_giftcardaccount/total_quote_giftcardaccount',
    'after' => 
    array (
      0 => 'wee',
      1 => 'discount',
      2 => 'tax',
      3 => 'tax_subtotal',
      4 => 'grand_total',
      5 => 'reward',
      6 => 'subtotal',
      7 => 'shipping',
      8 => 'nominal',
      9 => 'freeshipping',
      11 => 'tax_shipping',
      12 => 'weee',
    ),
    'before' => 
    array (
      0 => 'customerbalance',
    ),
    'renderer' => 'enterprise_giftcardaccount/checkout_cart_total',
    '_code' => 'giftcardaccount',
  ),
  'giftwrapping' => 
  array (
    'class' => 'enterprise_giftwrapping/total_quote_giftwrapping',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'nominal',
    ),
    'renderer' => 'enterprise_giftwrapping/checkout_totals',
    'before' => 
    array (
    ),
    '_code' => 'giftwrapping',
  ),
  'tax_giftwrapping' => 
  array (
    'class' => 'enterprise_giftwrapping/total_quote_tax_giftwrapping',
    'after' => 
    array (
      0 => 'tax',
      1 => 'subtotal',
      2 => 'shipping',
      3 => 'discount',
      4 => 'tax_subtotal',
      5 => 'freeshipping',
      6 => 'tax_shipping',
      7 => 'nominal',
      8 => 'weee',
    ),
    'before' => 
    array (
      0 => 'grand_total',
      1 => 'customerbalance',
      2 => 'giftcardaccount',
    ),
    '_code' => 'tax_giftwrapping',
  ),
  'reward' => 
  array (
    'class' => 'enterprise_reward/total_quote_reward',
    'after' => 
    array (
      0 => 'wee',
      1 => 'discount',
      2 => 'tax',
      3 => 'tax_subtotal',
      4 => 'grand_total',
      5 => 'subtotal',
      6 => 'shipping',
      7 => 'nominal',
      8 => 'freeshipping',
      9 => 'tax_subtotal',
      10 => 'tax_shipping',
      11 => 'weee',
      12 => 'subtotal',
      13 => 'shipping',
      14 => 'discount',
      15 => 'tax_subtotal',
      16 => 'freeshipping',
      17 => 'tax_shipping',
      18 => 'nominal',
      19 => 'weee',
      20 => 'freeshipping',
      21 => 'subtotal',
      22 => 'subtotal',
      23 => 'nominal',
      24 => 'subtotal',
      25 => 'nominal',
      26 => 'shipping',
      27 => 'freeshipping',
      28 => 'tax_subtotal',
      29 => 'discount',
      30 => 'tax',
      31 => 'tax_giftwrapping',
    ),
    'before' => 
    array (
      0 => 'giftcardaccount',
      1 => 'customerbalance',
      2 => 'customerbalance',
    ),
    'renderer' => 'enterprise_reward/checkout_total',
    '_code' => 'reward',
  ),
  'cashondelivery' => 
  array (
    'class' => 'cashondelivery/quote_total',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'discount',
      2 => 'shipping',
      3 => 'nominal',
      4 => 'subtotal',
      5 => 'shipping',
      6 => 'nominal',
      7 => 'freeshipping',
      8 => 'tax_subtotal',
      9 => 'tax_shipping',
      10 => 'weee',
      11 => 'subtotal',
      12 => 'freeshipping',
      13 => 'tax_subtotal',
      14 => 'nominal',
    ),
    'before' => 
    array (
      0 => 'tax',
      1 => 'grand_total',
      2 => 'grand_total',
      3 => 'customerbalance',
      4 => 'giftcardaccount',
      5 => 'tax_giftwrapping',
      6 => 'reward',
      7 => 'customerbalance',
      8 => 'giftcardaccount',
      9 => 'reward',
    ),
    'renderer' => 'cashondelivery/checkout_cod',
    'admin_renderer' => 'cashondelivery/adminhtml_sales_order_create_totals_cod',
    '_code' => 'cashondelivery',
  ),
  'cashondelivery_tax' => 
  array (
    'class' => 'cashondelivery/quote_taxTotal',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'discount',
      2 => 'shipping',
      3 => 'tax',
      4 => 'nominal',
      5 => 'subtotal',
      6 => 'shipping',
      7 => 'nominal',
      8 => 'freeshipping',
      9 => 'tax_subtotal',
      10 => 'tax_shipping',
      11 => 'weee',
      12 => 'subtotal',
      13 => 'freeshipping',
      14 => 'tax_subtotal',
      15 => 'nominal',
      16 => 'subtotal',
      17 => 'shipping',
      18 => 'discount',
      19 => 'tax_subtotal',
      20 => 'freeshipping',
      21 => 'tax_shipping',
      22 => 'nominal',
      23 => 'weee',
      24 => 'cashondelivery',
    ),
    'before' => 
    array (
      0 => 'grand_total',
      1 => 'customerbalance',
      2 => 'giftcardaccount',
      3 => 'reward',
    ),
    '_code' => 'cashondelivery_tax',
  ),
  'shippingprotection' => 
  array (
    'class' => 'n98_shippingprotection/quote_address_total_shippingprotection',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'discount',
      2 => 'shipping',
      3 => 'nominal',
      4 => 'subtotal',
      5 => 'shipping',
      6 => 'nominal',
      7 => 'freeshipping',
      8 => 'tax_subtotal',
      9 => 'tax_shipping',
      10 => 'weee',
      11 => 'subtotal',
      12 => 'freeshipping',
      13 => 'tax_subtotal',
      14 => 'nominal',
    ),
    'before' => 
    array (
      0 => 'tax',
      1 => 'grand_total',
      2 => 'grand_total',
      3 => 'customerbalance',
      4 => 'giftcardaccount',
      5 => 'tax_giftwrapping',
      6 => 'reward',
      7 => 'cashondelivery_tax',
      8 => 'customerbalance',
      9 => 'giftcardaccount',
      10 => 'reward',
    ),
    '_code' => 'shippingprotection',
  ),
  'shippingprotectiontax' => 
  array (
    'class' => 'n98_shippingprotection/quote_address_total_shippingprotectionTax',
    'after' => 
    array (
      0 => 'subtotal',
      1 => 'discount',
      2 => 'shipping',
      3 => 'tax',
      4 => 'nominal',
      5 => 'subtotal',
      6 => 'shipping',
      7 => 'nominal',
      8 => 'freeshipping',
      9 => 'tax_subtotal',
      10 => 'tax_shipping',
      11 => 'weee',
      12 => 'subtotal',
      13 => 'freeshipping',
      14 => 'tax_subtotal',
      15 => 'nominal',
      16 => 'subtotal',
      17 => 'shipping',
      18 => 'discount',
      19 => 'tax_subtotal',
      20 => 'freeshipping',
      21 => 'tax_shipping',
      22 => 'nominal',
      23 => 'weee',
      24 => 'cashondelivery',
      25 => 'shippingprotection',
    ),
    'before' => 
    array (
      0 => 'grand_total',
      1 => 'customerbalance',
      2 => 'giftcardaccount',
      3 => 'reward',
    ),
    '_code' => 'shippingprotectiontax',
  ),
)


Update: Magento Bug Ticket: https://jira.magento.com/browse/MCACE-129

解决方案

So finally, here is my patch for this issue.

It implements topological sorting as suggested by Vinai.

  1. Copy app/code/core/Mage/Sales/Model/Config/Ordered.php to app/code/local/Mage/Sales/Model/Config/Ordered.php
  2. Save the contents of the patch to a file total-sorting.patch and call patch -p0 app/code/local/Mage/Sales/Model/Config/Ordered.php

In case of upgrades make sure to re-apply these steps.

The patch is tested to work with Magento 1.7.0.2

--- app/code/core/Mage/Sales/Model/Config/Ordered.php   2012-08-14 14:19:50.306504947 +0200
+++ app/code/local/Mage/Sales/Model/Config/Ordered.php  2012-08-15 10:00:47.027003404 +0200
@@ -121,6 +121,78 @@
         return $totalConfig;
     }

+// [PATCHED CODE BEGIN]
+
+    /**
+     * Topological sort
+     *
+     * Copyright: http://www.calcatraz.com/blog/php-topological-sort-function-384
+     * And fix see comment on http://stackoverflow.com/questions/11953021/topological-sorting-in-php
+     *
+     * @param $nodeids Node Ids
+     * @param $edges Array of Edges. Each edge is specified as an array with two elements: The source and destination node of the edge
+     * @return array|null
+     */
+    function topological_sort($nodeids, $edges) {
+        $L = $S = $nodes = array();
+        foreach($nodeids as $id) {
+            $nodes[$id] = array('in'=>array(), 'out'=>array());
+            foreach($edges as $e) {
+                if ($id==$e[0]) { $nodes[$id]['out'][]=$e[1]; }
+                if ($id==$e[1]) { $nodes[$id]['in'][]=$e[0]; }
+            }
+        }
+        foreach ($nodes as $id=>$n) { if (empty($n['in'])) $S[]=$id; }
+        while ($id = array_shift($S)) {
+            if (!in_array($id, $L)) {
+                $L[] = $id;
+                foreach($nodes[$id]['out'] as $m) {
+                    $nodes[$m]['in'] = array_diff($nodes[$m]['in'], array($id));
+                    if (empty($nodes[$m]['in'])) { $S[] = $m; }
+                }
+                $nodes[$id]['out'] = array();
+            }
+        }
+        foreach($nodes as $n) {
+            if (!empty($n['in']) or !empty($n['out'])) {
+                return null; // not sortable as graph is cyclic
+            }
+        }
+        return $L;
+    }
+
+    /**
+     * Sort config array
+     *
+     * public to be easily accessable by test
+     *
+     * @param $configArray
+     * @return array
+     */
+    public function _topSortConfigArray($configArray)
+    {
+        $nodes = array_keys($configArray);
+        $edges = array();
+
+        foreach ($configArray as $code => $data) {
+            $_code = $data['_code'];
+            if (!isset($configArray[$_code])) continue;
+            foreach ($data['before'] as $beforeCode) {
+                if (!isset($configArray[$beforeCode])) continue;
+                $edges[] = array($_code, $beforeCode);
+            }
+
+            foreach ($data['after'] as $afterCode) {
+                if (!isset($configArray[$afterCode])) continue;
+                $edges[] = array($afterCode, $_code);
+            }
+        }
+        return $this->topological_sort($nodes, $edges);
+    }
+
+// [PATCHED CODE END]
+
+
     /**
      * Aggregate before/after information from all items and sort totals based on this data
      *
@@ -138,38 +210,16 @@
         // invoke simple sorting if the first element contains the "sort_order" key
         reset($configArray);
         $element = current($configArray);
+        // [PATCHED CODE BEGIN]
         if (isset($element['sort_order'])) {
             uasort($configArray, array($this, '_compareSortOrder'));
+            $sortedCollectors = array_keys($configArray);
+
         } else {
-            foreach ($configArray as $code => $data) {
-                foreach ($data['before'] as $beforeCode) {
-                    if (!isset($configArray[$beforeCode])) {
-                        continue;
-                    }
-                    $configArray[$code]['before'] = array_unique(array_merge(
-                        $configArray[$code]['before'], $configArray[$beforeCode]['before']
-                    ));
-                    $configArray[$beforeCode]['after'] = array_merge(
-                        $configArray[$beforeCode]['after'], array($code), $data['after']
-                    );
-                    $configArray[$beforeCode]['after'] = array_unique($configArray[$beforeCode]['after']);
-                }
-                foreach ($data['after'] as $afterCode) {
-                    if (!isset($configArray[$afterCode])) {
-                        continue;
-                    }
-                    $configArray[$code]['after'] = array_unique(array_merge(
-                        $configArray[$code]['after'], $configArray[$afterCode]['after']
-                    ));
-                    $configArray[$afterCode]['before'] = array_merge(
-                        $configArray[$afterCode]['before'], array($code), $data['before']
-                    );
-                    $configArray[$afterCode]['before'] = array_unique($configArray[$afterCode]['before']);
-                }
-            }
-            uasort($configArray, array($this, '_compareTotals'));
+            $sortedCollectors = $this->_topSortConfigArray($configArray);
         }
-        $sortedCollectors = array_keys($configArray);
+        // [PATCHED CODE END]
+
         if (Mage::app()->useCache('config')) {
             Mage::app()->saveCache(serialize($sortedCollectors), $this->_collectorsCacheKey, array(
                     Mage_Core_Model_Config::CACHE_TAG
@@ -196,27 +246,6 @@
     }

     /**
-     * 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

EDIT: There is also another suggested change (for Magento 2): https://github.com/magento/magento2/pull/49

这篇关于排序算法:Magento 结帐总额排序错误导致运费计算错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

In PHP how can you clear a WSDL cache?(在 PHP 中如何清除 WSDL 缓存?)
failed to open stream: HTTP wrapper does not support writeable connections(无法打开流:HTTP 包装器不支持可写连接)
Stop caching for PHP 5.5.3 in MAMP(在 MAMP 中停止缓存 PHP 5.5.3)
Caching HTTP responses when they are dynamically created by PHP(缓存由 PHP 动态创建的 HTTP 响应)
Memcached vs APC which one should I choose?(Memcached 与 APC 我应该选择哪一个?)
What is causing quot;Unable to allocate memory for poolquot; in PHP?(是什么导致“无法为池分配内存?在 PHP 中?)