PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/magento/module-tax/Model/TaxCalculation.php

https://gitlab.com/yousafsyed/easternglamor
PHP | 389 lines | 228 code | 35 blank | 126 comment | 13 complexity | dc439da71437176f1b0fe2238f5b6d23 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Tax\Model;
  7. use Magento\Tax\Api\TaxCalculationInterface;
  8. use Magento\Tax\Api\TaxClassManagementInterface;
  9. use Magento\Tax\Api\Data\TaxDetailsItemInterface;
  10. use Magento\Tax\Api\Data\QuoteDetailsItemInterface;
  11. use Magento\Tax\Api\Data\TaxDetailsInterfaceFactory;
  12. use Magento\Tax\Api\Data\TaxDetailsItemInterfaceFactory;
  13. use Magento\Tax\Model\Calculation\AbstractCalculator;
  14. use Magento\Tax\Model\Calculation\CalculatorFactory;
  15. use Magento\Tax\Model\Config;
  16. use Magento\Tax\Model\TaxDetails\AppliedTax;
  17. use Magento\Tax\Model\TaxDetails\AppliedTaxRate;
  18. use Magento\Tax\Model\TaxDetails\TaxDetails;
  19. use Magento\Store\Model\StoreManagerInterface;
  20. /**
  21. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  22. */
  23. class TaxCalculation implements TaxCalculationInterface
  24. {
  25. /**
  26. * Tax Details factory
  27. *
  28. * @var TaxDetailsInterfaceFactory
  29. */
  30. protected $taxDetailsDataObjectFactory;
  31. /**
  32. * Tax configuration object
  33. *
  34. * @var Config
  35. */
  36. protected $config;
  37. /**
  38. * Tax calculation model
  39. *
  40. * @var Calculation
  41. */
  42. protected $calculationTool;
  43. /**
  44. * Array to hold discount compensations for items
  45. *
  46. * @var array
  47. */
  48. protected $discountTaxCompensations;
  49. /**
  50. * Tax details item factory
  51. *
  52. * @var TaxDetailsItemInterfaceFactory
  53. */
  54. protected $taxDetailsItemDataObjectFactory;
  55. /**
  56. * @var \Magento\Store\Model\StoreManagerInterface
  57. */
  58. protected $storeManager;
  59. /**
  60. * Item code to Item object array.
  61. *
  62. * @var QuoteDetailsItemInterface[]
  63. */
  64. private $keyedItems;
  65. /**
  66. * Parent item code to children item array.
  67. *
  68. * @var QuoteDetailsItemInterface[][]
  69. */
  70. private $parentToChildren;
  71. /**
  72. * Tax Class Management
  73. *
  74. * @var TaxClassManagementInterface
  75. */
  76. protected $taxClassManagement;
  77. /**
  78. * Calculator Factory
  79. *
  80. * @var CalculatorFactory
  81. */
  82. protected $calculatorFactory;
  83. /**
  84. * @var \Magento\Framework\Api\DataObjectHelper
  85. */
  86. protected $dataObjectHelper;
  87. /**
  88. * @param Calculation $calculation
  89. * @param CalculatorFactory $calculatorFactory
  90. * @param Config $config
  91. * @param TaxDetailsInterfaceFactory $taxDetailsDataObjectFactory
  92. * @param TaxDetailsItemInterfaceFactory $taxDetailsItemDataObjectFactory
  93. * @param StoreManagerInterface $storeManager
  94. * @param TaxClassManagementInterface $taxClassManagement
  95. * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
  96. */
  97. public function __construct(
  98. Calculation $calculation,
  99. CalculatorFactory $calculatorFactory,
  100. Config $config,
  101. TaxDetailsInterfaceFactory $taxDetailsDataObjectFactory,
  102. TaxDetailsItemInterfaceFactory $taxDetailsItemDataObjectFactory,
  103. StoreManagerInterface $storeManager,
  104. TaxClassManagementInterface $taxClassManagement,
  105. \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
  106. ) {
  107. $this->calculationTool = $calculation;
  108. $this->calculatorFactory = $calculatorFactory;
  109. $this->config = $config;
  110. $this->taxDetailsDataObjectFactory = $taxDetailsDataObjectFactory;
  111. $this->taxDetailsItemDataObjectFactory = $taxDetailsItemDataObjectFactory;
  112. $this->storeManager = $storeManager;
  113. $this->taxClassManagement = $taxClassManagement;
  114. $this->dataObjectHelper = $dataObjectHelper;
  115. }
  116. /**
  117. * {@inheritdoc}
  118. */
  119. public function calculateTax(
  120. \Magento\Tax\Api\Data\QuoteDetailsInterface $quoteDetails,
  121. $storeId = null,
  122. $round = true
  123. ) {
  124. if ($storeId === null) {
  125. $storeId = $this->storeManager->getStore()->getStoreId();
  126. }
  127. // initial TaxDetails data
  128. $taxDetailsData = [
  129. TaxDetails::KEY_SUBTOTAL => 0.0,
  130. TaxDetails::KEY_TAX_AMOUNT => 0.0,
  131. TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0,
  132. TaxDetails::KEY_APPLIED_TAXES => [],
  133. TaxDetails::KEY_ITEMS => [],
  134. ];
  135. $items = $quoteDetails->getItems();
  136. if (empty($items)) {
  137. return $this->taxDetailsDataObjectFactory->create()
  138. ->setSubtotal(0.0)
  139. ->setTaxAmount(0.0)
  140. ->setDiscountTaxCompensationAmount(0.0)
  141. ->setAppliedTaxes([])
  142. ->setItems([]);
  143. }
  144. $this->computeRelationships($items);
  145. $calculator = $this->calculatorFactory->create(
  146. $this->config->getAlgorithm($storeId),
  147. $storeId,
  148. $quoteDetails->getBillingAddress(),
  149. $quoteDetails->getShippingAddress(),
  150. $this->taxClassManagement->getTaxClassId($quoteDetails->getCustomerTaxClassKey(), 'customer'),
  151. $quoteDetails->getCustomerId()
  152. );
  153. $processedItems = [];
  154. /** @var QuoteDetailsItemInterface $item */
  155. foreach ($this->keyedItems as $item) {
  156. if (isset($this->parentToChildren[$item->getCode()])) {
  157. $processedChildren = [];
  158. foreach ($this->parentToChildren[$item->getCode()] as $child) {
  159. $processedItem = $this->processItem($child, $calculator, $round);
  160. $taxDetailsData = $this->aggregateItemData($taxDetailsData, $processedItem);
  161. $processedItems[$processedItem->getCode()] = $processedItem;
  162. $processedChildren[] = $processedItem;
  163. }
  164. $processedItem = $this->calculateParent($processedChildren, $item->getQuantity());
  165. $processedItem->setCode($item->getCode());
  166. $processedItem->setType($item->getType());
  167. } else {
  168. $processedItem = $this->processItem($item, $calculator, $round);
  169. $taxDetailsData = $this->aggregateItemData($taxDetailsData, $processedItem);
  170. }
  171. $processedItems[$processedItem->getCode()] = $processedItem;
  172. }
  173. $taxDetailsDataObject = $this->taxDetailsDataObjectFactory->create();
  174. $this->dataObjectHelper->populateWithArray(
  175. $taxDetailsDataObject,
  176. $taxDetailsData,
  177. '\Magento\Tax\Api\Data\TaxDetailsInterface'
  178. );
  179. $taxDetailsDataObject->setItems($processedItems);
  180. return $taxDetailsDataObject;
  181. }
  182. /**
  183. * {@inheritdoc}
  184. */
  185. public function getDefaultCalculatedRate(
  186. $productTaxClassID,
  187. $customerId = null,
  188. $storeId = null
  189. ) {
  190. return $this->getRate($productTaxClassID, $customerId, $storeId, true);
  191. }
  192. /**
  193. * {@inheritdoc}
  194. */
  195. public function getCalculatedRate(
  196. $productTaxClassID,
  197. $customerId = null,
  198. $storeId = null
  199. ) {
  200. return $this->getRate($productTaxClassID, $customerId, $storeId);
  201. }
  202. /**
  203. * Calculate rate based on default parameter
  204. *
  205. * @param int $productTaxClassID
  206. * @param int|null $customerId
  207. * @param string|null $storeId
  208. * @param bool $isDefault
  209. * @return float
  210. */
  211. protected function getRate(
  212. $productTaxClassID,
  213. $customerId = null,
  214. $storeId = null,
  215. $isDefault = false
  216. ) {
  217. if ($storeId === null) {
  218. $storeId = $this->storeManager->getStore()->getStoreId();
  219. }
  220. if (!$isDefault) {
  221. $addressRequestObject = $this->calculationTool->getRateRequest(null, null, null, $storeId, $customerId);
  222. } else {
  223. $addressRequestObject = $this->calculationTool->getDefaultRateRequest($storeId, $customerId);
  224. }
  225. $addressRequestObject->setProductClassId($productTaxClassID);
  226. return $this->calculationTool->getRate($addressRequestObject);
  227. }
  228. /**
  229. * Computes relationships between items, primarily the child to parent relationship.
  230. *
  231. * @param QuoteDetailsItemInterface[] $items
  232. * @return void
  233. */
  234. private function computeRelationships($items)
  235. {
  236. $this->keyedItems = [];
  237. $this->parentToChildren = [];
  238. foreach ($items as $item) {
  239. if ($item->getParentCode() === null) {
  240. $this->keyedItems[$item->getCode()] = $item;
  241. } else {
  242. $this->parentToChildren[$item->getParentCode()][] = $item;
  243. }
  244. }
  245. }
  246. /**
  247. * Calculate item tax with customized rounding level
  248. *
  249. * @param QuoteDetailsItemInterface $item
  250. * @param AbstractCalculator $calculator
  251. * @param bool $round
  252. * @return TaxDetailsItemInterface
  253. */
  254. protected function processItem(
  255. QuoteDetailsItemInterface $item,
  256. AbstractCalculator $calculator,
  257. $round = true
  258. ) {
  259. $quantity = $this->getTotalQuantity($item);
  260. return $calculator->calculate($item, $quantity, $round);
  261. }
  262. /**
  263. * Calculate row information for item based on children calculation
  264. *
  265. * @param TaxDetailsItemInterface[] $children
  266. * @param int $quantity
  267. * @return TaxDetailsItemInterface
  268. */
  269. protected function calculateParent($children, $quantity)
  270. {
  271. $rowTotal = 0.00;
  272. $rowTotalInclTax = 0.00;
  273. $rowTax = 0.00;
  274. $taxableAmount = 0.00;
  275. foreach ($children as $child) {
  276. $rowTotal += $child->getRowTotal();
  277. $rowTotalInclTax += $child->getRowTotalInclTax();
  278. $rowTax += $child->getRowTax();
  279. $taxableAmount += $child->getTaxableAmount();
  280. }
  281. $price = $this->calculationTool->round($rowTotal / $quantity);
  282. $priceInclTax = $this->calculationTool->round($rowTotalInclTax / $quantity);
  283. $taxDetailsItemDataObject = $this->taxDetailsItemDataObjectFactory->create()
  284. ->setPrice($price)
  285. ->setPriceInclTax($priceInclTax)
  286. ->setRowTotal($rowTotal)
  287. ->setRowTotalInclTax($rowTotalInclTax)
  288. ->setRowTax($rowTax);
  289. return $taxDetailsItemDataObject;
  290. }
  291. /**
  292. * Add row total item amount to subtotal
  293. *
  294. * @param array $taxDetailsData
  295. * @param TaxDetailsItemInterface $item
  296. * @return array
  297. */
  298. protected function aggregateItemData($taxDetailsData, TaxDetailsItemInterface $item)
  299. {
  300. $taxDetailsData[TaxDetails::KEY_SUBTOTAL]
  301. = $taxDetailsData[TaxDetails::KEY_SUBTOTAL] + $item->getRowTotal();
  302. $taxDetailsData[TaxDetails::KEY_TAX_AMOUNT]
  303. = $taxDetailsData[TaxDetails::KEY_TAX_AMOUNT] + $item->getRowTax();
  304. $taxDetailsData[TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT] =
  305. $taxDetailsData[TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT]
  306. + $item->getDiscountTaxCompensationAmount();
  307. $itemAppliedTaxes = $item->getAppliedTaxes();
  308. if ($itemAppliedTaxes === null) {
  309. return $taxDetailsData;
  310. }
  311. $appliedTaxes = $taxDetailsData[TaxDetails::KEY_APPLIED_TAXES];
  312. foreach ($itemAppliedTaxes as $taxId => $itemAppliedTax) {
  313. if (!isset($appliedTaxes[$taxId])) {
  314. //convert rate data object to array
  315. $rates = [];
  316. $rateDataObjects = $itemAppliedTax->getRates();
  317. foreach ($rateDataObjects as $rateDataObject) {
  318. $rates[$rateDataObject->getCode()] = [
  319. AppliedTaxRate::KEY_CODE => $rateDataObject->getCode(),
  320. AppliedTaxRate::KEY_TITLE => $rateDataObject->getTitle(),
  321. AppliedTaxRate::KEY_PERCENT => $rateDataObject->getPercent(),
  322. ];
  323. }
  324. $appliedTaxes[$taxId] = [
  325. AppliedTax::KEY_AMOUNT => $itemAppliedTax->getAmount(),
  326. AppliedTax::KEY_PERCENT => $itemAppliedTax->getPercent(),
  327. AppliedTax::KEY_RATES => $rates,
  328. AppliedTax::KEY_TAX_RATE_KEY => $itemAppliedTax->getTaxRateKey(),
  329. ];
  330. } else {
  331. $appliedTaxes[$taxId][AppliedTax::KEY_AMOUNT] += $itemAppliedTax->getAmount();
  332. }
  333. }
  334. $taxDetailsData[TaxDetails::KEY_APPLIED_TAXES] = $appliedTaxes;
  335. return $taxDetailsData;
  336. }
  337. /**
  338. * Calculates the total quantity for this item.
  339. *
  340. * What this really means is that if this is a child item, it return the parent quantity times
  341. * the child quantity and return that as the child's quantity.
  342. *
  343. * @param QuoteDetailsItemInterface $item
  344. * @return float
  345. */
  346. protected function getTotalQuantity(QuoteDetailsItemInterface $item)
  347. {
  348. if ($item->getParentCode()) {
  349. $parentQuantity = $this->keyedItems[$item->getParentCode()]->getQuantity();
  350. return $parentQuantity * $item->getQuantity();
  351. }
  352. return $item->getQuantity();
  353. }
  354. }