AbstractItems.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Sales\Block\Adminhtml\Items;
  7. use Magento\Sales\Model\Order;
  8. use Magento\Sales\Model\Order\Creditmemo\Item;
  9. /**
  10. * Abstract items renderer
  11. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  12. * @api
  13. * @since 100.0.2
  14. */
  15. class AbstractItems extends \Magento\Backend\Block\Template
  16. {
  17. /**
  18. * Block alias fallback
  19. */
  20. const DEFAULT_TYPE = 'default';
  21. /**
  22. * Renderers for other column with column name key
  23. * block => the block name
  24. * template => the template file
  25. * renderer => the block object
  26. *
  27. * @var array
  28. */
  29. protected $_columnRenders = [];
  30. /**
  31. * Flag - if it is set method canEditQty will return value of it
  32. *
  33. * @var bool|null
  34. */
  35. protected $_canEditQty;
  36. /**
  37. * Core registry
  38. *
  39. * @var \Magento\Framework\Registry
  40. */
  41. protected $_coreRegistry;
  42. /**
  43. * @var \Magento\CatalogInventory\Api\StockRegistryInterface
  44. */
  45. protected $stockRegistry;
  46. /**
  47. * @var \Magento\CatalogInventory\Api\StockConfigurationInterface
  48. */
  49. protected $stockConfiguration;
  50. /**
  51. * @param \Magento\Backend\Block\Template\Context $context
  52. * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
  53. * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration
  54. * @param \Magento\Framework\Registry $registry
  55. * @param array $data
  56. */
  57. public function __construct(
  58. \Magento\Backend\Block\Template\Context $context,
  59. \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
  60. \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration,
  61. \Magento\Framework\Registry $registry,
  62. array $data = []
  63. ) {
  64. $this->stockRegistry = $stockRegistry;
  65. $this->stockConfiguration = $stockConfiguration;
  66. $this->_coreRegistry = $registry;
  67. parent::__construct($context, $data);
  68. }
  69. /**
  70. * Add column renderers
  71. *
  72. * @param array $blocks
  73. * @return $this
  74. */
  75. public function setColumnRenders(array $blocks)
  76. {
  77. foreach ($blocks as $blockName) {
  78. $block = $this->getLayout()->getBlock($blockName);
  79. if ($block->getRenderedBlock() === null) {
  80. $block->setRenderedBlock($this);
  81. }
  82. $this->_columnRenders[$blockName] = $block;
  83. }
  84. return $this;
  85. }
  86. /**
  87. * Retrieve item renderer block
  88. *
  89. * @param string $type
  90. * @return \Magento\Framework\View\Element\AbstractBlock
  91. * @throws \RuntimeException
  92. */
  93. public function getItemRenderer($type)
  94. {
  95. /** @var $renderer \Magento\Sales\Block\Adminhtml\Items\AbstractItems */
  96. $renderer = $this->getChildBlock($type) ?: $this->getChildBlock(self::DEFAULT_TYPE);
  97. if (!$renderer instanceof \Magento\Framework\View\Element\BlockInterface) {
  98. throw new \RuntimeException('Renderer for type "' . $type . '" does not exist.');
  99. }
  100. $renderer->setColumnRenders($this->getLayout()->getGroupChildNames($this->getNameInLayout(), 'column'));
  101. return $renderer;
  102. }
  103. /**
  104. * Retrieve column renderer block
  105. *
  106. * @param string $column
  107. * @param string $compositePart
  108. * @return \Magento\Framework\View\Element\AbstractBlock
  109. */
  110. public function getColumnRenderer($column, $compositePart = '')
  111. {
  112. $column = 'column_' . $column;
  113. if (isset($this->_columnRenders[$column . '_' . $compositePart])) {
  114. $column .= '_' . $compositePart;
  115. }
  116. if (!isset($this->_columnRenders[$column])) {
  117. return false;
  118. }
  119. return $this->_columnRenders[$column];
  120. }
  121. /**
  122. * Retrieve rendered item html content
  123. *
  124. * @param \Magento\Framework\DataObject $item
  125. * @return string
  126. */
  127. public function getItemHtml(\Magento\Framework\DataObject $item)
  128. {
  129. if ($item->getOrderItem()) {
  130. $type = $item->getOrderItem()->getProductType();
  131. } else {
  132. $type = $item->getProductType();
  133. }
  134. return $this->getItemRenderer($type)->setItem($item)->setCanEditQty($this->canEditQty())->toHtml();
  135. }
  136. /**
  137. * Retrieve rendered item extra info html content
  138. *
  139. * @param \Magento\Framework\DataObject $item
  140. * @return string
  141. */
  142. public function getItemExtraInfoHtml(\Magento\Framework\DataObject $item)
  143. {
  144. $extraInfoBlock = $this->getChildBlock('order_item_extra_info');
  145. if ($extraInfoBlock) {
  146. return $extraInfoBlock->setItem($item)->toHtml();
  147. }
  148. return '';
  149. }
  150. /**
  151. * Retrieve rendered column html content
  152. *
  153. * @param \Magento\Framework\DataObject $item
  154. * @param string $column the column key
  155. * @param string $field the custom item field
  156. * @return string
  157. */
  158. public function getColumnHtml(\Magento\Framework\DataObject $item, $column, $field = null)
  159. {
  160. if ($item->getOrderItem()) {
  161. $block = $this->getColumnRenderer($column, $item->getOrderItem()->getProductType());
  162. } else {
  163. $block = $this->getColumnRenderer($column, $item->getProductType());
  164. }
  165. if ($block) {
  166. $block->setItem($item);
  167. if ($field !== null) {
  168. $block->setField($field);
  169. }
  170. return $block->toHtml();
  171. }
  172. return '&nbsp;';
  173. }
  174. /**
  175. * Get credit memo
  176. *
  177. * @return mixed
  178. */
  179. public function getCreditmemo()
  180. {
  181. return $this->_coreRegistry->registry('current_creditmemo');
  182. }
  183. /**
  184. * ######################### SALES ##################################
  185. */
  186. /**
  187. * Retrieve available order
  188. *
  189. * @throws \Magento\Framework\Exception\LocalizedException
  190. * @return Order
  191. */
  192. public function getOrder()
  193. {
  194. if ($this->hasOrder()) {
  195. return $this->getData('order');
  196. }
  197. if ($this->_coreRegistry->registry('current_order')) {
  198. return $this->_coreRegistry->registry('current_order');
  199. }
  200. if ($this->_coreRegistry->registry('order')) {
  201. return $this->_coreRegistry->registry('order');
  202. }
  203. if ($this->getInvoice()) {
  204. return $this->getInvoice()->getOrder();
  205. }
  206. if ($this->getCreditmemo()) {
  207. return $this->getCreditmemo()->getOrder();
  208. }
  209. if ($this->getItem()->getOrder()) {
  210. return $this->getItem()->getOrder();
  211. }
  212. throw new \Magento\Framework\Exception\LocalizedException(__('We can\'t get the order instance right now.'));
  213. }
  214. /**
  215. * Retrieve price data object
  216. *
  217. * @return Order
  218. */
  219. public function getPriceDataObject()
  220. {
  221. $obj = $this->getData('price_data_object');
  222. if ($obj === null) {
  223. return $this->getOrder();
  224. }
  225. return $obj;
  226. }
  227. /**
  228. * Retrieve price attribute html content
  229. *
  230. * @param string $code
  231. * @param bool $strong
  232. * @param string $separator
  233. * @return string
  234. */
  235. public function displayPriceAttribute($code, $strong = false, $separator = '<br />')
  236. {
  237. if ($code == 'tax_amount' && $this->getOrder()->getRowTaxDisplayPrecision()) {
  238. return $this->displayRoundedPrices(
  239. $this->getPriceDataObject()->getData('base_' . $code),
  240. $this->getPriceDataObject()->getData($code),
  241. $this->getOrder()->getRowTaxDisplayPrecision(),
  242. $strong,
  243. $separator
  244. );
  245. } else {
  246. return $this->displayPrices(
  247. $this->getPriceDataObject()->getData('base_' . $code),
  248. $this->getPriceDataObject()->getData($code),
  249. $strong,
  250. $separator
  251. );
  252. }
  253. }
  254. /**
  255. * Retrieve price formatted html content
  256. *
  257. * @param float $basePrice
  258. * @param float $price
  259. * @param bool $strong
  260. * @param string $separator
  261. * @return string
  262. */
  263. public function displayPrices($basePrice, $price, $strong = false, $separator = '<br />')
  264. {
  265. return $this->displayRoundedPrices($basePrice, $price, 2, $strong, $separator);
  266. }
  267. /**
  268. * Display base and regular prices with specified rounding precision
  269. *
  270. * @param float $basePrice
  271. * @param float $price
  272. * @param int $precision
  273. * @param bool $strong
  274. * @param string $separator
  275. * @return string
  276. */
  277. public function displayRoundedPrices($basePrice, $price, $precision = 2, $strong = false, $separator = '<br />')
  278. {
  279. if ($this->getOrder()->isCurrencyDifferent()) {
  280. $res = '';
  281. $res .= $this->getOrder()->formatBasePricePrecision($basePrice, $precision);
  282. $res .= $separator;
  283. $res .= $this->getOrder()->formatPricePrecision($price, $precision, true);
  284. } else {
  285. $res = $this->getOrder()->formatPricePrecision($price, $precision);
  286. if ($strong) {
  287. $res = '<strong>' . $res . '</strong>';
  288. }
  289. }
  290. return $res;
  291. }
  292. /**
  293. * Retrieve tax calculation html content
  294. *
  295. * @param \Magento\Framework\DataObject $item
  296. * @return string
  297. */
  298. public function displayTaxCalculation(\Magento\Framework\DataObject $item)
  299. {
  300. if ($item->getTaxPercent() && $item->getTaxString() == '') {
  301. $percents = [$item->getTaxPercent()];
  302. } elseif ($item->getTaxString()) {
  303. $percents = explode(\Magento\Tax\Model\Config::CALCULATION_STRING_SEPARATOR, $item->getTaxString());
  304. } else {
  305. return '0%';
  306. }
  307. foreach ($percents as &$percent) {
  308. $percent = sprintf('%.2f%%', $percent);
  309. }
  310. return implode(' + ', $percents);
  311. }
  312. /**
  313. * Retrieve tax with percent html content
  314. *
  315. * @param \Magento\Framework\DataObject $item
  316. * @return string
  317. */
  318. public function displayTaxPercent(\Magento\Framework\DataObject $item)
  319. {
  320. if ($item->getTaxPercent()) {
  321. return sprintf('%s%%', $item->getTaxPercent() + 0);
  322. } else {
  323. return '0%';
  324. }
  325. }
  326. /**
  327. * INVOICES
  328. */
  329. /**
  330. * Check shipment availability for current invoice
  331. *
  332. * @return bool
  333. */
  334. public function canCreateShipment()
  335. {
  336. foreach ($this->getInvoice()->getAllItems() as $item) {
  337. if ($item->getOrderItem()->getQtyToShip()) {
  338. return true;
  339. }
  340. }
  341. return false;
  342. }
  343. /**
  344. * Setter for flag _canEditQty
  345. *
  346. * @param bool $value
  347. * @return $this
  348. * @see self::_canEditQty
  349. * @see self::canEditQty
  350. */
  351. public function setCanEditQty($value)
  352. {
  353. $this->_canEditQty = $value;
  354. return $this;
  355. }
  356. /**
  357. * Check availability to edit quantity of item
  358. *
  359. * @return bool
  360. */
  361. public function canEditQty()
  362. {
  363. /**
  364. * If parent block has set
  365. */
  366. if ($this->_canEditQty !== null) {
  367. return $this->_canEditQty;
  368. }
  369. /**
  370. * Disable editing of quantity of item if creating of shipment forced
  371. * and ship partially disabled for order
  372. */
  373. if ($this->getOrder()->getForcedShipmentWithInvoice()
  374. && ($this->canShipPartially($this->getOrder()) || $this->canShipPartiallyItem($this->getOrder()))
  375. ) {
  376. return false;
  377. }
  378. if ($this->getOrder()->getPayment()->canCapture()) {
  379. return $this->getOrder()->getPayment()->canCapturePartial();
  380. }
  381. return true;
  382. }
  383. /**
  384. * Check capture availability
  385. *
  386. * @return bool
  387. */
  388. public function canCapture()
  389. {
  390. if ($this->_authorization->isAllowed('Magento_Sales::capture')) {
  391. return $this->getInvoice()->canCapture();
  392. }
  393. return false;
  394. }
  395. /**
  396. * Retrieve formatted price
  397. *
  398. * @param float $price
  399. * @return string
  400. */
  401. public function formatPrice($price)
  402. {
  403. return $this->getOrder()->formatPrice($price);
  404. }
  405. /**
  406. * Retrieve source
  407. *
  408. * @return \Magento\Sales\Model\Order\Invoice
  409. */
  410. public function getSource()
  411. {
  412. return $this->getInvoice();
  413. }
  414. /**
  415. * Retrieve invoice model instance
  416. *
  417. * @return \Magento\Sales\Model\Order\Invoice
  418. */
  419. public function getInvoice()
  420. {
  421. return $this->_coreRegistry->registry('current_invoice');
  422. }
  423. /**
  424. * @param \Magento\Store\Model\Store $store
  425. * @return bool
  426. */
  427. public function canReturnToStock($store = null)
  428. {
  429. return $this->stockConfiguration->canSubtractQty($store);
  430. }
  431. /**
  432. * Whether to show 'Return to stock' checkbox for item
  433. *
  434. * @param Item $item
  435. * @return bool
  436. */
  437. public function canReturnItemToStock($item = null)
  438. {
  439. if (null !== $item) {
  440. if (!$item->hasCanReturnToStock()) {
  441. $stockItem = $this->stockRegistry->getStockItem(
  442. $item->getOrderItem()->getProductId(),
  443. $item->getOrderItem()->getStore()->getWebsiteId()
  444. );
  445. $item->setCanReturnToStock($stockItem->getManageStock());
  446. }
  447. return $item->getCanReturnToStock();
  448. }
  449. return $this->canReturnToStock();
  450. }
  451. /**
  452. * Whether to show 'Return to stock' column for item parent
  453. *
  454. * @param Item $item
  455. * @return bool
  456. */
  457. public function canParentReturnToStock($item = null)
  458. {
  459. if ($item !== null) {
  460. if ($item->getCreditmemo()->getOrder()->hasCanReturnToStock()) {
  461. return $item->getCreditmemo()->getOrder()->getCanReturnToStock();
  462. }
  463. } elseif ($this->getOrder()->hasCanReturnToStock()) {
  464. return $this->getOrder()->getCanReturnToStock();
  465. }
  466. return $this->canReturnToStock();
  467. }
  468. /**
  469. * Return true if can ship partially
  470. *
  471. * @param Order|null $order
  472. * @return bool
  473. */
  474. public function canShipPartially($order = null)
  475. {
  476. if ($order === null || !$order instanceof Order) {
  477. $order = $this->_coreRegistry->registry('current_shipment')->getOrder();
  478. }
  479. $value = $order->getCanShipPartially();
  480. if ($value !== null && !$value) {
  481. return false;
  482. }
  483. return true;
  484. }
  485. /**
  486. * Return true if can ship items partially
  487. *
  488. * @param Order|null $order
  489. * @return bool
  490. */
  491. public function canShipPartiallyItem($order = null)
  492. {
  493. if ($order === null || !$order instanceof Order) {
  494. $order = $this->_coreRegistry->registry('current_shipment')->getOrder();
  495. }
  496. $value = $order->getCanShipPartiallyItem();
  497. if ($value !== null && !$value) {
  498. return false;
  499. }
  500. return true;
  501. }
  502. /**
  503. * Check is shipment is regular
  504. *
  505. * @return bool
  506. */
  507. public function isShipmentRegular()
  508. {
  509. if (!$this->canShipPartiallyItem() || !$this->canShipPartially()) {
  510. return false;
  511. }
  512. return true;
  513. }
  514. }