"Arrêter le traitement des règles" ne s'applique pas à tous les éléments

8

Il semble y avoir un bug avec "Arrêter le traitement des règles supplémentaires" dans Magento CE1.9 / EE1.13 où seul le premier article de votre panier reçoit la remise.

Je m'attendrais à ce que: si j'ai plusieurs règles de panier d'achat, chacune d'elles ayant "Arrêter le traitement des règles: Oui", seule la première de ces règles serait appliquée, mais elle s'appliquerait intégralement à tous les articles correspondants pour cette règle.

Ce qui se passe: la remise n'est appliquée qu'au premier article du panier, après quoi le traitement des règles est arrêté.

Voir les captures d'écran: La remise que j'attends pour le panier entier est de 50 $, mais en raison de "Arrêter le traitement des règles", je ne vois que 25 $.

Panneau d'administration Magento

Paiement Frontend Magento

Joseph McDermott
la source

Réponses:

7

Je pense que cela pourrait être dû au fait que le _calculateur est effectivement stocké en tant que singleton dans la classe Mage_SalesRule_Model_Quote_Discount, ce qui signifie que le deuxième élément à traiter frappera $ this -> _ stopFTHERRules == true et sous caution.

Mon processus de réflexion consiste à stocker l'ID de la règle $ qui est OK pour être traité, permettant à d'autres éléments de traiter uniquement cette règle.

Selon CE1.9.0.1 et EE1.14.0.1

Mage_SalesRule_Model_Validator ligne 316

- if ($this->_stopFurtherRules) {
+ if ($this->_stopFurtherRules !== false && $rule->getId() != $this->_stopFurtherRules) {

Mage_SalesRule_Model_Validator ligne 514

- $this->_stopFurtherRules = true;
+ $this->_stopFurtherRules = $rule->getId();

C'est ma solution proposée, je serais intéressé d'entendre les raisons pour lesquelles c'est une terrible idée!

Joseph McDermott
la source
2

Ce qui a fonctionné pour moi a été de réinitialiser l' indicateur d' arrêt des règles supplémentaires après que chaque élément a été traité pour permettre à l'élément suivant de vérifier les règles par rapport à lui.

ajoutez cette ligne:

$this->_stopFurtherRules = false;

directement après cette boucle dans la process()méthode:

foreach ($this->_getRules() as $rule) {
    ...
}

C'était en ligne 518, pour moi.

À mon avis, Magento a le dos à l'avant. Il itère les éléments, puis les règles de chaque élément. Il devrait être en train d'itérer les règles, puis les articles, de sorte qu'une règle puisse s'appliquer à l'ensemble du panier et empêcher ensuite de nouvelles remises.

Walf
la source
Le problème avec cette approche est (et c'est une supposition juste en regardant le code, ive non testé) que vous perdez la fonctionnalité «arrêter d'autres règles», c'est-à-dire. toutes vos règles seront traitées, même si vous avez une règle qui ne doit être appliquée que d'elle-même. Mise à jour: je suis d'accord avec le commentaire suivant, je pense qu'il devrait traiter les règles puis les éléments.
Joseph McDermott
@JosephMcDermott Ce n'est pas correct. Il arrête toujours le traitement ultérieur des règles pour cet élément. Pour de nombreuses remises, il s'arrêtera à la même règle pour chaque article. Pour tous les articles auxquels une règle précédemment mise en correspondance ne s'applique pas, ils ne peuvent être réduits que dans la mesure où d'autres règles applicables le permettent. Et Magento ne permet-il pas d'utiliser un seul code promo à la fois?
Walf
Je pense que vous avez mal compris l'intention du drapeau d'arrêt de règles supplémentaires, c'est pour le niveau de la règle et non pour le niveau de l'objet. Si vous avez deux règles promotionnelles, dont aucune ne nécessite un code promotionnel, la première à 30% si vous dépensez 300 £, la seconde à 20% si vous dépensez 200 £, vous marquerez la première règle comme une priorité inférieure avec des règles supplémentaires. traitement: oui pour que le client n'obtienne que 30% de remise, au lieu de 30% suivi de% 20. Ou vous pourriez avoir une vente globale de 10% sur tout (pas de promo) mais si le client entre un code promotionnel, vous ne voulez pas que le client l'obtienne, alors utilisez les règles d'arrêt supplémentaires.
Joseph McDermott
@JosephMcDermott Non, je ne l'ai pas fait. Je connais bien le but de ce drapeau, mais Magento ne l'utilise clairement pas comme on l'attend. Ma solution permet à chaque élément de respecter les règles, au moins jusqu'à ce qu'il atteigne ce drapeau. Je suis sûr qu'il existe un meilleur moyen d'empêcher complètement le traitement des règles et de réduire le panier entier, mais je vous garantis que c'est beaucoup plus complexe que de réinitialiser une variable.
Walf
D'accord, au moins, nous sommes d'accord pour dire que Magento a mal agi :) Le public a maintenant le choix entre deux solutions, qui devraient au moins orienter les développeurs dans la bonne direction.
Joseph McDermott
2

Ce problème a été résolu dans une version ultérieure de Magento CE. Dans 1.9.2.1, vous pouvez trouver la solution, mais elle a peut-être été corrigée plus tôt.

Le code d'origine ressemble à ceci:

$appliedRuleIds = array();
foreach ($this->_getRules() as $rule) {
    if ($this->_stopFurtherRules) {
        break;
    }

Et le code fixe devrait être:

$appliedRuleIds = array();
$this->_stopFurtherRules = false;
foreach ($this->_getRules() as $rule) {
    // The if-clause is removed
    ...    

La différence est le $this->_stopFurtherRules = false;etif ($this->_stopFurtherRules) {...}

Rien d'autre.

Ou, si vous êtes sur 1.9, vous pouvez simplement remplacer l'intégralité du fichier sans danger.

J'espère que cela aide quelqu'un.

Wouter
la source
1

Pour tout ce qui doit résoudre ce problème, doit remplacer la méthode de processus pour que la classe Mage_SalesRule_Model_Validator soit comme ci-dessous

public function process(Mage_Sales_Model_Quote_Item_Abstract $item)
{
    $item->setDiscountAmount(0);
    $item->setBaseDiscountAmount(0);
    $item->setDiscountPercent(0);
    $quote      = $item->getQuote();
    $address    = $this->_getAddress($item);

    $itemPrice              = $this->_getItemPrice($item);
    $baseItemPrice          = $this->_getItemBasePrice($item);
    $itemOriginalPrice      = $this->_getItemOriginalPrice($item);
    $baseItemOriginalPrice  = $this->_getItemBaseOriginalPrice($item);

    if ($itemPrice < 0) {
        return $this;
    }

    $appliedRuleIds = array();
    $this->_stopFurtherRules = false;
    foreach ($this->_getRules() as $rule) {

        /* @var $rule Mage_SalesRule_Model_Rule */
        if (!$this->_canProcessRule($rule, $address)) {
            continue;
        }

        if (!$rule->getActions()->validate($item)) {
            continue;
        }

        $qty = $this->_getItemQty($item, $rule);
        $rulePercent = min(100, $rule->getDiscountAmount());

        $discountAmount = 0;
        $baseDiscountAmount = 0;
        //discount for original price
        $originalDiscountAmount = 0;
        $baseOriginalDiscountAmount = 0;

        switch ($rule->getSimpleAction()) {
            case Mage_SalesRule_Model_Rule::TO_PERCENT_ACTION:
                $rulePercent = max(0, 100-$rule->getDiscountAmount());
            //no break;
            case Mage_SalesRule_Model_Rule::BY_PERCENT_ACTION:
                $step = $rule->getDiscountStep();
                if ($step) {
                    $qty = floor($qty/$step)*$step;
                }
                $_rulePct = $rulePercent/100;
                $discountAmount    = ($qty * $itemPrice - $item->getDiscountAmount()) * $_rulePct;
                $baseDiscountAmount = ($qty * $baseItemPrice - $item->getBaseDiscountAmount()) * $_rulePct;
                //get discount for original price
                $originalDiscountAmount    = ($qty * $itemOriginalPrice - $item->getDiscountAmount()) * $_rulePct;
                $baseOriginalDiscountAmount =
                    ($qty * $baseItemOriginalPrice - $item->getDiscountAmount()) * $_rulePct;

                if (!$rule->getDiscountQty() || $rule->getDiscountQty()>$qty) {
                    $discountPercent = min(100, $item->getDiscountPercent()+$rulePercent);
                    $item->setDiscountPercent($discountPercent);
                }
                break;
            case Mage_SalesRule_Model_Rule::TO_FIXED_ACTION:
                $quoteAmount = $quote->getStore()->convertPrice($rule->getDiscountAmount());
                $discountAmount    = $qty * ($itemPrice-$quoteAmount);
                $baseDiscountAmount = $qty * ($baseItemPrice-$rule->getDiscountAmount());
                //get discount for original price
                $originalDiscountAmount    = $qty * ($itemOriginalPrice-$quoteAmount);
                $baseOriginalDiscountAmount = $qty * ($baseItemOriginalPrice-$rule->getDiscountAmount());
                break;

            case Mage_SalesRule_Model_Rule::BY_FIXED_ACTION:
                $step = $rule->getDiscountStep();
                if ($step) {
                    $qty = floor($qty/$step)*$step;
                }
                $quoteAmount        = $quote->getStore()->convertPrice($rule->getDiscountAmount());
                $discountAmount     = $qty * $quoteAmount;
                $baseDiscountAmount = $qty * $rule->getDiscountAmount();
                break;

            case Mage_SalesRule_Model_Rule::CART_FIXED_ACTION:
                if (empty($this->_rulesItemTotals[$rule->getId()])) {
                    Mage::throwException(Mage::helper('salesrule')->__('Item totals are not set for rule.'));
                }

                /**
                 * prevent applying whole cart discount for every shipping order, but only for first order
                 */
                if ($quote->getIsMultiShipping()) {
                    $usedForAddressId = $this->getCartFixedRuleUsedForAddress($rule->getId());
                    if ($usedForAddressId && $usedForAddressId != $address->getId()) {
                        break;
                    } else {
                        $this->setCartFixedRuleUsedForAddress($rule->getId(), $address->getId());
                    }
                }
                $cartRules = $address->getCartFixedRules();
                if (!isset($cartRules[$rule->getId()])) {
                    $cartRules[$rule->getId()] = $rule->getDiscountAmount();
                }

                if ($cartRules[$rule->getId()] > 0) {
                    if ($this->_rulesItemTotals[$rule->getId()]['items_count'] <= 1) {
                        $quoteAmount = $quote->getStore()->convertPrice($cartRules[$rule->getId()]);
                        $baseDiscountAmount = min($baseItemPrice * $qty, $cartRules[$rule->getId()]);
                    } else {
                        $discountRate = $baseItemPrice * $qty /
                            $this->_rulesItemTotals[$rule->getId()]['base_items_price'];
                        $maximumItemDiscount = $rule->getDiscountAmount() * $discountRate;
                        $quoteAmount = $quote->getStore()->convertPrice($maximumItemDiscount);

                        $baseDiscountAmount = min($baseItemPrice * $qty, $maximumItemDiscount);
                        $this->_rulesItemTotals[$rule->getId()]['items_count']--;
                    }

                    $discountAmount = min($itemPrice * $qty, $quoteAmount);
                    $discountAmount = $quote->getStore()->roundPrice($discountAmount);
                    $baseDiscountAmount = $quote->getStore()->roundPrice($baseDiscountAmount);

                    //get discount for original price
                    $originalDiscountAmount = min($itemOriginalPrice * $qty, $quoteAmount);
                    $baseOriginalDiscountAmount = $quote->getStore()->roundPrice($baseItemOriginalPrice);

                    $cartRules[$rule->getId()] -= $baseDiscountAmount;
                }
                $address->setCartFixedRules($cartRules);

                break;

            case Mage_SalesRule_Model_Rule::BUY_X_GET_Y_ACTION:
                $x = $rule->getDiscountStep();
                $y = $rule->getDiscountAmount();
                if (!$x || $y > $x) {
                    break;
                }
                $buyAndDiscountQty = $x + $y;

                $fullRuleQtyPeriod = floor($qty / $buyAndDiscountQty);
                $freeQty  = $qty - $fullRuleQtyPeriod * $buyAndDiscountQty;

                $discountQty = $fullRuleQtyPeriod * $y;
                if ($freeQty > $x) {
                    $discountQty += $freeQty - $x;
                }

                $discountAmount    = $discountQty * $itemPrice;
                $baseDiscountAmount = $discountQty * $baseItemPrice;
                //get discount for original price
                $originalDiscountAmount    = $discountQty * $itemOriginalPrice;
                $baseOriginalDiscountAmount = $discountQty * $baseItemOriginalPrice;
                break;
        }

        $result = new Varien_Object(array(
            'discount_amount'      => $discountAmount,
            'base_discount_amount' => $baseDiscountAmount,
        ));
        Mage::dispatchEvent('salesrule_validator_process', array(
            'rule'    => $rule,
            'item'    => $item,
            'address' => $address,
            'quote'   => $quote,
            'qty'     => $qty,
            'result'  => $result,
        ));

        $discountAmount = $result->getDiscountAmount();
        $baseDiscountAmount = $result->getBaseDiscountAmount();

        $percentKey = $item->getDiscountPercent();
        /**
         * Process "delta" rounding
         */
        if ($percentKey) {
            $delta      = isset($this->_roundingDeltas[$percentKey]) ? $this->_roundingDeltas[$percentKey] : 0;
            $baseDelta  = isset($this->_baseRoundingDeltas[$percentKey])
                ? $this->_baseRoundingDeltas[$percentKey]
                : 0;
            $discountAmount += $delta;
            $baseDiscountAmount += $baseDelta;

            $this->_roundingDeltas[$percentKey]     = $discountAmount -
                $quote->getStore()->roundPrice($discountAmount);
            $this->_baseRoundingDeltas[$percentKey] = $baseDiscountAmount -
                $quote->getStore()->roundPrice($baseDiscountAmount);
            $discountAmount = $quote->getStore()->roundPrice($discountAmount);
            $baseDiscountAmount = $quote->getStore()->roundPrice($baseDiscountAmount);
        } else {
            $discountAmount     = $quote->getStore()->roundPrice($discountAmount);
            $baseDiscountAmount = $quote->getStore()->roundPrice($baseDiscountAmount);
        }

        /**
         * We can't use row total here because row total not include tax
         * Discount can be applied on price included tax
         */

        $itemDiscountAmount = $item->getDiscountAmount();
        $itemBaseDiscountAmount = $item->getBaseDiscountAmount();

        $discountAmount     = min($itemDiscountAmount + $discountAmount, $itemPrice * $qty);
        $baseDiscountAmount = min($itemBaseDiscountAmount + $baseDiscountAmount, $baseItemPrice * $qty);

        $item->setDiscountAmount($discountAmount);
        $item->setBaseDiscountAmount($baseDiscountAmount);

        $item->setOriginalDiscountAmount($originalDiscountAmount);
        $item->setBaseOriginalDiscountAmount($baseOriginalDiscountAmount);

        $appliedRuleIds[$rule->getRuleId()] = $rule->getRuleId();

        $this->_maintainAddressCouponCode($address, $rule);
        $this->_addDiscountDescription($address, $rule);

        if ($rule->getStopRulesProcessing()) {
            $this->_stopFurtherRules = true;
            break;
        }
    }

    $item->setAppliedRuleIds(join(',',$appliedRuleIds));
    $address->setAppliedRuleIds($this->mergeIds($address->getAppliedRuleIds(), $appliedRuleIds));
    $quote->setAppliedRuleIds($this->mergeIds($quote->getAppliedRuleIds(), $appliedRuleIds));

    return $this;
}
Ledian Hymetllari
la source