Skip to content

Commit 20e44ea

Browse files
committed
Issue #2867019 by bojanz: Clean up the integration between coupons and promotions
1 parent 7887beb commit 20e44ea

16 files changed

+241
-366
lines changed

modules/promotion/commerce_promotion.services.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ services:
1212
tags:
1313
- { name: commerce_order.order_processor, priority: 50 }
1414

15-
commerce_promotion.coupon_order_processor:
16-
class: Drupal\commerce_promotion\CouponOrderProcessor
17-
arguments: ['@entity_type.manager']
18-
tags:
19-
- { name: commerce_order.order_processor, priority: 50 }
20-
2115
commerce_promotion.usage:
2216
class: Drupal\commerce_promotion\PromotionUsage
2317
arguments: ['@database']

modules/promotion/src/CouponOrderProcessor.php

Lines changed: 0 additions & 77 deletions
This file was deleted.

modules/promotion/src/Element/CouponRedemptionForm.php

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
use Drupal\commerce\Element\CommerceElementBase;
66
use Drupal\commerce_order\Entity\OrderInterface;
7-
use Drupal\commerce_promotion\Entity\CouponInterface;
8-
use Drupal\commerce_promotion\Entity\PromotionInterface;
97
use Drupal\Component\Utility\NestedArray;
108
use Drupal\Core\Form\FormStateInterface;
119

@@ -219,51 +217,16 @@ public static function validateForm(array &$element, FormStateInterface $form_st
219217
return;
220218
}
221219
}
222-
223-
$order_type_storage = $entity_type_manager->getStorage('commerce_order_type');
224-
/** @var \Drupal\commerce_promotion\PromotionStorageInterface $promotion_storage */
225-
$promotion_storage = $entity_type_manager->getStorage('commerce_promotion');
226-
/** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
227-
$order_type = $order_type_storage->load($order->bundle());
228-
$promotion = $promotion_storage->loadByCoupon($order_type, $order->getStore(), $coupon);
229-
if (empty($promotion)) {
220+
if (!$coupon->available($order)) {
230221
$form_state->setErrorByName($code_path, t('Coupon is invalid'));
231222
return;
232223
}
233-
if (!self::couponApplies($order, $promotion, $coupon)) {
224+
if (!$coupon->getPromotion()->applies($order)) {
234225
$form_state->setErrorByName($code_path, t('Coupon is invalid'));
235226
return;
236227
}
237228

238229
$form_state->setValueForElement($element, $coupon);
239230
}
240231

241-
/**
242-
* Checks whether a coupon applies.
243-
*
244-
* @param \Drupal\commerce_order\Entity\OrderInterface $order
245-
* The order.
246-
* @param \Drupal\commerce_promotion\Entity\PromotionInterface $promotion
247-
* The promotion.
248-
* @param \Drupal\commerce_promotion\Entity\CouponInterface $coupon
249-
* The coupon.
250-
*
251-
* @return bool
252-
* Returns TRUE if the coupon applies, FALSE otherwise.
253-
*/
254-
protected static function couponApplies(OrderInterface $order, PromotionInterface $promotion, CouponInterface $coupon) {
255-
if ($promotion->applies($order)) {
256-
return TRUE;
257-
}
258-
else {
259-
foreach ($order->getItems() as $orderItem) {
260-
if ($promotion->applies($orderItem)) {
261-
return TRUE;
262-
}
263-
}
264-
}
265-
266-
return FALSE;
267-
}
268-
269232
}

modules/promotion/src/Entity/Coupon.php

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Drupal\commerce_promotion\Entity;
44

5+
use Drupal\commerce_order\Entity\OrderInterface;
56
use Drupal\Core\Field\BaseFieldDefinition;
67
use Drupal\Core\Entity\ContentEntityBase;
78
use Drupal\Core\Entity\EntityTypeInterface;
@@ -71,18 +72,32 @@ public function setCode($code) {
7172
/**
7273
* {@inheritdoc}
7374
*/
74-
public function isActive() {
75+
public function isEnabled() {
7576
return (bool) $this->getEntityKey('status');
7677
}
7778

7879
/**
7980
* {@inheritdoc}
8081
*/
81-
public function setActive($active) {
82-
$this->set('status', (bool) $active);
82+
public function setEnabled($enabled) {
83+
$this->set('status', (bool) $enabled);
8384
return $this;
8485
}
8586

87+
/**
88+
* {@inheritdoc}
89+
*/
90+
public function available(OrderInterface $order) {
91+
if (!$this->isEnabled()) {
92+
return FALSE;
93+
}
94+
if (!$this->getPromotion()->available($order)) {
95+
return FALSE;
96+
}
97+
98+
return TRUE;
99+
}
100+
86101
/**
87102
* {@inheritdoc}
88103
*/
@@ -99,7 +114,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
99114

100115
$fields['code'] = BaseFieldDefinition::create('string')
101116
->setLabel(t('Coupon code'))
102-
->setDescription(t('The Coupon entity code.'))
117+
->setDescription(t('The unique, machine-readable identifier for a coupon.'))
103118
->addConstraint('CouponCode')
104119
->setSettings([
105120
'max_length' => 50,
@@ -119,14 +134,18 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
119134
->setDisplayConfigurable('view', TRUE);
120135

121136
$fields['status'] = BaseFieldDefinition::create('boolean')
122-
->setLabel(t('Active status'))
123-
->setDescription(t('A boolean indicating whether the Coupon is active.'))
137+
->setLabel(t('Status'))
138+
->setDescription(t('Whether the coupon is enabled.'))
124139
->setDefaultValue(TRUE)
125-
->setDisplayOptions('form', [
126-
'type' => 'boolean_checkbox',
127-
'weight' => 99,
140+
->setRequired(TRUE)
141+
->setSettings([
142+
'on_label' => t('Enabled'),
143+
'off_label' => t('Disabled'),
128144
])
129-
->setDisplayConfigurable('form', TRUE);
145+
->setDisplayOptions('form', [
146+
'type' => 'options_buttons',
147+
'weight' => 0,
148+
]);
130149

131150
return $fields;
132151
}

modules/promotion/src/Entity/CouponInterface.php

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Drupal\commerce_promotion\Entity;
44

5+
use Drupal\commerce_order\Entity\OrderInterface;
56
use Drupal\Core\Entity\ContentEntityInterface;
67

78
/**
@@ -39,28 +40,40 @@ public function getCode();
3940
* @param string $code
4041
* The coupon code.
4142
*
42-
* @return \Drupal\commerce_promotion\Entity\CouponInterface
43-
* The coupon.
43+
* @return $this
4444
*/
4545
public function setCode($code);
4646

4747
/**
48-
* Returns the coupon status indicator.
48+
* Gets whether the coupon is enabled.
4949
*
5050
* @return bool
51-
* TRUE if the coupon is active.
51+
* TRUE if the coupon is enabled, FALSE otherwise.
5252
*/
53-
public function isActive();
53+
public function isEnabled();
5454

5555
/**
56-
* Sets the status of a coupon.
56+
* Sets whether the coupon is enabled.
5757
*
58-
* @param bool $active
59-
* TRUE to make coupon active, FALSE to set it to inactive.
58+
* @param bool $enabled
59+
* Whether the coupon is enabled.
6060
*
61-
* @return \Drupal\commerce_promotion\Entity\CouponInterface
62-
* The coupon.
61+
* @return $this
6362
*/
64-
public function setActive($active);
63+
public function setEnabled($enabled);
64+
65+
/**
66+
* Checks whether the coupon is available for the given order.
67+
*
68+
* Ensures that the parent promotion is available, the coupon
69+
* is enabled, and the usage limits are respected.
70+
*
71+
* @param \Drupal\commerce_order\Entity\OrderInterface $order
72+
* The order.
73+
*
74+
* @return bool
75+
* TRUE if coupon is available, FALSE otherwise.
76+
*/
77+
public function available(OrderInterface $order);
6578

6679
}

modules/promotion/src/Entity/Promotion.php

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,8 @@ public function getEndDate() {
280280
/**
281281
* {@inheritdoc}
282282
*/
283-
public function setEndDate(DrupalDateTime $end_date) {
284-
$this->get('end_date')->value = $end_date->format('Y-m-d');
283+
public function setEndDate(DrupalDateTime $end_date = NULL) {
284+
$this->get('end_date')->value = $end_date ? $end_date->format('Y-m-d') : NULL;
285285
return $this;
286286
}
287287

@@ -333,6 +333,38 @@ public function setWeight($weight) {
333333
return $this;
334334
}
335335

336+
/**
337+
* {@inheritdoc}
338+
*/
339+
public function available(OrderInterface $order) {
340+
if (!$this->isEnabled()) {
341+
return FALSE;
342+
}
343+
if (!in_array($order->bundle(), $this->getOrderTypeIds())) {
344+
return FALSE;
345+
}
346+
if (!in_array($order->getStoreId(), $this->getStoreIds())) {
347+
return FALSE;
348+
}
349+
$time = \Drupal::service('commerce.time')->getRequestTime();
350+
if ($this->getStartDate()->format('U') > $time) {
351+
return FALSE;
352+
}
353+
$end_date = $this->getEndDate();
354+
if ($end_date && $end_date->format('U') <= $time) {
355+
return FALSE;
356+
}
357+
if ($usage_limit = $this->getUsageLimit()) {
358+
/** @var \Drupal\commerce_promotion\PromotionUsageInterface $usage */
359+
$usage = \Drupal::service('commerce_promotion.usage');
360+
if ($usage_limit <= $usage->getUsage($this)) {
361+
return FALSE;
362+
}
363+
}
364+
365+
return TRUE;
366+
}
367+
336368
/**
337369
* {@inheritdoc}
338370
*/
@@ -585,7 +617,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
585617
->setDefaultValue(TRUE)
586618
->setRequired(TRUE)
587619
->setSettings([
588-
'on_label' => t('Active'),
620+
'on_label' => t('Enabled'),
589621
'off_label' => t('Disabled'),
590622
])
591623
->setDisplayOptions('form', [

modules/promotion/src/Entity/PromotionInterface.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public function getEndDate();
207207
*
208208
* @return $this
209209
*/
210-
public function setEndDate(DrupalDateTime $end_date);
210+
public function setEndDate(DrupalDateTime $end_date = NULL);
211211

212212
/**
213213
* Gets the promotion compatibility.
@@ -264,7 +264,25 @@ public function getWeight();
264264
public function setWeight($weight);
265265

266266
/**
267-
* Checks whether the promotion entity can be applied.
267+
* Checks whether the promotion is available for the given order.
268+
*
269+
* Ensures that the order type and store match the promotion's,
270+
* that the promotion is enabled, the current date matches the
271+
* start and end dates, and the usage limits are respected.
272+
*
273+
* @param \Drupal\commerce_order\Entity\OrderInterface $order
274+
* The order.
275+
*
276+
* @return bool
277+
* TRUE if promotion is available, FALSE otherwise.
278+
*/
279+
public function available(OrderInterface $order);
280+
281+
/**
282+
* Checks whether the promotion can be applied to the given order.
283+
*
284+
* Ensures that the promotion is compatible with other
285+
* promotions on the order, and that the conditions pass.
268286
*
269287
* @param \Drupal\commerce_order\Entity\OrderInterface $order
270288
* The order.

0 commit comments

Comments
 (0)