Skip to content

Commit 1eafa87

Browse files
committed
Issue #2902880 by bojanz: Move coupons to a new tab
1 parent ade8018 commit 1eafa87

16 files changed

+472
-21
lines changed

modules/payment/src/Entity/Payment.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
* },
4848
* links = {
4949
* "collection" = "/admin/commerce/orders/{commerce_order}/payments",
50-
* "canonical" = "/admin/commerce/orders/{commerce_order}/payments/commerce_payment/edit",
50+
* "edit-form" = "/admin/commerce/orders/{commerce_order}/payments/commerce_payment/edit",
5151
* "operation-form" = "/admin/commerce/orders/{commerce_order}/payments/{commerce_payment}/operation/{operation}",
5252
* "delete-form" = "/admin/commerce/orders/{commerce_order}/payments/{commerce_payment}/delete",
5353
* },
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
entity.commerce_promotion.add_form:
22
route_name: entity.commerce_promotion.add_form
3-
title: 'Add a new promotion'
3+
title: 'Add promotion'
44
appears_on:
55
- entity.commerce_promotion.collection
6+
7+
entity.commerce_promotion_coupon.add_form:
8+
route_name: entity.commerce_promotion_coupon.add_form
9+
title: 'Add coupon'
10+
appears_on:
11+
- entity.commerce_promotion_coupon.collection

modules/promotion/commerce_promotion.links.task.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ entity.commerce_promotion.edit_form:
77
route_name: entity.commerce_promotion.edit_form
88
base_route: entity.commerce_promotion.edit_form
99
title: Edit
10+
11+
entity.commerce_promotion_coupon.collection:
12+
route_name: entity.commerce_promotion_coupon.collection
13+
base_route: entity.commerce_promotion.edit_form
14+
title: Coupons
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
entity.commerce_promotion_coupon.collection:
2+
path: '/promotion/{commerce_promotion}/coupons'
3+
defaults:
4+
_entity_list: 'commerce_promotion_coupon'
5+
_title: 'Coupons'
6+
options:
7+
_admin_route: TRUE
8+
parameters:
9+
commerce_promotion:
10+
type: 'entity:commerce_promotion'
11+
requirements:
12+
_entity_access: 'commerce_promotion.update'
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Drupal\commerce_promotion;
4+
5+
use Drupal\Core\Access\AccessResult;
6+
use Drupal\Core\Entity\EntityAccessControlHandler;
7+
use Drupal\Core\Entity\EntityInterface;
8+
use Drupal\Core\Session\AccountInterface;
9+
10+
/**
11+
* Controls coupon access based on the parent promotion.
12+
*/
13+
class CouponAccessControlHandler extends EntityAccessControlHandler {
14+
15+
/**
16+
* {@inheritdoc}
17+
*/
18+
public function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
19+
/** @var \Drupal\Core\Access\AccessResult $result */
20+
$result = parent::checkAccess($entity, $operation, $account);
21+
22+
if ($result->isNeutral()) {
23+
/** @var \Drupal\commerce_promotion\Entity\CouponInterface $entity */
24+
$result = $entity->getPromotion()->access('update', $account, TRUE);
25+
}
26+
27+
return $result;
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
public function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
34+
return AccessResult::allowedIfHasPermissions($account, [
35+
'administer commerce_promotion',
36+
'update commerce_promotion',
37+
], 'OR');
38+
}
39+
40+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
3+
namespace Drupal\commerce_promotion;
4+
5+
use Drupal\Core\Entity\EntityInterface;
6+
use Drupal\Core\Entity\EntityListBuilder;
7+
use Drupal\Core\Entity\EntityTypeInterface;
8+
use Drupal\Core\Entity\EntityStorageInterface;
9+
use Drupal\Core\Routing\RouteMatchInterface;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
11+
12+
/**
13+
* Defines the list builder for coupons.
14+
*/
15+
class CouponListBuilder extends EntityListBuilder {
16+
17+
/**
18+
* The current route match.
19+
*
20+
* @var \Drupal\Core\Routing\RouteMatchInterface
21+
*/
22+
protected $routeMatch;
23+
24+
/**
25+
* The usage.
26+
*
27+
* @var \Drupal\commerce_promotion\PromotionUsageInterface
28+
*/
29+
protected $usage;
30+
31+
/**
32+
* The usage counts.
33+
*
34+
* @var array
35+
*/
36+
protected $usageCounts;
37+
38+
/**
39+
* Constructs a new CouponListBuilder object.
40+
*
41+
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
42+
* The entity type definition.
43+
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
44+
* The entity storage.
45+
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
46+
* The current route match.
47+
* @param \Drupal\commerce_promotion\PromotionUsageInterface $usage
48+
* The usage.
49+
*/
50+
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, RouteMatchInterface $route_match, PromotionUsageInterface $usage) {
51+
parent::__construct($entity_type, $storage);
52+
53+
$this->routeMatch = $route_match;
54+
$this->usage = $usage;
55+
}
56+
57+
/**
58+
* {@inheritdoc}
59+
*/
60+
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
61+
return new static(
62+
$entity_type,
63+
$container->get('entity.manager')->getStorage($entity_type->id()),
64+
$container->get('current_route_match'),
65+
$container->get('commerce_promotion.usage')
66+
);
67+
}
68+
69+
/**
70+
* {@inheritdoc}
71+
*/
72+
public function load() {
73+
$promotion = $this->routeMatch->getParameter('commerce_promotion');
74+
$coupons = $this->storage->loadMultipleByPromotion($promotion);
75+
// Load the usage counts for each coupon.
76+
$this->usageCounts = $this->usage->loadMultiple($coupons);
77+
78+
return $coupons;
79+
}
80+
81+
/**
82+
* {@inheritdoc}
83+
*/
84+
public function buildHeader() {
85+
$header['code'] = $this->t('Code');
86+
$header['usage'] = $this->t('Usage');
87+
return $header + parent::buildHeader();
88+
}
89+
90+
/**
91+
* {@inheritdoc}
92+
*/
93+
public function buildRow(EntityInterface $entity) {
94+
/** @var \Drupal\commerce_promotion\Entity\CouponInterface $entity */
95+
$current_usage = $this->usageCounts[$entity->id()];
96+
$usage_limit = $entity->getUsageLimit();
97+
$usage_limit = $usage_limit ?: $this->t('Unlimited');
98+
$row['code'] = $entity->label();
99+
if (!$entity->isEnabled()) {
100+
$row['code'] .= ' (' . $this->t('Disabled') . ')';
101+
}
102+
$row['usage'] = $current_usage . ' / ' . $usage_limit;
103+
104+
return $row + parent::buildRow($entity);
105+
}
106+
107+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Drupal\commerce_promotion;
4+
5+
use Drupal\Core\Entity\EntityTypeInterface;
6+
use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
7+
8+
/**
9+
* Provides routes for the Coupon entity.
10+
*/
11+
class CouponRouteProvider extends AdminHtmlRouteProvider {
12+
13+
/**
14+
* {@inheritdoc}
15+
*/
16+
protected function getAddFormRoute(EntityTypeInterface $entity_type) {
17+
$route = parent::getAddFormRoute($entity_type);
18+
$route->setOption('parameters', [
19+
'commerce_promotion' => [
20+
'type' => 'entity:commerce_promotion',
21+
],
22+
]);
23+
// Coupons can be created if the parent promotion can be updated.
24+
$requirements = $route->getRequirements();
25+
unset($requirements['_entity_create_access']);
26+
$requirements['_entity_access'] = 'commerce_promotion.update';
27+
$route->setRequirements($requirements);
28+
29+
return $route;
30+
}
31+
32+
}

modules/promotion/src/CouponStorage.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Drupal\commerce_promotion;
44

55
use Drupal\commerce\CommerceContentEntityStorage;
6+
use Drupal\commerce_promotion\Entity\PromotionInterface;
67

78
/**
89
* Defines the coupon storage.
@@ -14,8 +15,14 @@ class CouponStorage extends CommerceContentEntityStorage implements CouponStorag
1415
*/
1516
public function loadByCode($code) {
1617
$coupons = $this->loadByProperties(['code' => $code, 'status' => TRUE]);
17-
1818
return reset($coupons);
1919
}
2020

21+
/**
22+
* {@inheritdoc}
23+
*/
24+
public function loadMultipleByPromotion(PromotionInterface $promotion) {
25+
return $this->loadByProperties(['promotion_id' => $promotion->id()]);
26+
}
27+
2128
}

modules/promotion/src/CouponStorageInterface.php

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

33
namespace Drupal\commerce_promotion;
44

5+
use Drupal\commerce_promotion\Entity\PromotionInterface;
56
use Drupal\Core\Entity\ContentEntityStorageInterface;
67

78
/**
@@ -20,4 +21,15 @@ interface CouponStorageInterface extends ContentEntityStorageInterface {
2021
*/
2122
public function loadByCode($code);
2223

24+
/**
25+
* Loads all coupons for the given promotion.
26+
*
27+
* @param \Drupal\commerce_promotion\Entity\PromotionInterface $promotion
28+
* The promotion.
29+
*
30+
* @return \Drupal\commerce_promotion\Entity\CouponInterface[]
31+
* The coupons.
32+
*/
33+
public function loadMultipleByPromotion(PromotionInterface $promotion);
34+
2335
}

modules/promotion/src/Entity/Coupon.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@
2121
* plural = "@count coupons",
2222
* ),
2323
* handlers = {
24+
* "list_builder" = "Drupal\commerce_promotion\CouponListBuilder",
2425
* "storage" = "Drupal\commerce_promotion\CouponStorage",
26+
* "access" = "Drupal\commerce_promotion\CouponAccessControlHandler",
2527
* "views_data" = "Drupal\views\EntityViewsData",
2628
* "form" = {
27-
* "add" = "Drupal\Core\Entity\ContentEntityForm",
28-
* "edit" = "Drupal\Core\Entity\ContentEntityForm",
29+
* "add" = "Drupal\commerce_promotion\Form\CouponForm",
30+
* "edit" = "Drupal\commerce_promotion\Form\CouponForm",
2931
* "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm"
3032
* },
33+
* "route_provider" = {
34+
* "default" = "Drupal\commerce_promotion\CouponRouteProvider",
35+
* },
3136
* },
3237
* base_table = "commerce_promotion_coupon",
3338
* admin_permission = "administer commerce_promotion",
@@ -37,10 +42,25 @@
3742
* "uuid" = "uuid",
3843
* "status" = "status",
3944
* },
45+
* links = {
46+
* "add-form" = "/promotion/{commerce_promotion}/coupons/add",
47+
* "edit-form" = "/promotion/{commerce_promotion}/coupons/{commerce_promotion_coupon}/edit",
48+
* "delete-form" = "/promotion/{commerce_promotion}/coupons/{commerce_promotion_coupon}/delete",
49+
* "collection" = "/promotion/{commerce_promotion}/coupons",
50+
* },
4051
* )
4152
*/
4253
class Coupon extends ContentEntityBase implements CouponInterface {
4354

55+
/**
56+
* {@inheritdoc}
57+
*/
58+
protected function urlRouteParameters($rel) {
59+
$uri_route_parameters = parent::urlRouteParameters($rel);
60+
$uri_route_parameters['commerce_promotion'] = $this->getPromotionId();
61+
return $uri_route_parameters;
62+
}
63+
4464
/**
4565
* {@inheritdoc}
4666
*/
@@ -121,6 +141,20 @@ public function available(OrderInterface $order) {
121141
return TRUE;
122142
}
123143

144+
/**
145+
* {@inheritdoc}
146+
*/
147+
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
148+
parent::postSave($storage, $update);
149+
150+
// Ensure there's a reference on each promotion.
151+
$promotion = $this->getPromotion();
152+
if ($promotion && !$promotion->hasCoupon($this)) {
153+
$promotion->addCoupon($this);
154+
$promotion->save();
155+
}
156+
}
157+
124158
/**
125159
* {@inheritdoc}
126160
*/

0 commit comments

Comments
 (0)