From cf5e5b8012ca6da0d7a4a70c128ba36cecb85e36 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 6 Nov 2023 13:15:38 +0100 Subject: [PATCH] Bugfix: Show the payment fee tax when the product has a different tax rate --- .../Quote/Address/Total/PaymentFeeTax.php | 88 ++++++++++++--- Plugin/Tax/Helper/DataPlugin.php | 104 ++++++++++++++++++ .../Quote/Address/Total/PaymentFeeTaxTest.php | 1 + etc/adminhtml/di.xml | 4 + 4 files changed, 184 insertions(+), 13 deletions(-) create mode 100644 Plugin/Tax/Helper/DataPlugin.php diff --git a/Model/PaymentFee/Quote/Address/Total/PaymentFeeTax.php b/Model/PaymentFee/Quote/Address/Total/PaymentFeeTax.php index 1e6f0d8afcb..b4772d7947c 100644 --- a/Model/PaymentFee/Quote/Address/Total/PaymentFeeTax.php +++ b/Model/PaymentFee/Quote/Address/Total/PaymentFeeTax.php @@ -6,25 +6,30 @@ namespace Mollie\Payment\Model\PaymentFee\Quote\Address\Total; +use Magento\Customer\Api\AccountManagementInterface as CustomerAccountManagement; +use Magento\Customer\Api\Data\AddressInterfaceFactory as CustomerAddressFactory; +use Magento\Customer\Api\Data\RegionInterfaceFactory as CustomerAddressRegionFactory; use Magento\Framework\Pricing\PriceCurrencyInterface; use Magento\Quote\Api\Data\ShippingAssignmentInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address\Total; use Magento\Quote\Model\Quote\Address\Total\AbstractTotal; +use Magento\Tax\Api\Data\QuoteDetailsInterfaceFactory; +use Magento\Tax\Api\Data\QuoteDetailsItemExtensionInterfaceFactory; +use Magento\Tax\Api\Data\QuoteDetailsItemInterfaceFactory; +use Magento\Tax\Api\Data\TaxClassKeyInterface; +use Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory; +use Magento\Tax\Api\TaxCalculationInterface; +use Magento\Tax\Helper\Data as TaxHelper; +use Magento\Tax\Model\Config as TaxConfig; use Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector; use Mollie\Payment\Config; use Mollie\Payment\Exceptions\UnknownPaymentFeeType; -use Mollie\Payment\Service\Config\PaymentFee as PaymentFeeConfig; use Mollie\Payment\Service\PaymentFee\Calculate; use Mollie\Payment\Service\PaymentFee\Result; -class PaymentFeeTax extends AbstractTotal +class PaymentFeeTax extends CommonTaxCollector { - /** - * @var PaymentFeeConfig - */ - private $paymentFeeConfig; - /** * @var PriceCurrencyInterface */ @@ -41,14 +46,55 @@ class PaymentFeeTax extends AbstractTotal private $config; public function __construct( - PaymentFeeConfig $paymentFeeConfig, - PriceCurrencyInterface $priceCurrency, + TaxConfig $taxConfig, + TaxCalculationInterface $taxCalculationService, + QuoteDetailsInterfaceFactory $quoteDetailsDataObjectFactory, + QuoteDetailsItemInterfaceFactory $quoteDetailsItemDataObjectFactory, + TaxClassKeyInterfaceFactory $taxClassKeyDataObjectFactory, + CustomerAddressFactory $customerAddressFactory, + CustomerAddressRegionFactory $customerAddressRegionFactory, Calculate $calculate, - Config $config + PriceCurrencyInterface $priceCurrency, + Config $config, + TaxHelper $taxHelper = null, + QuoteDetailsItemExtensionInterfaceFactory $quoteDetailsItemExtensionInterfaceFactory = null, + ?CustomerAccountManagement $customerAccountManagement = null ) { - $this->paymentFeeConfig = $paymentFeeConfig; - $this->priceCurrency = $priceCurrency; + $parent = new \ReflectionClass(parent::class); + $parentConstructor = $parent->getConstructor(); + + // The parent call fails when running setup:di:compile in 2.4.3 and lower due to an extra parameter. + if ($parentConstructor->getNumberOfParameters() == 9) { + // @phpstan-ignore-next-line + parent::__construct( + $taxConfig, + $taxCalculationService, + $quoteDetailsDataObjectFactory, + $quoteDetailsItemDataObjectFactory, + $taxClassKeyDataObjectFactory, + $customerAddressFactory, + $customerAddressRegionFactory, + $taxHelper, + $quoteDetailsItemExtensionInterfaceFactory + ); + } else { + // @phpstan-ignore-next-line + parent::__construct( + $taxConfig, + $taxCalculationService, + $quoteDetailsDataObjectFactory, + $quoteDetailsItemDataObjectFactory, + $taxClassKeyDataObjectFactory, + $customerAddressFactory, + $customerAddressRegionFactory, + $taxHelper, + $quoteDetailsItemExtensionInterfaceFactory, + $customerAccountManagement + ); + } + $this->calculate = $calculate; + $this->priceCurrency = $priceCurrency; $this->config = $config; } @@ -74,10 +120,26 @@ public function collect(Quote $quote, ShippingAssignmentInterface $shippingAssig $this->addAssociatedTaxable($shippingAssignment, $result, $quote); + $feeDataObject = $this->quoteDetailsItemDataObjectFactory->create() + ->setType('mollie_payment_fee') + ->setCode('mollie_payment_fee') + ->setQuantity(1); + + $feeDataObject->setUnitPrice($result->getRoundedAmount()); + $feeDataObject->setTaxClassKey( + $this->taxClassKeyDataObjectFactory->create() + ->setType(TaxClassKeyInterface::TYPE_ID) + ->setValue(4) + ); + $feeDataObject->setIsTaxIncluded(true); + + $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$feeDataObject]); + + $this->taxCalculationService->calculateTax($quoteDetails, $quote->getStoreId()); + parent::collect($quote, $shippingAssignment, $total); $extensionAttributes = $quote->getExtensionAttributes(); - if (!$extensionAttributes) { return $this; } diff --git a/Plugin/Tax/Helper/DataPlugin.php b/Plugin/Tax/Helper/DataPlugin.php new file mode 100644 index 00000000000..58b90e589d1 --- /dev/null +++ b/Plugin/Tax/Helper/DataPlugin.php @@ -0,0 +1,104 @@ +orderTaxManagement = $orderTaxManagement; + } + + public function afterGetCalculatedTaxes(object $callable, array $result, $source): array + { + if (!$source instanceof InvoiceInterface && + !$source instanceof CreditmemoInterface + ) { + return $result; + } + + $order = $source->getOrder(); + $orderTaxDetails = $this->orderTaxManagement->getOrderTaxDetails($order->getId()); + + $items = array_filter($orderTaxDetails->getItems(), function (Item $item) { + return $item->getType() == 'mollie_payment_fee_tax'; + }); + + if (count($items) === 0) { + return $result; + } + + foreach ($items as $item) { + $result = $this->aggregateTaxes($result, $item, 1); + } + + return $result; + } + + /** + * Copied from \Magento\Tax\Helper\Data::_aggregateTaxes + * + * @param $taxClassAmount + * @param OrderTaxDetailsItemInterface $itemTaxDetail + * @param $ratio + * @return array + */ + private function aggregateTaxes($taxClassAmount, OrderTaxDetailsItemInterface $itemTaxDetail, $ratio) + { + $itemAppliedTaxes = $itemTaxDetail->getAppliedTaxes(); + foreach ($itemAppliedTaxes as $itemAppliedTax) { + $taxAmount = $itemAppliedTax->getAmount() * $ratio; + $baseTaxAmount = $itemAppliedTax->getBaseAmount() * $ratio; + + if (0 == $taxAmount && 0 == $baseTaxAmount) { + continue; + } + $taxCode = $this->getKeyByName($taxClassAmount, $itemAppliedTax->getCode()); + if (!isset($taxClassAmount[$taxCode])) { + $taxClassAmount[$taxCode]['title'] = $itemAppliedTax->getTitle(); + $taxClassAmount[$taxCode]['percent'] = $itemAppliedTax->getPercent(); + $taxClassAmount[$taxCode]['tax_amount'] = $taxAmount; + $taxClassAmount[$taxCode]['base_tax_amount'] = $baseTaxAmount; + } else { + $taxClassAmount[$taxCode]['tax_amount'] += $taxAmount; + $taxClassAmount[$taxCode]['base_tax_amount'] += $baseTaxAmount; + } + } + + return $taxClassAmount; + } + + /** + * @param array $taxClassAmount + * @param string $name + * @return string|int + */ + private function getKeyByName(array $taxClassAmount, string $name) + { + foreach ($taxClassAmount as $key => $tax) { + if ($tax['title'] === $name) { + return $key; + } + } + + return $name; + } +} diff --git a/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTaxTest.php b/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTaxTest.php index 57bf27b752b..116ec18d9ad 100644 --- a/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTaxTest.php +++ b/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTaxTest.php @@ -103,6 +103,7 @@ private function getShippingAssignment() { /** @var AddressInterface $address */ $address = $this->objectManager->create(AddressInterface::class); + $address->setQuote($this->getQuote()); /** @var ShippingInterface $shipping */ $shipping = $this->objectManager->create(ShippingInterface::class); diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml index 6bb0a891f96..43c49dd54e7 100644 --- a/etc/adminhtml/di.xml +++ b/etc/adminhtml/di.xml @@ -49,4 +49,8 @@ + + + +