Sylius: Two promotions, where one invalidates the other, makes checkout impossible

Created on 20 Nov 2018  路  11Comments  路  Source: Sylius/Sylius

Sylius version affected: 1.2.10

Description
If you have two promotions that both apply for a cart, but when both apply, checkout becomes impossible if one changes the order value as such to invalidate the other.

Steps to reproduce

  1. Create a Christmas promotion (this is a demo one)
  2. no usage limit
  3. Priority 0
  4. no date restriction
  5. configuration

    • One rule

  6. - - Cart Quantity = 0

    • One action

  7. - - Order Percentage discount = 26
  8. Create a New Years promotion (this is also a demo one)
  9. usage limit = 10
  10. priority 2
  11. dates to cover the testing period
  12. configuration

    • One rule

  13. - - item total = $100

    • One action

  14. - - order fixed discount = 10
  15. Go to the front-end
  16. Add 1 $100 item to the basket (20% tax item)
  17. Proceed through the checkout to the complete page, and confirm both promotions have applied bringing the total down to $79.92 (free shipping)
  18. Click place order
  19. You should see an error of "Error. You are no longer eligible for this promotion New Year."

Possible Solution
Not a clue at the moment

Most helpful comment

I encountered the same bug on 1.3.* with default fixtures.

After taking a deeper look I found out where the problem hides:
in PromotionProcessor and OrderPromotionIntegrityChecker promotions are applied in different order. Therefore while applying promotion in PromotionProcessor the new_year promotion is applied first and conditions fit. At checkout the OrderPromotionIntegrityChecker applies christmas promotion first which decreased order total so that the new_year promotion is no longer eligible.

To reproduce the bug make sure that christmas promotion drops order total below 100$.

So it is a Bug and not a Potential Bug.

screen shot 2019-02-12 at 11 31 24

Possible solution

Add the following usort to OrderPromotionIntegrityChecker to be sure that promotion are ordered by priority (same as they are retrieved in PromotionProcessor).

````
// we create a new promotion collection and remove them from cart
// so we can verify with original conditions (without the price being applied before check)

$promotions = $order->getPromotions()->toArray();

usort(
$promotions,
function($promotion1, $promotion2)
{
if ($promotion1->getPriority() == $promotion2->getPriority()) {
return 0;
}

    return ($promotion1->getPriority() > $promotion2->getPriority()) ? -1 : 1;
}

);
````

All 11 comments

Thank you, Alex, for the report, we will take a deeper look at it in the nearest future (hopefully) and come up with some solution ideas :)

Do you think you could provide a Behat scenario describing this bug? BTW, I suppose Cart Quantity = 0 is not a valid configuration of the promotion rule (unless it's a typo :))

Hey @Zales0123 I'll see if I can get one together. I think the fixture that generates the test data picks a random integer, but seemingly 0 is valid if set manually.

Well, then it should not be for sure, minimum cart quantity should always be 1 :)

two bugs in one! :joy:
I've never written a behat scenario before, but here goes!

Feature: Ensure promotions work correctly
    In order to checkout successfully with promotions
    As a Visitor
    I want to checkout successfully with promotions

    Background:
        Given the store operates on a single channel in "United States"
        And default tax zone is "US"
        And the store has included in price "US VAT" tax rate of 23% for "Clothes" within the "US" zone
        And the store has a product "PHP T-Shirt" priced at "$100.00"
        And it belongs to "Clothes" tax category
        And there is a promotion "Christmas"
        And the promotion gives "12%" off
        And there is a promotion "New Year"
        And the promotion gives "$10.00" off if item total is greater than "$100.00"
        And the store ships everywhere for free
        And the store allows paying offline

    @ui
    Scenario: Basket with several promotions is able to checkout
        Given I have product "PHP T-Shirt" in the cart
        And I have completed addressing step with email "[email protected]" and "United States" based shipping address
        And I have proceeded order with "Free" shipping method and "Offline" payment
            When I confirm my order
            Then an email with the summary of order placed by "[email protected]" should be sent to him`

I encountered the same bug on 1.3.* with default fixtures.

After taking a deeper look I found out where the problem hides:
in PromotionProcessor and OrderPromotionIntegrityChecker promotions are applied in different order. Therefore while applying promotion in PromotionProcessor the new_year promotion is applied first and conditions fit. At checkout the OrderPromotionIntegrityChecker applies christmas promotion first which decreased order total so that the new_year promotion is no longer eligible.

To reproduce the bug make sure that christmas promotion drops order total below 100$.

So it is a Bug and not a Potential Bug.

screen shot 2019-02-12 at 11 31 24

Possible solution

Add the following usort to OrderPromotionIntegrityChecker to be sure that promotion are ordered by priority (same as they are retrieved in PromotionProcessor).

````
// we create a new promotion collection and remove them from cart
// so we can verify with original conditions (without the price being applied before check)

$promotions = $order->getPromotions()->toArray();

usort(
$promotions,
function($promotion1, $promotion2)
{
if ($promotion1->getPriority() == $promotion2->getPriority()) {
return 0;
}

    return ($promotion1->getPriority() > $promotion2->getPriority()) ? -1 : 1;
}

);
````

This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in a week if no further activity occurs. Thank you for your contributions.

Please do not stale

Any progress on this important bug? There are moments when it is impossible to checkout due to this.

Hi @tautelis

It looks like you created something that looked like a solution. Could you create a PR to demonstrate that it works and create tests for it?

Should be fixed by #10603.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

javiereguiluz picture javiereguiluz  路  3Comments

tchapi picture tchapi  路  3Comments

Chrysweel picture Chrysweel  路  3Comments

bnd170 picture bnd170  路  3Comments

hmonglee picture hmonglee  路  3Comments