Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
AbstractCalculator.php
Go to the documentation of this file.
1 <?php
7 
8 use Magento\Customer\Api\Data\AddressInterface as CustomerAddress;
9 use Magento\Tax\Api\Data\AppliedTaxInterfaceFactory;
10 use Magento\Tax\Api\Data\AppliedTaxRateInterfaceFactory;
13 use Magento\Tax\Api\Data\TaxDetailsItemInterfaceFactory;
16 
20 abstract class AbstractCalculator
21 {
25  const KEY_REGULAR_DELTA_ROUNDING = 'regular';
26 
27  const KEY_APPLIED_TAX_DELTA_ROUNDING = 'applied_tax_amount';
28 
29  const KEY_TAX_BEFORE_DISCOUNT_DELTA_ROUNDING = 'tax_before_discount';
34 
40  protected $calculationTool;
41 
47  protected $storeId;
48 
55 
61  protected $customerId;
62 
68  protected $shippingAddress;
69 
75  protected $billingAddress;
76 
82  protected $config;
83 
96  private $addressRateRequest = null;
97 
109  protected $roundingDeltas;
110 
117 
122 
127 
140  public function __construct(
141  TaxClassManagementInterface $taxClassService,
142  TaxDetailsItemInterfaceFactory $taxDetailsItemDataObjectFactory,
143  AppliedTaxInterfaceFactory $appliedTaxDataObjectFactory,
144  AppliedTaxRateInterfaceFactory $appliedTaxRateDataObjectFactory,
146  \Magento\Tax\Model\Config $config,
147  $storeId,
148  \Magento\Framework\DataObject $addressRateRequest = null
149  ) {
150  $this->taxClassManagement = $taxClassService;
151  $this->taxDetailsItemDataObjectFactory = $taxDetailsItemDataObjectFactory;
152  $this->appliedTaxDataObjectFactory = $appliedTaxDataObjectFactory;
153  $this->appliedTaxRateDataObjectFactory = $appliedTaxRateDataObjectFactory;
154  $this->calculationTool = $calculationTool;
155  $this->config = $config;
156  $this->storeId = $storeId;
157  $this->addressRateRequest = $addressRateRequest;
158  }
159 
167  public function setBillingAddress(CustomerAddress $billingAddress)
168  {
169  $this->billingAddress = $billingAddress;
170  }
171 
178  public function setShippingAddress(CustomerAddress $shippingAddress)
179  {
180  $this->shippingAddress = $shippingAddress;
181  }
182 
190  {
191  $this->customerTaxClassId = $customerTaxClassId;
192  }
193 
200  public function setCustomerId($customerId)
201  {
202  $this->customerId = $customerId;
203  }
204 
205  // @codeCoverageIgnoreEnd
206 
215  public function calculate(QuoteDetailsItemInterface $item, $quantity, $round = true)
216  {
217  if ($item->getIsTaxIncluded()) {
218  return $this->calculateWithTaxInPrice($item, $quantity, $round);
219  } else {
220  return $this->calculateWithTaxNotInPrice($item, $quantity, $round);
221  }
222  }
223 
232  abstract protected function calculateWithTaxInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true);
233 
242  abstract protected function calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $quantity, $round = true);
243 
256  protected function getAddressRateRequest()
257  {
258  if (null == $this->addressRateRequest) {
259  $this->addressRateRequest = $this->calculationTool->getRateRequest(
260  $this->shippingAddress,
261  $this->billingAddress,
262  $this->customerTaxClassId,
263  $this->storeId,
264  $this->customerId
265  );
266  }
267  return $this->addressRateRequest;
268  }
269 
277  protected function isSameRateAsStore($rate, $storeRate)
278  {
279  if ((bool)$this->config->crossBorderTradeEnabled($this->storeId)) {
280  return true;
281  } else {
282  return (abs($rate - $storeRate) < 0.00001);
283  }
284  }
285 
303  protected function getAppliedTax($rowTax, $appliedRate)
304  {
305  $appliedTaxDataObject = $this->appliedTaxDataObjectFactory->create();
306  $appliedTaxDataObject->setAmount($rowTax);
307  $appliedTaxDataObject->setPercent($appliedRate['percent']);
308  $appliedTaxDataObject->setTaxRateKey($appliedRate['id']);
309 
311  $rateDataObjects = [];
312  foreach ($appliedRate['rates'] as $rate) {
313  //Skipped position, priority and rule_id
314  $rateDataObjects[$rate['code']] = $this->appliedTaxRateDataObjectFactory->create()
315  ->setPercent($rate['percent'])
316  ->setCode($rate['code'])
317  ->setTitle($rate['title']);
318  }
319  $appliedTaxDataObject->setRates($rateDataObjects);
320  return $appliedTaxDataObject;
321  }
322 
352  protected function getAppliedTaxes($rowTax, $totalTaxRate, $appliedRates)
353  {
355  $appliedTaxes = [];
356  $totalAppliedAmount = 0;
357  foreach ($appliedRates as $appliedRate) {
358  if ($appliedRate['percent'] == 0) {
359  continue;
360  }
361 
362  $appliedAmount = $rowTax / $totalTaxRate * $appliedRate['percent'];
363  //Use delta rounding to split tax amounts for each tax rates between items
364  $appliedAmount = $this->deltaRound(
365  $appliedAmount,
366  $appliedRate['id'],
367  true,
368  self::KEY_APPLIED_TAX_DELTA_ROUNDING
369  );
370  if ($totalAppliedAmount + $appliedAmount > $rowTax) {
371  $appliedAmount = $rowTax - $totalAppliedAmount;
372  }
373  $totalAppliedAmount += $appliedAmount;
374 
375  $appliedTaxDataObject = $this->appliedTaxDataObjectFactory->create();
376  $appliedTaxDataObject->setAmount($appliedAmount);
377  $appliedTaxDataObject->setPercent($appliedRate['percent']);
378  $appliedTaxDataObject->setTaxRateKey($appliedRate['id']);
379 
381  $rateDataObjects = [];
382  foreach ($appliedRate['rates'] as $rate) {
383  //Skipped position, priority and rule_id
384  $rateDataObjects[$rate['code']] = $this->appliedTaxRateDataObjectFactory->create()
385  ->setPercent($rate['percent'])
386  ->setCode($rate['code'])
387  ->setTitle($rate['title']);
388  }
389  $appliedTaxDataObject->setRates($rateDataObjects);
390  $appliedTaxes[$appliedTaxDataObject->getTaxRateKey()] = $appliedTaxDataObject;
391  }
392 
393  return $appliedTaxes;
394  }
395 
406  protected function deltaRound($price, $rate, $direction, $type = self::KEY_REGULAR_DELTA_ROUNDING, $round = true)
407  {
408  if ($price) {
409  $rate = (string)$rate;
410  $type = $type . $direction;
411  // initialize the delta to a small number to avoid non-deterministic behavior with rounding of 0.5
412  $delta = isset($this->roundingDeltas[$type][$rate]) ?
413  $this->roundingDeltas[$type][$rate] :
414  0.000001;
415  $price += $delta;
416  $roundPrice = $price;
417  if ($round) {
418  $roundPrice = $this->calculationTool->round($roundPrice);
419  }
420  $this->roundingDeltas[$type][$rate] = $price - $roundPrice;
421  $price = $roundPrice;
422  }
423  return $price;
424  }
425 
436  protected function calculatePriceInclTax($storePriceInclTax, $storeRate, $customerRate, $round = true)
437  {
438  $storeTax = $this->calculationTool->calcTaxAmount($storePriceInclTax, $storeRate, true, false);
439  $priceExclTax = $storePriceInclTax - $storeTax;
440  $customerTax = $this->calculationTool->calcTaxAmount($priceExclTax, $customerRate, false, false);
441  $customerPriceInclTax = $priceExclTax + $customerTax;
442  if ($round) {
443  $customerPriceInclTax = $this->calculationTool->round($customerPriceInclTax);
444  }
445  return $customerPriceInclTax;
446  }
447 }
calculateWithTaxInPrice(QuoteDetailsItemInterface $item, $quantity, $round=true)
__construct(TaxClassManagementInterface $taxClassService, TaxDetailsItemInterfaceFactory $taxDetailsItemDataObjectFactory, AppliedTaxInterfaceFactory $appliedTaxDataObjectFactory, AppliedTaxRateInterfaceFactory $appliedTaxRateDataObjectFactory, Calculation $calculationTool, \Magento\Tax\Model\Config $config, $storeId, \Magento\Framework\DataObject $addressRateRequest=null)
calculateWithTaxNotInPrice(QuoteDetailsItemInterface $item, $quantity, $round=true)
$price
$type
Definition: item.phtml:13
calculate(QuoteDetailsItemInterface $item, $quantity, $round=true)
setShippingAddress(CustomerAddress $shippingAddress)
deltaRound($price, $rate, $direction, $type=self::KEY_REGULAR_DELTA_ROUNDING, $round=true)
setBillingAddress(CustomerAddress $billingAddress)
calculatePriceInclTax($storePriceInclTax, $storeRate, $customerRate, $round=true)