/** * Get formatted error message from GetTaxResult * * @param GetTaxResult $getTaxResult * @return string */ protected function getErrorMessageFromGetTaxResult(GetTaxResult $getTaxResult) { $message = ''; $message .= __('Result code: ') . $getTaxResult->getResultCode() . PHP_EOL; /** @var \AvaTax\Message $avataxMessage */ foreach ($getTaxResult->getMessages() as $avataxMessage) { $message .= __('Message:') . PHP_EOL; $message .= __(' Name: ') . $avataxMessage->getName() . PHP_EOL; $message .= __(' Summary: ') . $avataxMessage->getSummary() . PHP_EOL; $message .= __(' Details: ') . $avataxMessage->getDetails() . PHP_EOL; $message .= __(' RefersTo: ') . $avataxMessage->getRefersTo() . PHP_EOL; $message .= __(' Severity: ') . $avataxMessage->getSeverity() . PHP_EOL; $message .= __(' Source: ') . $avataxMessage->getSource() . PHP_EOL; } return $message; }
/** * 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; }