排序算法:Magento结帐总额未正确排序,导致错误的运费计算

2021/01/05 04:41 · php ·  · 0评论

Magento中有一项功能,您可以通过指定总计之前和之后的总计时间来定义总计计算的顺序。

我添加了一个自定义总计,如果将以下行添加到config.xml中,则排序是错误的。错误的方法:tax_shipping之前 shipping这将导致运输费用的税款增加两次。

但这违反了条件

tax_shipping
after: shipping

我的猜测:整套规则中一定有一些矛盾。但是我怎么找到它呢?

这是我添加的唯一规则。如果没有此规则,tax_shipping则排序为shipping

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

在下面,我将usort调用返回的排序后的数组粘贴到其中。Mage_Sales_Model_Quote_Address_Total_Collector::_getSortedCollectorCodes()
对于那些没有安装Magento的用户,代码如下:

/**
 * 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\n";
        $loginfo .= "after: ".implode(',',$data['after'])."\n";
        $loginfo .= "before: ".implode(',',$data['before'])."\n";
        $loginfo .= "\n";
    }
    Mage::log($loginfo);

    ...

日志输出:

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: 

编辑:

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

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

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

可视化

编辑2:

我认为这是毫无用处的。

因此,在合并after / before条目之前,我对数组进行了可视化处理。(之后$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 =>'总和',
      1 =>'运费',
      2 =>'免费送货',
      3 =>'tax_subtotal',
      4 =>“折扣”,
      5 =>'税',
      6 =>'weee',
      7 =>'礼物包装',
      8 =>'cashondelivery',
      9 =>'cashondelivery_tax',
      10 =>'运输保护',
      11 =>'运费保护税',
    ),
    '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 =>'总和',
      1 =>“折扣”,
      2 =>'tax_shipping',
      3 =>'税',
      4 =>'cashondelivery',
      5 =>'cashondelivery_tax',
      6 =>'运输保护',
      7 =>'运费保护税',
    ),
    '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 =>'cashondelivery',
      9 =>'cashondelivery_tax',
      10 =>'运输保护',
      11 =>'运费保护税',
    ),
    'renderer'=>'tax / checkout_grandtotal',
    'admin_renderer'=>'adminhtml / sales_order_create_totals_grandtotal',
    '之前'=> 
    数组(
      0 =>'customerbalance',
      1 =>'giftcardaccount',
      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 =>'weee',
    ),
    '之前'=> 
    数组(
      0 =>'总和',
      1 =>'税',
      2 =>'customerbalance',
      3 =>'giftcardaccount',
      4 =>'奖励',
      5 =>'cashondelivery',
      6 =>'cashondelivery_tax',
      7 =>'运输保护',
      8 =>'运费保护税',
    ),
    '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 =>'weee',
      5 =>'customerbalance',
      6 =>'giftcardaccount',
      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 =>'weee',
      8 =>'cashondelivery',
      9 =>'运输保护',
    ),
    '之前'=> 
    数组(
      0 =>'总和',
      1 =>'customerbalance',
      2 =>'giftcardaccount',
      3 =>'tax_giftwrapping',
      4 =>'奖励',
      5 =>'cashondelivery_tax',
      6 =>'运费保护税',
    ),
    'renderer'=>'tax / checkout_tax',
    'admin_renderer'=>'adminhtml / sales_order_create_totals_tax',
    '_code'=>'税收',
  ),
  'weee'=> 
  数组(
    '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',
  ),
  'customerbalance'=> 
  数组(
    'class'=>'enterprise_customerbalance / total_quote_customerbalance',
    '之后'=> 
    数组(
      0 =>'wee',
      1 =>“折扣”,
      2 =>'税',
      3 =>'tax_subtotal',
      4 =>'grand_total',
      5 =>'奖励',
      6 =>'giftcardaccount',
      7 =>'小计',
      8 =>'运费',
      9 =>'标称',
      10 =>'免费送货',
      11 =>'tax_shipping',
      12 =>'weee',
    ),
    'renderer'=>'enterprise_customerbalance / checkout_total',
    '之前'=> 
    数组(
    ),
    '_code'=>'customerbalance',
  ),
  'giftcardaccount'=> 
  数组(
    'class'=>'enterprise_giftcardaccount / total_quote_giftcardaccount',
    '之后'=> 
    数组(
      0 =>'wee',
      1 =>“折扣”,
      2 =>'税',
      3 =>'tax_subtotal',
      4 =>'grand_total',
      5 =>'奖励',
      6 =>'小计',
      7 =>'运费',
      8 =>'nominal',
      9 =>'免费送货',
      11 =>'tax_shipping',
      12 =>'weee',
    ),
    '之前'=> 
    数组(
      0 =>'customerbalance',
    ),
    '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 =>'nominal',
      8 =>'weee',
    ),
    '之前'=> 
    数组(
      0 =>'总和',
      1 =>'customerbalance',
      2 =>'giftcardaccount',
    ),
    '_code'=>'tax_giftwrapping',
  ),
  '奖励'=> 
  数组(
    'class'=>'enterprise_reward / total_quote_reward',
    '之后'=> 
    数组(
      0 =>'wee',
      1 =>“折扣”,
      2 =>'税',
      3 =>'tax_subtotal',
      4 =>'grand_total',
      5 =>'小计',
      6 =>'运费',
      7 =>'nominal',
      8 =>'免费送货',
      9 =>'tax_subtotal',
      10 =>'tax_shipping',
      11 =>'weee',
      12 =>'小计',
      13 =>'运费',
      14 =>“折扣”,
      15 =>'tax_subtotal',
      16 =>'免费送货',
      17 =>'tax_shipping',
      18 =>'nominal',
      19 =>'weee',
      20 =>'免费送货',
      21 =>'小计',
      22 =>'小计',
      23 =>'nominal',
      24 =>'小计',
      25 =>'nominal',
      26 =>'运费',
      27 =>'免费送货',
      28 =>'tax_subtotal',
      29 =>“折扣”,
      30 =>'税',
      31 =>'tax_giftwrapping',
    ),
    '之前'=> 
    数组(
      0 =>'giftcardaccount',
      1 =>'customerbalance',
      2 =>'customerbalance',
    ),
    'renderer'=>'enterprise_reward / checkout_total',
    '_code'=>'奖励',
  ),
  'cashondelivery'=> 
  数组(
    'class'=>'cashondelivery / quote_total',
    '之后'=> 
    数组(
      0 =>'小计',
      1 =>“折扣”,
      2 =>'运费',
      3 =>'标称',
      4 =>'小计',
      5 =>'运费',
      6 =>'标称',
      7 =>'免费送货',
      8 =>'tax_subtotal',
      9 =>'tax_shipping',
      10 =>'weee',
      11 =>'小计',
      12 =>'免费送货',
      13 =>'tax_subtotal',
      14 =>'nominal',
    ),
    '之前'=> 
    数组(
      0 =>'税',
      1 =>'grand_total',
      2 =>'grand_total',
      3 =>'customerbalance',
      4 =>'giftcardaccount',
      5 =>'tax_giftwrapping',
      6 =>'奖励',
      7 =>'customerbalance',
      8 =>'giftcardaccount',
      9 =>'奖励',
    ),
    'renderer'=>'cashondelivery / checkout_cod',
    'admin_renderer'=>'cashondelivery / adminhtml_sales_order_create_totals_cod',
    '_code'=>'cashondelivery',
  ),
  'cashondelivery_tax'=> 
  数组(
    'class'=>'cashondelivery / quote_taxTotal',
    '之后'=> 
    数组(
      0 =>'小计',
      1 =>“折扣”,
      2 =>'运费',
      3 =>'税',
      4 =>'标称',
      5 =>'小计',
      6 =>'运费',
      7 =>'nominal',
      8 =>'免费送货',
      9 =>'tax_subtotal',
      10 =>'tax_shipping',
      11 =>'weee',
      12 =>'小计',
      13 =>'免费送货',
      14 =>'tax_subtotal',
      15 =>'nominal',
      16 =>'小计',
      17 =>'运费',
      18 =>“折扣”,
      19 =>'tax_subtotal',
      20 =>'免费送货',
      21 =>'tax_shipping',
      22 =>'标称',
      23 =>'weee',
      24 =>'cashondelivery',
    ),
    '之前'=> 
    数组(
      0 =>'总和',
      1 =>'customerbalance',
      2 =>'giftcardaccount',
      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 =>'weee',
      11 =>'小计',
      12 =>'免费送货',
      13 =>'tax_subtotal',
      14 =>'nominal',
    ),
    '之前'=> 
    数组(
      0 =>'税',
      1 =>'grand_total',
      2 =>'grand_total',
      3 =>'customerbalance',
      4 =>'giftcardaccount',
      5 =>'tax_giftwrapping',
      6 =>'奖励',
      7 =>'cashondelivery_tax',
      8 =>'customerbalance',
      9 =>'giftcardaccount',
      10 =>'奖励',
    ),
    '_code'=>'运输保护',
  ),
  '出货保护税'=> 
  数组(
    'class'=>'n98_shippingprotection / quote_address_total_shippingprotectionTax',
    '之后'=> 
    数组(
      0 =>'小计',
      1 =>“折扣”,
      2 =>'运费',
      3 =>'税',
      4 =>'标称',
      5 =>'小计',
      6 =>'运费',
      7 =>'nominal',
      8 =>'免费送货',
      9 =>'tax_subtotal',
      10 =>'tax_shipping',
      11 =>'weee',
      12 =>'小计',
      13 =>'免费送货',
      14 =>'tax_subtotal',
      15 =>'nominal',
      16 =>'小计',
      17 =>'运费',
      18 =>“折扣”,
      19 =>'tax_subtotal',
      20 =>'免费送货',
      21 =>'tax_shipping',
      22 =>'标称',
      23 =>'weee',
      24 =>'cashondelivery',
      25 =>'shippingprotection',
    ),
    '之前'=> 
    数组(
      0 =>'总和',
      1 =>'customerbalance',
      2 =>'giftcardaccount',
      3 =>'奖励',
    ),
    '_code'=>'货运保护税',
  ),

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

感谢您坚持使用@Alex,这是一个更好的答案,带有更好的解释:)我的第一个答案是错误的。

PHP为所有数组排序功能实现了快速排序(参考zend_qsort.c)。

如果数组中的两个记录相同,则它们的位置将被交换。

问题是礼品包装纸的总记录,据此,记录_compareTotals()大于小计名义金额等于所有其他总计

根据不同的原顺序$confArray输入数组,并在枢轴元素的位置是合法的交换giftwrap用如折扣,因为两者是平等的,即使折扣越大然后出货

从排序算法的角度来看,这可能使问题更清晰:

  • 运送<tax_shipping
  • 礼品包装==运送
  • 礼品包装== tax_shipping

即使原始问题是选择快速排序来构建有向非循环依赖图,也有几种可能的解决方案

  • 一个(较差的,短期的)解决方案是在礼品包装的总数中添加更多的依赖性,即使到目前为止还没有出现的其他总数可能还有更多的问题。
  • 真正的解决方案是针对该问题实施拓扑排序算法。

有趣的是,周围没有很多PHP软件包。有一个孤立的PEAR包Structures_Graph使用该方法可能是快速的解决方案,但这意味着将转换$confArrayStructures_Graph结构(因此可能不是那么快)。

Wikipedia很好地解释了问题,因此推出自己的解决方案可能是一个有趣的挑战。德国Wikipedia拓扑排序分页符把问题变成逻辑步骤,并且还具有PERL一个很好的例子算法。

最后,这是我针对此问题的补丁。

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

  1. 复制app/code/core/Mage/Sales/Model/Config/Ordered.phpapp/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
+++应用程序/代码/本地/法师/销售/模型/配置/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边缘数组。每个边缘都指定为具有两个元素的数组:边缘的源节点和目标节点
+ * @返回数组|空
+ * /
+函数topological_sort($ nodeids,$ edges){
+ $ L = $ S = $ nodes = array();
+ foreach($ nodeids as $ id){
+ $ nodes [$ id] = array('in'=> array(),'out'=> array());
+ foreach($ edges为$ 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为$ n){
+ if(!empty($ n ['in'])或!empty($ n ['out'])){
+返回null; //无法排序,因为图是循环的
+}
+}
+返回$ L;
+}
+
+ / **
+ *排序配置数组
+ *
+ *公开,可通过测试轻松访问
+ *
+ * @参数$ configArray
+ * @返回数组
+ * /
+公共功能_topSortConfigArray($ configArray)
+ {
+ $ nodes = array_keys($ configArray);
+ $ edges = array();
+
+ foreach($ configArray as $ code => $ data){
+ $ _code = $ data ['_ code'];
+如果(!isset($ configArray [$ _ code]))继续;
+ foreach($ data ['before'] as $ beforeCode){
+如果(!isset($ configArray [$ beforeCode]))继续;
+ $ edges [] = array($ _ code,$ beforeCode);
+}
+
+ foreach($ data ['after'] as $ afterCode){
+如果(!isset($ configArray [$ afterCode]])继续;
+ $ edges [] = array($ afterCode,$ _code);
+}
+}
+返回$ this-> topological_sort($ nodes,$ edges);
+}
+
+ // [PATCHED CODE END]
+
+
     / **
      *汇总所有项目之前/之后的信息,并根据此数据对总计进行排序
      *
@@ -138,38 +210,16 @@
         //如果第一个元素包含“ sort_order”键,则调用简单排序
         reset($ configArray);
         $ element = current($ configArray);
+ // [PATCHED CODE BEGIN]
         如果(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);
+ // [PATCHED CODE END]
+
         如果(Mage :: app()-> useCache('config')){
             法师:: app()-> saveCache(serialize($ sortedCollectors),$ this-> _ collectorsCacheKey,array(
                     Mage_Core_Model_Config :: CACHE_TAG
@@ -196,27 +246,6 @@
     }

     / **
-*在比较之前/之后使用的回调
-*
-* @参数数组$ a
-* @参数数组$ b
-* @返回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进行比较的回调
      *
      * @参数数组$ a

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

编辑:这个答案是错误的请参阅评论中的讨论。


正如Vinai所指出的那样,问题在于即使参数不相等,订单函数也会返回0。我修改了该函数以使其退回到按键的字符串顺序,如下所示:

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 = strcmp($aCode, $bCode); // was $res = 0 before
    }
    return $res;
}

我决定使用Plan B,使getSortedCollectors重载...直截了当,并给了我绝对的控制权,如果可以的话,如果我要引入新模块,我必须检查是否需要在此处添加它们

<?php
class YourModule_Sales_Model_Total_Quote_Collector extends Mage_Sales_Model_Quote_Address_Total_Collector {

    protected function _getSortedCollectorCodes() {
        return array(
            'nominal',
            'subtotal',
            'msrp',
            'freeshipping',
            'tax_subtotal',
            'weee',
            'shipping',
            'tax_shipping',
            'floorfee',
            'bottlediscount',
            'discount',
            'tax',
            'grand_total',
        );
    }

}

好多年以来一直被困在这里!!! +

现在,我知道为什么过去的某些项目很难调整,因为我只能说关于凌晨和税收组合的噩梦,却从来不明白为什么,昨天我找到了原因,后来我发现了这篇文章,真可惜..但是大多数我需要知道响应才能搜索问题的时间。

下面的代码至少对linux负责的人来说是显而易见的解决方案,基本上是下面的代码,我基本上使用了古老的linux命令tsort,它按照我们在这里需要的方式具体地进行了拓扑排序。

对于我们中间的昆虫学和考古学家来说,一些指针http://www.gnu.org/software/coreutils/manual/html_node/tsort-invocation.html,我正在使用正宗的80年代技术... yummmm

    /**
 * 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 {
        foreach ($configArray as $code => $data) {
            foreach ($data['before'] as $beforeCode) {
                if (!isset($configArray[$beforeCode])) {
                    continue;
                }
                $configArray[$code]['before'] = array_merge(
                        $configArray[$code]['before'],
                        $configArray[$beforeCode]['before']);
                $configArray[$code]['before'] = array_unique(
                        $configArray[$code]['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_merge(
                        $configArray[$code]['after'],
                        $configArray[$afterCode]['after']);
                $configArray[$code]['after'] = array_unique(
                        $configArray[$code]['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'));
        $res = "";
        foreach ($configArray as $code => $data) {
            foreach ($data['before'] as $beforeCode) {
                if (!isset($configArray[$beforeCode])) {
                    continue;
                }
                $res = $res . "$code $beforeCode\n";
            }
            foreach ($data['after'] as $afterCode) {
                if (!isset($configArray[$afterCode])) {
                    continue;
                }
                $res = $res . "$afterCode $code\n";
            }
        }
        file_put_contents(Mage::getBaseDir('tmp')."/graph.txt", $res);
        $sortedCollectors=explode("\n",shell_exec('tsort '.Mage::getBaseDir('tmp')."/graph.txt"),-1);           
    }
    if (Mage::app()->useCache('config')) {
        Mage::app()
                ->saveCache(serialize($sortedCollectors),
                        $this->_collectorsCacheKey,
                        array(Mage_Core_Model_Config::CACHE_TAG));
    }
    return $sortedCollectors;
}

为了完整起见,我已经发布了完整的函数,当然,至少对于我来说,它就像是一种魅力……

上面的讨论清楚地表明了问题所在。如果没有在集合的任何两个元素之间定义顺序功能的情况,那么通常的排序就不适用于该数据集。如果只有一些关系定义为“部分依赖”,则必须使用拓扑排序来遵守声明的“ before”和“ after”语句。在我的测试中,这些声明在结果集中被打破,并在那里添加了其他模块。不足之处在于,它不仅影响附加模块,而且可能以不可预测的方式更改整个排序结果。因此,我实现了可以解决问题的标准拓扑排序:

/**
 * The source data of the nodes and their dependencies, they are not required to be symmetrical node cold list other
 * node in 'after' but not present in its 'before' list:
 * @param $configArray
 *  $configArray = [
 *    <nodeCode>     => ["_code"=> <nodeCode>, "after"=> [<dependsOnNodeCode>, ...], "before"=> [<dependedByCode>, ...] ],
 *     ...
 * ]
 * The procedure updates adjacency list , to have every edge to be listed in the both nodes (in one 'after' and other 'before')
 */
function normalizeDependencies(&$configArray) {
    //For each node in the source data
    foreach ($configArray as $code => $data) {
            //Look up all nodes listed 'before' and update their 'after' for consistency
        foreach ($data['before'] as $beforeCode) {
            if (!isset($configArray[$beforeCode])) {
                continue;
            }
            $configArray[$beforeCode]['after'] = array_unique(array_merge(
                $configArray[$beforeCode]['after'], array($code)
            ));
        }
            //Look up all nodes listed 'after' and update their 'before' for consistency
        foreach ($data['after'] as $afterCode) {
            if (!isset($configArray[$afterCode])) {
                continue;
            }
            $configArray[$afterCode]['before'] = array_unique(array_merge(
                $configArray[$afterCode]['before'], array($code)
            ));
        }
    }
}

/**
 *  http://en.wikipedia.org/wiki/Topological_sorting
 *  Implements Kahn (1962) algorithms
 */
function topoSort(&$array) {
    normalizeDependencies($array);
    $result = array(); // Empty list that will contain the sorted elements
    $front = array(); // Set of all nodeCodes with no incoming edges
    //Push all items with no predecessors in S;
    foreach ($array as $code => $data) {
        if ( empty ($data['after']) ) {
            $front[] = $code;
        }
    }
    // While 'front' is not empty
    while (! empty ($front)) {
        //Deque 'frontier' from 'front'
        $frontierCode = array_shift($front);
        //Push it in 'result'
        $result[$frontierCode]= $array[$frontierCode];
        // Remove all outgoing edges from 'frontier';
        while (! empty ($array[$frontierCode]['before'])) {
            $afterCode = array_shift($array[$frontierCode]['before']);
            // remove corresponding edge e from the graph
            $array[$afterCode]['after'] = array_remove($array[$afterCode]['after'], $frontierCode);
            //* if, no more decencies put node into processing queue:
            // if m has no other incoming edges then
            if ( empty ($array[$afterCode]['after']) ) {
                // insert m into 'front'
                array_push($front, $afterCode);
            }
        }
    }
    if(count($result) != count($array)){
        saveGraph($array, 'mage-dependencies.dot');
        throw new Exception("Acyclic dependencies in data, see graphviz diagram: mage-dependencies.dot for details.");
    }
    return $result;
}
 /**
 * Could not find better way to remove value from array
 *
 * @param $array
 * @param $value
 * @return array
 */
protected function array_remove($array, $value){
    $cp = array();
    foreach($array as $b) {
        if($b != $value){
            $cp[]=$b;
        }
    }
    return $cp;
}

/**
 *  Saves graph in the graphviz format for visualisation:
 *    >dot -Tpng /tmp/dotfile.dot > viz-graph.png
 */
function saveGraph($configArray, $fileName){
    $fp = fopen($fileName,'w');
    fwrite($fp,"digraph TotalOrder\n");
    fwrite($fp,"{\n");
    foreach($configArray as $code=>$data) {
        fwrite($fp,"$code;\n");
        foreach($data['before'] as $beforeCode) {
            fwrite($fp,"$beforeCode -> $code;\n");
        }
        foreach($data['after'] as $afterCode) {
            fwrite($fp,"$code -> $afterCode;\n");
        }
    }
    fwrite($fp,"}\n");
    fclose($fp);
}

问题是,将其(或其他拓扑分类)放入Magento发布/热修复程序有多困难?

本文地址:http://php.askforanswer.com/paixusuanfamagentojiezhangzongeweizhengquepaixudaozhicuowudeyunfeijisuan.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!