"Interrompi ulteriore elaborazione delle regole" non applicabile a tutti gli articoli


8

Sembra che ci sia un bug con "Interrompi ulteriore elaborazione delle regole" in Magento CE1.9 / EE1.13 in cui solo il primo articolo nel carrello riceve lo sconto.

Mi aspetterei: se ho più regole del carrello della spesa, ognuna delle quali con "Interrompi ulteriore elaborazione regole: Sì", verrebbe applicata solo la prima di queste regole, tuttavia verrebbe applicata per intero a tutti gli articoli corrispondenti per quella regola.

Cosa sta succedendo: lo sconto viene applicato solo al primo articolo nel carrello, dopodiché viene interrotta l'elaborazione della regola.

Vedi schermate: lo sconto che mi aspetto per l'intero carrello è di $ 50, ma a causa di "Interrompi ulteriore elaborazione delle regole" vedo solo $ 25.

Pannello di amministrazione di Magento

Magento Frontend Checkout

Risposte:


7

Penso che ciò potrebbe essere dovuto al fatto che _calculator è effettivamente archiviato come singleton all'interno della classe Mage_SalesRule_Model_Quote_Discount, il che significa che il secondo elemento da elaborare colpirà $ this -> _ stopFurtherRules == true and bail.

Il mio processo di pensiero è quello di memorizzare l'ID della regola $ che è OK per essere elaborato, consentendo ad altri elementi di elaborare solo questa regola.

Secondo CE1.9.0.1 e EE1.14.0.1

Mage_SalesRule_Model_Validator line 316

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

Mage_SalesRule_Model_Validator line 514

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

Questa è la mia soluzione proposta, sarei interessato a sentire i motivi per cui questa è una terribile idea!


2

Ciò che ha funzionato per me è stato reimpostare il flag di stop ulteriori regole dopo che ogni articolo è stato elaborato per consentire all'elemento successivo di verificare le regole rispetto ad esso.

aggiungi questa riga:

$this->_stopFurtherRules = false;

direttamente dopo questo ciclo nel process()metodo:

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

Era on line 518, per me.

A mio avviso, Magento ce l'ha davanti. Ripete le voci, quindi le regole per ciascuna voce. Dovrebbe iterare le regole, quindi gli articoli, quindi una regola può applicarsi all'intero carrello e solo allora prevenire ulteriori sconti.


Il problema con questo approccio è (e questo è un presupposto solo dal dare un'occhiata al codice, se non testato) si perde la funzionalità "stop ulteriori regole", vale a dire. tutte le tue regole verranno elaborate, anche se hai una regola che dovrebbe essere applicata da sola. Aggiornamento: sono d'accordo con il commento back-to-front, credo che dovrebbe elaborare regole quindi elementi.
Joseph McDermott,

@JosephMcDermott Non è corretto. Ferma ancora l'ulteriore elaborazione delle regole per quell'elemento. Per molti sconti, si fermerà alla stessa regola per ogni articolo. Per tutti gli articoli a cui non si applica una regola precedentemente abbinata, possono essere scontati solo nella misura consentita da altre regole applicabili. E Magento non consente solo un singolo codice coupon da utilizzare contemporaneamente?
Walf,

Penso che tu abbia frainteso l'intenzione del flag di stop ulteriore regole, è per livello di regola e non a livello di oggetto. Se hai due regole promozionali, nessuna delle quali richiede un codice promozionale, la prima per il 30% se spendi £ 300, la seconda per il 20% se spendi £ 200, contrassegnerai la prima regola come priorità più bassa con stop ulteriori regole elaborazione: sì, in modo che il cliente ottenga solo uno sconto del 30%, invece del 30% seguito da% 20. Oppure potresti avere una vendita globale del 10% di sconto su tutto (nessuna promozione) ma se il cliente inserisce un codice promozionale non vuoi che lo ottenga, quindi usa stop ulteriori regole.
Joseph McDermott,

@JosephMcDermott No, non l'ho fatto. Sono ben consapevole dello scopo di quella bandiera, ma Magento chiaramente non lo usa come ci si aspetta. La mia soluzione consente ad ogni oggetto di seguire le regole, almeno fino a quando non colpiscono quella bandiera. Sono sicuro che esiste un modo migliore per impedire un'ulteriore elaborazione delle regole e scontare l'intero carrello, ma garantisco che è molto più complesso del ripristino di una variabile.
Walf,

OK, almeno concordiamo sul fatto che Magento lo abbia fatto male :) Il pubblico ora ha due soluzioni tra cui scegliere, ognuna delle quali dovrebbe almeno indirizzare gli sviluppatori nella giusta direzione.
Joseph McDermott,

2

Questo problema è stato risolto in una versione successiva di Magento CE. In 1.9.2.1 è possibile trovare la soluzione, ma potrebbe essere stata riparata prima.

Il codice originale è simile al seguente:

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

E il codice fisso dovrebbe essere:

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

La differenza è il $this->_stopFurtherRules = false;eif ($this->_stopFurtherRules) {...}

Nient'altro.

Oppure, se sei su 1.9 puoi semplicemente sostituire l'intero file senza pericolo.

Spero che questo aiuti qualcuno.


1

Per tutto ciò che deve risolvere quel problema, dovrebbe sovrascrivere il metodo di processo per la classe Mage_SalesRule_Model_Validator per essere come sotto

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.