UnitBaseCalculator.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Tax\Model\Calculation;
  7. use Magento\Tax\Api\Data\QuoteDetailsItemInterface;
  8. class UnitBaseCalculator extends AbstractCalculator
  9. {
  10. /**
  11. * {@inheritdoc}
  12. */
  13. protected function roundAmount(
  14. $amount,
  15. $rate = null,
  16. $direction = null,
  17. $type = self::KEY_REGULAR_DELTA_ROUNDING,
  18. $round = true,
  19. $item = null
  20. ) {
  21. if ($item->getAssociatedItemCode()) {
  22. // Use delta rounding of the product's instead of the weee's
  23. $type = $type . $item->getAssociatedItemCode();
  24. } else {
  25. $type = $type . $item->getCode();
  26. }
  27. return $this->deltaRound($amount, $rate, $direction, $type, $round);
  28. }
  29. /**
  30. * {@inheritdoc}
  31. */
  32. protected function calculateWithTaxInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true)
  33. {
  34. $taxRateRequest = $this->getAddressRateRequest()->setProductClassId(
  35. $this->taxClassManagement->getTaxClassId($item->getTaxClassKey())
  36. );
  37. $rate = $this->calculationTool->getRate($taxRateRequest);
  38. $storeRate = $storeRate = $this->calculationTool->getStoreRate($taxRateRequest, $this->storeId);
  39. // Calculate $priceInclTax
  40. $applyTaxAfterDiscount = $this->config->applyTaxAfterDiscount($this->storeId);
  41. $priceInclTax = $this->calculationTool->round($item->getUnitPrice());
  42. if (!$this->isSameRateAsStore($rate, $storeRate)) {
  43. $priceInclTax = $this->calculatePriceInclTax($priceInclTax, $storeRate, $rate, $round);
  44. }
  45. $uniTax = $this->calculationTool->calcTaxAmount($priceInclTax, $rate, true, false);
  46. $deltaRoundingType = self::KEY_REGULAR_DELTA_ROUNDING;
  47. if ($applyTaxAfterDiscount) {
  48. $deltaRoundingType = self::KEY_TAX_BEFORE_DISCOUNT_DELTA_ROUNDING;
  49. }
  50. $uniTax = $this->roundAmount($uniTax, $rate, true, $deltaRoundingType, $round, $item);
  51. $price = $priceInclTax - $uniTax;
  52. //Handle discount
  53. $discountTaxCompensationAmount = 0;
  54. $discountAmount = $item->getDiscountAmount();
  55. if ($applyTaxAfterDiscount) {
  56. //TODO: handle originalDiscountAmount
  57. $unitDiscountAmount = $discountAmount / $quantity;
  58. $taxableAmount = max($priceInclTax - $unitDiscountAmount, 0);
  59. $unitTaxAfterDiscount = $this->calculationTool->calcTaxAmount(
  60. $taxableAmount,
  61. $rate,
  62. true,
  63. false
  64. );
  65. $unitTaxAfterDiscount = $this->roundAmount(
  66. $unitTaxAfterDiscount,
  67. $rate,
  68. true,
  69. self::KEY_REGULAR_DELTA_ROUNDING,
  70. $round,
  71. $item
  72. );
  73. // Set discount tax compensation
  74. $unitDiscountTaxCompensationAmount = $uniTax - $unitTaxAfterDiscount;
  75. $discountTaxCompensationAmount = $unitDiscountTaxCompensationAmount * $quantity;
  76. $uniTax = $unitTaxAfterDiscount;
  77. }
  78. $rowTax = $uniTax * $quantity;
  79. // Calculate applied taxes
  80. /** @var \Magento\Tax\Api\Data\AppliedTaxInterface[] $appliedTaxes */
  81. $appliedRates = $this->calculationTool->getAppliedRates($taxRateRequest);
  82. $appliedTaxes = $this->getAppliedTaxes($rowTax, $rate, $appliedRates);
  83. return $this->taxDetailsItemDataObjectFactory->create()
  84. ->setCode($item->getCode())
  85. ->setType($item->getType())
  86. ->setRowTax($rowTax)
  87. ->setPrice($price)
  88. ->setPriceInclTax($priceInclTax)
  89. ->setRowTotal($price * $quantity)
  90. ->setRowTotalInclTax($priceInclTax * $quantity)
  91. ->setDiscountTaxCompensationAmount($discountTaxCompensationAmount)
  92. ->setAssociatedItemCode($item->getAssociatedItemCode())
  93. ->setTaxPercent($rate)
  94. ->setAppliedTaxes($appliedTaxes);
  95. }
  96. /**
  97. * {@inheritdoc}
  98. */
  99. protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true)
  100. {
  101. $taxRateRequest = $this->getAddressRateRequest()->setProductClassId(
  102. $this->taxClassManagement->getTaxClassId($item->getTaxClassKey())
  103. );
  104. $rate = $this->calculationTool->getRate($taxRateRequest);
  105. $appliedRates = $this->calculationTool->getAppliedRates($taxRateRequest);
  106. $applyTaxAfterDiscount = $this->config->applyTaxAfterDiscount($this->storeId);
  107. $discountAmount = $item->getDiscountAmount();
  108. $discountTaxCompensationAmount = 0;
  109. // Calculate $price
  110. $price = $this->calculationTool->round($item->getUnitPrice());
  111. $unitTaxes = [];
  112. $unitTaxesBeforeDiscount = [];
  113. $appliedTaxes = [];
  114. //Apply each tax rate separately
  115. foreach ($appliedRates as $appliedRate) {
  116. $taxId = $appliedRate['id'];
  117. $taxRate = $appliedRate['percent'];
  118. $unitTaxPerRate = $this->calculationTool->calcTaxAmount($price, $taxRate, false, false);
  119. $deltaRoundingType = self::KEY_REGULAR_DELTA_ROUNDING;
  120. if ($applyTaxAfterDiscount) {
  121. $deltaRoundingType = self::KEY_TAX_BEFORE_DISCOUNT_DELTA_ROUNDING;
  122. }
  123. $unitTaxPerRate = $this->roundAmount($unitTaxPerRate, $taxId, false, $deltaRoundingType, $round, $item);
  124. $unitTaxAfterDiscount = $unitTaxPerRate;
  125. //Handle discount
  126. if ($applyTaxAfterDiscount) {
  127. //TODO: handle originalDiscountAmount
  128. $unitDiscountAmount = $discountAmount / $quantity;
  129. $taxableAmount = max($price - $unitDiscountAmount, 0);
  130. $unitTaxAfterDiscount = $this->calculationTool->calcTaxAmount(
  131. $taxableAmount,
  132. $taxRate,
  133. false,
  134. false
  135. );
  136. $unitTaxAfterDiscount = $this->roundAmount(
  137. $unitTaxAfterDiscount,
  138. $taxId,
  139. false,
  140. self::KEY_REGULAR_DELTA_ROUNDING,
  141. $round,
  142. $item
  143. );
  144. }
  145. $appliedTaxes[$taxId] = $this->getAppliedTax(
  146. $unitTaxAfterDiscount * $quantity,
  147. $appliedRate
  148. );
  149. $unitTaxes[] = $unitTaxAfterDiscount;
  150. $unitTaxesBeforeDiscount[] = $unitTaxPerRate;
  151. }
  152. $unitTax = array_sum($unitTaxes);
  153. $unitTaxBeforeDiscount = array_sum($unitTaxesBeforeDiscount);
  154. $rowTax = $unitTax * $quantity;
  155. $priceInclTax = $price + $unitTaxBeforeDiscount;
  156. return $this->taxDetailsItemDataObjectFactory->create()
  157. ->setCode($item->getCode())
  158. ->setType($item->getType())
  159. ->setRowTax($rowTax)
  160. ->setPrice($price)
  161. ->setPriceInclTax($priceInclTax)
  162. ->setRowTotal($price * $quantity)
  163. ->setRowTotalInclTax($priceInclTax * $quantity)
  164. ->setDiscountTaxCompensationAmount($discountTaxCompensationAmount)
  165. ->setAssociatedItemCode($item->getAssociatedItemCode())
  166. ->setTaxPercent($rate)
  167. ->setAppliedTaxes($appliedTaxes);
  168. }
  169. }