/** * Create AppliedTax data object based on applied tax rates and tax amount * * @param float $rowTax * @param float $totalTaxRate * @param array $appliedRates May contain multiple tax rates when catalog price includes tax * example: * [ * [ * 'id' => 'id1', * 'percent' => 7.5, * 'rates' => [ * 'code' => 'code1', * 'title' => 'title1', * 'percent' => 5.3, * ], * ], * [ * 'id' => 'id2', * 'percent' => 8.5, * 'rates' => [ * 'code' => 'code2', * 'title' => 'title2', * 'percent' => 7.3, * ], * ], * ] * @return \Magento\Tax\Api\Data\AppliedTaxInterface[] */ protected function getAppliedTaxes($rowTax, $totalTaxRate, $appliedRates) { /** @var \Magento\Tax\Api\Data\AppliedTaxInterface[] $appliedTaxes */ $appliedTaxes = []; $totalAppliedAmount = 0; foreach ($appliedRates as $appliedRate) { if ($appliedRate['percent'] == 0) { continue; } $appliedAmount = $rowTax / $totalTaxRate * $appliedRate['percent']; //Use delta rounding to split tax amounts for each tax rates between items $appliedAmount = $this->deltaRound($appliedAmount, $appliedRate['id'], true, self::KEY_APPLIED_TAX_DELTA_ROUNDING); if ($totalAppliedAmount + $appliedAmount > $rowTax) { $appliedAmount = $rowTax - $totalAppliedAmount; } $totalAppliedAmount += $appliedAmount; $appliedTaxDataObject = $this->appliedTaxDataObjectFactory->create(); $appliedTaxDataObject->setAmount($appliedAmount); $appliedTaxDataObject->setPercent($appliedRate['percent']); $appliedTaxDataObject->setTaxRateKey($appliedRate['id']); /** @var \Magento\Tax\Api\Data\AppliedTaxRateInterface[] $rateDataObjects */ $rateDataObjects = []; foreach ($appliedRate['rates'] as $rate) { //Skipped position, priority and rule_id $rateDataObjects[$rate['code']] = $this->appliedTaxRateDataObjectFactory->create()->setPercent($rate['percent'])->setCode($rate['code'])->setTitle($rate['title']); } $appliedTaxDataObject->setRates($rateDataObjects); $appliedTaxes[$appliedTaxDataObject->getTaxRateKey()] = $appliedTaxDataObject; } return $appliedTaxes; }
/** * Convert the AvaTax Tax Summary to a Magento object * * @see \Magento\Tax\Model\Calculation\AbstractCalculator::getAppliedTax() * * @param GetTaxResult $getTaxResult * @param float $rowTax * @return \Magento\Tax\Api\Data\AppliedTaxInterface */ protected function getAppliedTax(GetTaxResult $getTaxResult, $rowTax) { $totalPercent = 0.0; $taxNames = []; /** @var \Magento\Tax\Api\Data\AppliedTaxRateInterface[] $rateDataObjects */ $rateDataObjects = []; /** * There are rare situations in which the Tax Summary from AvaTax will contain items that have the same TaxName, * JurisCode, JurisName, and Rate. e.g., an order with shipping with VAT tax from Germany (example below). * To account for this, we need to group rates by a combination of JurisCode and JurisName. Otherwise the same * rate will get added twice. This is problematic for two reasons: * 1. If a merchant has configured Magento to "Display Full Tax Summary" then the user will see the same * same rate with the same percentage displayed twice. This will be confusing. * 2. When an order is placed, the \Magento\Tax\Model\Plugin\OrderSave::saveOrderTax method populates the * sales_order_tax[_item] tables with information based on the information contained in the Applied Taxes * array. Having duplicates rates will throw things off. * * 'TaxSummary' => ['TaxDetail' => [ * // This rate was applied to shipping * 0 => [ * 'JurisType' => 'State', * 'JurisCode' => 'DE', * 'TaxType' => 'Sales', * 'Taxable' => '20', * 'Rate' => '0.189995', * 'Tax' => '3.8', * 'JurisName' => 'GERMANY', * 'TaxName' => 'Standard Rate', * 'Country' => 'DE', * 'Region' => 'DE', * 'TaxCalculated' => '3.8', * ], * // This rate was applied to products * 1 => [ * 'JurisType' => 'State', * 'JurisCode' => 'DE', * 'TaxType' => 'Sales', * 'Base' => '150', * 'Taxable' => '150', * 'Rate' => '0.190000', * 'Tax' => '28.5', * 'JurisName' => 'GERMANY', * 'TaxName' => 'Standard Rate', * 'Country' => 'DE', * 'Region' => 'DE', * 'TaxCalculated' => '28.5', * ] * ]] */ $taxRatesByCode = []; /* @var \AvaTax\TaxDetail $row */ foreach ($getTaxResult->getTaxSummary() as $key => $row) { $arrayKey = $row->getJurisCode() . '_' . $row->getJurisName(); // Since the total percent is for display purposes only, round to 5 digits. Since the tax percent returned // from AvaTax is not the actual tax rate, but the effective rate, rounding makes the presentation make more // sense to the user. For example, a tax rate may be 19%, but AvaTax may return a value of 0.189995. $roundedRate = round((double) $row->getRate(), 4); $ratePercent = $roundedRate * Tax::RATE_MULTIPLIER; if (!isset($taxRatesByCode[$arrayKey])) { $taxRatesByCode[$arrayKey] = ['id' => $key . '_' . $row->getJurisCode(), 'ratePercent' => $ratePercent, 'taxName' => $row->getTaxName(), 'jurisCode' => $row->getJurisCode(), 'taxable' => (double) $row->getTaxable(), 'tax' => (double) $row->getTax()]; } elseif ($taxRatesByCode[$arrayKey]['ratePercent'] != $ratePercent) { /** * There are rare situations in which a duplicate rate will have a slightly different percentage (see * example in DocBlock above). In these cases, we will just determine the "effective" rate" ourselves. */ $taxRatesByCode[$arrayKey]['taxable'] += (double) $row->getTaxable(); $taxRatesByCode[$arrayKey]['tax'] += (double) $row->getTax(); // Avoid division by 0 if ($taxRatesByCode[$arrayKey]['taxable'] > 0) { $blendedRate = $taxRatesByCode[$arrayKey]['tax'] / $taxRatesByCode[$arrayKey]['taxable']; $taxRatesByCode[$arrayKey]['ratePercent'] = $blendedRate; } } } foreach ($taxRatesByCode as $rowArray) { $ratePercent = $rowArray['ratePercent']; $totalPercent += $ratePercent; $taxCode = $rowArray['jurisCode']; $taxName = $rowArray['taxName']; $taxNames[] = $rowArray['taxName']; // In case jurisdiction codes are duplicated, prepending the $key ensures we have a unique ID $id = $rowArray['id']; // Skipped position, priority and rule_id $rateDataObjects[$id] = $this->appliedTaxRateDataObjectFactory->create()->setPercent($ratePercent)->setCode($taxCode)->setTitle($taxName); } $rateKey = implode(' - ', $taxNames); $appliedTaxDataObject = $this->appliedTaxDataObjectFactory->create(); $appliedTaxDataObject->setAmount($rowTax); $appliedTaxDataObject->setPercent($totalPercent); $appliedTaxDataObject->setTaxRateKey($rateKey); $appliedTaxDataObject->setRates($rateDataObjects); return $appliedTaxDataObject; }