EE 1.14.2 / CE 1.9.2: los artículos de cotización no se fusionaron correctamente al iniciar sesión (productos duplicados en el carrito)

16

Encontré un error extraño en Magento EE 1.14.2 (también afecta a CE 1.9.2) con el carrito.

Pasos para reproducir:

  1. Inicie sesión como cliente A
  2. Añade el producto X al carrito
  3. Cambiar a un navegador diferente
  4. Añade el producto X al carrito
  5. Inicie sesión como cliente A

Carrito esperado:

  • 2 x producto X

Carro real:

  • 1 x producto X
  • 1 x producto X

Es decir, los productos no se fusionan.

En lugar de cambiar el navegador, también puede borrar la cookie de sesión o elegir una cantidad diferente para el producto.

El peor efecto secundario de esto es que se aplica la cantidad máxima de pedido por artículo. En mi caso, había un descuento del 100% en un producto, pero solo se podía pedir una vez. Con este pequeño truco, puedes pedirlo en cualquier cantidad, gratis.

¿Por qué sucede esto y cómo puedo evitarlo?

Fabian Schmengler
fuente

Respuestas:

18

Bonito resumen del error de arriba, Fabian!

Para cualquier otro usuario que se encuentre con este error, ya hay un parche de Magento para esto.

Como cliente de Enterprise, puede solicitar / descargar PATCH_SUPEE-6190_EE_1.14.2.0_v1.shpara arreglar esto.

Actualización 24.02.2016: Esto también se abordó en el último parche SUPEE-7405 v 1.1. Según Fabian en Twitter (vea este y los siguientes tweets ) existe la posibilidad de que aún no se resuelva por completo. Por favor, pruébalo tú mismo también.

En cuanto a EE 1.14.2.0, la solución es:

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']);
                     }
                 }

Nota: Por lo general, no publicaría el código EE aquí, pero como el problema / los archivos son los mismos que en CE y no afectan a una función exclusiva de EE, espero que esté bien.

Anna Völkl
fuente
44
Yo apruebo esto.
philwinkle
55
Lo dejaremos pasar.
Benmarks
1
Deslice es entonces.
Marius
Esto funciona mejor que mi solución, lo que causó problemas con los productos combinados. ¡Gracias por compartir!
Fabian Schmengler
1
Desafortunadamente, esto aún se puede omitir si agrega el producto una vez a través de la lista de productos y una vez a través de la página de detalles del producto, porque el parámetro "productos relacionados" solo está presente en el último caso. También puede agregar "productos relacionados" a las unset()llamadas, pero aún así no es seguro porque cualquier parámetro POST arbitrario se agrega a la opción buyRequest también. Voy a ignorar esta opción por completo en su lugar.
Fabian Schmengler
15

Resultó que este es un error Mage_Sales_Model_Quote_Item::compare()que se introdujo en Magento CE 1.9.2 / EE 1.14.2. El método se usa para comparar artículos para decidir si son el mismo producto y se pueden combinar (durante el inicio de sesión y al agregar productos al carrito).

Al comparar todas las opciones personalizadas, debe omitir las opciones que no son representativas ( _notRepresentOptions), es decir, la opción info_buyRequest .

En versiones anteriores de Magento, se veía así:

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

Y funcionó correctamente. Ahora se ve así:

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

y la comprobación adicional de hasCustomOptions()causa el error descrito. ¿Por qué? Parece que el cheque se ha agregado para mantener siempre separados los productos con opciones personalizadas. No creo que tenga sentido, al menos no en la forma en que se implementa, pero habrá alguna razón de la que no estoy al tanto.

Sin embargo, $item->getProduct()->hasCustomOptions()siempre devuelve verdadero para los artículos de cotización

Este es el método:

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

Pero $this->_customOptionstambién contiene elinfo_buyRequest opción del artículo de presupuesto.

Para una solución discreta, traté de eliminar la info_buyRequestopción de todos los productos en un observador ensales_quote_merge_before , sin éxito.

La razón radica en Mage_Sales_Model_Quote_Item_Abstract::getProduct()que la opción se copia nuevamente del artículo de presupuesto:

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

    [...]

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

Solución

Creé una reescritura Mage_Sales_Model_Quote_Itemcon una anulación para getProduct()no incluir la info_buyRequestopción en este punto:

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

Esto causó problemas con los productos combinados, la alternativa a continuación o el parche oficial descrito por @ AnnaVölkl es una mejor solución

Alternativa

También puede eliminar la infracción && !$item->getProduct()->hasCustomOptions()en el compare()método si está reescribiendo el modelo del artículo de todos modos. No sé qué problema trató de resolver, pero creó más ...

Actualización 29 de enero de 2016

Informé esto a Magento y obtuve la respuesta de que no podían reproducir el problema, por lo que el parche no entrará en la edición de la comunidad (Presentación APPSEC-1321).

Esto significa que, si tiene el problema, debe aplicar el parche empresarial SUPEE-6190 después de cada actualización o utilizar una reescritura de clase en su lugar.

Fabian Schmengler
fuente
However, $item->getProduct()->hasCustomOptions() always returns true for quote items!Está comprobando los datos del producto para ver las opciones personalizadas, no el artículo de presupuesto :)
kanevbgbe
1
@kanevbgbe sorprendentemente, no. Magento "prepara" la instancia del producto asociada con un artículo de presupuesto y agrega sus valores de opciones personalizadas
Fabian Schmengler
Sé que al agregar al carrito la instancia del producto está completamente cargada (en comparación con la carga de cotización), por lo que se establece desde fuera de los algoritmos de cotización directamente a la instancia del elemento de cotización a través de setProduct (), tal vez esta verificación tenga un resultado diferente .
kanevbgbe
1

Como puedo ver, la respuesta anterior ya está disponible en la última versión de Magento, pero aún recibimos el problema. No funcionó porque hemos hecho muchas personalizaciones. Pensé en compartir la solución.

Para nosotros fue muy simple, ya que solo utilizamos productos simples. Entonces, ampliamos la función de comparación de combinación de comillas a esto:

NS_Module_Model_Sales_Quote_Item extiende Mage_Sales_Model_Quote_Item {

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

}

y agregado

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

pero. para aquellos que también usan productos configurables, entonces podría no ser útil para usted. En ese caso, puede imprimir ambas matrices: $ itemOptionValue y $ optionValue y ver la diferencia. desarmar todas las claves adicionales que no son comunes en ambas matrices. Eso debería resolver el problema.

Arif Ahmad
fuente
-1

Simplemente puede agregar una opción al producto en el evento sales_quote_add_item:

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

Enlace de referencia: ¿ Deshabilitar fusión de posiciones de carro?

gelanivishal
fuente
Esa es una solución, pero deshabilitar completamente la fusión de elementos generalmente no es deseable.
Fabian Schmengler