Totals.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Sales\Block\Order;
  7. use Magento\Sales\Model\Order;
  8. /**
  9. * @api
  10. * @since 100.0.2
  11. */
  12. class Totals extends \Magento\Framework\View\Element\Template
  13. {
  14. /**
  15. * Associated array of totals
  16. * array(
  17. * $totalCode => $totalObject
  18. * )
  19. *
  20. * @var array
  21. */
  22. protected $_totals;
  23. /**
  24. * @var Order|null
  25. */
  26. protected $_order = null;
  27. /**
  28. * Core registry
  29. *
  30. * @var \Magento\Framework\Registry
  31. */
  32. protected $_coreRegistry = null;
  33. /**
  34. * @param \Magento\Framework\View\Element\Template\Context $context
  35. * @param \Magento\Framework\Registry $registry
  36. * @param array $data
  37. */
  38. public function __construct(
  39. \Magento\Framework\View\Element\Template\Context $context,
  40. \Magento\Framework\Registry $registry,
  41. array $data = []
  42. ) {
  43. $this->_coreRegistry = $registry;
  44. parent::__construct($context, $data);
  45. }
  46. /**
  47. * Initialize self totals and children blocks totals before html building
  48. *
  49. * @return $this
  50. */
  51. protected function _beforeToHtml()
  52. {
  53. $this->_initTotals();
  54. foreach ($this->getLayout()->getChildBlocks($this->getNameInLayout()) as $child) {
  55. if (method_exists($child, 'initTotals') && is_callable([$child, 'initTotals'])) {
  56. $child->initTotals();
  57. }
  58. }
  59. return parent::_beforeToHtml();
  60. }
  61. /**
  62. * Get order object
  63. *
  64. * @return Order
  65. */
  66. public function getOrder()
  67. {
  68. if ($this->_order === null) {
  69. if ($this->hasData('order')) {
  70. $this->_order = $this->_getData('order');
  71. } elseif ($this->_coreRegistry->registry('current_order')) {
  72. $this->_order = $this->_coreRegistry->registry('current_order');
  73. } elseif ($this->getParentBlock()->getOrder()) {
  74. $this->_order = $this->getParentBlock()->getOrder();
  75. }
  76. }
  77. return $this->_order;
  78. }
  79. /**
  80. * @param Order $order
  81. * @return $this
  82. */
  83. public function setOrder($order)
  84. {
  85. $this->_order = $order;
  86. return $this;
  87. }
  88. /**
  89. * Get totals source object
  90. *
  91. * @return Order
  92. */
  93. public function getSource()
  94. {
  95. return $this->getOrder();
  96. }
  97. /**
  98. * Initialize order totals array
  99. *
  100. * @return $this
  101. */
  102. protected function _initTotals()
  103. {
  104. $source = $this->getSource();
  105. $this->_totals = [];
  106. $this->_totals['subtotal'] = new \Magento\Framework\DataObject(
  107. ['code' => 'subtotal', 'value' => $source->getSubtotal(), 'label' => __('Subtotal')]
  108. );
  109. /**
  110. * Add shipping
  111. */
  112. if (!$source->getIsVirtual() && ((double)$source->getShippingAmount() || $source->getShippingDescription())) {
  113. $this->_totals['shipping'] = new \Magento\Framework\DataObject(
  114. [
  115. 'code' => 'shipping',
  116. 'field' => 'shipping_amount',
  117. 'value' => $this->getSource()->getShippingAmount(),
  118. 'label' => __('Shipping & Handling'),
  119. ]
  120. );
  121. }
  122. /**
  123. * Add discount
  124. */
  125. if ((double)$this->getSource()->getDiscountAmount() != 0) {
  126. if ($this->getSource()->getDiscountDescription()) {
  127. $discountLabel = __('Discount (%1)', $source->getDiscountDescription());
  128. } else {
  129. $discountLabel = __('Discount');
  130. }
  131. $this->_totals['discount'] = new \Magento\Framework\DataObject(
  132. [
  133. 'code' => 'discount',
  134. 'field' => 'discount_amount',
  135. 'value' => $source->getDiscountAmount(),
  136. 'label' => $discountLabel,
  137. ]
  138. );
  139. }
  140. $this->_totals['grand_total'] = new \Magento\Framework\DataObject(
  141. [
  142. 'code' => 'grand_total',
  143. 'field' => 'grand_total',
  144. 'strong' => true,
  145. 'value' => $source->getGrandTotal(),
  146. 'label' => __('Grand Total'),
  147. ]
  148. );
  149. /**
  150. * Base grandtotal
  151. */
  152. if ($this->getOrder()->isCurrencyDifferent()) {
  153. $this->_totals['base_grandtotal'] = new \Magento\Framework\DataObject(
  154. [
  155. 'code' => 'base_grandtotal',
  156. 'value' => $this->getOrder()->formatBasePrice($source->getBaseGrandTotal()),
  157. 'label' => __('Grand Total to be Charged'),
  158. 'is_formated' => true,
  159. ]
  160. );
  161. }
  162. return $this;
  163. }
  164. /**
  165. * Add new total to totals array after specific total or before last total by default
  166. *
  167. * @param \Magento\Framework\DataObject $total
  168. * @param null|string $after
  169. * @return $this
  170. */
  171. public function addTotal(\Magento\Framework\DataObject $total, $after = null)
  172. {
  173. if ($after !== null && $after != 'last' && $after != 'first') {
  174. $totals = [];
  175. $added = false;
  176. foreach ($this->_totals as $code => $item) {
  177. $totals[$code] = $item;
  178. if ($code == $after) {
  179. $added = true;
  180. $totals[$total->getCode()] = $total;
  181. }
  182. }
  183. if (!$added) {
  184. $last = array_pop($totals);
  185. $totals[$total->getCode()] = $total;
  186. $totals[$last->getCode()] = $last;
  187. }
  188. $this->_totals = $totals;
  189. } elseif ($after == 'last') {
  190. $this->_totals[$total->getCode()] = $total;
  191. } elseif ($after == 'first') {
  192. $totals = [$total->getCode() => $total];
  193. $this->_totals = array_merge($totals, $this->_totals);
  194. } else {
  195. $last = array_pop($this->_totals);
  196. $this->_totals[$total->getCode()] = $total;
  197. $this->_totals[$last->getCode()] = $last;
  198. }
  199. return $this;
  200. }
  201. /**
  202. * Add new total to totals array before specific total or after first total by default
  203. *
  204. * @param \Magento\Framework\DataObject $total
  205. * @param null|string $before
  206. * @return $this
  207. */
  208. public function addTotalBefore(\Magento\Framework\DataObject $total, $before = null)
  209. {
  210. if ($before !== null) {
  211. if (!is_array($before)) {
  212. $before = [$before];
  213. }
  214. foreach ($before as $beforeTotals) {
  215. if (isset($this->_totals[$beforeTotals])) {
  216. $totals = [];
  217. foreach ($this->_totals as $code => $item) {
  218. if ($code == $beforeTotals) {
  219. $totals[$total->getCode()] = $total;
  220. }
  221. $totals[$code] = $item;
  222. }
  223. $this->_totals = $totals;
  224. return $this;
  225. }
  226. }
  227. }
  228. $totals = [];
  229. $first = array_shift($this->_totals);
  230. $totals[$first->getCode()] = $first;
  231. $totals[$total->getCode()] = $total;
  232. foreach ($this->_totals as $code => $item) {
  233. $totals[$code] = $item;
  234. }
  235. $this->_totals = $totals;
  236. return $this;
  237. }
  238. /**
  239. * Get Total object by code
  240. *
  241. * @param string $code
  242. * @return mixed
  243. */
  244. public function getTotal($code)
  245. {
  246. if (isset($this->_totals[$code])) {
  247. return $this->_totals[$code];
  248. }
  249. return false;
  250. }
  251. /**
  252. * Delete total by specific
  253. *
  254. * @param string $code
  255. * @return $this
  256. */
  257. public function removeTotal($code)
  258. {
  259. unset($this->_totals[$code]);
  260. return $this;
  261. }
  262. /**
  263. * Apply sort orders to totals array.
  264. * Array should have next structure
  265. * array(
  266. * $totalCode => $totalSortOrder
  267. * )
  268. *
  269. *
  270. * @param array $order
  271. * @return $this
  272. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  273. */
  274. public function applySortOrder($order)
  275. {
  276. \uksort(
  277. $this->_totals,
  278. function ($code1, $code2) use ($order) {
  279. return ($order[$code1] ?? 0) <=> ($order[$code2] ?? 0);
  280. }
  281. );
  282. return $this;
  283. }
  284. /**
  285. * get totals array for visualization
  286. *
  287. * @param array|null $area
  288. * @return array
  289. */
  290. public function getTotals($area = null)
  291. {
  292. $totals = [];
  293. if ($area === null) {
  294. $totals = $this->_totals;
  295. } else {
  296. $area = (string)$area;
  297. foreach ($this->_totals as $total) {
  298. $totalArea = (string)$total->getArea();
  299. if ($totalArea == $area) {
  300. $totals[] = $total;
  301. }
  302. }
  303. }
  304. return $totals;
  305. }
  306. /**
  307. * Format total value based on order currency
  308. *
  309. * @param \Magento\Framework\DataObject $total
  310. * @return string
  311. */
  312. public function formatValue($total)
  313. {
  314. if (!$total->getIsFormated()) {
  315. return $this->getOrder()->formatPrice($total->getValue());
  316. }
  317. return $total->getValue();
  318. }
  319. }