From bb77ea4567a007678693f23aece2dd5b495b9107 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Fri, 3 Jan 2025 13:16:51 +0800 Subject: [PATCH 01/12] Clearer for debugging --- src/services/ShippingCategories.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/ShippingCategories.php b/src/services/ShippingCategories.php index 4931c23f78..5c8a553cb0 100644 --- a/src/services/ShippingCategories.php +++ b/src/services/ShippingCategories.php @@ -108,7 +108,9 @@ public function getAllShippingCategoriesAsList(?int $storeId = null): array */ public function getShippingCategoryById(int $shippingCategoryId, ?int $storeId = null): ?ShippingCategory { - return $this->getAllShippingCategories($storeId)->firstWhere('id', $shippingCategoryId); + $shippingCategories = $this->getAllShippingCategories($storeId); + $first = $shippingCategories->firstWhere('id', $shippingCategoryId); + return $first; } /** From 57e5fcadfbb65c4d85463d3dd23fc0cc0de0457b Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Fri, 3 Jan 2025 13:19:53 +0800 Subject: [PATCH 02/12] Use teller in tax adjuster --- src/adjusters/Tax.php | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index 70c95e54b4..a2648fe913 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -22,6 +22,7 @@ use DvK\Vat\Validator; use Exception; use Illuminate\Support\Collection; +use Money\Teller; use yii\base\InvalidConfigException; use function in_array; @@ -146,9 +147,9 @@ private function _getAdjustments(TaxRate $taxRate): array $amount = -$this->_getTaxAmount($orderTaxableAmount, $taxRate->rate, $taxRate->include); if ($taxRate->taxable === TaxRateRecord::TAXABLE_ORDER_TOTAL_PRICE) { - $this->_costRemovedForOrderTotalPrice += $amount; + $this->_costRemovedForOrderTotalPrice = $this->_getTeller()->add($this->_costRemovedForOrderTotalPrice, $amount); } elseif ($taxRate->taxable === TaxRateRecord::TAXABLE_ORDER_TOTAL_SHIPPING) { - $this->_costRemovedForOrderShipping += $amount; + $this->_costRemovedForOrderShipping = $this->_getTeller()->add($this->_costRemovedForOrderShipping, $amount); } $adjustment = $this->_createAdjustment($taxRate); @@ -298,17 +299,16 @@ protected function getTaxRates(?int $storeId = null): Collection */ private function _getTaxAmount($taxableAmount, $rate, $included): float { + $teller = $this->_getTeller(); if (!$included) { - $incTax = $taxableAmount * (1 + $rate); - $incTax = Currency::round($incTax); - $tax = $incTax - $taxableAmount; + $incTax = $teller->multiply($taxableAmount, (1 + $rate)); + $tax = $teller->subtract($incTax, $taxableAmount); } else { - $exTax = $taxableAmount / (1 + $rate); - $exTax = Currency::round($exTax); - $tax = $taxableAmount - $exTax; + $exTax = $teller->divide($taxableAmount, (1 + $rate)); + $tax = $teller->subtract($taxableAmount, $exTax); } - return $tax; + return (float)$tax; } /** @@ -430,4 +430,14 @@ private function _getTaxAddress(): ?Address return $address; } + + /** + * @return Teller + * @throws InvalidConfigException + * @since 5.3.0 + */ + private function _getTeller(): Teller + { + return Plugin::getInstance()->getCurrencies()->getTeller($this->_order->currency); + } } From c917748eb81c017f05cf6c7dd4a8a2c3febe1a86 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Fri, 3 Jan 2025 15:24:07 +0800 Subject: [PATCH 03/12] WIP --- CHANGELOG-WIP.md | 4 ++ src/adjusters/Tax.php | 92 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index 9de90153c7..5b9b410e9b 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -1,5 +1,9 @@ # Release Notes for Craft Commerce (WIP) +### Fixed + +- Fixed a PHP error that could occur when calculating tax totals. ([#3822](https://github.com/craftcms/commerce/issues/3822)) + ### Store Management - Order conditions can now have a “Coupon Code” rule. ([#3776](https://github.com/craftcms/commerce/discussions/3776)) - Order conditions can now have a “Payment Gateway” rule. ([#3722](https://github.com/craftcms/commerce/discussions/3722)) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index a2648fe913..49ee1c5360 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -79,12 +79,49 @@ class Tax extends Component implements AdjusterInterface private float $_costRemovedForOrderShipping = 0; /** - * Track the additional discounts created inside the tax adjuster for order total price + * Track the additional discounts created inside the tax adjuster for order shipping + * This should not be modified directly, use _addAmountRemovedForOrderShipping() instead * * @var float + * @see _addAmountRemovedForOrderTotalPrice() */ private float $_costRemovedForOrderTotalPrice = 0; + /** + * The way to internally interact with the _costRemovedForOrderShipping property + * + * @param float $amount + * @return void + * @throws Exception + */ + private function _addAmountRemovedForOrderShipping(float $amount): void + { + if($amount < 0) + { + throw new Exception('Amount added to the total removed shipping must be a positive number'); + } + + $this->_costRemovedForOrderShipping += $amount; + } + + + /** + * The way to interact with the _costRemovedForOrderTotalPrice property + * + * @param float $amount + * @return void + * @throws Exception + */ + private function _addAmountRemovedForOrderTotalPrice(float $amount): void + { + if($amount < 0) + { + throw new Exception('Amount added to the total removed price must be a positive number'); + } + + $this->_costRemovedForOrderTotalPrice += $amount; + } + /** * @inheritdoc */ @@ -123,6 +160,7 @@ private function _getAdjustments(TaxRate $taxRate): array { $adjustments = []; $hasValidEuVatId = false; + $teller = $this->_getTeller(); $zoneMatches = $taxRate->getIsEverywhere() || ($taxRate->getTaxZone() && $this->_matchAddress($taxRate->getTaxZone())); @@ -134,7 +172,7 @@ private function _getAdjustments(TaxRate $taxRate): array $removeDueToVat = ($zoneMatches && $hasValidEuVatId && $taxRate->removeVatIncluded); if ($removeIncluded || $removeDueToVat) { - // Is this an order level tax rate? + // Remove included tax for order level taxable. if (in_array($taxRate->taxable, TaxRateRecord::ORDER_TAXABALES, false)) { $orderTaxableAmount = 0; @@ -144,41 +182,68 @@ private function _getAdjustments(TaxRate $taxRate): array $orderTaxableAmount = $this->_order->getTotalShippingCost(); } - $amount = -$this->_getTaxAmount($orderTaxableAmount, $taxRate->rate, $taxRate->include); + $orderLevelAmountToBeRemovedByDiscount = $this->_getTaxAmount($orderTaxableAmount, $taxRate->rate, $taxRate->include); if ($taxRate->taxable === TaxRateRecord::TAXABLE_ORDER_TOTAL_PRICE) { - $this->_costRemovedForOrderTotalPrice = $this->_getTeller()->add($this->_costRemovedForOrderTotalPrice, $amount); + $this->_addAmountRemovedForOrderTotalPrice($orderLevelAmountToBeRemovedByDiscount); } elseif ($taxRate->taxable === TaxRateRecord::TAXABLE_ORDER_TOTAL_SHIPPING) { - $this->_costRemovedForOrderShipping = $this->_getTeller()->add($this->_costRemovedForOrderShipping, $amount); + $this->_addAmountRemovedForOrderShipping($orderLevelAmountToBeRemovedByDiscount); } $adjustment = $this->_createAdjustment($taxRate); // We need to display the adjustment that removed the included tax $adjustment->name = Craft::t('site', $taxRate->name) . ' ' . Craft::t('commerce', 'Removed'); - $adjustment->amount = $amount; + $adjustment->amount = -$orderLevelAmountToBeRemovedByDiscount; $adjustment->type = 'discount'; // TODO Not use a discount adjustment, but modify the price of the item instead. #COM-26 $adjustment->included = false; $adjustments[] = $adjustment; } + // Not an order level taxable, add tax adjustments to the line items. if (!in_array($taxRate->taxable, TaxRateRecord::ORDER_TAXABALES, false)) { // Not an order level taxable, add tax adjustments to the line items. foreach ($this->_order->getLineItems() as $item) { if ($item->taxCategoryId == $taxRate->taxCategoryId) { if ($taxRate->taxable == TaxRateRecord::TAXABLE_PURCHASABLE) { - $taxableAmount = $item->salePrice - Currency::round($item->getDiscount() / $item->qty); - $amount = -($taxableAmount - ($taxableAmount / (1 + $taxRate->rate))); - $amount = $amount * $item->qty; + // taxableAmount = salePrice - (discount / qty) + $taxableAmount = $teller->subtract( + $item->salePrice, + $teller->divide( + $item->getDiscount(), // float amount of discount + $item->qty + ) + ); + + // amount = taxableAmount - (taxableAmount / (1 + taxRate)) + $amount = $teller->subtract( + $taxableAmount, + $teller->divide( + $taxableAmount, + (1 + $taxRate->rate) + ) + ); + + // make amount negative + $amount = (float)$teller->multiply($amount, $item->qty); } else { $taxableAmount = $item->getTaxableSubtotal($taxRate->taxable); - $amount = -($taxableAmount - ($taxableAmount / (1 + $taxRate->rate))); + // amount = taxableAmount - (taxableAmount / (1 + taxRate)) + $amount = $teller->subtract( + $taxableAmount, + $teller->divide( + $taxableAmount, + (1 + $taxRate->rate) + ) + ); + + // make amount negative + $amount = (float)$amount; } - $amount = Currency::round($amount); $adjustment = $this->_createAdjustment($taxRate); // We need to display the adjustment that removed the included tax $adjustment->name = Craft::t('site', $taxRate->name) . ' ' . Craft::t('commerce', 'Removed'); - $adjustment->amount = $amount; + $adjustment->amount = -$amount; $adjustment->setLineItem($item); $adjustment->type = 'discount'; $adjustment->included = false; @@ -195,6 +260,7 @@ private function _getAdjustments(TaxRate $taxRate): array } } } + // Return the removed included taxes as discounts. return $adjustments; } @@ -301,7 +367,7 @@ private function _getTaxAmount($taxableAmount, $rate, $included): float { $teller = $this->_getTeller(); if (!$included) { - $incTax = $teller->multiply($taxableAmount, (1 + $rate)); + $incTax = $teller->multiply($taxableAmount, (1 + $rate)); $tax = $teller->subtract($incTax, $taxableAmount); } else { $exTax = $teller->divide($taxableAmount, (1 + $rate)); From 45ba29ed39c6f3e2e003623497c37f4101b1bde0 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 8 Jan 2025 20:28:20 +0800 Subject: [PATCH 04/12] Fix --- src/base/Purchasable.php | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/base/Purchasable.php b/src/base/Purchasable.php index 2539fbd90e..e80c55ca24 100644 --- a/src/base/Purchasable.php +++ b/src/base/Purchasable.php @@ -437,15 +437,6 @@ public function getStoreId(): int return $this->getStore()->id; } - /** - * @return bool - * @throws InvalidConfigException - */ - public function getIsAvailableForPurchase(): bool - { - return Plugin::getInstance()->getPurchasables()->isPurchasableAvailable($this); - } - /** * @inheritdoc * @throws InvalidConfigException @@ -987,7 +978,7 @@ private function _getStock(): int */ public function getIsOutOfStockPurchasingAllowed(): bool { - return $this->allowOutOfStockPurchases; + return Plugin::getInstance()->getPurchasables()->isPurchasableOutOfStockPurchasingAllowed($this); } /** From c2f3a22dc04867179cf88d8283767bc0054160ea Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 8 Jan 2025 20:34:59 +0800 Subject: [PATCH 05/12] Cleanup --- src/adjusters/Tax.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index 2889dc356e..d854c34187 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -96,8 +96,7 @@ class Tax extends Component implements AdjusterInterface */ private function _addAmountRemovedForOrderShipping(float $amount): void { - if($amount < 0) - { + if ($amount < 0) { throw new Exception('Amount added to the total removed shipping must be a positive number'); } @@ -114,12 +113,11 @@ private function _addAmountRemovedForOrderShipping(float $amount): void */ private function _addAmountRemovedForOrderTotalPrice(float $amount): void { - if($amount < 0) - { + if ($amount < 0) { throw new Exception('Amount added to the total removed price must be a positive number'); } - $this->_costRemovedForOrderTotalPrice += $amount; + $this->_costRemovedForOrderTotalPrice = $this->_getTeller()->add($this->_costRemovedForOrderTotalPrice, $amount); } /** @@ -370,7 +368,7 @@ private function _getTaxAmount($taxableAmount, $rate, $included): float { $teller = $this->_getTeller(); if (!$included) { - $incTax = $teller->multiply($taxableAmount, (1 + $rate)); + $incTax = $teller->multiply($taxableAmount, (1 + $rate)); $tax = $teller->subtract($incTax, $taxableAmount); } else { $exTax = $teller->divide($taxableAmount, (1 + $rate)); From b43e94197bde3d1c79e0e5f66eb4883f4cb8f422 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 8 Jan 2025 21:06:51 +0800 Subject: [PATCH 06/12] wip --- src/adjusters/Tax.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index d854c34187..06fe226428 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -80,8 +80,8 @@ class Tax extends Component implements AdjusterInterface /** * Track the additional discounts created inside the tax adjuster for order shipping - * This should not be modified directly, use _addAmountRemovedForOrderShipping() instead * + * @internal This should not be modified directly, use _addAmountRemovedForOrderShipping() instead * @var float * @see _addAmountRemovedForOrderTotalPrice() */ @@ -252,7 +252,7 @@ private function _getAdjustments(TaxRate $taxRate): array $objectId = spl_object_hash($item); // We use this ID since some line items are not saved in the DB yet and have no ID. if (isset($this->_costRemovedByLineItem[$objectId])) { - $this->_costRemovedByLineItem[$objectId] += $amount; + $this->_costRemovedByLineItem[$objectId] = (float)$this->_getTeller()->add($this->_costRemovedByLineItem[$objectId], $amount); } else { $this->_costRemovedByLineItem[$objectId] = $amount; } @@ -290,12 +290,12 @@ private function _getAdjustments(TaxRate $taxRate): array if ($taxRate->taxable === TaxRateRecord::TAXABLE_ORDER_TOTAL_PRICE) { $orderTaxableAmount = $this->_getOrderTotalTaxablePrice($this->_order); - $orderTaxableAmount += $this->_costRemovedForOrderTotalPrice; + $orderTaxableAmount = (float)$this->_getTeller()->add($orderTaxableAmount, $this->_costRemovedForOrderTotalPrice); } if ($taxRate->taxable === TaxRateRecord::TAXABLE_ORDER_TOTAL_SHIPPING) { $orderTaxableAmount = $this->_order->getTotalShippingCost(); - $orderTaxableAmount += $this->_costRemovedForOrderShipping; + $orderTaxableAmount = (float)$this->_getTeller()->add($orderTaxableAmount, $this->_costRemovedForOrderShipping); } $orderTax = $this->_getTaxAmount($orderTaxableAmount, $taxRate->rate, $taxRate->include); From 7b973e1f374884bbb2a2614a73f17751fd0f1800 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Sun, 2 Feb 2025 12:39:43 +0800 Subject: [PATCH 07/12] restore to original order --- src/adjusters/Tax.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index 071a9b35b0..b54165892b 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -223,8 +223,7 @@ private function _getAdjustments(TaxRate $taxRate): array ) ); - // make amount negative - $amount = (float)$teller->multiply($amount, $item->qty); + $amount = -(float)$teller->multiply($amount, $item->qty); } else { $taxableAmount = $item->getTaxableSubtotal($taxRate->taxable); // amount = taxableAmount - (taxableAmount / (1 + taxRate)) @@ -236,13 +235,12 @@ private function _getAdjustments(TaxRate $taxRate): array ) ); - // make amount negative - $amount = (float)$amount; + $amount = -(float)$amount; } $adjustment = $this->_createAdjustment($taxRate); // We need to display the adjustment that removed the included tax $adjustment->name = Craft::t('site', $taxRate->name) . ' ' . Craft::t('commerce', 'Removed'); - $adjustment->amount = -$amount; + $adjustment->amount = $amount; $adjustment->setLineItem($item); $adjustment->type = 'discount'; $adjustment->included = false; From 7555049642caa68a58121925ed887f5ab916f6a3 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Sun, 2 Feb 2025 12:47:28 +0800 Subject: [PATCH 08/12] Must be float --- src/adjusters/Tax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index b54165892b..a06f87f09e 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -115,7 +115,7 @@ private function _addAmountRemovedForOrderTotalPrice(float $amount): void throw new Exception('Amount added to the total removed price must be a positive number'); } - $this->_costRemovedForOrderTotalPrice = $this->_getTeller()->add($this->_costRemovedForOrderTotalPrice, $amount); + $this->_costRemovedForOrderTotalPrice = (float)$this->_getTeller()->add($this->_costRemovedForOrderTotalPrice, $amount); } /** From df36bbb3f44bc70673c2a5cf002f8e8fc3de8cfd Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Mon, 3 Feb 2025 19:01:19 +0800 Subject: [PATCH 09/12] Keep consistent --- src/adjusters/Tax.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index c1bb1b4fc5..6ddb874081 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -94,8 +94,8 @@ class Tax extends Component implements AdjusterInterface */ private function _addAmountRemovedForOrderShipping(float $amount): void { - if ($amount < 0) { - throw new Exception('Amount added to the total removed shipping must be a positive number'); + if ($amount > 0) { + throw new Exception('Amount added to the total removed shipping must be a negative number'); } $this->_costRemovedForOrderShipping += $amount; @@ -111,8 +111,8 @@ private function _addAmountRemovedForOrderShipping(float $amount): void */ private function _addAmountRemovedForOrderTotalPrice(float $amount): void { - if ($amount < 0) { - throw new Exception('Amount added to the total removed price must be a positive number'); + if ($amount > 0) { + throw new Exception('Amount added to the total removed price must be a negative number'); } $this->_costRemovedForOrderTotalPrice = (float)$this->_getTeller()->add($this->_costRemovedForOrderTotalPrice, $amount); @@ -181,7 +181,7 @@ private function _getAdjustments(TaxRate $taxRate): array $orderTaxableAmount = $this->_order->getTotalShippingCost(); } - $orderLevelAmountToBeRemovedByDiscount = $this->_getTaxAmount($orderTaxableAmount, $taxRate->rate, $taxRate->include); + $orderLevelAmountToBeRemovedByDiscount = -$this->_getTaxAmount($orderTaxableAmount, $taxRate->rate, $taxRate->include); if ($taxRate->taxable === TaxRateRecord::TAXABLE_ORDER_TOTAL_PRICE) { $this->_addAmountRemovedForOrderTotalPrice($orderLevelAmountToBeRemovedByDiscount); @@ -192,7 +192,7 @@ private function _getAdjustments(TaxRate $taxRate): array $adjustment = $this->_createAdjustment($taxRate); // We need to display the adjustment that removed the included tax $adjustment->name = Craft::t('site', $taxRate->name) . ' ' . Craft::t('commerce', 'Removed'); - $adjustment->amount = -$orderLevelAmountToBeRemovedByDiscount; + $adjustment->amount = $orderLevelAmountToBeRemovedByDiscount; $adjustment->type = 'discount'; // TODO Not use a discount adjustment, but modify the price of the item instead. #COM-26 $adjustment->included = false; From 970c9681740c476974cc0106a0c8db9a036d5308 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Mon, 3 Feb 2025 19:03:03 +0800 Subject: [PATCH 10/12] Cleanup --- src/adjusters/Tax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index 6ddb874081..216f2531f8 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -98,7 +98,7 @@ private function _addAmountRemovedForOrderShipping(float $amount): void throw new Exception('Amount added to the total removed shipping must be a negative number'); } - $this->_costRemovedForOrderShipping += $amount; + $this->_costRemovedForOrderShipping = (float)$this->_getTeller()->add($this->_costRemovedForOrderShipping, $amount); } From 27ae3f72d1078b8c5959d0879ba9176442d85fd5 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Mon, 3 Feb 2025 19:10:38 +0800 Subject: [PATCH 11/12] Cleanup --- src/adjusters/Tax.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index 216f2531f8..68c5577ae4 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -19,7 +19,6 @@ use craft\commerce\models\TaxRate; use craft\commerce\Plugin; use craft\commerce\records\TaxRate as TaxRateRecord; -use craft\commerce\services\Taxes; use craft\commerce\taxidvalidators\EuVatIdValidator; use craft\elements\Address; use DvK\Vat\Validator; @@ -77,7 +76,7 @@ class Tax extends Component implements AdjusterInterface private float $_costRemovedForOrderShipping = 0; /** - * Track the additional discounts created inside the tax adjuster for order shipping + * Track the additional discounts created inside the tax adjuster for total price * * @internal This should not be modified directly, use _addAmountRemovedForOrderShipping() instead * @var float From e4942cceffe09f1dac05f732185cf78e068691f5 Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Mon, 3 Feb 2025 19:25:04 +0800 Subject: [PATCH 12/12] Finish remaining calcs --- src/adjusters/Tax.php | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index 68c5577ae4..aa3d4f8a24 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -316,13 +316,30 @@ private function _getAdjustments(TaxRate $taxRate): array * since the discount adjustments we just added won't be picked up in getTaxableSubtotal() */ if ($taxRate->taxable == TaxRateRecord::TAXABLE_PURCHASABLE) { - $purchasableAmount = $item->salePrice - Currency::round($item->getDiscount() / $item->qty); - $purchasableAmount += Currency::round(($this->_costRemovedByLineItem[$objectId] ?? 0) / $item->qty); - $purchasableTax = $this->_getTaxAmount($purchasableAmount, $taxRate->rate, $taxRate->include); - $itemTax = $purchasableTax * $item->qty; //already rounded +// $item->salePrice - Currency::round($item->getDiscount() / $item->qty); + $purchasableAmount = $this->_getTeller()->subtract( + $item->salePrice, + $this->_getTeller()->divide( + $item->getDiscount(), + $item->qty + ) + ); + + $purchasableAmount = $this->_getTeller()->add( + $purchasableAmount, + $this->_getTeller()->divide( + ($this->_costRemovedByLineItem[$objectId] ?? 0), + $item->qty + ) + ); + $purchasableTax = $this->_getTaxAmount((float)$purchasableAmount, $taxRate->rate, $taxRate->include); + $itemTax = $this->_getTeller()->multiply($purchasableTax, $item->qty); //already rounded } else { $taxableAmount = $item->getTaxableSubtotal($taxRate->taxable); - $taxableAmount += $this->_costRemovedByLineItem[$objectId] ?? 0; + $taxableAmount = (float)$this->_getTeller()->add( + $taxableAmount, + $this->_costRemovedByLineItem[$objectId] ?? 0 + ); $itemTax = $this->_getTaxAmount($taxableAmount, $taxRate->rate, $taxRate->include); } @@ -363,7 +380,7 @@ private function _getTaxAmount($taxableAmount, $rate, $included): float { $teller = $this->_getTeller(); if (!$included) { - $incTax = $teller->multiply($taxableAmount, (1 + $rate)); + $incTax = $teller->multiply($taxableAmount, (1 + $rate)); $tax = $teller->subtract($incTax, $taxableAmount); } else { $exTax = $teller->divide($taxableAmount, (1 + $rate)); @@ -476,7 +493,13 @@ private function _getOrderTotalTaxablePrice(Order $order): float $taxAdjustments = $order->getTotalTax(); $includedTaxAdjustments = $order->getTotalTaxIncluded(); - return $itemTotal + $allNonIncludedAdjustmentsTotal - ($taxAdjustments + $includedTaxAdjustments); + $totals = (float)$this->_getTeller()->add($itemTotal, $allNonIncludedAdjustmentsTotal); + $adjustments = (float)$this->_getTeller()->add($taxAdjustments, $includedTaxAdjustments); + + return (float)$this->_getTeller()->subtract( + $totals, + $adjustments + ); } /**