Skip to content

Commit

Permalink
Issue #2929103 by istavros, bojanz: Promotions have incorrect conditi…
Browse files Browse the repository at this point in the history
…on logic when the operator is "OR"
  • Loading branch information
istavros authored and bojanz committed Jun 15, 2018
1 parent 5b9e0e0 commit 55280a3
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 21 deletions.
27 changes: 21 additions & 6 deletions modules/promotion/src/Entity/Promotion.php
Original file line number Diff line number Diff line change
Expand Up @@ -464,20 +464,35 @@ public function applies(OrderInterface $order) {
/** @var \Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface $condition */
return $condition->getEntityTypeId() == 'commerce_order_item';
});
$order_conditions = new ConditionGroup($order_conditions, $this->getConditionOperator());
$order_item_conditions = new ConditionGroup($order_item_conditions, $this->getConditionOperator());
$condition_operator = $this->getConditionOperator();
$order_conditions = new ConditionGroup($order_conditions, $condition_operator);
$order_item_conditions = new ConditionGroup($order_item_conditions, $condition_operator);

if (!$order_conditions->evaluate($order)) {
$order_conditions_apply = $order_conditions->evaluate($order);
if ($condition_operator == 'AND' && !$order_conditions_apply) {
return FALSE;
}

$order_item_conditions_apply = FALSE;
foreach ($order->getItems() as $order_item) {
// Order item conditions must match at least one order item.
if ($order_item_conditions->evaluate($order_item)) {
return TRUE;
$order_item_conditions_apply = TRUE;
break;
}
}

return FALSE;
// Empty condition groups are TRUE by default, which leads to incorrect
// logic when there are multiple groups, such as here.
$order_conditions_apply = $order_conditions->getConditions() && $order_conditions_apply;
$order_item_conditions_apply = $order_item_conditions->getConditions() && $order_item_conditions_apply;

if ($condition_operator == 'AND') {
return $order_conditions_apply && $order_item_conditions_apply;
}
elseif ($condition_operator == 'OR') {
return $order_conditions_apply || $order_item_conditions_apply;
}
}

/**
Expand All @@ -493,7 +508,7 @@ public function apply(OrderInterface $order) {
/** @var \Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface $condition */
return $condition->getEntityTypeId() == 'commerce_order_item';
});
$order_item_conditions = new ConditionGroup($order_item_conditions, 'AND');
$order_item_conditions = new ConditionGroup($order_item_conditions, $this->getConditionOperator());
// Apply the offer to order items that pass the conditions.
foreach ($order->getItems() as $order_item) {
if ($order_item_conditions->evaluate($order_item)) {
Expand Down
67 changes: 52 additions & 15 deletions modules/promotion/tests/src/Kernel/PromotionConditionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Drupal\commerce_order\Entity\OrderItem;
use Drupal\commerce_order\Entity\OrderItemType;
use Drupal\commerce_order\Entity\Order;
use Drupal\commerce_price\Price;
use Drupal\commerce_promotion\Entity\Promotion;
use Drupal\Tests\commerce\Kernel\CommerceKernelTestBase;

Expand Down Expand Up @@ -141,12 +142,17 @@ public function testOrderCondition() {
$this->order->save();
$result = $promotion->applies($this->order);
$this->assertTrue($result);

// No order total can satisfy both conditions.
$promotion->setConditionOperator('AND');
$result = $promotion->applies($this->order);
$this->assertFalse($result);
}

/**
* Tests promotions with an order item condition.
* Tests promotions with both order and order item conditions.
*/
public function testOrderItemCondition() {
public function testMixedCondition() {
// Starts now, enabled. No end time.
$promotion = Promotion::create([
'name' => 'Promotion 1',
Expand All @@ -165,7 +171,7 @@ public function testOrderItemCondition() {
'target_plugin_configuration' => [
'operator' => '>',
'amount' => [
'number' => '10.00',
'number' => '30.00',
'currency_code' => 'USD',
],
],
Expand All @@ -174,7 +180,7 @@ public function testOrderItemCondition() {
'target_plugin_id' => 'order_item_quantity',
'target_plugin_configuration' => [
'operator' => '>',
'quantity' => 2,
'quantity' => 1,
],
],
],
Expand All @@ -184,31 +190,62 @@ public function testOrderItemCondition() {

$order_item = OrderItem::create([
'type' => 'test',
'quantity' => 2,
'quantity' => 4,
'unit_price' => [
'number' => '20.00',
'number' => '10.00',
'currency_code' => 'USD',
],
]);
$order_item->save();

// AND: Both conditions apply.
$this->order->addItem($order_item);
$this->order->save();
$result = $promotion->applies($this->order);
$this->assertTrue($result);

// OR: Both conditions apply.
$promotion->setConditionOperator('OR');
$result = $promotion->applies($this->order);
$this->assertTrue($result);

// AND: Neither condition applies.
$order_item->setQuantity(1);
$order_item->save();
$this->order->save();
$promotion->setConditionOperator('AND');
$result = $promotion->applies($this->order);
$this->assertFalse($result);

$order_item = OrderItem::create([
'type' => 'test',
'quantity' => 4,
'unit_price' => [
'number' => '20.00',
'currency_code' => 'USD',
],
]);
// OR: Neither condition applies.
$promotion->setConditionOperator('OR');
$result = $promotion->applies($this->order);
$this->assertFalse($result);

// AND: Order condition fails, order item condition passes.
$order_item->setQuantity(2);
$order_item->save();
$this->order->save();
$promotion->setConditionOperator('AND');
$result = $promotion->applies($this->order);
$this->assertFalse($result);

// OR: Order condition fails, order item condition passes.
$promotion->setConditionOperator('OR');
$result = $promotion->applies($this->order);
$this->assertTrue($result);

// AND: Order condition passes, order item condition fails.
$order_item->setUnitPrice(new Price('40', 'USD'));
$order_item->setQuantity(1);
$order_item->save();
$this->order->addItem($order_item);
$this->order->save();
$promotion->setConditionOperator('AND');
$result = $promotion->applies($this->order);
$this->assertFalse($result);

// OR: Order condition passes, order item condition fails.
$promotion->setConditionOperator('OR');
$result = $promotion->applies($this->order);
$this->assertTrue($result);
}
Expand Down

0 comments on commit 55280a3

Please sign in to comment.