TotalsCollector.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Quote\Model\Quote;
  7. use Magento\Quote\Model\Quote\Address\Total\Collector;
  8. use Magento\Quote\Model\Quote\Address\Total\CollectorFactory;
  9. use Magento\Quote\Model\Quote\Address\Total\CollectorInterface;
  10. /**
  11. * Class TotalsCollector
  12. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  13. */
  14. class TotalsCollector
  15. {
  16. /**
  17. * Total models collector
  18. *
  19. * @var \Magento\Quote\Model\Quote\Address\Total\Collector
  20. */
  21. protected $totalCollector;
  22. /**
  23. * @var \Magento\Quote\Model\Quote\Address\Total\CollectorFactory
  24. */
  25. protected $totalCollectorFactory;
  26. /**
  27. * Application Event Dispatcher
  28. *
  29. * @var \Magento\Framework\Event\ManagerInterface
  30. */
  31. protected $eventManager;
  32. /**
  33. * @var \Magento\Store\Model\StoreManagerInterface
  34. */
  35. protected $storeManager;
  36. /**
  37. * @var \Magento\Quote\Model\Quote\Address\TotalFactory
  38. */
  39. protected $totalFactory;
  40. /**
  41. * @var \Magento\Quote\Model\Quote\TotalsCollectorList
  42. */
  43. protected $collectorList;
  44. /**
  45. * Quote validator
  46. *
  47. * @var \Magento\Quote\Model\QuoteValidator
  48. */
  49. protected $quoteValidator;
  50. /**
  51. * @var \Magento\Quote\Model\ShippingFactory
  52. */
  53. protected $shippingFactory;
  54. /**
  55. * @var \Magento\Quote\Model\ShippingAssignmentFactory
  56. */
  57. protected $shippingAssignmentFactory;
  58. /**
  59. * @param Collector $totalCollector
  60. * @param CollectorFactory $totalCollectorFactory
  61. * @param \Magento\Framework\Event\ManagerInterface $eventManager
  62. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  63. * @param Address\TotalFactory $totalFactory
  64. * @param TotalsCollectorList $collectorList
  65. * @param \Magento\Quote\Model\ShippingFactory $shippingFactory
  66. * @param \Magento\Quote\Model\ShippingAssignmentFactory $shippingAssignmentFactory
  67. * @param \Magento\Quote\Model\QuoteValidator $quoteValidator
  68. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  69. */
  70. public function __construct(
  71. Collector $totalCollector,
  72. CollectorFactory $totalCollectorFactory,
  73. \Magento\Framework\Event\ManagerInterface $eventManager,
  74. \Magento\Store\Model\StoreManagerInterface $storeManager,
  75. \Magento\Quote\Model\Quote\Address\TotalFactory $totalFactory,
  76. \Magento\Quote\Model\Quote\TotalsCollectorList $collectorList,
  77. \Magento\Quote\Model\ShippingFactory $shippingFactory,
  78. \Magento\Quote\Model\ShippingAssignmentFactory $shippingAssignmentFactory,
  79. \Magento\Quote\Model\QuoteValidator $quoteValidator
  80. ) {
  81. $this->totalCollector = $totalCollector;
  82. $this->totalCollectorFactory = $totalCollectorFactory;
  83. $this->eventManager = $eventManager;
  84. $this->storeManager = $storeManager;
  85. $this->totalFactory = $totalFactory;
  86. $this->collectorList = $collectorList;
  87. $this->shippingFactory = $shippingFactory;
  88. $this->shippingAssignmentFactory = $shippingAssignmentFactory;
  89. $this->quoteValidator = $quoteValidator;
  90. }
  91. /**
  92. * Collect quote totals.
  93. *
  94. * @param \Magento\Quote\Model\Quote $quote
  95. * @return Address\Total
  96. */
  97. public function collectQuoteTotals(\Magento\Quote\Model\Quote $quote)
  98. {
  99. if ($quote->isVirtual()) {
  100. return $this->collectAddressTotals($quote, $quote->getBillingAddress());
  101. }
  102. return $this->collectAddressTotals($quote, $quote->getShippingAddress());
  103. }
  104. /**
  105. * Collect quote.
  106. *
  107. * @param \Magento\Quote\Model\Quote $quote
  108. * @return \Magento\Quote\Model\Quote\Address\Total
  109. */
  110. public function collect(\Magento\Quote\Model\Quote $quote)
  111. {
  112. /** @var \Magento\Quote\Model\Quote\Address\Total $total */
  113. $total = $this->totalFactory->create(\Magento\Quote\Model\Quote\Address\Total::class);
  114. $this->eventManager->dispatch(
  115. 'sales_quote_collect_totals_before',
  116. ['quote' => $quote]
  117. );
  118. $this->_collectItemsQtys($quote);
  119. $total->setSubtotal(0);
  120. $total->setBaseSubtotal(0);
  121. $total->setSubtotalWithDiscount(0);
  122. $total->setBaseSubtotalWithDiscount(0);
  123. $total->setGrandTotal(0);
  124. $total->setBaseGrandTotal(0);
  125. /** @var \Magento\Quote\Model\Quote\Address $address */
  126. foreach ($quote->getAllAddresses() as $address) {
  127. $addressTotal = $this->collectAddressTotals($quote, $address);
  128. $total->setShippingAmount($addressTotal->getShippingAmount());
  129. $total->setBaseShippingAmount($addressTotal->getBaseShippingAmount());
  130. $total->setShippingDescription($addressTotal->getShippingDescription());
  131. $total->setSubtotal((float)$total->getSubtotal() + $addressTotal->getSubtotal());
  132. $total->setBaseSubtotal((float)$total->getBaseSubtotal() + $addressTotal->getBaseSubtotal());
  133. $total->setSubtotalWithDiscount(
  134. (float)$total->getSubtotalWithDiscount() + $addressTotal->getSubtotalWithDiscount()
  135. );
  136. $total->setBaseSubtotalWithDiscount(
  137. (float)$total->getBaseSubtotalWithDiscount() + $addressTotal->getBaseSubtotalWithDiscount()
  138. );
  139. $total->setGrandTotal((float)$total->getGrandTotal() + $addressTotal->getGrandTotal());
  140. $total->setBaseGrandTotal((float)$total->getBaseGrandTotal() + $addressTotal->getBaseGrandTotal());
  141. }
  142. $this->quoteValidator->validateQuoteAmount($quote, $quote->getGrandTotal());
  143. $this->quoteValidator->validateQuoteAmount($quote, $quote->getBaseGrandTotal());
  144. $this->_validateCouponCode($quote);
  145. $this->eventManager->dispatch(
  146. 'sales_quote_collect_totals_after',
  147. ['quote' => $quote]
  148. );
  149. return $total;
  150. }
  151. /**
  152. * Validate coupon code.
  153. *
  154. * @param \Magento\Quote\Model\Quote $quote
  155. * @return $this
  156. */
  157. protected function _validateCouponCode(\Magento\Quote\Model\Quote $quote)
  158. {
  159. $code = $quote->getData('coupon_code');
  160. if (strlen($code)) {
  161. $addressHasCoupon = false;
  162. $addresses = $quote->getAllAddresses();
  163. if (count($addresses) > 0) {
  164. foreach ($addresses as $address) {
  165. if ($address->hasCouponCode()) {
  166. $addressHasCoupon = true;
  167. }
  168. }
  169. if (!$addressHasCoupon) {
  170. $quote->setCouponCode('');
  171. }
  172. }
  173. }
  174. return $this;
  175. }
  176. /**
  177. * Collect items qty
  178. *
  179. * @param \Magento\Quote\Model\Quote $quote
  180. * @return $this
  181. */
  182. protected function _collectItemsQtys(\Magento\Quote\Model\Quote $quote)
  183. {
  184. $quoteItems = $quote->getAllVisibleItems();
  185. $quote->setItemsCount(0);
  186. $quote->setItemsQty(0);
  187. $quote->setVirtualItemsQty(0);
  188. foreach ($quoteItems as $item) {
  189. if ($item->getParentItem()) {
  190. continue;
  191. }
  192. $children = $item->getChildren();
  193. if ($children && $item->isShipSeparately()) {
  194. foreach ($children as $child) {
  195. if ($child->getProduct()->getIsVirtual()) {
  196. $quote->setVirtualItemsQty($quote->getVirtualItemsQty() + $child->getQty() * $item->getQty());
  197. }
  198. }
  199. }
  200. if ($item->getProduct()->getIsVirtual()) {
  201. $quote->setVirtualItemsQty($quote->getVirtualItemsQty() + $item->getQty());
  202. }
  203. $quote->setItemsCount($quote->getItemsCount() + 1);
  204. $quote->setItemsQty((float)$quote->getItemsQty() + $item->getQty());
  205. }
  206. return $this;
  207. }
  208. /**
  209. * Collect address total.
  210. *
  211. * @param \Magento\Quote\Model\Quote $quote
  212. * @param Address $address
  213. * @return Address\Total
  214. */
  215. public function collectAddressTotals(
  216. \Magento\Quote\Model\Quote $quote,
  217. \Magento\Quote\Model\Quote\Address $address
  218. ) {
  219. /** @var \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment */
  220. $shippingAssignment = $this->shippingAssignmentFactory->create();
  221. /** @var \Magento\Quote\Api\Data\ShippingInterface $shipping */
  222. $shipping = $this->shippingFactory->create();
  223. $shipping->setMethod($address->getShippingMethod());
  224. $shipping->setAddress($address);
  225. $shippingAssignment->setShipping($shipping);
  226. $shippingAssignment->setItems($address->getAllItems());
  227. /** @var \Magento\Quote\Model\Quote\Address\Total $total */
  228. $total = $this->totalFactory->create(\Magento\Quote\Model\Quote\Address\Total::class);
  229. $this->eventManager->dispatch(
  230. 'sales_quote_address_collect_totals_before',
  231. [
  232. 'quote' => $quote,
  233. 'shipping_assignment' => $shippingAssignment,
  234. 'total' => $total
  235. ]
  236. );
  237. foreach ($this->collectorList->getCollectors($quote->getStoreId()) as $collector) {
  238. /** @var CollectorInterface $collector */
  239. $collector->collect($quote, $shippingAssignment, $total);
  240. }
  241. $this->eventManager->dispatch(
  242. 'sales_quote_address_collect_totals_after',
  243. [
  244. 'quote' => $quote,
  245. 'shipping_assignment' => $shippingAssignment,
  246. 'total' => $total
  247. ]
  248. );
  249. $address->addData($total->getData());
  250. $address->setAppliedTaxes($total->getAppliedTaxes());
  251. return $total;
  252. }
  253. }