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

Фактическая корзина:

  • 1 х продукт х
  • 1 х продукт х

Т.е. продукты не объединены.

Вместо того, чтобы переключать браузер, вы также можете очистить cookie сессии или выбрать другой кол-во для продукта.

Худшим побочным эффектом этого является то, что максимальное количество заказа применяется для каждой позиции. В моем случае на товар была скидка 100%, но заказать его можно было только один раз. С этим маленьким трюком вы можете заказать его в любом количестве бесплатно.

Почему это происходит и как я могу предотвратить это?

Фабиан Шменглер
источник

Ответы:

18

Хорошее завершение ошибки выше, Фабиан!

Для любых других пользователей, которые столкнутся с этой ошибкой, уже есть патч от Magento для этого.

Как корпоративный клиент, вы можете запросить / скачать, PATCH_SUPEE-6190_EE_1.14.2.0_v1.shчтобы это исправить.

Обновление 24.02.2016: это также исправлено в последнем патче SUPEE-7405 v 1.1. Согласно Фабиану в Твиттере (см. Этот и последующие твиты ), есть вероятность, что он все еще не решен полностью. Пожалуйста, проверьте это сами.

Что касается 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-only, я надеюсь, что все в порядке.

Анна Фёлькл
источник
4
Я одобряю это.
Philwinkle
5
Мы позволим этому скользить.
отметки
1
Слайд это тогда.
Мариус
Это работает лучше, чем мое исправление, которое вызвало проблемы с комплектными продуктами. Спасибо, что поделился!
Фабиан Шменглер
1
К сожалению, это все еще можно обойти, если добавить продукт один раз через список продуктов и один раз через страницу сведений о продукте, поскольку параметр related_products присутствует только в последнем случае. Вы также можете добавить «related_products» к unset()вызовам, но это по-прежнему небезопасно, поскольку любой произвольный параметр POST также добавляется в опцию buyRequest. Я собираюсь полностью игнорировать эту опцию.
Фабиан Шменглер
15

Оказалось, что это ошибка в Mage_Sales_Model_Quote_Item::compare()том, что было введено в Magento CE 1.9.2 / EE 1.14.2. Этот метод используется для сравнения товаров, чтобы определить, являются ли они одним и тем же товаром и могут ли быть объединены (при входе в систему и при добавлении товаров в корзину).

При сравнении всех пользовательских параметров следует пропустить параметры, которые не являются represanative ( _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()всегда возвращает истину для цитат!

Это метод:

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()методе, если вы все равно переписываете модель элемента. Я не знаю, какую проблему он пытался решить, но он создал больше ...

Обновление 29 января 2016

Я сообщил об этом в Magento и получил ответ, что они не могут воспроизвести проблему, поэтому исправление не войдет в выпуск сообщества (представление APPSEC-1321).

Это означает, что если у вас есть проблема, вам нужно применять исправление Enterprise SUPEE-6190 после каждого обновления или использовать вместо него переписывание классов.

Фабиан Шменглер
источник
However, $item->getProduct()->hasCustomOptions() always returns true for quote items!Он проверяет данные продукта для пользовательских опций, а не цитаты :)
kanevbgbe
1
@kanevbgbe на удивление, нет. Magento «готовит» экземпляр продукта, связанный с элементом цитаты, и добавляет значения пользовательских опций
Фабиан Шменглер,
Я знаю, что при добавлении в корзину экземпляр продукта полностью загружен (по сравнению с загрузкой цитаты), поэтому он устанавливается вне алгоритмов цитаты непосредственно в экземпляр элемента цитаты через setProduct (), возможно, тогда эта проверка будет иметь другой вывод ,
kanevbgbe
1

Как я вижу выше, ответ уже доступен в последней версии Magento, но проблема все еще не решена. Это не сработало, потому что мы сделали много настроек. Мысль о том, чтобы поделиться решением.

Для нас это было очень просто, так как мы используем только простые продукты. Итак, мы расширили функцию сравнения слиянием цитат до этой:

NS_Module_Model_Sales_Quote_Item extends 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'));

Ссылка: Отключить слияние позиций корзины?

gelanivishal
источник
Это обходной путь, но полное отключение слияния элементов обычно нежелательно.
Фабиан Шменглер