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,106 @@ 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+ if ($ item ->getExtensionAttributes ()) {
164+ $ item ->getExtensionAttributes ()->setDiscounts (null );
165+ }
166+ $ item ->setDiscountAmount (0 );
167+ $ item ->setBaseDiscountAmount (0 );
168+ $ item ->setDiscountPercent (0 );
169+ if ($ item ->getChildren () && $ item ->isChildrenCalculated ()) {
170+ foreach ($ item ->getChildren () as $ child ) {
171+ $ child ->setDiscountAmount (0 );
172+ $ child ->setBaseDiscountAmount (0 );
173+ $ child ->setDiscountPercent (0 );
174+ }
175+ }
176+ }
177+ $ this ->calculator ->init ($ store ->getWebsiteId (), $ quote ->getCustomerGroupId (), $ quote ->getCouponCode ());
178+ $ this ->calculator ->initTotals ($ items , $ address );
179+ $ items = $ this ->calculator ->sortItemsByPriority ($ items , $ address );
180+ $ rules = $ this ->calculator ->getRules ($ address );
181+ /** @var Rule $rule */
182+ foreach ($ rules as $ rule ) {
183+ /** @var Item $item */
184+ foreach ($ items as $ item ) {
185+ if ($ item ->getNoDiscount () || !$ this ->calculator ->canApplyDiscount ($ item ) || $ item ->getParentItem ()) {
186+ continue ;
153187 }
188+ $ eventArgs ['item ' ] = $ item ;
189+ $ this ->eventManager ->dispatch ('sales_quote_address_discount_item ' , $ eventArgs );
190+ $ this ->calculator ->process ($ item , $ rule );
191+ }
192+ $ appliedRuleIds = $ quote ->getAppliedRuleIds () ? explode (', ' , $ quote ->getAppliedRuleIds ()) : [];
193+ if ($ rule ->getStopRulesProcessing () && in_array ($ rule ->getId (), $ appliedRuleIds )) {
194+ break ;
195+ }
196+ $ this ->calculator ->initTotals ($ items , $ address );
197+ }
198+ foreach ($ items as $ item ) {
199+ if (!isset ($ itemsAggregate [$ item ->getId ()])) {
154200 continue ;
155201 }
156- // to determine the child item discount, we calculate the parent
157202 if ($ item ->getParentItem ()) {
158203 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 );
204+ } elseif ($ item ->getHasChildren () && $ item ->isChildrenCalculated ()) {
166205 foreach ($ item ->getChildren () as $ child ) {
167206 $ eventArgs ['item ' ] = $ child ;
168207 $ this ->eventManager ->dispatch ('sales_quote_address_discount_item ' , $ eventArgs );
169208 $ this ->aggregateItemDiscount ($ child , $ total );
170209 }
171- } else {
172- $ this ->calculator ->process ($ item );
173- $ this ->aggregateItemDiscount ($ item , $ total );
174210 }
211+ $ this ->aggregateItemDiscount ($ item , $ total );
175212 if ($ item ->getExtensionAttributes ()) {
176- $ this ->aggregateDiscountPerRule ($ item , $ address, $ addressDiscountAggregator );
213+ $ this ->aggregateDiscountPerRule ($ item , $ address );
177214 }
178215 }
179-
180216 $ this ->calculator ->prepareDescription ($ address );
181217 $ total ->setDiscountDescription ($ address ->getDiscountDescription ());
182218 $ total ->setSubtotalWithDiscount ($ total ->getSubtotal () + $ total ->getDiscountAmount ());
183219 $ total ->setBaseSubtotalWithDiscount ($ total ->getBaseSubtotal () + $ total ->getBaseDiscountAmount ());
184220 $ address ->setDiscountAmount ($ total ->getDiscountAmount ());
185221 $ address ->setBaseDiscountAmount ($ total ->getBaseDiscountAmount ());
186-
187222 return $ this ;
188223 }
189224
@@ -273,13 +308,11 @@ public function fetch(Quote $quote, Total $total)
273308 *
274309 * @param AbstractItem $item
275310 * @param AddressInterface $address
276- * @param array $addressDiscountAggregator
277311 * @return void
278312 */
279313 private function aggregateDiscountPerRule (
280314 AbstractItem $ item ,
281- AddressInterface $ address ,
282- array &$ addressDiscountAggregator
315+ AddressInterface $ address
283316 ) {
284317 $ discountBreakdown = $ item ->getExtensionAttributes ()->getDiscounts ();
285318 if ($ discountBreakdown ) {
@@ -288,15 +321,17 @@ private function aggregateDiscountPerRule(
288321 $ discount = $ value ->getDiscountData ();
289322 $ ruleLabel = $ value ->getRuleLabel ();
290323 $ ruleID = $ value ->getRuleID ();
291- if (isset ($ addressDiscountAggregator [$ ruleID ])) {
324+ if (isset ($ this -> addressDiscountAggregator [$ ruleID ])) {
292325 /** @var RuleDiscount $cartDiscount */
293- $ cartDiscount = $ addressDiscountAggregator [$ ruleID ];
326+ $ cartDiscount = $ this -> addressDiscountAggregator [$ ruleID ];
294327 $ discountData = $ cartDiscount ->getDiscountData ();
295- $ discountData ->setBaseAmount ($ discountData ->getBaseAmount ()+$ discount ->getBaseAmount ());
296- $ discountData ->setAmount ($ discountData ->getAmount ()+$ discount ->getAmount ());
297- $ discountData ->setOriginalAmount ($ discountData ->getOriginalAmount ()+$ discount ->getOriginalAmount ());
328+ $ discountData ->setBaseAmount ($ discountData ->getBaseAmount () + $ discount ->getBaseAmount ());
329+ $ discountData ->setAmount ($ discountData ->getAmount () + $ discount ->getAmount ());
330+ $ discountData ->setOriginalAmount (
331+ $ discountData ->getOriginalAmount () + $ discount ->getOriginalAmount ()
332+ );
298333 $ discountData ->setBaseOriginalAmount (
299- $ discountData ->getBaseOriginalAmount ()+ $ discount ->getBaseOriginalAmount ()
334+ $ discountData ->getBaseOriginalAmount () + $ discount ->getBaseOriginalAmount ()
300335 );
301336 } else {
302337 $ data = [
@@ -313,10 +348,10 @@ private function aggregateDiscountPerRule(
313348 ];
314349 /** @var RuleDiscount $cartDiscount */
315350 $ cartDiscount = $ this ->discountInterfaceFactory ->create (['data ' => $ data ]);
316- $ addressDiscountAggregator [$ ruleID ] = $ cartDiscount ;
351+ $ this -> addressDiscountAggregator [$ ruleID ] = $ cartDiscount ;
317352 }
318353 }
319354 }
320- $ address ->getExtensionAttributes ()->setDiscounts (array_values ($ addressDiscountAggregator ));
355+ $ address ->getExtensionAttributes ()->setDiscounts (array_values ($ this -> addressDiscountAggregator ));
321356 }
322357}
0 commit comments