"Weitere Regelverarbeitung stoppen" gilt nicht für alle Elemente


8

In Magento CE1.9 / EE1.13 scheint ein Fehler mit "Weitere Regelverarbeitung stoppen" aufgetreten zu sein, bei dem nur der erste Artikel in Ihrem Warenkorb den Rabatt erhält.

Ich würde erwarten: Wenn ich mehrere Warenkorbregeln habe, von denen jede "Weitere Regelverarbeitung stoppen: Ja" enthält, wird nur die erste dieser Regeln angewendet, jedoch vollständig auf alle übereinstimmenden Artikel für diese Regel.

Was passiert: Der Rabatt wird nur auf den ersten Artikel im Warenkorb angewendet, wonach die Regelverarbeitung gestoppt wird.

Siehe Screenshots: Der Rabatt, den ich für den gesamten Warenkorb erwarte, beträgt 50 US-Dollar, aber aufgrund von "Weitere Regelverarbeitung stoppen" werden nur 25 US-Dollar angezeigt.

Magento Admin Panel

Magento Frontend Checkout

Antworten:


7

Ich denke, dies könnte daran liegen, dass der _Calculator effektiv als Singleton in der Mage_SalesRule_Model_Quote_Discount-Klasse gespeichert ist, was bedeutet, dass das zweite zu verarbeitende Element $ this -> _ stopFurtherRules == true und Kaution trifft.

Mein Denkprozess besteht darin, die ID der $ -Regel zu speichern, die verarbeitet werden kann, sodass weitere Elemente nur diese Regel verarbeiten können.

Gemäß CE1.9.0.1 und EE1.14.0.1

Mage_SalesRule_Model_Validator Zeile 316

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

Mage_SalesRule_Model_Validator Zeile 514

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

Dies ist meine vorgeschlagene Lösung. Ich würde mich freuen, Gründe zu hören, warum dies eine schreckliche Idee ist!


2

Was für mich funktioniert hat, war das Zurücksetzen des Flags zum Stoppen weiterer Regeln, nachdem jedes Element verarbeitet wurde, damit das nächste Element die Regeln damit vergleichen kann.

füge diese Zeile hinzu:

$this->_stopFurtherRules = false;

direkt nach dieser Schleife in der process()Methode:

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

Das war 518für mich online .

Meiner Meinung nach hat Magento es von hinten nach vorne. Es iteriert die Elemente und dann die Regeln für jedes Element. Es sollten die Regeln und dann die Artikel wiederholt werden, damit eine Regel für den gesamten Warenkorb gelten kann und erst dann weitere Rabatte verhindert werden können.


Das Problem bei diesem Ansatz ist (und dies ist eine Annahme, wenn Sie nur einen Blick auf den Code werfen, der nicht getestet wurde), dass Sie die Funktionalität "Weitere Regeln stoppen" verlieren, d. H. Alle Ihre Regeln werden verarbeitet, auch wenn Sie eine Regel haben, die nur für sich allein angewendet werden sollte. Update: Ich stimme dem Back-to-Front-Kommentar zu. Ich glaube, er sollte Regeln und dann Elemente verarbeiten.
Joseph McDermott

@ JosephMcDermott Das stimmt nicht. Die weitere Verarbeitung der Regeln für dieses Element wird weiterhin gestoppt. Bei vielen Rabatten wird für jeden Artikel dieselbe Regel angewendet. Für Elemente, für die eine zuvor übereinstimmende Regel nicht gilt, können sie nur so weit abgezinst werden, wie es andere geltende Regeln zulassen. Und lässt Magento nicht zu, dass nur ein einziger Gutscheincode gleichzeitig verwendet wird?
Walf

Ich denke, Sie haben die Absicht des Flags "Weitere Regeln stoppen" falsch verstanden. Dies gilt für die Regelebene und nicht für die Elementebene. Wenn Sie zwei Promo-Regeln haben, für die keine einen Promo-Code erfordert, die erste für 30%, wenn Sie 300 £ ausgeben, die zweite für 20%, wenn Sie 200 £ ausgeben, würden Sie die erste Regel mit niedrigerer Priorität als niedrigere Priorität markieren, um weitere Regeln zu stoppen Verarbeitung: Ja, damit der Kunde nur 30% Rabatt erhält, anstatt 30%, gefolgt von% 20. Oder Sie haben einen weltweiten Verkauf von 10% Rabatt auf alles (keine Promo), aber wenn der Kunde einen Promo-Code eingibt, möchten Sie nicht, dass der Kunde diesen erhält. Verwenden Sie daher die Regeln für weitere Stopps.
Joseph McDermott

@ JosephMcDermott Nein, habe ich nicht. Ich bin mir des Zwecks dieser Flagge sehr wohl bewusst, aber Magento verwendet sie eindeutig nicht wie erwartet. Meine Lösung ermöglicht es jedem Element, die Regeln zu durchlaufen, zumindest bis sie diese Flagge treffen. Ich bin mir sicher, dass es eine bessere Möglichkeit gibt, die weitere Regelverarbeitung vollständig zu verhindern und den gesamten Warenkorb zu reduzieren, aber ich garantiere, dass dies weitaus komplexer ist als das Zurücksetzen einer Variablen.
Walf

OK, zumindest sind wir uns einig, dass Magento dies schlecht gemacht hat :) Die Öffentlichkeit hat jetzt zwei Lösungen zur Auswahl, von denen jede zumindest die Entwickler in die richtige Richtung weisen sollte.
Joseph McDermott

2

Dies wurde in einer späteren Version von Magento CE behoben. In 1.9.2.1 finden Sie die Lösung, die jedoch möglicherweise früher behoben wurde.

Der ursprüngliche Code sieht folgendermaßen aus:

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

Und der feste Code sollte sein:

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

Der Unterschied ist das $this->_stopFurtherRules = false;undif ($this->_stopFurtherRules) {...}

Nichts anderes.

Wenn Sie mit 1.9 arbeiten, können Sie einfach die gesamte Datei ohne Gefahr ersetzen.

Hoffe das hilft jemandem.


1

Für alles, was dieses Problem beheben muss, sollte die Prozessmethode für die Mage_SalesRule_Model_Validator-Klasse wie folgt überschrieben werden

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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.