Data.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Tax\Helper;
  7. use Magento\Framework\Pricing\PriceCurrencyInterface;
  8. use Magento\Store\Model\Store;
  9. use Magento\Customer\Model\Address;
  10. use Magento\Tax\Model\Config;
  11. use Magento\Tax\Api\OrderTaxManagementInterface;
  12. use Magento\Sales\Model\Order\Invoice;
  13. use Magento\Sales\Model\Order\Creditmemo;
  14. use Magento\Tax\Api\Data\OrderTaxDetailsItemInterface;
  15. use Magento\Sales\Model\EntityInterface;
  16. use Magento\Framework\Serialize\Serializer\Json;
  17. use Magento\Framework\App\ObjectManager;
  18. /**
  19. * Tax helper
  20. *
  21. * @SuppressWarnings(PHPMD.TooManyFields)
  22. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  23. * @api
  24. * @since 100.0.2
  25. */
  26. class Data extends \Magento\Framework\App\Helper\AbstractHelper
  27. {
  28. /**
  29. * Default tax class for customers
  30. */
  31. const CONFIG_DEFAULT_CUSTOMER_TAX_CLASS = 'tax/classes/default_customer_tax_class';
  32. /**
  33. * Default tax class for products
  34. */
  35. const CONFIG_DEFAULT_PRODUCT_TAX_CLASS = 'tax/classes/default_product_tax_class';
  36. /**
  37. * Tax configuration object
  38. *
  39. * @var Config
  40. */
  41. protected $_config;
  42. /**
  43. * Postcode cut to this length when creating search templates
  44. *
  45. * @var integer
  46. */
  47. protected $_postCodeSubStringLength = 10;
  48. /**
  49. * Json Helper
  50. *
  51. * @var \Magento\Framework\Json\Helper\Data
  52. */
  53. protected $jsonHelper;
  54. /**
  55. * @var \Magento\Store\Model\StoreManagerInterface
  56. */
  57. protected $_storeManager;
  58. /**
  59. * @var \Magento\Framework\Locale\FormatInterface
  60. */
  61. protected $_localeFormat;
  62. /**
  63. * @var \Magento\Tax\Model\ResourceModel\Sales\Order\Tax\CollectionFactory
  64. */
  65. protected $_orderTaxCollectionFactory;
  66. /**
  67. * @var \Magento\Framework\Locale\ResolverInterface
  68. */
  69. protected $_localeResolver;
  70. /**
  71. * @var \Magento\Catalog\Helper\Data
  72. */
  73. protected $catalogHelper;
  74. /**
  75. * @var OrderTaxManagementInterface
  76. */
  77. protected $orderTaxManagement;
  78. /**
  79. * @var PriceCurrencyInterface
  80. */
  81. protected $priceCurrency;
  82. /**
  83. * @var Json
  84. */
  85. private $serializer;
  86. /**
  87. * Constructor
  88. *
  89. * @param \Magento\Framework\App\Helper\Context $context
  90. * @param \Magento\Framework\Json\Helper\Data $jsonHelper
  91. * @param Config $taxConfig
  92. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  93. * @param \Magento\Framework\Locale\FormatInterface $localeFormat
  94. * @param \Magento\Tax\Model\ResourceModel\Sales\Order\Tax\CollectionFactory $orderTaxCollectionFactory
  95. * @param \Magento\Framework\Locale\ResolverInterface $localeResolver
  96. * @param \Magento\Catalog\Helper\Data $catalogHelper
  97. * @param OrderTaxManagementInterface $orderTaxManagement
  98. * @param PriceCurrencyInterface $priceCurrency
  99. * @param Json $serializer
  100. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  101. */
  102. public function __construct(
  103. \Magento\Framework\App\Helper\Context $context,
  104. \Magento\Framework\Json\Helper\Data $jsonHelper,
  105. Config $taxConfig,
  106. \Magento\Store\Model\StoreManagerInterface $storeManager,
  107. \Magento\Framework\Locale\FormatInterface $localeFormat,
  108. \Magento\Tax\Model\ResourceModel\Sales\Order\Tax\CollectionFactory $orderTaxCollectionFactory,
  109. \Magento\Framework\Locale\ResolverInterface $localeResolver,
  110. \Magento\Catalog\Helper\Data $catalogHelper,
  111. OrderTaxManagementInterface $orderTaxManagement,
  112. PriceCurrencyInterface $priceCurrency,
  113. Json $serializer = null
  114. ) {
  115. parent::__construct($context);
  116. $this->priceCurrency = $priceCurrency;
  117. $this->_config = $taxConfig;
  118. $this->jsonHelper = $jsonHelper;
  119. $this->_storeManager = $storeManager;
  120. $this->_localeFormat = $localeFormat;
  121. $this->_orderTaxCollectionFactory = $orderTaxCollectionFactory;
  122. $this->_localeResolver = $localeResolver;
  123. $this->catalogHelper = $catalogHelper;
  124. $this->orderTaxManagement = $orderTaxManagement;
  125. $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
  126. }
  127. /**
  128. * Return max postcode length to create search templates
  129. *
  130. * @return int $len
  131. */
  132. public function getPostCodeSubStringLength()
  133. {
  134. $len = (int) $this->_postCodeSubStringLength;
  135. if ($len <= 0) {
  136. $len = 10;
  137. }
  138. return $len;
  139. }
  140. /**
  141. * Get tax configuration object
  142. *
  143. * @return Config
  144. */
  145. public function getConfig()
  146. {
  147. return $this->_config;
  148. }
  149. /**
  150. * Check if product prices inputed include tax
  151. *
  152. * @param null|int|string|Store $store
  153. * @return bool
  154. */
  155. public function priceIncludesTax($store = null)
  156. {
  157. return $this->_config->priceIncludesTax($store) || $this->_config->getNeedUseShippingExcludeTax();
  158. }
  159. /**
  160. * Check what taxes should be applied after discount
  161. *
  162. * @param null|int|string|Store $store
  163. * @return bool
  164. */
  165. public function applyTaxAfterDiscount($store = null)
  166. {
  167. return $this->_config->applyTaxAfterDiscount($store);
  168. }
  169. /**
  170. * Get product price display type
  171. * 1 - Excluding tax
  172. * 2 - Including tax
  173. * 3 - Both
  174. *
  175. * @param null|int|string|Store $store
  176. * @return int
  177. */
  178. public function getPriceDisplayType($store = null)
  179. {
  180. return $this->_config->getPriceDisplayType($store);
  181. }
  182. /**
  183. * Check if necessary do product price conversion
  184. * If it necessary will be returned conversion type (minus or plus)
  185. *
  186. * @param null|int|string|Store $store
  187. * @return bool
  188. */
  189. public function needPriceConversion($store = null)
  190. {
  191. return $this->_config->needPriceConversion($store);
  192. }
  193. /**
  194. * Check if need display full tax summary information in totals block
  195. *
  196. * @param null|int|string|Store $store
  197. * @return bool
  198. */
  199. public function displayFullSummary($store = null)
  200. {
  201. return $this->_config->displayCartFullSummary($store);
  202. }
  203. /**
  204. * Check if need display zero tax in subtotal
  205. *
  206. * @param null|int|string|Store $store
  207. * @return bool
  208. */
  209. public function displayZeroTax($store = null)
  210. {
  211. return $this->_config->displayCartZeroTax($store);
  212. }
  213. /**
  214. * Check if need display cart prices included tax
  215. *
  216. * @param null|int|string|Store $store
  217. * @return bool
  218. */
  219. public function displayCartPriceInclTax($store = null)
  220. {
  221. return $this->_config->displayCartPricesInclTax($store);
  222. }
  223. /**
  224. * Check if need display cart prices excluding price
  225. *
  226. * @param null|int|string|Store $store
  227. * @return bool
  228. */
  229. public function displayCartPriceExclTax($store = null)
  230. {
  231. return $this->_config->displayCartPricesExclTax($store);
  232. }
  233. /**
  234. * Check if need display cart prices excluding and including tax
  235. *
  236. * @param null|int|string|Store $store
  237. * @return bool
  238. */
  239. public function displayCartBothPrices($store = null)
  240. {
  241. return $this->_config->displayCartPricesBoth($store);
  242. }
  243. /**
  244. * Check if need display order prices included tax
  245. *
  246. * @param null|int|string|Store $store
  247. * @return bool
  248. */
  249. public function displaySalesPriceInclTax($store = null)
  250. {
  251. return $this->_config->displaySalesPricesInclTax($store);
  252. }
  253. /**
  254. * Check if need display order prices excluding price
  255. *
  256. * @param null|int|string|Store $store
  257. * @return bool
  258. */
  259. public function displaySalesPriceExclTax($store = null)
  260. {
  261. return $this->_config->displaySalesPricesExclTax($store);
  262. }
  263. /**
  264. * Check if need display order prices excluding and including tax
  265. *
  266. * @param null|int|string|Store $store
  267. * @return bool
  268. */
  269. public function displaySalesBothPrices($store = null)
  270. {
  271. return $this->_config->displaySalesPricesBoth($store);
  272. }
  273. /**
  274. * Check if we need display price include and exclude tax for order/invoice subtotal
  275. *
  276. * @param null|int|string|Store $store
  277. * @return bool
  278. */
  279. public function displaySalesSubtotalBoth($store = null)
  280. {
  281. return $this->_config->displaySalesSubtotalBoth($store);
  282. }
  283. /**
  284. * Check if we need display price include tax for order/invoice subtotal
  285. *
  286. * @param null|int|string|Store $store
  287. * @return bool
  288. */
  289. public function displaySalesSubtotalInclTax($store = null)
  290. {
  291. return $this->_config->displaySalesSubtotalInclTax($store);
  292. }
  293. /**
  294. * Check if we need display price exclude tax for order/invoice subtotal
  295. *
  296. * @param null|int|string|Store $store
  297. * @return bool
  298. */
  299. public function displaySalesSubtotalExclTax($store = null)
  300. {
  301. return $this->_config->displaySalesSubtotalExclTax($store);
  302. }
  303. /**
  304. * Get prices javascript format json
  305. *
  306. * @param null|int|string|Store $store
  307. * @return string
  308. */
  309. public function getPriceFormat($store = null)
  310. {
  311. $this->_localeResolver->emulate($store);
  312. $priceFormat = $this->_localeFormat->getPriceFormat();
  313. $this->_localeResolver->revert();
  314. if ($store) {
  315. $priceFormat['pattern'] = $this->_storeManager->getStore($store)->getCurrentCurrency()->getOutputFormat();
  316. }
  317. return $this->jsonHelper->jsonEncode($priceFormat);
  318. }
  319. /**
  320. * Check if we have display in catalog prices including tax
  321. *
  322. * @return bool
  323. */
  324. public function displayPriceIncludingTax()
  325. {
  326. return $this->getPriceDisplayType() == Config::DISPLAY_TYPE_INCLUDING_TAX;
  327. }
  328. /**
  329. * Check if we have display in catalog prices excluding tax
  330. *
  331. * @return bool
  332. */
  333. public function displayPriceExcludingTax()
  334. {
  335. return $this->getPriceDisplayType() == Config::DISPLAY_TYPE_EXCLUDING_TAX;
  336. }
  337. /**
  338. * Check if we have display in catalog prices including and excluding tax
  339. *
  340. * @param null|int|string|Store $store
  341. * @return bool
  342. */
  343. public function displayBothPrices($store = null)
  344. {
  345. return $this->getPriceDisplayType($store) == Config::DISPLAY_TYPE_BOTH;
  346. }
  347. /**
  348. * Check if shipping prices include tax
  349. *
  350. * @param null|string|bool|int|Store $store
  351. * @return bool
  352. */
  353. public function shippingPriceIncludesTax($store = null)
  354. {
  355. return $this->_config->shippingPriceIncludesTax($store);
  356. }
  357. /**
  358. * Get shipping price display type
  359. *
  360. * @param null|string|bool|int|Store $store
  361. * @return int
  362. */
  363. public function getShippingPriceDisplayType($store = null)
  364. {
  365. return $this->_config->getShippingPriceDisplayType($store);
  366. }
  367. /**
  368. * Returns whether the shipping price should display with taxes included
  369. *
  370. * @return bool
  371. */
  372. public function displayShippingPriceIncludingTax()
  373. {
  374. return $this->getShippingPriceDisplayType() == Config::DISPLAY_TYPE_INCLUDING_TAX;
  375. }
  376. /**
  377. * Returns whether the shipping price should display without taxes
  378. *
  379. * @return bool
  380. */
  381. public function displayShippingPriceExcludingTax()
  382. {
  383. return $this->getShippingPriceDisplayType() == Config::DISPLAY_TYPE_EXCLUDING_TAX;
  384. }
  385. /**
  386. * Returns whether the shipping price should display both with and without taxes
  387. *
  388. * @return bool
  389. */
  390. public function displayShippingBothPrices()
  391. {
  392. return $this->getShippingPriceDisplayType() == Config::DISPLAY_TYPE_BOTH;
  393. }
  394. /**
  395. * Get tax class id specified for shipping tax estimation
  396. *
  397. * @param null|string|bool|int|Store $store
  398. * @return int
  399. */
  400. public function getShippingTaxClass($store)
  401. {
  402. return $this->_config->getShippingTaxClass($store);
  403. }
  404. /**
  405. * Get shipping price
  406. *
  407. * @param float $price
  408. * @param bool|null $includingTax
  409. * @param Address|null $shippingAddress
  410. * @param int|null $ctc
  411. * @param null|string|bool|int|Store $store
  412. * @return float
  413. */
  414. public function getShippingPrice($price, $includingTax = null, $shippingAddress = null, $ctc = null, $store = null)
  415. {
  416. $pseudoProduct = new \Magento\Framework\DataObject();
  417. $pseudoProduct->setTaxClassId($this->getShippingTaxClass($store));
  418. $billingAddress = false;
  419. if ($shippingAddress && $shippingAddress->getQuote() && $shippingAddress->getQuote()->getBillingAddress()) {
  420. $billingAddress = $shippingAddress->getQuote()->getBillingAddress();
  421. }
  422. $price = $this->catalogHelper->getTaxPrice(
  423. $pseudoProduct,
  424. $price,
  425. $includingTax,
  426. $shippingAddress,
  427. $billingAddress,
  428. $ctc,
  429. $store,
  430. $this->shippingPriceIncludesTax($store)
  431. );
  432. return $price;
  433. }
  434. /**
  435. * Get configuration setting "Apply Discount On Prices Including Tax" value
  436. *
  437. * @param null|string|bool|int|Store $store
  438. * @return bool
  439. */
  440. public function discountTax($store = null)
  441. {
  442. return $this->_config->discountTax($store);
  443. }
  444. /**
  445. * Get value of "Apply Tax On" custom/original price configuration settings
  446. *
  447. * @param null|string|bool|int|Store $store
  448. * @return string|null
  449. */
  450. public function getTaxBasedOn($store = null)
  451. {
  452. return $this->scopeConfig->getValue(
  453. Config::CONFIG_XML_PATH_BASED_ON,
  454. \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
  455. $store
  456. );
  457. }
  458. /**
  459. * Check if tax can be applied to custom price
  460. *
  461. * @param null|string|bool|int|Store $store
  462. * @return bool
  463. */
  464. public function applyTaxOnCustomPrice($store = null)
  465. {
  466. return (int) $this->scopeConfig->getValue(
  467. Config::CONFIG_XML_PATH_APPLY_ON,
  468. \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
  469. $store
  470. ) == 0;
  471. }
  472. /**
  473. * Check if tax should be applied just to original price
  474. *
  475. * @param null|string|bool|int|Store $store
  476. * @return bool
  477. */
  478. public function applyTaxOnOriginalPrice($store = null)
  479. {
  480. return (int) $this->scopeConfig->getValue(
  481. Config::CONFIG_XML_PATH_APPLY_ON,
  482. \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
  483. $store
  484. ) == 1;
  485. }
  486. /**
  487. * Get taxes/discounts calculation sequence
  488. *
  489. * This sequence depends on "Catalog price include tax", "Apply Tax After Discount"
  490. * and "Apply Discount On Prices Including Tax" configuration options.
  491. *
  492. * @param null|int|string|Store $store
  493. * @return string
  494. */
  495. public function getCalculationSequence($store = null)
  496. {
  497. return $this->_config->getCalculationSequence($store);
  498. }
  499. /**
  500. * Get tax calculation algorithm code
  501. *
  502. * @param null|string|bool|int|Store $store
  503. * @return string
  504. */
  505. public function getCalculationAlgorithm($store = null)
  506. {
  507. return $this->_config->getAlgorithm($store);
  508. }
  509. /**
  510. * Get calculated taxes for each tax class
  511. *
  512. * This method returns array with format:
  513. * array(
  514. * $index => array(
  515. * 'tax_amount' => $taxAmount,
  516. * 'base_tax_amount' => $baseTaxAmount,
  517. * 'title' => $title,
  518. * 'percent' => $percent
  519. * )
  520. * )
  521. *
  522. * @param \Magento\Sales\Model\Order|\Magento\Sales\Model\Order\Invoice
  523. * |\Magento\Sales\Model\Order\Creditmemo $source
  524. * @return array
  525. */
  526. public function getCalculatedTaxes($source)
  527. {
  528. $taxClassAmount = [];
  529. if (empty($source)) {
  530. return $taxClassAmount;
  531. }
  532. $current = $source;
  533. if ($source instanceof Invoice || $source instanceof Creditmemo) {
  534. $source = $current->getOrder();
  535. }
  536. if ($current == $source) {
  537. $taxClassAmount = $this->calculateTaxForOrder($current);
  538. } else {
  539. $taxClassAmount = $this->calculateTaxForItems($source, $current);
  540. }
  541. foreach ($taxClassAmount as $key => $tax) {
  542. $taxClassAmount[$key]['tax_amount'] = $this->priceCurrency->round($tax['tax_amount']);
  543. $taxClassAmount[$key]['base_tax_amount'] = $this->priceCurrency->round($tax['base_tax_amount']);
  544. }
  545. return array_values($taxClassAmount);
  546. }
  547. /**
  548. * Accumulates the pre-calculated taxes for each tax class
  549. *
  550. * This method accepts and returns the 'taxClassAmount' array with format:
  551. * array(
  552. * $index => array(
  553. * 'tax_amount' => $taxAmount,
  554. * 'base_tax_amount' => $baseTaxAmount,
  555. * 'title' => $title,
  556. * 'percent' => $percent
  557. * )
  558. * )
  559. *
  560. * @param array $taxClassAmount
  561. * @param OrderTaxDetailsItemInterface $itemTaxDetail
  562. * @param float $ratio
  563. * @return array
  564. */
  565. private function _aggregateTaxes($taxClassAmount, OrderTaxDetailsItemInterface $itemTaxDetail, $ratio)
  566. {
  567. $itemAppliedTaxes = $itemTaxDetail->getAppliedTaxes();
  568. foreach ($itemAppliedTaxes as $itemAppliedTax) {
  569. $taxAmount = $itemAppliedTax->getAmount() * $ratio;
  570. $baseTaxAmount = $itemAppliedTax->getBaseAmount() * $ratio;
  571. if (0 == $taxAmount && 0 == $baseTaxAmount) {
  572. continue;
  573. }
  574. $taxCode = $itemAppliedTax->getCode();
  575. if (!isset($taxClassAmount[$taxCode])) {
  576. $taxClassAmount[$taxCode]['title'] = $itemAppliedTax->getTitle();
  577. $taxClassAmount[$taxCode]['percent'] = $itemAppliedTax->getPercent();
  578. $taxClassAmount[$taxCode]['tax_amount'] = $taxAmount;
  579. $taxClassAmount[$taxCode]['base_tax_amount'] = $baseTaxAmount;
  580. } else {
  581. $taxClassAmount[$taxCode]['tax_amount'] += $taxAmount;
  582. $taxClassAmount[$taxCode]['base_tax_amount'] += $baseTaxAmount;
  583. }
  584. }
  585. return $taxClassAmount;
  586. }
  587. /**
  588. * Returns the array of tax rates for the order
  589. *
  590. * @param \Magento\Sales\Model\Order $order
  591. * @return array
  592. */
  593. protected function _getTaxRateSubtotals($order)
  594. {
  595. return $this->_orderTaxCollectionFactory->create()->loadByOrder($order)->toArray();
  596. }
  597. /**
  598. * Retrieve default customer tax class from config
  599. *
  600. * @return string|null
  601. */
  602. public function getDefaultCustomerTaxClass()
  603. {
  604. return $this->scopeConfig->getValue(
  605. self::CONFIG_DEFAULT_CUSTOMER_TAX_CLASS,
  606. \Magento\Store\Model\ScopeInterface::SCOPE_STORE
  607. );
  608. }
  609. /**
  610. * Retrieve default product tax class from config
  611. *
  612. * @return string|null
  613. */
  614. public function getDefaultProductTaxClass()
  615. {
  616. return $this->scopeConfig->getValue(
  617. self::CONFIG_DEFAULT_PRODUCT_TAX_CLASS,
  618. \Magento\Store\Model\ScopeInterface::SCOPE_STORE
  619. );
  620. }
  621. /**
  622. * Return whether cross border trade is enabled or not
  623. *
  624. * @param null|int|string|Store $store
  625. * @return bool
  626. */
  627. public function isCrossBorderTradeEnabled($store = null)
  628. {
  629. return (bool) $this->_config->crossBorderTradeEnabled($store);
  630. }
  631. /**
  632. * @param EntityInterface $current
  633. * @return array
  634. */
  635. protected function calculateTaxForOrder(EntityInterface $current)
  636. {
  637. $taxClassAmount = [];
  638. $orderTaxDetails = $this->orderTaxManagement->getOrderTaxDetails($current->getId());
  639. $appliedTaxes = $orderTaxDetails->getAppliedTaxes();
  640. foreach ($appliedTaxes as $appliedTax) {
  641. $taxCode = $appliedTax->getCode();
  642. $taxClassAmount[$taxCode]['tax_amount'] = $appliedTax->getAmount();
  643. $taxClassAmount[$taxCode]['base_tax_amount'] = $appliedTax->getBaseAmount();
  644. $taxClassAmount[$taxCode]['title'] = $appliedTax->getTitle();
  645. $taxClassAmount[$taxCode]['percent'] = $appliedTax->getPercent();
  646. }
  647. return $taxClassAmount;
  648. }
  649. /**
  650. * @param EntityInterface $order
  651. * @param EntityInterface $salesItem
  652. * @return array
  653. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  654. */
  655. protected function calculateTaxForItems(EntityInterface $order, EntityInterface $salesItem)
  656. {
  657. $taxClassAmount = [];
  658. $orderTaxDetails = $this->orderTaxManagement->getOrderTaxDetails($order->getId());
  659. // Apply any taxes for the items
  660. /** @var $item \Magento\Sales\Model\Order\Invoice\Item|\Magento\Sales\Model\Order\Creditmemo\Item */
  661. foreach ($salesItem->getItems() as $item) {
  662. $orderItem = $item->getOrderItem();
  663. $orderItemId = $orderItem->getId();
  664. $orderItemTax = $orderItem->getTaxAmount();
  665. $itemTax = $item->getTaxAmount();
  666. if (!$itemTax || !(float)$orderItemTax) {
  667. continue;
  668. }
  669. //An invoiced item or credit memo item can have a different qty than its order item qty
  670. $itemRatio = $itemTax / $orderItemTax;
  671. $itemTaxDetails = $orderTaxDetails->getItems();
  672. foreach ($itemTaxDetails as $itemTaxDetail) {
  673. //Aggregate taxable items associated with an item
  674. if ($itemTaxDetail->getItemId() == $orderItemId) {
  675. $taxClassAmount = $this->_aggregateTaxes($taxClassAmount, $itemTaxDetail, $itemRatio);
  676. } elseif ($itemTaxDetail->getAssociatedItemId() == $orderItemId) {
  677. $taxableItemType = $itemTaxDetail->getType();
  678. $ratio = $itemRatio;
  679. if ($item->getTaxRatio()) {
  680. $taxRatio = $this->serializer->unserialize($item->getTaxRatio());
  681. if (isset($taxRatio[$taxableItemType])) {
  682. $ratio = $taxRatio[$taxableItemType];
  683. }
  684. }
  685. $taxClassAmount = $this->_aggregateTaxes($taxClassAmount, $itemTaxDetail, $ratio);
  686. }
  687. }
  688. }
  689. // Apply any taxes for shipping
  690. $shippingTaxAmount = $salesItem->getShippingTaxAmount();
  691. $originalShippingTaxAmount = $order->getShippingTaxAmount();
  692. if ($shippingTaxAmount && $originalShippingTaxAmount &&
  693. $shippingTaxAmount != 0 && (float)$originalShippingTaxAmount
  694. ) {
  695. //An invoice or credit memo can have a different qty than its order
  696. $shippingRatio = $shippingTaxAmount / $originalShippingTaxAmount;
  697. $itemTaxDetails = $orderTaxDetails->getItems();
  698. foreach ($itemTaxDetails as $itemTaxDetail) {
  699. //Aggregate taxable items associated with shipping
  700. if ($itemTaxDetail->getType() == \Magento\Quote\Model\Quote\Address::TYPE_SHIPPING) {
  701. $taxClassAmount = $this->_aggregateTaxes($taxClassAmount, $itemTaxDetail, $shippingRatio);
  702. }
  703. }
  704. }
  705. return $taxClassAmount;
  706. }
  707. /**
  708. * Check whether display price is affected by different tax rates
  709. *
  710. * @param null|int|string|Store $store
  711. * @return bool
  712. */
  713. public function isCatalogPriceDisplayAffectedByTax($store = null)
  714. {
  715. if ($this->displayBothPrices($store)) {
  716. return true;
  717. }
  718. $priceInclTax = $this->priceIncludesTax($store);
  719. if ($priceInclTax) {
  720. return ($this->isCrossBorderTradeEnabled($store) xor $this->displayPriceIncludingTax());
  721. } else {
  722. return $this->displayPriceIncludingTax();
  723. }
  724. }
  725. }