“Hentikan Pemrosesan Aturan Lebih Lanjut” tidak berlaku untuk semua item

8

Tampaknya ada bug dengan "Hentikan Pemrosesan Aturan Lebih Lanjut" di Magento CE1.9 / EE1.13 di mana hanya item pertama di keranjang Anda yang menerima diskon.

Saya berharap: Jika saya memiliki beberapa aturan keranjang belanja, yang masing-masing memiliki "Hentikan Pemrosesan Aturan Lebih Lanjut: Ya", hanya yang pertama dari aturan ini yang akan diterapkan, namun itu akan diterapkan secara penuh ke semua item yang cocok untuk aturan itu.

Apa yang terjadi: Diskon hanya diterapkan ke item pertama dalam troli, setelah itu pemrosesan aturan dihentikan.

Lihat tangkapan layar: Diskon yang saya harapkan untuk seluruh keranjang adalah $ 50, tetapi karena "Hentikan Pemrosesan Aturan Lebih Lanjut" Saya hanya melihat $ 25.

Panel Admin Magento

Frontend Magento Checkout

Joseph McDermott
sumber

Jawaban:

7

Saya pikir ini bisa jadi karena _calculator secara efektif disimpan sebagai singleton dalam kelas Mage_SalesRule_Model_Quote_Discount, artinya item kedua yang akan diproses akan mencapai $ this -> _ stopF FurtherRules == true dan bail.

Proses pemikiran saya adalah menyimpan ID aturan $ yang OK untuk diproses, memungkinkan item selanjutnya hanya memproses aturan ini.

Sesuai CE1.9.0.1 dan EE1.14.0.1

Mage_SalesRule_Model_Validator baris 316

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

Mage_SalesRule_Model_Validator baris 514

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

Ini adalah solusi yang saya usulkan, saya akan tertarik mendengar alasan mengapa ini adalah ide yang buruk!

Joseph McDermott
sumber
2

Apa yang berhasil bagi saya adalah menyetel ulang hentikan tanda aturan selanjutnya setelah setiap item diproses untuk memungkinkan item berikutnya memeriksa aturan yang menentangnya.

tambahkan baris ini:

$this->_stopFurtherRules = false;

langsung setelah loop ini dalam process()metode:

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

Itu sesuai 518untuk saya.

Menurut pendapat saya, Magento memiliki back-to-front. Itu item iterates, maka aturan untuk setiap item. Seharusnya iterasi aturan, maka item, sehingga aturan dapat berlaku untuk seluruh keranjang dan hanya kemudian mencegah diskon lebih lanjut.

Walf
sumber
Masalah dengan pendekatan itu adalah (dan ini adalah asumsi hanya dari melihat kode, ive tidak diuji) Anda kehilangan fungsionalitas 'hentikan aturan lebih lanjut', yaitu. semua aturan Anda akan diproses, bahkan jika Anda memiliki aturan yang hanya boleh diterapkan sendiri. Pembaruan: Saya setuju dengan komentar back-to-front, saya percaya ini harus memproses aturan dan item.
Joseph McDermott
@JosephMcDermott Itu tidak benar. Itu masih berhenti memproses aturan lebih lanjut untuk item itu. Untuk banyak diskon, itu akan berhenti pada aturan yang sama untuk setiap item. Untuk setiap item yang tidak sesuai dengan aturan yang sebelumnya cocok, mereka hanya dapat didiskon sebanyak yang diizinkan oleh aturan lain yang berlaku. Dan bukankah Magento hanya mengizinkan satu kode kupon digunakan sekaligus?
Walf
Saya pikir Anda telah salah memahami maksud dari berhenti aturan bendera lebih lanjut, itu untuk tingkat aturan bukan tingkat item. Jika Anda memiliki dua aturan promo, yang keduanya tidak memerlukan kode promo, yang pertama untuk 30% jika Anda menghabiskan £ 300, yang kedua untuk 20% jika Anda menghabiskan £ 200, Anda akan menandai aturan pertama sebagai prioritas yang lebih rendah dengan menghentikan aturan lebih lanjut pemrosesan: ya sehingga pelanggan hanya akan mendapatkan diskon 30%, bukannya 30% diikuti oleh% 20. Atau Anda mungkin memiliki penjualan global diskon 10% semuanya (tidak ada promo) tetapi jika pelanggan memasukkan kode promo Anda tidak ingin pelanggan mendapatkan ini, jadi gunakan hentikan aturan lebih lanjut.
Joseph McDermott
@ JosephephMcDermott Tidak, saya belum. Saya menyadari tujuan dari bendera itu, tetapi Magento jelas tidak menggunakannya seperti yang diharapkan. Solusi saya memungkinkan setiap item untuk melewati aturan, setidaknya sampai mereka mencapai bendera itu. Saya yakin ada cara yang lebih baik untuk mencegah pemrosesan aturan lebih lanjut sepenuhnya dan diskon seluruh keranjang, tapi saya jamin itu jauh lebih kompleks daripada mengatur ulang satu variabel.
Walf
OK well, setidaknya kami setuju bahwa Magento telah melakukan hal ini dengan buruk :) Publik sekarang memiliki dua solusi untuk dipilih, yang salah satu dari mereka setidaknya harus mengarahkan pengembang ke arah yang benar.
Joseph McDermott
2

Ini diperbaiki dalam versi Magento CE yang lebih baru. Di 1.9.2.1 Anda dapat menemukan solusinya, tetapi mungkin sudah diperbaiki lebih cepat.

Kode aslinya terlihat seperti ini:

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

Dan kode tetapnya harus:

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

Perbedaannya adalah $this->_stopFurtherRules = false;danif ($this->_stopFurtherRules) {...}

Tidak ada lagi.

Atau, jika Anda berada di 1.9 Anda dapat dengan mudah mengganti seluruh file tanpa bahaya.

Semoga ini bisa membantu seseorang.

Wouter
sumber
1

Untuk semua yang perlu memperbaiki masalah itu, harus menimpa metode proses untuk kelas Mage_SalesRule_Model_Validator menjadi seperti di bawah ini

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
sumber