Resolve conflicts between 1.12 and 1.13

This commit is contained in:
Jakub Tobiasz 2023-07-04 15:01:06 +02:00
commit 9701a7b4d9
No known key found for this signature in database
GPG key ID: 6434250CB3525233
12 changed files with 404 additions and 58 deletions

View file

@ -145,11 +145,24 @@ which has the services that implement the `OrderTaxesApplicatorInterface <https:
Calculators
'''''''''''
For calculating Taxes **Sylius** is using the `DefaultCalculator <https://github.com/Sylius/Sylius/blob/master/src/Sylius/Component/Taxation/Calculator/DefaultCalculator.php>`_.
For calculating Taxes **Sylius** is using tax calculators.
To select a proper service we have a one that decides for us
- the `DelegatingCalculator <https://github.com/Sylius/Sylius/blob/{current_version}/src/Sylius/Component/Taxation/Calculator/DelegatingCalculator.php>`_.
Basing on the **TaxRate** assigned on the Product it will get its calculator type calculate the amount properly.
You can create your custom calculator for taxes by creating a class that implements
the `CalculatorInterface <https://github.com/Sylius/Sylius/blob/master/src/Sylius/Component/Taxation/Calculator/CalculatorInterface.php>`_
the `CalculatorInterface <https://github.com/Sylius/Sylius/blob/{current_version}/src/Sylius/Component/Taxation/Calculator/CalculatorInterface.php>`_
and registering it as a ``sylius.tax_calculator.your_calculator_name`` service.
Built-in Calculators
''''''''''''''''''''
The already defined calculators in Sylius:
* **DefaultCalculator** - calculates the ``amount`` with rounding.
* **DecimalCalculator** - calculates the ``amount`` without rounding, which results in a distribution of decimal values among the items.
Learn more
----------

View file

@ -1,5 +1,5 @@
@applying_taxes
Feature: Apply correct taxes for items with tax rate included in price
Feature: Applying correct taxes for items with tax rate included in price
In order to pay proper amount when buying goods with tax rate included in price
As a Visitor
I want to have correct taxes applied to my order
@ -7,14 +7,36 @@ Feature: Apply correct taxes for items with tax rate included in price
Background:
Given the store operates on a single channel in "United States"
And default tax zone is "US"
And the store has included in price "VAT" tax rate of 23% for "Clothes" within the "US" zone
And the store has a product "PHP T-Shirt" priced at "$100.00"
And the store has included in price "VAT" tax rate of 20% for "Clothes" within the "US" zone
And the store has a product "PHP T-Shirt" priced at "$19.70"
And it belongs to "Clothes" tax category
And the store has a product "Symfony T-Shirt" priced at "$19.70"
And it belongs to "Clothes" tax category
@ui @api
Scenario: Proper taxes for taxed product
Scenario: Applying correct taxes for a single item with tax rate included in price
When I add product "PHP T-Shirt" to the cart
Then my cart total should be "$100.00"
And my included in price taxes should be "$18.70"
Then my cart total should be "$19.70"
And my included in price taxes should be "$3.28"
And there should be one item in my cart
And total price of "PHP T-Shirt" item should be "$100.00"
And total price of "PHP T-Shirt" item should be "$19.70"
@ui @api
Scenario: Applying correct taxes for a single item with multiple units with tax rate included in price
When I add 2 products "PHP T-Shirt" to the cart
Then my cart total should be "$39.40"
And my included in price taxes should be "$6.57"
And total price of "PHP T-Shirt" item should be "$39.40"
@ui @api
Scenario: Applying correct taxes for multiple items with tax rate included in price and default calculator
When I add products "PHP T-Shirt" and "Symfony T-Shirt" to the cart
Then my cart total should be "$39.40"
And my included in price taxes should be "$6.56"
@ui @api
Scenario: Applying correct taxes for multiple items with tax rate included in price and decimal calculator
Given the "VAT" tax rate has decimal calculator configured
When I add products "PHP T-Shirt" and "Symfony T-Shirt" to the cart
Then my cart total should be "$39.40"
And my included in price taxes should be "$6.57"

View file

@ -89,6 +89,7 @@ final class CartContext implements Context
}
/**
* @When /^I add (products "([^"]+)" and "([^"]+)") to the cart$/
* @When /^I add (products "([^"]+)", "([^"]+)" and "([^"]+)") to the cart$/
*/
public function iAddMultipleProductsToTheCart(array $products): void

View file

@ -156,6 +156,16 @@ final class TaxationContext implements Context
$this->objectManager->flush();
}
/**
* @Given the :taxRate tax rate has :calculator calculator configured
*/
public function theTaxRateHasCalculatorConfigured(TaxRateInterface $taxRate, string $calculator): void
{
$taxRate->setCalculator($calculator);
$this->objectManager->flush();
}
/**
* @param string $taxCategoryName
*

View file

@ -17,6 +17,7 @@ default:
- sylius.behat.context.transform.shared_storage
- sylius.behat.context.transform.shipping_method
- sylius.behat.context.transform.tax_category
- sylius.behat.context.transform.tax_rate
- sylius.behat.context.transform.zone
- sylius.behat.context.setup.channel

View file

@ -17,6 +17,7 @@ default:
- sylius.behat.context.transform.shared_storage
- sylius.behat.context.transform.shipping_method
- sylius.behat.context.transform.tax_category
- sylius.behat.context.transform.tax_rate
- sylius.behat.context.transform.zone
- sylius.behat.context.setup.channel

View file

@ -35,6 +35,7 @@
<argument type="service" id="sylius.factory.adjustment" />
<argument type="service" id="sylius.integer_distributor" />
<argument type="service" id="sylius.tax_rate_resolver" />
<argument type="service" id="sylius.proportional_integer_distributor" />
<tag name="sylius.taxation.items.applicator" />
</service>
<service id="sylius.taxation.order_item_units_taxes_applicator" class="Sylius\Component\Core\Taxation\Applicator\OrderItemUnitsTaxesApplicator">

View file

@ -36,6 +36,9 @@
<service id="sylius.tax_calculator.default" class="Sylius\Component\Taxation\Calculator\DefaultCalculator">
<tag name="sylius.tax_calculator" calculator="default" />
</service>
<service id="sylius.tax_calculator.decimal" class="Sylius\Component\Taxation\Calculator\DecimalCalculator">
<tag name="sylius.tax_calculator" calculator="decimal" />
</service>
<service id="sylius.tax_rate_resolver" class="Sylius\Component\Taxation\Resolver\TaxRateResolver">
<argument type="service" id="sylius.repository.tax_rate" />

View file

@ -15,8 +15,10 @@ namespace Sylius\Component\Core\Taxation\Applicator;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Core\Distributor\IntegerDistributorInterface;
use Sylius\Component\Core\Distributor\ProportionalIntegerDistributorInterface;
use Sylius\Component\Core\Model\AdjustmentInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Core\Model\OrderItemUnitInterface;
use Sylius\Component\Core\Model\TaxRateInterface;
use Sylius\Component\Order\Factory\AdjustmentFactoryInterface;
@ -31,18 +33,54 @@ class OrderItemsTaxesApplicator implements OrderTaxesApplicatorInterface
private AdjustmentFactoryInterface $adjustmentFactory,
private IntegerDistributorInterface $distributor,
private TaxRateResolverInterface $taxRateResolver,
private ?ProportionalIntegerDistributorInterface $proportionalIntegerDistributor = null,
) {
}
/**
* @throws \InvalidArgumentException
*/
/** @throws \InvalidArgumentException */
public function apply(OrderInterface $order, ZoneInterface $zone): void
{
foreach ($order->getItems() as $item) {
$quantity = $item->getQuantity();
Assert::notSame($quantity, 0, 'Cannot apply tax to order item with 0 quantity.');
$this->checkItemsQuantities($order);
if ($this->proportionalIntegerDistributor === null) {
$this->applyWithoutDistributionToItems($order, $zone);
return;
}
$items = $order->getItems()->getValues();
$itemTaxFloatAmounts = [];
$itemTaxRates = [];
foreach ($items as $index => $item) {
/** @var TaxRateInterface|null $taxRate */
$taxRate = $this->taxRateResolver->resolve($item->getVariant(), ['zone' => $zone]);
if (null === $taxRate) {
$itemTaxFloatAmounts[$index] = 0;
continue;
}
$itemTaxFloatAmounts[$index] = $this->calculator->calculate($item->getTotal(), $taxRate);
$itemTaxRates[$index] = $taxRate;
}
$itemTaxWholeAmounts = array_map(fn (float $amount) => (int) round($amount), $itemTaxFloatAmounts);
$itemTotalTaxWholeAmount = (int) round(array_sum($itemTaxFloatAmounts));
$itemSplitTaxes = $this->proportionalIntegerDistributor->distribute($itemTaxWholeAmounts, $itemTotalTaxWholeAmount);
foreach ($items as $index => $item) {
if (0 === $itemSplitTaxes[$index] || !isset($itemTaxRates[$index])) {
continue;
}
$this->distributeTaxesToUnits($itemSplitTaxes[$index], $item->getQuantity(), $item, $itemTaxRates[$index]);
}
}
private function applyWithoutDistributionToItems(OrderInterface $order, ZoneInterface $zone): void
{
foreach ($order->getItems() as $item) {
/** @var TaxRateInterface|null $taxRate */
$taxRate = $this->taxRateResolver->resolve($item->getVariant(), ['zone' => $zone]);
if (null === $taxRate) {
@ -50,19 +88,8 @@ class OrderItemsTaxesApplicator implements OrderTaxesApplicatorInterface
}
$totalTaxAmount = $this->calculator->calculate($item->getTotal(), $taxRate);
$splitTaxes = $this->distributor->distribute($totalTaxAmount, $quantity);
$i = 0;
/** @var OrderItemUnitInterface $unit */
foreach ($item->getUnits() as $unit) {
if (0 === $splitTaxes[$i]) {
continue;
}
$this->addAdjustment($unit, $splitTaxes[$i], $taxRate);
++$i;
}
$this->distributeTaxesToUnits($totalTaxAmount, $item->getQuantity(), $item, $taxRate);
}
}
@ -81,4 +108,33 @@ class OrderItemsTaxesApplicator implements OrderTaxesApplicatorInterface
);
$unit->addAdjustment($unitTaxAdjustment);
}
private function distributeTaxesToUnits(
float $totalTaxAmount,
int $quantity,
OrderItemInterface $item,
TaxRateInterface $taxRate
): void {
$unitSplitTaxes = $this->distributor->distribute($totalTaxAmount, $quantity);
$units = $item->getUnits()->getValues();
foreach ($units as $index => $unit) {
if (!array_key_exists($index, $unitSplitTaxes)) {
$index = count($unitSplitTaxes) - 1;
}
if (0 === $unitSplitTaxes[$index]) {
continue;
}
$this->addAdjustment($unit, $unitSplitTaxes[$index], $taxRate);
}
}
private function checkItemsQuantities(OrderInterface $order): void
{
foreach ($order->getItems() as $item) {
Assert::notSame($item->getQuantity(), 0, 'Cannot apply tax to order item with 0 quantity.');
}
}
}

View file

@ -0,0 +1,78 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sylius\Component\Core\Test\Tests\Taxation\Applicator;
use PHPUnit\Framework\TestCase;
use Sylius\Component\Addressing\Model\Zone;
use Sylius\Component\Core\Distributor\IntegerDistributor;
use Sylius\Component\Core\Distributor\ProportionalIntegerDistributor;
use Sylius\Component\Core\Model\Adjustment;
use Sylius\Component\Core\Model\Order;
use Sylius\Component\Core\Model\OrderItem;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Core\Model\OrderItemUnit;
use Sylius\Component\Core\Model\ProductVariant;
use Sylius\Component\Core\Model\TaxRate;
use Sylius\Component\Core\Model\TaxRateInterface;
use Sylius\Component\Core\Taxation\Applicator\OrderItemsTaxesApplicator;
use Sylius\Component\Order\Factory\AdjustmentFactory;
use Sylius\Component\Resource\Factory\Factory;
use Sylius\Component\Taxation\Calculator\DecimalCalculator;
use Sylius\Component\Taxation\Resolver\TaxRateResolverInterface;
final class OrderItemsTaxesApplicatorTest extends TestCase
{
public function test_it_calculates_tax_with_decimal_precision(): void
{
$applicator = new OrderItemsTaxesApplicator(
new DecimalCalculator(),
new AdjustmentFactory(new Factory(Adjustment::class)),
new IntegerDistributor(),
$this->createConfiguredMock(TaxRateResolverInterface::class, [
'resolve' => $this->createTaxRate(),
]),
new ProportionalIntegerDistributor(),
);
$order = new Order();
for ($i = 0; $i < 20; $i++) {
$order->addItem($this->createOrderItem());
}
$applicator->apply($order, new Zone());
$this->assertEquals(39400, $order->getTotal());
$this->assertEquals(6567, $order->getTaxTotal());
$this->assertEquals(32833, $order->getTotal() - $order->getTaxTotal());
}
public function createOrderItem(): OrderItemInterface
{
$item = new OrderItem();
$item->setVariant(new ProductVariant());
$item->setUnitPrice(1970);
$item->addUnit(new OrderItemUnit($item));
return $item;
}
public function createTaxRate(): TaxRateInterface
{
$taxRate = new TaxRate();
$taxRate->setCode('standard');
$taxRate->setName('Standard');
$taxRate->setAmount(0.2);
$taxRate->setIncludedInPrice(true);
return $taxRate;
}
}

View file

@ -14,10 +14,11 @@ declare(strict_types=1);
namespace spec\Sylius\Component\Core\Taxation\Applicator;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use Sylius\Component\Addressing\Model\ZoneInterface;
use Sylius\Component\Core\Distributor\IntegerDistributorInterface;
use Sylius\Component\Core\Distributor\ProportionalIntegerDistributorInterface;
use Sylius\Component\Core\Model\AdjustmentInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
@ -35,7 +36,7 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
CalculatorInterface $calculator,
AdjustmentFactoryInterface $adjustmentsFactory,
IntegerDistributorInterface $distributor,
TaxRateResolverInterface $taxRateResolver,
TaxRateResolverInterface $taxRateResolver
): void {
$this->beConstructedWith($calculator, $adjustmentsFactory, $distributor, $taxRateResolver);
}
@ -45,15 +46,13 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
$this->shouldImplement(OrderTaxesApplicatorInterface::class);
}
function it_applies_taxes_on_units_based_on_item_total_and_rate(
function it_applies_taxes_on_units_based_on_item_total_and_rate_without_distribution_on_items(
CalculatorInterface $calculator,
AdjustmentFactoryInterface $adjustmentsFactory,
IntegerDistributorInterface $distributor,
TaxRateResolverInterface $taxRateResolver,
AdjustmentInterface $taxAdjustment1,
AdjustmentInterface $taxAdjustment2,
Collection $items,
Collection $units,
OrderInterface $order,
OrderItemInterface $orderItem,
OrderItemUnitInterface $unit1,
@ -62,10 +61,7 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
TaxRateInterface $taxRate,
ZoneInterface $zone,
): void {
$order->getItems()->willReturn($items);
$items->count()->willReturn(1);
$items->getIterator()->willReturn(new \ArrayIterator([$orderItem->getWrappedObject()]));
$order->getItems()->willReturn(new ArrayCollection([$orderItem->getWrappedObject()]));
$orderItem->getQuantity()->willReturn(2);
@ -81,8 +77,7 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
$taxRate->getAmount()->willReturn(0.1);
$taxRate->isIncludedInPrice()->willReturn(false);
$orderItem->getUnits()->willReturn($units);
$units->getIterator()->willReturn(new \ArrayIterator([$unit1->getWrappedObject(), $unit2->getWrappedObject()]));
$orderItem->getUnits()->willReturn(new ArrayCollection([$unit1->getWrappedObject(), $unit2->getWrappedObject()]));
$distributor->distribute(100, 2)->willReturn([50, 50]);
@ -122,24 +117,13 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
function it_does_nothing_if_tax_rate_cannot_be_resolved(
TaxRateResolverInterface $taxRateResolver,
Collection $items,
\Iterator $iterator,
OrderInterface $order,
OrderItemInterface $orderItem,
ProductVariantInterface $productVariant,
ZoneInterface $zone,
): void {
$order->getItems()->willReturn($items);
$items->count()->willReturn(1);
$items->getIterator()->willReturn($iterator);
$iterator->rewind()->shouldBeCalled();
$iterator->valid()->willReturn(true, false)->shouldBeCalled();
$iterator->current()->willReturn($orderItem);
$iterator->next()->shouldBeCalled();
$order->getItems()->willReturn(new ArrayCollection([$orderItem->getWrappedObject()]));
$orderItem->getQuantity()->willReturn(5);
$orderItem->getVariant()->willReturn($productVariant);
$taxRateResolver->resolve($productVariant, ['zone' => $zone])->willReturn(null);
@ -153,8 +137,6 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
AdjustmentFactoryInterface $adjustmentsFactory,
IntegerDistributorInterface $distributor,
TaxRateResolverInterface $taxRateResolver,
Collection $items,
Collection $units,
OrderInterface $order,
OrderItemInterface $orderItem,
OrderItemUnitInterface $unit1,
@ -163,10 +145,7 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
TaxRateInterface $taxRate,
ZoneInterface $zone,
): void {
$order->getItems()->willReturn($items);
$items->count()->willReturn(1);
$items->getIterator()->willReturn(new \ArrayIterator([$orderItem->getWrappedObject()]));
$order->getItems()->willReturn(new ArrayCollection([$orderItem->getWrappedObject()]));
$orderItem->getQuantity()->willReturn(2);
$orderItem->getVariant()->willReturn($productVariant);
@ -179,8 +158,7 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
$taxRate->getLabel()->willReturn('Simple tax (0%)');
$taxRate->isIncludedInPrice()->willReturn(false);
$orderItem->getUnits()->willReturn($units);
$units->getIterator()->willReturn(new \ArrayIterator([$unit1->getWrappedObject(), $unit2->getWrappedObject()]));
$orderItem->getUnits()->willReturn(new ArrayCollection([$unit1->getWrappedObject(), $unit2->getWrappedObject()]));
$distributor->distribute(0, 2)->willReturn([0, 0]);
@ -191,4 +169,158 @@ final class OrderItemsTaxesApplicatorSpec extends ObjectBehavior
$this->apply($order, $zone);
}
function it_applies_taxes_on_units_based_on_item_total_and_rate_with_distribution_on_items(
CalculatorInterface $calculator,
AdjustmentFactoryInterface $adjustmentsFactory,
IntegerDistributorInterface $distributor,
TaxRateResolverInterface $taxRateResolver,
ProportionalIntegerDistributorInterface $proportionalIntegerDistributor,
AdjustmentInterface $taxAdjustment1,
AdjustmentInterface $taxAdjustment2,
AdjustmentInterface $taxAdjustment3,
OrderInterface $order,
OrderItemInterface $orderItem1,
OrderItemInterface $orderItem2,
OrderItemUnitInterface $unit1,
OrderItemUnitInterface $unit2,
OrderItemUnitInterface $unit3,
ProductVariantInterface $productVariant1,
ProductVariantInterface $productVariant2,
TaxRateInterface $taxRate,
ZoneInterface $zone,
): void {
$this->beConstructedWith($calculator, $adjustmentsFactory, $distributor, $taxRateResolver, $proportionalIntegerDistributor);
$order->getItems()->willReturn(new ArrayCollection([
$orderItem1->getWrappedObject(),
$orderItem2->getWrappedObject(),
]));
$orderItem1->getQuantity()->willReturn(2);
$orderItem1->getTotal()->willReturn(1000);
$orderItem1->getUnits()->willReturn(new ArrayCollection([$unit1->getWrappedObject(), $unit2->getWrappedObject()]));
$orderItem1->getVariant()->willReturn($productVariant1);
$taxRateResolver->resolve($productVariant1, ['zone' => $zone])->willReturn($taxRate);
$orderItem2->getQuantity()->willReturn(1);
$orderItem2->getTotal()->willReturn(1000);
$orderItem2->getUnits()->willReturn(new ArrayCollection([$unit3->getWrappedObject()]));
$orderItem2->getVariant()->willReturn($productVariant2);
$taxRateResolver->resolve($productVariant2, ['zone' => $zone])->willReturn($taxRate);
$calculator->calculate(1000, $taxRate)->willReturn(100.40);
$proportionalIntegerDistributor->distribute([100, 100], 201)->willReturn([101, 100]);
$taxRate->getLabel()->willReturn('Simple tax (10%)');
$taxRate->getCode()->willReturn('simple_tax');
$taxRate->getName()->willReturn('Simple tax');
$taxRate->getAmount()->willReturn(0.1);
$taxRate->isIncludedInPrice()->willReturn(false);
$distributor->distribute(101, 2)->willReturn([51, 50]);
$distributor->distribute(100, 1)->willReturn([100]);
$adjustmentsFactory
->createWithData(
AdjustmentInterface::TAX_ADJUSTMENT,
'Simple tax (10%)',
51,
false,
[
'taxRateCode' => 'simple_tax',
'taxRateName' => 'Simple tax',
'taxRateAmount' => 0.1,
],
)
->willReturn($taxAdjustment1)
;
$adjustmentsFactory
->createWithData(
AdjustmentInterface::TAX_ADJUSTMENT,
'Simple tax (10%)',
50,
false,
[
'taxRateCode' => 'simple_tax',
'taxRateName' => 'Simple tax',
'taxRateAmount' => 0.1,
],
)
->willReturn($taxAdjustment2)
;
$adjustmentsFactory
->createWithData(
AdjustmentInterface::TAX_ADJUSTMENT,
'Simple tax (10%)',
100,
false,
[
'taxRateCode' => 'simple_tax',
'taxRateName' => 'Simple tax',
'taxRateAmount' => 0.1,
],
)
->willReturn($taxAdjustment3)
;
$unit1->addAdjustment($taxAdjustment1)->shouldBeCalled();
$unit2->addAdjustment($taxAdjustment2)->shouldBeCalled();
$unit3->addAdjustment($taxAdjustment3)->shouldBeCalled();
$this->apply($order, $zone);
}
function it_does_not_apply_taxes_with_distribution_on_items_if_the_given_item_has_no_tax_rate(
CalculatorInterface $calculator,
AdjustmentFactoryInterface $adjustmentsFactory,
IntegerDistributorInterface $distributor,
TaxRateResolverInterface $taxRateResolver,
ProportionalIntegerDistributorInterface $proportionalIntegerDistributor,
OrderInterface $order,
OrderItemInterface $orderItem,
OrderItemUnitInterface $unit,
ProductVariantInterface $productVariant,
ZoneInterface $zone,
): void {
$this->beConstructedWith($calculator, $adjustmentsFactory, $distributor, $taxRateResolver, $proportionalIntegerDistributor);
$order->getItems()->willReturn(new ArrayCollection([$orderItem->getWrappedObject()]));
$orderItem->getQuantity()->willReturn(1);
$orderItem->getTotal()->willReturn(1000);
$orderItem->getUnits()->willReturn(new ArrayCollection([$unit->getWrappedObject()]));
$orderItem->getVariant()->willReturn($productVariant);
$taxRateResolver->resolve($productVariant, ['zone' => $zone])->willReturn(null);
$calculator->calculate(1000, Argument::any())->shouldNotBeCalled();
$proportionalIntegerDistributor->distribute([0], 0)->willReturn([0, 0]);
$distributor->distribute(Argument::any())->shouldNotBeCalled();
$adjustmentsFactory->createWithData(Argument::any())->shouldNotBeCalled();
$this->apply($order, $zone);
}
function it_throws_an_invalid_argument_exception_if_order_item_has_0_quantity_during_distribution_on_items(
CalculatorInterface $calculator,
AdjustmentFactoryInterface $adjustmentsFactory,
IntegerDistributorInterface $distributor,
TaxRateResolverInterface $taxRateResolver,
ProportionalIntegerDistributorInterface $proportionalIntegerDistributor,
OrderInterface $order,
OrderItemInterface $orderItem,
ZoneInterface $zone,
): void {
$this->beConstructedWith($calculator, $adjustmentsFactory, $distributor, $taxRateResolver, $proportionalIntegerDistributor);
$items = new ArrayCollection([$orderItem->getWrappedObject()]);
$order->getItems()->willReturn($items);
$orderItem->getQuantity()->willReturn(0);
$this->shouldThrow(\InvalidArgumentException::class)->during('apply', [$order, $zone]);
}
}

View file

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Component\Taxation\Calculator;
use Sylius\Component\Taxation\Model\TaxRateInterface;
final class DecimalCalculator implements CalculatorInterface
{
public function calculate(float $base, TaxRateInterface $rate): float
{
if ($rate->isIncludedInPrice()) {
return $base - ($base / (1 + $rate->getAmount()));
}
return $base * $rate->getAmount();
}
}