1111use Magento \Quote \Api \Data \AddressInterface ;
1212use Magento \Quote \Api \Data \ShippingAssignmentInterface ;
1313use Magento \Quote \Model \Quote ;
14+ use Magento \Quote \Model \Quote \Address ;
1415use Magento \Quote \Model \Quote \Address \Total ;
1516use Magento \Quote \Model \Quote \Address \Total \AbstractTotal ;
1617use Magento \Quote \Model \Quote \Item ;
2021use Magento \SalesRule \Api \Data \RuleDiscountInterfaceFactory ;
2122use Magento \SalesRule \Model \Data \RuleDiscount ;
2223use Magento \SalesRule \Model \Discount \PostProcessorFactory ;
24+ use Magento \SalesRule \Model \Rule ;
2325use Magento \SalesRule \Model \Validator ;
2426use Magento \Store \Model \StoreManagerInterface ;
27+ use Magento \SalesRule \Model \RulesApplier ;
2528
2629/**
2730 * Discount totals calculation model.
@@ -66,21 +69,33 @@ class Discount extends AbstractTotal
6669 */
6770 private $ discountDataInterfaceFactory ;
6871
72+ /**
73+ * @var RulesApplier|null
74+ */
75+ private $ rulesApplier ;
76+
77+ /**
78+ * @var array
79+ */
80+ private $ addressDiscountAggregator = [];
81+
6982 /**
7083 * @param ManagerInterface $eventManager
7184 * @param StoreManagerInterface $storeManager
7285 * @param Validator $validator
7386 * @param PriceCurrencyInterface $priceCurrency
7487 * @param RuleDiscountInterfaceFactory|null $discountInterfaceFactory
7588 * @param DiscountDataInterfaceFactory|null $discountDataInterfaceFactory
89+ * @param RulesApplier|null $rulesApplier
7690 */
7791 public function __construct (
7892 ManagerInterface $ eventManager ,
7993 StoreManagerInterface $ storeManager ,
8094 Validator $ validator ,
8195 PriceCurrencyInterface $ priceCurrency ,
8296 RuleDiscountInterfaceFactory $ discountInterfaceFactory = null ,
83- DiscountDataInterfaceFactory $ discountDataInterfaceFactory = null
97+ DiscountDataInterfaceFactory $ discountDataInterfaceFactory = null ,
98+ RulesApplier $ rulesApplier = null
8499 ) {
85100 $ this ->setCode (self ::COLLECTOR_TYPE_CODE );
86101 $ this ->eventManager = $ eventManager ;
@@ -91,6 +106,8 @@ public function __construct(
91106 ?: ObjectManager::getInstance ()->get (RuleDiscountInterfaceFactory::class);
92107 $ this ->discountDataInterfaceFactory = $ discountDataInterfaceFactory
93108 ?: ObjectManager::getInstance ()->get (DiscountDataInterfaceFactory::class);
109+ $ this ->rulesApplier = $ rulesApplier
110+ ?: ObjectManager::getInstance ()->get (RulesApplier::class);
94111 }
95112
96113 /**
@@ -102,88 +119,103 @@ public function __construct(
102119 * @return $this
103120 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
104121 * @SuppressWarnings(PHPMD.NPathComplexity)
122+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
105123 */
106124 public function collect (
107125 Quote $ quote ,
108126 ShippingAssignmentInterface $ shippingAssignment ,
109127 Total $ total
110128 ) {
111129 parent ::collect ($ quote , $ shippingAssignment , $ total );
112-
113130 $ store = $ this ->storeManager ->getStore ($ quote ->getStoreId ());
131+ /** @var Address $address */
114132 $ address = $ shippingAssignment ->getShipping ()->getAddress ();
115-
116133 if ($ quote ->currentPaymentWasSet ()) {
117134 $ address ->setPaymentMethod ($ quote ->getPayment ()->getMethod ());
118135 }
119-
120136 $ this ->calculator ->reset ($ address );
121-
122- $ items = $ shippingAssignment ->getItems ();
123- if (!count ($ items )) {
137+ $ itemsAggregate = [];
138+ foreach ($ shippingAssignment ->getItems () as $ item ) {
139+ $ itemId = $ item ->getId ();
140+ $ itemsAggregate [$ itemId ] = $ item ;
141+ }
142+ $ items = [];
143+ foreach ($ quote ->getAllAddresses () as $ quoteAddress ) {
144+ foreach ($ quoteAddress ->getAllItems () as $ item ) {
145+ $ items [] = $ item ;
146+ }
147+ }
148+ if (!$ items || !$ itemsAggregate ) {
124149 return $ this ;
125150 }
126-
127151 $ eventArgs = [
128152 'website_id ' => $ store ->getWebsiteId (),
129153 'customer_group_id ' => $ quote ->getCustomerGroupId (),
130154 'coupon_code ' => $ quote ->getCouponCode (),
131155 ];
132-
133- $ this ->calculator ->init ($ store ->getWebsiteId (), $ quote ->getCustomerGroupId (), $ quote ->getCouponCode ());
134- $ this ->calculator ->initTotals ($ items , $ address );
135-
136156 $ address ->setDiscountDescription ([]);
137- $ items = $ this ->calculator ->sortItemsByPriority ($ items , $ address );
138157 $ address ->getExtensionAttributes ()->setDiscounts ([]);
139- $ addressDiscountAggregator = [];
140-
141- /** @var Item $item */
158+ $ this -> addressDiscountAggregator = [];
159+ $ address -> setCartFixedRules ([]);
160+ $ quote -> setCartFixedRules ([]);
142161 foreach ($ items as $ item ) {
143- if ($ item ->getNoDiscount () || !$ this ->calculator ->canApplyDiscount ($ item )) {
144- $ item ->setDiscountAmount (0 );
145- $ item ->setBaseDiscountAmount (0 );
146-
147- // ensure my children are zeroed out
148- if ($ item ->getHasChildren () && $ item ->isChildrenCalculated ()) {
149- foreach ($ item ->getChildren () as $ child ) {
150- $ child ->setDiscountAmount (0 );
151- $ child ->setBaseDiscountAmount (0 );
152- }
162+ $ this ->rulesApplier ->setAppliedRuleIds ($ item , []);
163+ $ item ->setDiscountAmount (0 );
164+ $ item ->setBaseDiscountAmount (0 );
165+ $ item ->setDiscountPercent (0 );
166+ if ($ item ->getChildren () && $ item ->isChildrenCalculated ()) {
167+ foreach ($ item ->getChildren () as $ child ) {
168+ $ child ->setDiscountAmount (0 );
169+ $ child ->setBaseDiscountAmount (0 );
170+ $ child ->setDiscountPercent (0 );
171+ }
172+ }
173+ }
174+ $ this ->calculator ->init ($ store ->getWebsiteId (), $ quote ->getCustomerGroupId (), $ quote ->getCouponCode ());
175+ $ this ->calculator ->initTotals ($ items , $ address );
176+ $ items = $ this ->calculator ->sortItemsByPriority ($ items , $ address );
177+ $ rules = $ this ->calculator ->getRules ($ address );
178+ /** @var Rule $rule */
179+ foreach ($ rules as $ rule ) {
180+ /** @var Item $item */
181+ foreach ($ items as $ item ) {
182+ if ($ item ->getNoDiscount () || !$ this ->calculator ->canApplyDiscount ($ item ) || $ item ->getParentItem ()) {
183+ continue ;
153184 }
185+ $ eventArgs ['item ' ] = $ item ;
186+ $ this ->eventManager ->dispatch ('sales_quote_address_discount_item ' , $ eventArgs );
187+ $ this ->calculator ->process ($ item , $ rule );
188+ }
189+ $ appliedRuleIds = $ quote ->getAppliedRuleIds () ? explode (', ' , $ quote ->getAppliedRuleIds ()) : [];
190+ if ($ rule ->getStopRulesProcessing () && in_array ($ rule ->getId (), $ appliedRuleIds )) {
191+ break ;
192+ }
193+ $ this ->calculator ->initTotals ($ items , $ address );
194+ }
195+ foreach ($ items as $ item ) {
196+ if (!isset ($ itemsAggregate [$ item ->getId ()])) {
154197 continue ;
155198 }
156- // to determine the child item discount, we calculate the parent
157199 if ($ item ->getParentItem ()) {
158200 continue ;
159- }
160-
161- $ eventArgs ['item ' ] = $ item ;
162- $ this ->eventManager ->dispatch ('sales_quote_address_discount_item ' , $ eventArgs );
163-
164- if ($ item ->getHasChildren () && $ item ->isChildrenCalculated ()) {
165- $ this ->calculator ->process ($ item );
201+ } elseif ($ item ->getHasChildren () && $ item ->isChildrenCalculated ()) {
166202 foreach ($ item ->getChildren () as $ child ) {
167203 $ eventArgs ['item ' ] = $ child ;
168204 $ this ->eventManager ->dispatch ('sales_quote_address_discount_item ' , $ eventArgs );
169205 $ this ->aggregateItemDiscount ($ child , $ total );
170206 }
171- } else {
172- $ this ->calculator ->process ($ item );
173- $ this ->aggregateItemDiscount ($ item , $ total );
174207 }
208+ $ this ->aggregateItemDiscount ($ item , $ total );
175209 if ($ item ->getExtensionAttributes ()) {
176- $ this ->aggregateDiscountPerRule ($ item , $ address, $ addressDiscountAggregator );
210+ $ this ->aggregateDiscountPerRule ($ item , $ address );
177211 }
178212 }
179-
180213 $ this ->calculator ->prepareDescription ($ address );
181214 $ total ->setDiscountDescription ($ address ->getDiscountDescription ());
182215 $ total ->setSubtotalWithDiscount ($ total ->getSubtotal () + $ total ->getDiscountAmount ());
183216 $ total ->setBaseSubtotalWithDiscount ($ total ->getBaseSubtotal () + $ total ->getBaseDiscountAmount ());
184217 $ address ->setDiscountAmount ($ total ->getDiscountAmount ());
185218 $ address ->setBaseDiscountAmount ($ total ->getBaseDiscountAmount ());
186-
187219 return $ this ;
188220 }
189221
@@ -273,13 +305,11 @@ public function fetch(Quote $quote, Total $total)
273305 *
274306 * @param AbstractItem $item
275307 * @param AddressInterface $address
276- * @param array $addressDiscountAggregator
277308 * @return void
278309 */
279310 private function aggregateDiscountPerRule (
280311 AbstractItem $ item ,
281- AddressInterface $ address ,
282- array &$ addressDiscountAggregator
312+ AddressInterface $ address
283313 ) {
284314 $ discountBreakdown = $ item ->getExtensionAttributes ()->getDiscounts ();
285315 if ($ discountBreakdown ) {
@@ -288,15 +318,17 @@ private function aggregateDiscountPerRule(
288318 $ discount = $ value ->getDiscountData ();
289319 $ ruleLabel = $ value ->getRuleLabel ();
290320 $ ruleID = $ value ->getRuleID ();
291- if (isset ($ addressDiscountAggregator [$ ruleID ])) {
321+ if (isset ($ this -> addressDiscountAggregator [$ ruleID ])) {
292322 /** @var RuleDiscount $cartDiscount */
293- $ cartDiscount = $ addressDiscountAggregator [$ ruleID ];
323+ $ cartDiscount = $ this -> addressDiscountAggregator [$ ruleID ];
294324 $ discountData = $ cartDiscount ->getDiscountData ();
295- $ discountData ->setBaseAmount ($ discountData ->getBaseAmount ()+$ discount ->getBaseAmount ());
296- $ discountData ->setAmount ($ discountData ->getAmount ()+$ discount ->getAmount ());
297- $ discountData ->setOriginalAmount ($ discountData ->getOriginalAmount ()+$ discount ->getOriginalAmount ());
325+ $ discountData ->setBaseAmount ($ discountData ->getBaseAmount () + $ discount ->getBaseAmount ());
326+ $ discountData ->setAmount ($ discountData ->getAmount () + $ discount ->getAmount ());
327+ $ discountData ->setOriginalAmount (
328+ $ discountData ->getOriginalAmount () + $ discount ->getOriginalAmount ()
329+ );
298330 $ discountData ->setBaseOriginalAmount (
299- $ discountData ->getBaseOriginalAmount ()+ $ discount ->getBaseOriginalAmount ()
331+ $ discountData ->getBaseOriginalAmount () + $ discount ->getBaseOriginalAmount ()
300332 );
301333 } else {
302334 $ data = [
@@ -313,10 +345,10 @@ private function aggregateDiscountPerRule(
313345 ];
314346 /** @var RuleDiscount $cartDiscount */
315347 $ cartDiscount = $ this ->discountInterfaceFactory ->create (['data ' => $ data ]);
316- $ addressDiscountAggregator [$ ruleID ] = $ cartDiscount ;
348+ $ this -> addressDiscountAggregator [$ ruleID ] = $ cartDiscount ;
317349 }
318350 }
319351 }
320- $ address ->getExtensionAttributes ()->setDiscounts (array_values ($ addressDiscountAggregator ));
352+ $ address ->getExtensionAttributes ()->setDiscounts (array_values ($ this -> addressDiscountAggregator ));
321353 }
322354}
0 commit comments