EE 1.14.2 / CE 1.9.2:报价项目在登录时未正确合并(购物车中的重复产品)


16

我在购物车的Magento EE 1.14.2中发现了一个奇怪的错误(也影响到CE 1.9.2)。

重现步骤:

  1. 以客户A身份登录
  2. 将产品X添加到购物车
  3. 切换到其他浏览器
  4. 将产品X添加到购物车
  5. 以客户A身份登录

预期购物车:

  • 2 x产品X

实际购物车:

  • 1 x产品X
  • 1 x产品X

即产品未合并。

除了切换浏览器,您还可以清除会话cookie或为产品选择其他数量。

最严重的副作用是每件商品应用了最大订购量。就我而言,产品有100%的折扣,但您只能订购一次。有了这个小技巧,您可以免费订购任何数量的产品。

为什么会发生这种情况,如何预防呢?

Answers:


18

很好地解决了上面的错误,Fabian!

对于将要遇到此错误的其他用户,Magento已经提供了一个补丁。

作为企业客户,您可以请求/下载PATCH_SUPEE-6190_EE_1.14.2.0_v1.sh以解决此问题。

2016年2月24日更新:最新的SUPEE-7405 v 1.1补丁也解决了此问题。根据Fabian在Twitter上的介绍(请参阅此内容和后续推文),有可能它仍未完全解决。也请自己进行测试。

对于EE 1.14.2.0,解决方案是:

diff --git a/app/code/core/Mage/Sales/Model/Quote/Item.php b/app/code/core/Mage/Sales/Model/Quote/Item.php
index 3554faa..d759249 100644
--- a/app/code/core/Mage/Sales/Model/Quote/Item.php
+++ b/app/code/core/Mage/Sales/Model/Quote/Item.php
@@ -502,8 +502,8 @@ class Mage_Sales_Model_Quote_Item extends Mage_Sales_Model_Quote_Item_Abstract
                         $itemOptionValue = $_itemOptionValue;
                         $optionValue = $_optionValue;
                         // looks like it does not break bundle selection qty
-                        unset($itemOptionValue['qty'], $itemOptionValue['uenc']);
-                        unset($optionValue['qty'], $optionValue['uenc']);
+                        unset($itemOptionValue['qty'], $itemOptionValue['uenc'], $itemOptionValue['form_key']);
+                        unset($optionValue['qty'], $optionValue['uenc'], $optionValue['form_key']);
                     }
                 }

注意:通常,我不会在此处发布EE代码,但是由于问题/文件与CE中相同,并且不会影响仅EE的功能,所以我希望这可以。


4
我同意这一点。
philwinkle '16

5
我们让它滑动。
benmarks

1
然后滑动它。
马里乌斯

这比我的修复效果更好,后者导致捆绑产品出现问题。感谢分享!
Fabian Schmengler,

1
不幸的是,如果您一次通过产品列表添加产品,又一次通过产品详细信息页面添加产品,那么仍然可以绕开它,因为“ related_products”参数仅在后一种情况下存在。您也可以在unset()调用中添加“ related_products” ,但由于将任意POST参数也添加到buyRequest选项中,因此仍然不安全。我将完全忽略此选项。
Fabian Schmengler '16

15

原来,这是Mage_Sales_Model_Quote_Item::compare()Magento CE 1.9.2 / EE 1.14.2中引入的错误。该方法用于比较项目,以确定它们是否是同一产品,并且可以合并(在登录期间以及将产品添加到购物车时)。

比较所有自定义选项时,应跳过非代表性的选项(_notRepresentOptions),即info_buyRequest选项。

在以前的Magento版本中,它看起来像这样:

foreach ($this->getOptions() as $option) {
    if (in_array($option->getCode(), $this->_notRepresentOptions)) {
        continue;
    }

并正常工作。现在看起来像这样:

foreach ($this->getOptions() as $option) {
    if (in_array($option->getCode(), $this->_notRepresentOptions)
        && !$item->getProduct()->hasCustomOptions()
    ) {
        continue;
    }

以及其他检查是否hasCustomOptions()导致了所描述的错误。为什么?看起来已经添加了支票,以始终将带有自定义选项的产品分开。我认为这是没有道理的,至少在实施方式上没有意义,但是有一些我不知道的原因。

但是,$item->getProduct()->hasCustomOptions()对于报价项目,始终返回true!

这是方法:

public function hasCustomOptions()
{
    if (count($this->_customOptions)) {
        return true;
    } else {
        return false;
    }
}

而且$this->_customOptions还包含info_buyRequest来自报价项目的选项。

对于非干扰性的解决方案,我尝试info_buyRequest从上的观察者中的所有产品中删除该选项sales_quote_merge_before,但没有成功。

原因在于Mage_Sales_Model_Quote_Item_Abstract::getProduct()从报价项目本身再次复制选项:

public function getProduct()
{
    $product = $this->_getData('product');

    [...]

    if (is_array($this->_optionsByCode)) {
        $product->setCustomOptions($this->_optionsByCode);
    }
    return $product;
}

我为此创建了一个重写,Mage_Sales_Model_Quote_Item其中有一个替代,getProduct()以不包括该info_buyRequest选项:

public function getProduct() { $product = parent::getProduct(); $options = $product->getCustomOptions(); if (isset($options['info_buyRequest'])) { unset($options['info_buyRequest']); $product->setCustomOptions($options); } return $product; }

这给捆绑产品,下面的替代产品或@AnnaVölkl所述的官方补丁带来了麻烦,是一个更好的解决方案

另类

你也可以删除有问题&& !$item->getProduct()->hasCustomOptions()compare(),如果你反正重写项目模型法。我不知道它试图解决什么问题,但它创造了更多...

更新2016年1月29日

我将此问题报告给了Magento,并得到了他们无法重现该问题的答复,因此该补丁不会将其纳入社区版(提交APPSEC-1321)。

这意味着,如果遇到问题,则需要在每次更新后应用企业补丁SUPEE-6190 或改用类重写。


However, $item->getProduct()->hasCustomOptions() always returns true for quote items!它正在检查产品数据中的自定义选项,而不是报价项目:)
kanevbgbe

1
@kanevbgbe令人惊讶的是,没有。Magento“准备”与报价项目关联的产品实例并添加其自定义选项值
Fabian Schmengler,

我知道在添加到购物车操作中,产品实例已完全加载(与报价加载相比),因此它是从报价算法外部直接通过setProduct()设置为报价项目实例,也许然后此检查具有不同的输出。
kanevbgbe

1

如我所见,上述答案已经在Magento的最新版本中提供,但我们仍然遇到问题。它没有用,因为我们做了很多定制。想到共享解决方案。

对我们来说,这非常简单,因为我们只使用简单的产品。因此,我们将引号合并比较功能扩展为:

NS_Module_Model_Sales_Quote_Item扩展了Mage_Sales_Model_Quote_Item {

public function compare($item) {
    if ($this->getProductId() == $item->getProductId()) {
        return true;
    }
    return parent::compare($item);
}

}

并添加

<models>
   <sales>
      <rewrite>
         <quote_item>NS_Module_Model_Sales_Quote_Item</quote_item>
      </rewrite>
   </sales>
</models>

但。对于那些也在使用可配置产品的用户来说,这可能对您没有帮助。在那种情况下,您可以打印两个数组:$ itemOptionValue和$ optionValue并查看它们之间的区别。取消设置在两个阵列中都不通用的所有其他键。那应该解决问题。


-1

您可以在事件sales_quote_add_item中向产品添加一个选项:

$data['microtime'] = microtime(true);
$product->addCustomOption('do_not_merge', serialize($data));
$item->addOption($product->getCustomOption('do_not_merge'));

参考链接:禁用购物车位置合并?


这是一种解决方法,但是通常不希望完全禁用项目合并。
Fabian Schmengler,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.