Modifier le taux de taxe sur les articles de devis du panier et recalculer


31

J'ai une catégorie de produits qui (légalement) doivent voir leur taux de taxe modifié lorsque vous commandez plus qu'une certaine quantité. J'ai étendu les différents modèles de taxe pour que cela fonctionne lorsque vous ajoutez un nouveau produit au panier, mais je rencontre des problèmes lorsque l'utilisateur met à jour les quantités dans le panier ou ajoute des produits supplémentaires qui font basculer les quantités déjà dans le panier au-delà du seuil montant.

Problème 1:

Tout d'abord, je ne suis pas à 100% quel (s) événement (s) observer. J'ai essayé ce qui suit;

checkout_cart_save_after(basé sur ceci -> https://stackoverflow.com/questions/14362702/magento-programately-update-cart-via-event )

checkout_cart_update_items_after(basé sur ceci -> https://stackoverflow.com/questions/5104482/programmatically-add-product-to-cart-with-price-change )

sales_quote_save_before(basé sur ceci -> https://stackoverflow.com/questions/7638858/magento-recalculate-cart-total-in-observer )

Problème 2:

Je suis en mesure d'accéder aux articles du devis à partir du panier, il existe de nombreuses façons de le faire, semble-t-il. Je peux également parcourir les articles individuels du panier, mettre à jour les propriétés de ces articles, puis enregistrer les articles (au moins temporairement). Cependant, je ne suis pas en mesure de sauvegarder le devis et de recalculer les taxes lors du paiement.

Une partie de la raison est que même si je peux accéder au devis du panier, je ne sais pas quelle méthode utiliser pour pouvoir y écrire.

Ce que j'ai essayé:

Ce que j'ai essayé en termes d'accès au contenu du panier dépend de l'événement que j'ai observé, mais j'ai essayé tout ce qui suit;

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(); 

Celui qui semble au moins me permettre d'accéder au devis, de le parcourir et de mettre à jour les articles est

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

Cela me permet de mettre à jour chaque élément de devis lorsque je répète (je crois que l'utilisation de setters magiques car je ne trouve aucune méthode correspondante). J'espérais pouvoir mettre à jour l'ID de classe de taxe pour l'élément de devis, puis recalculer les taxes. Si j'utilise ce qui suit (où $ taxClassId est différent de celui déjà utilisé par chaque élément de devis);

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

Et puis enregistrez les résultats;

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

Cela montre que j'ai effectivement mis à jour cet élément de devis et changé le numéro d'identification fiscale. Cependant, si je continue et essaie d'enregistrer le devis modifié;

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

Et puis déboguez à nouveau;

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

Mes modifications n'ont pas été enregistrées, la modification de l'élément de devis a été réinitialisée et les totaux du panier ne sont pas recalculés. Commencer à se demander si trouver un grand bâtiment pour sauter pourrait être la solution pour celui-ci.


s'il vous plaît, quelqu'un m'aide à résoudre ce problème: stackoverflow.com/questions/27978781/… merci.
satish

Réponses:


35

Ma suggestion serait de mettre un observateur sur l' sales_quote_collect_totals_beforeévénement qui est déclenché dans la Mage_Sales_Model_Quote::collectTotalsméthode avant qu'il ne commence le processus de collecte totale. Ensuite, à l'intérieur de cette méthode d'observation, itérez les éléments de devis et modifiez la classe de taxe sur l'objet produit (déjà chargé) que vous pouvez récupérer à partir de l'élément de devis.

Après avoir défini les informations sur l'objet produit, quoi que vous fassiez, N'ESSAYEZ PAS de l'enregistrer dans la base de données. Le fait d'avoir la classe de taxe définie en tant que de besoin sur l'objet produit en mémoire sera suffisant pour que la logique de collecte des totaux soit trouvée dans la Mage_Tax_Model_Sales_Total_Quote_Taxcollecte sur la classe de taxe sur laquelle elle doit baser ses calculs. L'enregistrement du produit (comme vous semblez essayer de le faire dans votre exemple de code ci-dessus) entraînera des problèmes de performances majeurs, créera des conditions de concurrence critique dans le processus de calcul et n'est tout simplement pas une bonne pratique.

La raison pour laquelle les événements avec lesquels vous essayez de travailler ne vous permettent pas d'accomplir ce que vous essayez d'accomplir, car ils surviennent tous après le calcul total, un processus qui n'est exécuté qu'une seule fois avant d'enregistrer le devis.

Il convient de souligner le processus de collecte des totaux: une fois exécuté, sans faire de travail supplémentaire, vous ne pouvez pas le rappeler pour le recalculer en fonction des modifications que vous avez apportées aux éléments de devis. Voir ce lien que j'ai extrait de la série de blogs qu'un de mes collègues a récemment mis en place sur le processus de collecte des totaux:

Maintenant que vous comprenez ce qui se passe pendant le processus de collecte des totaux, vous pouvez trouver pratique ou nécessaire de l'appeler directement vous-même. Avant de commencer à vous sentir trop confiant avec l'utilisation de collectTotals à vos propres fins, gardez à l'esprit la règle suivante:

Les produits ne peuvent pas être ajoutés au devis après l'exécution de collectTotals!

. . . sauf si les caches d'articles des adresses de devis sont effacées.

Presque chaque méthode de "collecte" d'un modèle total repose sur la récupération des éléments de devis à partir de l'adresse et leur boucle. La première fois que getAllItems est exécuté sur une adresse de devis, la collection d'éléments est réellement mise en cache avec une clé unique, et c'est cette collection mise en cache qui est renvoyée lors des appels suivants.

S'il vous arrive d'avoir une idée de vraiment plonger dans les profondeurs du fonctionnement du processus de collecte des totaux, vous pouvez consulter la première des quatre séries de parties sur la collecte totale ici pour une lecture plus approfondie: Démêler les collectTotals de Magento: Introduction

Pour résumer, vous devez intercepter un événement qui s'exécute avant le processus de collecte des totaux (et avant que getAllItems soit appelé sur les adresses de devis) afin que les modifications que vous apportez aux articles soient utilisées par l'ensemble des collecteurs. Je n'ai pas vérifié que l' sales_quote_collect_totals_beforeévénement suggéré s'exécute avant tout appel à l' getAllItemsadresse de devis, mais je suis presque certain qu'il fonctionnera pour ce dont vous avez besoin. Si ce n'est pas le cas, j'espère avoir fourni suffisamment de contexte pour que vous puissiez déterminer quel événement vous devez attraper pour le faire fonctionner.


Cette réponse est bien au-delà de ce que j'espérais. Brillant, merci d'avoir pris le temps. Réponse fantastique.
McNab

Argh! Après avoir passé toute la journée là-dessus et ne pas l'avoir fait fonctionner, je me suis finalement rendu compte qu'il s'agissait d'un modèle mal réécrit ailleurs dans le module. Cela fonctionne parfaitement et j'en ai appris beaucoup, merci encore David.
McNab

@McNab Heureux d'apprendre que vous l'avez fait fonctionner. Certainement l'un des domaines les plus complexes de Magento traités. :)
davidalger

+1 sur le blog lié. Je l'ai ignoré les premières fois que j'ai lu. C'est d'une grande aide.
pspahn

6

Il y a un autre événement: sales_quote_item_set_productdans Mage_Sales_Model_Quote_Item :: setProduct

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

Vous pouvez modifier la classe de taxe sur les produits à l'aide de cet événement. Utilisez la méthode quoteItem getQty pour trouver la quantité ajoutée au panier.


Merci pour cela, il est utile de savoir - je me rends compte, d'après la réponse de @ davidalger, que je ne devrais pas modifier la classe de taxe du produit maintenant, mais le modèle de devis. Bon à savoir cependant.
McNab

Eh bien, vous pouvez réellement modifier la classe de taxe de l'article de devis, vous avez également accès en cas à l'objet quoteItem.
PandaWebStudio

Ah ok - j'ai mal compris. Merci d'avoir clarifié :)
McNab

@PandaWebStudio, comment définir le montant de taxe personnalisé pour l'élément de devis? voici ma question, magento.stackexchange.com/questions/274520/…
jafar pinjar

2

Peut-être que tenter de changer la classe fiscale n'est peut-être pas la meilleure approche. Pourquoi ne pas créer un produit similaire pour les produits qui utilisent la classe d'imposition la plus élevée et définir une quantité minimale dans le panier pour ce produit. Ensuite, utilisez le checkout_cart_update_items_afterpour échanger des produits en fonction de la quantité totale dans le panier?

Ce n'est certainement pas une «meilleure pratique», mais cela pourrait vous aider à penser dans une autre direction.

Vous pouvez également essayer de configurer quelque chose avec http://www.mageworx.com/multi-fees-magento-extension.html où vous ajoutez des «frais» pour la taxe supplémentaire. Cela pourrait en fait être une meilleure façon de le faire, mais cela n'apparaîtra pas dans les totaux de taxe, plus comme une ligne de total de commande supplémentaire.


Merci pour la réponse Sander. C'est une bonne idée et je n'y avais pas pensé. Je vais vous dire pourquoi je n'ai pas envie de cela - les produits sous-jacents fonctionnent tous via la suite uMarketplace d'Unirgy et cela a été un énorme travail de le faire arriver là où il se trouve car il fonctionne comme site de comparaison de prix. Je pense que cela peut être un gros travail. Si je ne parviens pas à mettre à jour cet élément de devis, je devrai le regarder.
McNab

Merde, je ne peux même pas voter pour votre réponse car j'ai dépensé tout mon représentant sur cette prime! +1 :) Votera quand j'en aurai d'autres.
McNab

haha pas de problème, je comprends le problème et ce ne serait pas une bonne solution pour l'affaire. J'irais probablement avec les multifees alors, c'est une solution «plus propre»
Sander Mangel

FWIW, je ne recommanderais pas cette voie… ne serait-ce que parce que cela rendrait la comptabilité des ventes et le suivi des stocks un cauchemar potentiel. Comme l'OP l'a dit, ce n'est pas la meilleure pratique, mais j'ajouterai: cela causera plus de problèmes qu'il n'en résoudrait.
davidalger

0

Utilisez ceci <sales_quote_collect_totals_before>

et dans votre fonction aimez

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

J'espère que cela fonctionnera définitivement

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.