すべてのアイテムに適用されない「以降のルール処理を停止」


8

Magento CE1.9 / EE1.13の「それ以上のルール処理の停止」には、カートの最初のアイテムのみが割引を受けるというバグがあるようです。

複数のショッピングカートルールがあり、それぞれに「以降のルール処理の停止:はい」がある場合、最初のルールのみが適用さますが、そのルールに一致するすべてのアイテムに完全に適用さます。

状況:割引はカートの最初のアイテムにのみ適用され、その後、ルール処理は停止します。

スクリーンショットを見る:カート全体に対して私が期待している割引は$ 50ですが、「それ以上のルール処理の停止」のため、$ 25しか表示されていません。

Magento管理パネル

Magentoフロントエンドチェックアウト

回答:


7

これは、_calculatorがMage_SalesRule_Model_Quote_Discountクラス内のシングルトンとして効果的に格納されているため、処理される2番目のアイテムが$ this-> _ stopFurtherRules == trueにヒットして保釈されるためと考えられます。

私の思考プロセスは、処理が許可されている$ ruleのIDを格納し、以降のアイテムがこのルールのみを処理できるようにすることです。

CE1.9.0.1およびEE1.14.0.1による

Mage_SalesRule_Model_Validatorライン316

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

Mage_SalesRule_Model_Validator行514

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

これは私の提案する解決策です。これがひどい考えである理由を知りたいです。


2

私にとってうまくいったのは、各アイテムが処理された後、次のアイテムがルールをチェックできるようにするために、stop more rulesフラグをリセットすることでした。

この行を追加します。

$this->_stopFurtherRules = false;

process()メソッドのこのループの直後:

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

それは518私にとってはオンラインでした。

私の意見では、Magentoはそれを裏返しにしています。それはアイテムを反復し、次に各アイテムのルールを反復します。それはルールを繰り返し、次にアイテムを繰り返す必要があります。そのため、ルールはカート全体に適用され、それ以上の割引を防ぐことができます。


そのアプローチの問題は(そして、これはコードをちらっと見ただけで、テストされていない場合の前提です)、「それ以上のルールを停止する」機能を失うことです。単独で適用する必要があるルールがある場合でも、すべてのルールが処理されます。更新:バックツーフロントのコメントに同意します。ルールを処理してからアイテムを処理する必要があると思います。
ジョセフマクダーモット

@JosephMcDermott不正解です。それでも、そのアイテムのルールの処理は停止します。多くの割引では、アイテムごとに同じルールで停止します。以前に一致したルールが適用されないアイテムについては、他の適用可能なルールが許可する範囲内でのみ割引を適用できます。また、Magentoは一度に1つのクーポンコードしか使用できないのではないですか?
Walf、

ルール停止フラグの意図を誤解していると思います。これはアイテムレベルではなく、ルールレベル用です。2つのプロモーションルールがあり、どちらもプロモーションコードを必要としない場合、1つ目は300%を使うと30%、2つ目は£200を使うと20%となり、最初のルールを優先度を低くして、さらにルールを停止します。処理:はい。これにより、顧客は30%割引の代わりに30%割引を受け、代わりに%20を受け取ります。または、全額10%割引(プロモーションなし)のグローバルセールがあるかもしれませんが、顧客がプロモーションコードを入力した場合、顧客にこれを取得させたくないので、それ以上のルールを使用しないでください。
ジョセフマクダーモット

@JosephMcDermottいいえ、私はしていません。私はそのフラグの目的をよく知っていますが、Magentoは明らかにそれを使用していません。私のソリューションでは、少なくともそれらのフラグに到達するまで、各アイテムがルールを通過することができます。これ以上のルール処理を完全に防ぎ、カート全体割り引くより良い方法があると確信していますが、1つの変数をリセットするよりもはるかに複雑であることを保証します。
Walf、2015

少なくとも、Magentoがこれをうまく行っていないことに同意します:)現在、一般の人々は2つのソリューションから選択できます。どちらのソリューションも、少なくとも開発者を正しい方向に向ける必要があります。
ジョセフマクダーモット

2

これは、Magento CEの新しいバージョンで修正されました。1.9.2.1では解決策を見つけることができますが、もっと早く修正された可能性があります。

元のコードは次のようになります。

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

そして修正されたコードは次のようになります:

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

differennceがある$this->_stopFurtherRules = false;if ($this->_stopFurtherRules) {...}

他には何もありません。

または、1.9を使用している場合は、危険を冒すことなくファイル全体を単純に置き換えることができます。

これが誰かを助けることを願っています。


1

その問題を修正する必要があるすべての場合、Mage_SalesRule_Model_Validatorクラスのプロセスメソッドを以下のようにオーバーライドする必要があります

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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.