Skip to content

Commit ebdb55c

Browse files
committed
Issue #2980195 by mglaman, bojanz: Fixed order offers should reduce each order item
1 parent 9f40b30 commit ebdb55c

3 files changed

Lines changed: 87 additions & 16 deletions

File tree

modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderFixedAmountOff.php

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionOffer;
44

55
use Drupal\commerce_order\Adjustment;
6+
use Drupal\commerce_order\PriceSplitterInterface;
7+
use Drupal\commerce_price\RounderInterface;
68
use Drupal\commerce_promotion\Entity\PromotionInterface;
79
use Drupal\Core\Entity\EntityInterface;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
811

912
/**
1013
* Provides the fixed amount off offer for orders.
1114
*
15+
* The discount is split between order items, to simplify VAT taxes and refunds.
16+
*
1217
* @CommercePromotionOffer(
1318
* id = "order_fixed_amount_off",
1419
* label = @Translation("Fixed amount off the order subtotal"),
@@ -17,6 +22,46 @@
1722
*/
1823
class OrderFixedAmountOff extends FixedAmountOffBase {
1924

25+
/**
26+
* The price splitter.
27+
*
28+
* @var \Drupal\commerce_order\PriceSplitterInterface
29+
*/
30+
protected $splitter;
31+
32+
/**
33+
* Constructs a new OrderFixedAmountOff object.
34+
*
35+
* @param array $configuration
36+
* A configuration array containing information about the plugin instance.
37+
* @param string $plugin_id
38+
* The pluginId for the plugin instance.
39+
* @param mixed $plugin_definition
40+
* The plugin implementation definition.
41+
* @param \Drupal\commerce_price\RounderInterface $rounder
42+
* The rounder.
43+
* @param \Drupal\commerce_order\PriceSplitterInterface $splitter
44+
* The splitter.
45+
*/
46+
public function __construct(array $configuration, $plugin_id, $plugin_definition, RounderInterface $rounder, PriceSplitterInterface $splitter) {
47+
parent::__construct($configuration, $plugin_id, $plugin_definition, $rounder);
48+
49+
$this->splitter = $splitter;
50+
}
51+
52+
/**
53+
* {@inheritdoc}
54+
*/
55+
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
56+
return new static(
57+
$configuration,
58+
$plugin_id,
59+
$plugin_definition,
60+
$container->get('commerce_price.rounder'),
61+
$container->get('commerce_order.price_splitter')
62+
);
63+
}
64+
2065
/**
2166
* {@inheritdoc}
2267
*/
@@ -25,23 +70,29 @@ public function apply(EntityInterface $entity, PromotionInterface $promotion) {
2570
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
2671
$order = $entity;
2772
$subtotal_price = $order->getSubTotalPrice();
28-
$adjustment_amount = $this->getAmount();
29-
if ($subtotal_price->getCurrencyCode() != $adjustment_amount->getCurrencyCode()) {
73+
$amount = $this->getAmount();
74+
if ($subtotal_price->getCurrencyCode() != $amount->getCurrencyCode()) {
3075
return;
3176
}
3277
// The promotion amount can't be larger than the subtotal, to avoid
3378
// potentially having a negative order total.
34-
if ($adjustment_amount->greaterThan($subtotal_price)) {
35-
$adjustment_amount = $subtotal_price;
79+
if ($amount->greaterThan($subtotal_price)) {
80+
$amount = $subtotal_price;
3681
}
82+
// Split the amount between order items.
83+
$amounts = $this->splitter->split($order, $amount);
3784

38-
$order->addAdjustment(new Adjustment([
39-
'type' => 'promotion',
40-
// @todo Change to label from UI when added in #2770731.
41-
'label' => t('Discount'),
42-
'amount' => $adjustment_amount->multiply('-1'),
43-
'source_id' => $promotion->id(),
44-
]));
85+
foreach ($order->getItems() as $order_item) {
86+
if (isset($amounts[$order_item->id()])) {
87+
$order_item->addAdjustment(new Adjustment([
88+
'type' => 'promotion',
89+
// @todo Change to label from UI when added in #2770731.
90+
'label' => t('Discount'),
91+
'amount' => $amounts[$order_item->id()]->multiply('-1'),
92+
'source_id' => $promotion->id(),
93+
]));
94+
}
95+
}
4596
}
4697

4798
}

modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderPercentageOff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public function apply(EntityInterface $entity, PromotionInterface $promotion) {
7070
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
7171
$order = $entity;
7272
$percentage = $this->getPercentage();
73-
// Calculate the order-level discount and split it across order items.
73+
// Calculate the order-level discount and split it between order items.
7474
$amount = $order->getSubtotalPrice()->multiply($percentage);
7575
$amount = $this->rounder->round($amount);
7676
$amounts = $this->splitter->split($order, $amount, $percentage);

modules/promotion/tests/src/Kernel/PromotionOfferTest.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,19 +170,39 @@ public function testOrderFixedAmountOff() {
170170
$this->order->state = 'draft';
171171
$this->order->save();
172172
$this->order = $this->reloadEntity($this->order);
173+
$order_items = $this->order->getItems();
174+
$order_item = reset($order_items);
175+
$adjustments = $order_item->getAdjustments();
176+
$this->assertEquals(1, count($adjustments));
177+
/** @var \Drupal\commerce_order\Adjustment $adjustment */
178+
$adjustment = reset($adjustments);
173179

174180
// Offer amount larger than the order subtotal.
175-
$this->assertEquals(1, count($this->order->getAdjustments()));
176-
$this->assertEquals(new Price('-20.00', 'USD'), $this->order->getAdjustments()[0]->getAmount());
181+
$this->assertEquals(0, count($this->order->getAdjustments()));
182+
$this->assertEquals(1, count($order_item->getAdjustments()));
183+
$this->assertEquals(new Price('20.00', 'USD'), $order_item->getTotalPrice());
184+
$this->assertEquals(new Price('0.00', 'USD'), $order_item->getAdjustedTotalPrice());
185+
$this->assertEquals(new Price('-20.00', 'USD'), $adjustment->getAmount());
177186
$this->assertEquals(new Price('0.00', 'USD'), $this->order->getTotalPrice());
178187

179188
// Offer amount smaller than the order subtotal.
180189
$order_item->setQuantity(2);
181190
$order_item->save();
182191
$this->order->save();
183192
$this->order = $this->reloadEntity($this->order);
184-
$this->assertEquals(1, count($this->order->getAdjustments()));
185-
$this->assertEquals(new Price('-25.00', 'USD'), $this->order->getAdjustments()[0]->getAmount());
193+
$order_items = $this->order->getItems();
194+
$order_item = reset($order_items);
195+
$adjustments = $order_item->getAdjustments();
196+
$this->assertEquals(1, count($adjustments));
197+
/** @var \Drupal\commerce_order\Adjustment $adjustment */
198+
$adjustment = reset($adjustments);
199+
200+
// Offer amount larger than the order subtotal.
201+
$this->assertEquals(0, count($this->order->getAdjustments()));
202+
$this->assertEquals(1, count($order_item->getAdjustments()));
203+
$this->assertEquals(new Price('40.00', 'USD'), $order_item->getTotalPrice());
204+
$this->assertEquals(new Price('15.00', 'USD'), $order_item->getAdjustedTotalPrice());
205+
$this->assertEquals(new Price('-25.00', 'USD'), $adjustment->getAmount());
186206
$this->assertEquals(new Price('15.00', 'USD'), $this->order->getTotalPrice());
187207
}
188208

0 commit comments

Comments
 (0)