Modifique la tasa de impuestos en los artículos de cotización del carrito y vuelva a calcular

31

Tengo una categoría de productos a los que (legalmente) se les debe cambiar su tasa de impuestos cuando realiza un pedido de más de una cantidad determinada. He ampliado los diversos modelos de impuestos para que esto funcione cuando agrega un nuevo producto al carrito, pero tengo problemas cuando el usuario actualiza las cantidades en el carrito o agrega productos adicionales que inclinan las cantidades que ya están en el carrito por encima del umbral cantidad.

Problema 1:

En primer lugar, no estoy al 100% de los eventos que debo observar. He intentado lo siguiente;

checkout_cart_save_after(basado en esto -> https://stackoverflow.com/questions/14362702/magento-programatic-update-cart-via-event )

checkout_cart_update_items_after(basado en esto -> https://stackoverflow.com/questions/5104482/programmatic-add-product-to-cart-with-price-change )

sales_quote_save_before(basado en esto -> https://stackoverflow.com/questions/7638858/magento-recalculate-cart-total-in-observer )

Problema 2:

Puedo acceder a los artículos de presupuesto desde el carrito, parece que hay muchas maneras de hacerlo. También puedo iterar a través de los artículos individuales en el carrito, actualizar las propiedades de esos artículos y luego guardarlos (al menos temporalmente). Sin embargo, no puedo guardar la cotización y volver a calcular los impuestos en el proceso de pago.

Parte de la razón es que, si bien puedo acceder a la cotización del carrito, no estoy seguro de qué método utilizar para poder escribir en él.

Lo que he probado:

Lo que he intentado en términos de acceso al contenido del carrito ha dependido del evento que he observado, pero he intentado todo lo siguiente;

1. 
$item = $observer->getQuoteItem;

2.
$cart = Mage::getSingleton('checkout/cart');
$cartItems = $cart->getCart()->getItems(); 

3.
$cart = $observer->getData('cart');
$quote = $cart->getData('quote');
$cartItems = $quote->getAllVisibleItems();

4.
$cartHelper = Mage::helper('checkout/cart');
$cartItems = $cartHelper->getCart()->getItems(); 

5.
$quote = Mage::getModel('checkout/cart');
$cartItems = $quote->getItems(); 

El que parece al menos permitirme acceder a la cotización, revisarlo y actualizar los elementos es

6.
$quote = Mage::getSingleton('checkout/session')->getQuote();
$cartItems = $quote->getAllVisibleItems();

Esto me permite actualizar cada elemento de cotización cuando itero (creo que usando setters mágicos ya que no puedo encontrar ningún método correspondiente). Esperaba poder actualizar la identificación de la clase de impuestos para el artículo de la cotización y luego volver a calcular los impuestos. Si uso lo siguiente (donde $ taxClassId es diferente al que ya usa cada artículo de cotización);

$item->setTaxClassId( $taxClassId );
$item->getProduct()->setIsSuperMode(true);
$item->save;

Y luego registrar los resultados;

Mage::log($item->debug(), null,'taxobserver.log', true);

Muestra que efectivamente actualicé este artículo de cotización y cambié la identificación fiscal. Sin embargo, si sigo adelante e intento guardar la cita modificada;

$quote->setTotalsCollectedFlag(false)->collectTotals();
$quote->save();     

Y luego depurar de nuevo;

Mage::log($item->debug(), null,'taxobserver.log', true);

Mis cambios no se han guardado, el cambio del artículo de presupuesto se ha restablecido y los totales del carrito no se vuelven a calcular. Comenzar a preguntarse si encontrar un edificio alto para saltar podría ser la solución para este.

McNab
fuente
por favor, alguien me ayude a resolver esto: stackoverflow.com/questions/27978781/… gracias.
satish

Respuestas:

35

Mi sugerencia sería poner un observador en el sales_quote_collect_totals_beforeevento que se activa en el Mage_Sales_Model_Quote::collectTotalsmétodo antes de que comience el proceso de recolección total. Luego, desde el interior de este método de observación, itere los artículos de cotización y cambie la clase de impuestos en el objeto del producto (ya cargado) que puede recuperar del artículo de cotización.

Después de configurar la información sobre el objeto del producto, haga lo que haga, NO intente guardarlo en la base de datos. Tener la clase de impuestos establecida según sea necesario en el objeto del producto en la memoria será lo suficientemente buena como para que la lógica de recopilación de totales se encuentre en la Mage_Tax_Model_Sales_Total_Quote_Taxrecolección en qué clase de impuestos debería basar sus cálculos. Guardar el producto (como parece estar tratando de hacer en el ejemplo de código anterior) causará problemas importantes de rendimiento, creará condiciones de carrera en el proceso de cálculo y simplemente no es una buena práctica.

La razón por la cual los eventos con los que está tratando de trabajar no le permiten lograr lo que está tratando de lograr es porque todos ocurren después del cálculo total, un proceso que solo se ejecuta una vez antes de guardar la cotización.

Vale la pena señalar que el proceso de recopilación de totales es que una vez ejecutado, sin hacer un trabajo adicional, no puede volver a llamarlo para que se vuelva a calcular en función de los cambios que haya realizado en los elementos de la cotización. Vea este enlace que tomé de la serie de blogs, una colección mía recientemente reunida en el proceso de recopilación de totales:

Ahora que comprende lo que ocurre durante el proceso de recolección de totales, puede que le resulte conveniente o necesario llamarlo directamente usted mismo. Sin embargo, antes de comenzar a sentirse demasiado seguro con el uso de collectTotals para sus propios fines, tenga en cuenta la siguiente regla:

¡No se pueden agregar productos a la cotización después de ejecutar collectTotals!

. . . a menos que se borren los cachés de elementos de las direcciones de presupuesto.

El método de "recopilación" de casi todos los modelos totales se basa en obtener los elementos de la cita de la dirección y recorrerlos. La primera vez que getAllItems se ejecuta en una dirección de presupuesto, la colección de artículos se almacena en caché con una clave única, y es esta colección en caché la que se devuelve en llamadas posteriores.

Si tiene la menor idea de sumergirse realmente en las profundidades de cómo funciona el proceso de recopilación de totales, puede consultar la primera de las series de cuatro partes sobre la recopilación total aquí para obtener una lectura más profunda: Desvelando los Totales de recopilación de Magento: Introducción

Para resumir, debe capturar un evento que se ejecute antes del proceso de recopilación de totales (y antes de que se llame a getAllItems en las direcciones de cotización) para que los cambios que realice en los artículos sean utilizados por los recolectores totales. No he verificado que el sales_quote_collect_totals_beforeevento sugerido se ejecute antes de cualquier llamada a la getAllItemsdirección de presupuesto, pero estoy casi seguro de que funcionará para lo que necesita. Pero si no, espero haber proporcionado suficiente contexto para que descubras qué evento necesitas atrapar para que funcione.

davidalger
fuente
Esta respuesta está mucho más allá de lo que esperaba. Brillante, gracias por tomarse el tiempo. Fantástica respuesta.
McNab
Argh! Después de pasar todo el día en esto y no lograr que funcionara, finalmente me di cuenta de que se trataba de un modelo mal reescrito en otra parte del módulo. Esto funciona perfectamente y he aprendido mucho de eso, gracias de nuevo David.
McNab
@McNab Me alegra saber que lo tienes funcionando. Definitivamente una de las áreas más complejas de Magento que se está tratando. :)
davidalger
+1 en el blog vinculado. Lo pasé por alto las primeras veces que lo leí. Es de gran ayuda.
pspahn
6

Hay otro evento: sales_quote_item_set_producten Mage_Sales_Model_Quote_Item :: setProduct

Mage::dispatchEvent('sales_quote_item_set_product', array(
            'product' => $product,
            'quote_item'=>$this
        ));

Puede modificar la clase de impuestos del producto utilizando este evento. Use el método quoteItem getQty para encontrar la cantidad agregada al carrito.

PandaWebStudio
fuente
Gracias por esto, es útil saberlo: me doy cuenta, según la respuesta de @davidalger, que no debería modificar la clase de impuestos del producto ahora, sino el modelo de cotización. Es bueno saberlo sin embargo.
McNab
Bueno, en realidad puede modificar la clase de impuestos del artículo de cotización, también tiene acceso en el evento al objeto quoteItem.
PandaWebStudio
Ah ok - no entendí Gracias por aclarar :)
McNab
@PandaWebStudio, ¿cómo establecer un monto de impuesto personalizado para el artículo de cotización? aquí está mi pregunta, magento.stackexchange.com/questions/274520/…
jafar pinjar
2

Quizás intentar cambiar la clase de impuestos podría no ser el mejor enfoque. ¿Por qué no crear un producto similar para aquellos productos que usan la clase de impuestos más alta y establecer una cantidad mínima en el carrito para ese producto? Luego, use checkout_cart_update_items_afterpara intercambiar productos según la cantidad total en el carrito.

Definitivamente, esta no es una "mejor práctica", pero podría ayudarlo a pensar en otra dirección.

Alternativamente, intente configurar algo con http://www.mageworx.com/multi-fees-magento-extension.html donde agrega una 'tarifa' por el impuesto adicional. En realidad, esta podría ser una mejor manera de hacerlo, pero no aparecerá en los totales de impuestos, más como una línea total de pedido adicional.

Sander Mangel
fuente
Gracias por la respuesta Sander. Es una buena idea y no lo había pensado. Te diré por qué no me gusta eso: los productos subyacentes se están ejecutando a través de la suite uMarketplace de Unirgy y ha sido una tarea monstruosa llevarlo a donde está, ya que se está ejecutando como un sitio de comparación de precios. Creo que puede ser un gran trabajo. Sin embargo, si no puedo actualizar este artículo de presupuesto, tendré que mirarlo.
McNab
¡Maldición, ni siquiera puedo votar tu respuesta ya que he gastado toda mi reputación en esta recompensa! +1 :) Votará cuando reciba un poco más.
McNab
jaja no hay problema, entiendo el problema y esta no sería una buena solución para el caso. Probablemente iría con las multifees entonces, esa es una solución 'más limpia'
Sander Mangel
FWIW, no sugeriría esta ruta ... aunque solo sea porque haría que la contabilidad de las ventas y el seguimiento del inventario sean una pesadilla potencial. Como dijo el OP, no es la mejor práctica, pero agregaré: Causará más problemas de los que resolvería.
davidalger
0

Utilizar esta <sales_quote_collect_totals_before>

y en tu función te gusta

 public function quoteCollectTotalsBefore(Varien_Event_Observer $observer)
    $quote = $observer->getQuote();
    foreach ($quote->getAllItems() as $item)
    {
        $product = $item->getProduct();
        $product->setTaxClassId($product_tax_class_id);
    }
 }

Espero que esto funcione definitivamente

Mage2Solutions
fuente