Skip to content

Commit e8d9f5b

Browse files
authored
LYNX-213: Multicoupon Admin UI
1 parent f659a70 commit e8d9f5b

File tree

9 files changed

+251
-132
lines changed

9 files changed

+251
-132
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*
6+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained from
13+
* Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\QuoteGraphQl\Model;
18+
19+
use Magento\Framework\Api\SearchCriteriaBuilder;
20+
use Magento\Framework\Exception\LocalizedException;
21+
use Magento\Quote\Api\Data\CartInterface;
22+
use Magento\Quote\Model\Quote;
23+
use Magento\SalesRule\Api\CouponRepositoryInterface;
24+
use Magento\SalesRule\Api\Data\CouponInterface;
25+
use Magento\SalesRule\Api\Data\RuleDiscountInterface;
26+
27+
class GetDiscounts
28+
{
29+
/**
30+
* @param CouponRepositoryInterface $couponRepository
31+
* @param SearchCriteriaBuilder $criteriaBuilder
32+
*/
33+
public function __construct(
34+
private readonly CouponRepositoryInterface $couponRepository,
35+
private readonly SearchCriteriaBuilder $criteriaBuilder
36+
) {
37+
}
38+
39+
/**
40+
* Get Discount Values
41+
*
42+
* @param Quote $quote
43+
* @param RuleDiscountInterface[]|null $discounts
44+
* @return array|null
45+
* @throws LocalizedException
46+
*/
47+
public function execute(Quote $quote, array $discounts): ?array
48+
{
49+
if (empty($discounts)) {
50+
return null;
51+
}
52+
53+
$discountValues = [];
54+
$coupon = $this->getCoupon($quote);
55+
foreach ($discounts as $value) {
56+
$discountData = $value->getDiscountData();
57+
$discountValues[] = [
58+
'label' => $value->getRuleLabel() ?: __('Discount'),
59+
'applied_to' => $discountData->getAppliedTo(),
60+
'amount' => [
61+
'value' => $discountData->getAmount(),
62+
'currency' => $quote->getQuoteCurrencyCode()
63+
],
64+
'coupon' => $this->getFormattedCoupon($coupon, (int) $value->getRuleID())
65+
];
66+
}
67+
68+
return $discountValues;
69+
}
70+
71+
/**
72+
* Get formatted coupon for the rule id
73+
*
74+
* @param CouponInterface|null $coupon
75+
* @param int $ruleId
76+
* @return array|null
77+
*/
78+
private function getFormattedCoupon(?CouponInterface $coupon, int $ruleId): ?array
79+
{
80+
if ($coupon && $coupon->getRuleId() && $coupon->getRuleId() == $ruleId) {
81+
return ['code' => $coupon->getCode()];
82+
}
83+
return null;
84+
}
85+
86+
/**
87+
* Retrieve coupon data object
88+
*
89+
* @param CartInterface $quote
90+
* @return CouponInterface|null
91+
* @throws LocalizedException
92+
*/
93+
private function getCoupon(CartInterface $quote): ?CouponInterface
94+
{
95+
$couponCode = $quote->getCouponCode();
96+
if (!$couponCode) {
97+
return null;
98+
}
99+
$couponModels = $this->couponRepository->getList(
100+
$this->criteriaBuilder->addFilter('code', $couponCode)->create()
101+
)->getItems();
102+
if (empty($couponModels)) {
103+
return null;
104+
}
105+
return reset($couponModels);
106+
}
107+
}

app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php

Lines changed: 11 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,26 @@
1515
use Magento\Quote\Model\Cart\Totals;
1616
use Magento\Quote\Model\Quote\Item;
1717
use Magento\QuoteGraphQl\Model\Cart\TotalsCollector;
18+
use Magento\QuoteGraphQl\Model\GetDiscounts;
1819

1920
/**
2021
* @inheritdoc
2122
*/
2223
class CartItemPrices implements ResolverInterface, ResetAfterRequestInterface
2324
{
24-
/**
25-
* @var TotalsCollector
26-
*/
27-
private $totalsCollector;
28-
2925
/**
3026
* @var Totals|null
3127
*/
3228
private $totals;
3329

3430
/**
35-
* CartItemPrices constructor
36-
*
3731
* @param TotalsCollector $totalsCollector
32+
* @param GetDiscounts $getDiscounts
3833
*/
3934
public function __construct(
40-
TotalsCollector $totalsCollector
35+
private readonly TotalsCollector $totalsCollector,
36+
private readonly GetDiscounts $getDiscounts
4137
) {
42-
$this->totalsCollector = $totalsCollector;
4338
}
4439

4540
/**
@@ -69,10 +64,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
6964

7065
/** calculate bundle product discount */
7166
if ($cartItem->getProductType() == 'bundle') {
72-
$discountValues = $this->getDiscountValues($cartItem, $currencyCode);
67+
$discounts = $cartItem->getExtensionAttributes()->getDiscounts() ?? [];
7368
$discountAmount = 0;
74-
foreach ((array) $discountValues as $discountValue) {
75-
$discountAmount += $discountValue['amount']['value'];
69+
foreach ($discounts as $discount) {
70+
$discountAmount += $discount->getDiscountData()->getAmount();
7671
}
7772
} else {
7873
$discountAmount = $cartItem->getDiscountAmount();
@@ -99,36 +94,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
9994
'currency' => $currencyCode,
10095
'value' => $discountAmount,
10196
],
102-
'discounts' => $this->getDiscountValues($cartItem, $currencyCode)
97+
'discounts' => $this->getDiscounts->execute(
98+
$cartItem->getQuote(),
99+
$cartItem->getExtensionAttributes()->getDiscounts() ?? []
100+
)
103101
];
104102
}
105-
106-
/**
107-
* Get Discount Values
108-
*
109-
* @param Item $cartItem
110-
* @param string $currencyCode
111-
* @return array|null
112-
*/
113-
private function getDiscountValues($cartItem, $currencyCode)
114-
{
115-
$itemDiscounts = $cartItem->getExtensionAttributes()->getDiscounts();
116-
if ($itemDiscounts) {
117-
$discountValues = [];
118-
foreach ($itemDiscounts as $value) {
119-
$discount = [];
120-
$amount = [];
121-
/* @var \Magento\SalesRule\Api\Data\DiscountDataInterface $discountData */
122-
$discountData = $value->getDiscountData();
123-
$discountAmount = $discountData->getAmount();
124-
$discount['label'] = $value->getRuleLabel() ?: __('Discount');
125-
$amount['value'] = $discountAmount;
126-
$amount['currency'] = $currencyCode;
127-
$discount['amount'] = $amount;
128-
$discountValues[] = $discount;
129-
}
130-
return $discountValues;
131-
}
132-
return null;
133-
}
134103
}

app/code/Magento/QuoteGraphQl/Model/Resolver/Discounts.php

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use Magento\Framework\GraphQl\Config\Element\Field;
1212
use Magento\Framework\GraphQl\Query\ResolverInterface;
1313
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
14-
use Magento\Quote\Model\Quote;
14+
use Magento\QuoteGraphQl\Model\GetDiscounts;
1515

1616
/**
1717
* @inheritdoc
@@ -20,6 +20,15 @@ class Discounts implements ResolverInterface
2020
{
2121
public const TYPE_SHIPPING = "SHIPPING";
2222
public const TYPE_ITEM = "ITEM";
23+
24+
/**
25+
* @param GetDiscounts $getDiscounts
26+
*/
27+
public function __construct(
28+
private readonly GetDiscounts $getDiscounts,
29+
) {
30+
}
31+
2332
/**
2433
* @inheritdoc
2534
*/
@@ -30,35 +39,9 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
3039
}
3140
$quote = $value['model'];
3241

33-
return $this->getDiscountValues($quote);
34-
}
35-
36-
/**
37-
* Get Discount Values
38-
*
39-
* @param Quote $quote
40-
* @return array
41-
*/
42-
private function getDiscountValues(Quote $quote)
43-
{
44-
$discountValues=[];
45-
$address = $quote->getShippingAddress();
46-
$totalDiscounts = $address->getExtensionAttributes()->getDiscounts();
47-
48-
if ($totalDiscounts && is_array($totalDiscounts)) {
49-
foreach ($totalDiscounts as $value) {
50-
$discount = [];
51-
$amount = [];
52-
$discount['label'] = $value->getRuleLabel() ?: __('Discount');
53-
/* @var \Magento\SalesRule\Api\Data\DiscountDataInterface $discountData */
54-
$discountData = $value->getDiscountData();
55-
$discount['applied_to'] = $discountData->getAppliedTo();
56-
$amount['value'] = $discountData->getAmount();
57-
$amount['currency'] = $quote->getQuoteCurrencyCode();
58-
$discount['amount'] = $amount;
59-
$discountValues[] = $discount;
60-
}
61-
}
62-
return $discountValues ?: null;
42+
return $this->getDiscounts->execute(
43+
$quote,
44+
$quote->getShippingAddress()->getExtensionAttributes()->getDiscounts() ?? []
45+
);
6346
}
6447
}

app/code/Magento/QuoteGraphQl/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"magento/module-graph-ql": "*",
1818
"magento/module-gift-message": "*",
1919
"magento/module-catalog-inventory": "*",
20-
"magento/module-eav-graph-ql": "*"
20+
"magento/module-eav-graph-ql": "*",
21+
"magento/module-sales-rule": "*"
2122
},
2223
"suggest": {
2324
"magento/module-graph-ql-cache": "*",

app/code/Magento/QuoteGraphQl/etc/schema.graphqls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ type Discount @doc(description:"Defines an individual discount. A discount can b
438438
amount: Money! @doc(description:"The amount of the discount.")
439439
applied_to: CartDiscountType! @doc(description:"The type of the entity the discount is applied to.")
440440
label: String! @doc(description:"A description of the discount.")
441+
coupon: AppliedCoupon @doc(description:"The coupon related to the discount.")
441442
}
442443

443444
enum CartDiscountType {

app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
namespace Magento\Sales\Controller\Adminhtml\Order;
99

1010
use Magento\Backend\App\Action;
11+
use Magento\Framework\App\ObjectManager;
1112
use Magento\Framework\View\Result\PageFactory;
1213
use Magento\Backend\Model\View\Result\ForwardFactory;
14+
use Magento\Sales\Model\Order\Create\ValidateCoupon;
1315

1416
/**
1517
* Adminhtml sales orders creation process controller
1618
*
1719
* @SuppressWarnings(PHPMD.NumberOfChildren)
1820
* @SuppressWarnings(PHPMD.AllPurposeAction)
21+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1922
*/
2023
abstract class Create extends \Magento\Backend\App\Action
2124
{
@@ -38,26 +41,33 @@ abstract class Create extends \Magento\Backend\App\Action
3841
*/
3942
protected $resultForwardFactory;
4043

44+
/**
45+
* @var ValidateCoupon
46+
*/
47+
private $validateCoupon;
48+
4149
/**
4250
* @param Action\Context $context
4351
* @param \Magento\Catalog\Helper\Product $productHelper
4452
* @param \Magento\Framework\Escaper $escaper
4553
* @param PageFactory $resultPageFactory
4654
* @param ForwardFactory $resultForwardFactory
47-
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
55+
* @param ValidateCoupon|null $validateCoupon
4856
*/
4957
public function __construct(
5058
Action\Context $context,
5159
\Magento\Catalog\Helper\Product $productHelper,
5260
\Magento\Framework\Escaper $escaper,
5361
PageFactory $resultPageFactory,
54-
ForwardFactory $resultForwardFactory
62+
ForwardFactory $resultForwardFactory,
63+
ValidateCoupon $validateCoupon = null
5564
) {
5665
parent::__construct($context);
5766
$productHelper->setSkipSaleableCheck(true);
5867
$this->escaper = $escaper;
5968
$this->resultPageFactory = $resultPageFactory;
6069
$this->resultForwardFactory = $resultForwardFactory;
70+
$this->validateCoupon = $validateCoupon ?: ObjectManager::getInstance()->get(ValidateCoupon::class);
6171
}
6272

6373
/**
@@ -311,41 +321,7 @@ protected function _processActionData($action = null)
311321
}
312322

313323
$data = $this->getRequest()->getPost('order');
314-
$couponCode = '';
315-
if (isset($data) && isset($data['coupon']['code'])) {
316-
$couponCode = trim($data['coupon']['code']);
317-
}
318-
319-
if (!empty($couponCode)) {
320-
$isApplyDiscount = false;
321-
foreach ($this->_getQuote()->getAllItems() as $item) {
322-
if (!$item->getNoDiscount()) {
323-
$isApplyDiscount = true;
324-
break;
325-
}
326-
}
327-
if (!$isApplyDiscount) {
328-
$this->messageManager->addErrorMessage(
329-
__(
330-
'"%1" coupon code was not applied. Do not apply discount is selected for item(s)',
331-
$this->escaper->escapeHtml($couponCode)
332-
)
333-
);
334-
} else {
335-
if ($this->_getQuote()->getCouponCode() !== $couponCode) {
336-
$this->messageManager->addErrorMessage(
337-
__(
338-
'The "%1" coupon code isn\'t valid. Verify the code and try again.',
339-
$this->escaper->escapeHtml($couponCode)
340-
)
341-
);
342-
} else {
343-
$this->messageManager->addSuccessMessage(__('The coupon code has been accepted.'));
344-
}
345-
}
346-
} elseif (isset($data['coupon']['code']) && $couponCode=='') {
347-
$this->messageManager->addSuccessMessage(__('The coupon code has been removed.'));
348-
}
324+
$this->validateCoupon->execute($this->_getQuote(), $data);
349325

350326
return $this;
351327
}

0 commit comments

Comments
 (0)