Skip to content

Commit

Permalink
Distribute discount adjustments over units
Browse files Browse the repository at this point in the history
  • Loading branch information
Zales0123 committed Jun 26, 2024
1 parent 2cde246 commit b052d06
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 25 deletions.
34 changes: 34 additions & 0 deletions src/Adder/DiscountAdjustmentsAdder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Adder;

use Sylius\Component\Core\Distributor\IntegerDistributorInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Order\Factory\AdjustmentFactoryInterface;

final class DiscountAdjustmentsAdder implements DiscountAdjustmentsAdderInterface
{
public function __construct(
private readonly IntegerDistributorInterface $integerDistributor,
private readonly AdjustmentFactoryInterface $adjustmentFactory,
) {
}

public function add(OrderItemInterface $orderItem, string $adjustmentType, int $discount): void
{
$discounts = $this->integerDistributor->distribute($discount, $orderItem->getQuantity());
$units = $orderItem->getUnits();

foreach ($discounts as $i => $discount) {
$adjustment = $this->adjustmentFactory->createWithData(
$adjustmentType,
'Custom discount',
$discount,
);

$units->get($i)->addAdjustment($adjustment);
}
}
}
13 changes: 13 additions & 0 deletions src/Adder/DiscountAdjustmentsAdderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Adder;

use Sylius\Component\Core\Model\AdjustmentInterface;
use Sylius\Component\Core\Model\OrderItemInterface;

interface DiscountAdjustmentsAdderInterface
{
public function add(OrderItemInterface $orderItem, string $adjustmentType, int $discount): void;
}
14 changes: 3 additions & 11 deletions src/Form/Type/CustomDiscountCollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,10 @@ public function configureOptions(OptionsResolver $resolver): void
}, $adjustments);
},
'setter' => function (AdjustableInterface &$adjustable, array $discounts): void {
$adjustable->removeAdjustments($this->adjustmentType);

/** @var int $discount */
foreach ($discounts as $discount) {
$adjustment = $this->adjustmentFactory->createWithData(
$this->adjustmentType,
$this->label,
-1 * $discount,
);
$adjustable->addAdjustment($adjustment);
}
$this->setDiscounts($adjustable, $discounts);
},
]);
}

abstract public function setDiscounts(AdjustableInterface $adjustable, array $discounts): void;
}
23 changes: 21 additions & 2 deletions src/Form/Type/OrderDiscountCollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,31 @@
namespace Setono\SyliusOrderEditPlugin\Form\Type;

use Setono\SyliusOrderEditPlugin\Model\AdjustmentTypes;
use Setono\SyliusOrderEditPlugin\Setter\OrderDiscountAdjustmentSetterInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Order\Factory\AdjustmentFactoryInterface;
use Sylius\Component\Order\Model\AdjustableInterface;
use Webmozart\Assert\Assert;

final class OrderDiscountCollectionType extends CustomDiscountCollectionType
{
public function __construct(AdjustmentFactoryInterface $adjustmentFactory)
{
public function __construct(
AdjustmentFactoryInterface $adjustmentFactory,
private readonly OrderDiscountAdjustmentSetterInterface $orderDiscountAdjustmentSetter,
) {
parent::__construct($adjustmentFactory, 'Custom discount', AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT);
}

/** @param OrderInterface $adjustable */
public function setDiscounts(AdjustableInterface $adjustable, array $discounts): void
{
Assert::isInstanceOf($adjustable, OrderInterface::class);

$adjustable->removeAdjustmentsRecursively($this->adjustmentType);

/** @var int $discount */
foreach ($discounts as $discount) {
$this->orderDiscountAdjustmentSetter->set($adjustable, $discount);
}
}
}
23 changes: 21 additions & 2 deletions src/Form/Type/OrderItemDiscountCollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,32 @@

namespace Setono\SyliusOrderEditPlugin\Form\Type;

use Setono\SyliusOrderEditPlugin\Adder\DiscountAdjustmentsAdderInterface;
use Setono\SyliusOrderEditPlugin\Model\AdjustmentTypes;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Order\Factory\AdjustmentFactoryInterface;
use Sylius\Component\Order\Model\AdjustableInterface;
use Webmozart\Assert\Assert;

final class OrderItemDiscountCollectionType extends CustomDiscountCollectionType
{
public function __construct(AdjustmentFactoryInterface $adjustmentFactory)
{
public function __construct(
AdjustmentFactoryInterface $adjustmentFactory,
private readonly DiscountAdjustmentsAdderInterface $discountAdjustmentsAdder,
) {
parent::__construct($adjustmentFactory, 'Custom item discount', AdjustmentTypes::SETONO_ADMIN_ORDER_ITEM_DISCOUNT);
}

/** @param OrderItemInterface $adjustable */
public function setDiscounts(AdjustableInterface $adjustable, array $discounts): void
{
Assert::isInstanceOf($adjustable, OrderItemInterface::class);

$adjustable->removeAdjustmentsRecursively($this->adjustmentType);

/** @var int $discount */
foreach ($discounts as $discount) {
$this->discountAdjustmentsAdder->add($adjustable, $this->adjustmentType, -$discount);
}
}
}
15 changes: 10 additions & 5 deletions src/Resources/config/services/form.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<!-- Form types -->
<service id="setono_sylius_order_edit.form.type.order_item"
class="Setono\SyliusOrderEditPlugin\Form\Type\OrderItemType">
<service
id="setono_sylius_order_edit.form.type.order_item"
class="Setono\SyliusOrderEditPlugin\Form\Type\OrderItemType"
>
<argument type="service" id="sylius.order_item_quantity_modifier"/>
<argument>%sylius.model.order_item.class%</argument>
<argument>%sylius.form.type.order_item.validation_groups%</argument>

<tag name="form.type"/>
</service>

Expand All @@ -17,6 +18,7 @@
class="Setono\SyliusOrderEditPlugin\Form\Type\OrderDiscountCollectionType"
>
<argument type="service" id="sylius.factory.adjustment" />
<argument type="service" id="setono_sylius_order_edit.setter.order_discount_adjustment" />
<tag name="form.type"/>
</service>

Expand All @@ -25,12 +27,15 @@
class="Setono\SyliusOrderEditPlugin\Form\Type\OrderItemDiscountCollectionType"
>
<argument type="service" id="sylius.factory.adjustment" />
<argument type="service" id="setono_sylius_order_edit.adder.discount_adjustments" />
<tag name="form.type"/>
</service>

<!-- Form type extensions -->
<service id="setono_sylius_order_edit.form.extension.order"
class="Setono\SyliusOrderEditPlugin\Form\Extension\OrderTypeExtension">
<service
id="setono_sylius_order_edit.form.extension.order"
class="Setono\SyliusOrderEditPlugin\Form\Extension\OrderTypeExtension"
>
<tag name="form.type_extension"/>
</service>
</services>
Expand Down
16 changes: 16 additions & 0 deletions src/Resources/config/services/order_processing.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,21 @@
<argument type="service" id="doctrine.orm.entity_manager" />
<argument type="service" id="setono_sylius_order_edit.event_bus" />
</service>

<service
id="setono_sylius_order_edit.adder.discount_adjustments"
class="Setono\SyliusOrderEditPlugin\Adder\DiscountAdjustmentsAdder"
>
<argument type="service" id="sylius.integer_distributor" />
<argument type="service" id="sylius.factory.adjustment" />
</service>

<service
id="setono_sylius_order_edit.setter.order_discount_adjustment"
class="Setono\SyliusOrderEditPlugin\Setter\OrderDiscountAdjustmentSetter"
>
<argument type="service" id="Sylius\Component\Core\Distributor\MinimumPriceDistributorInterface" />
<argument type="service" id="setono_sylius_order_edit.adder.discount_adjustments" />
</service>
</services>
</container>
35 changes: 35 additions & 0 deletions src/Setter/OrderDiscountAdjustmentSetter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Setter;

use Setono\SyliusOrderEditPlugin\Adder\DiscountAdjustmentsAdderInterface;
use Setono\SyliusOrderEditPlugin\Model\AdjustmentTypes;
use Sylius\Component\Core\Distributor\MinimumPriceDistributorInterface;
use Sylius\Component\Core\Model\OrderInterface;

final class OrderDiscountAdjustmentSetter implements OrderDiscountAdjustmentSetterInterface
{
public function __construct(
private readonly MinimumPriceDistributorInterface $minimumPriceDistributor,
private readonly DiscountAdjustmentsAdderInterface $orderItemDiscountAdjustmentAdder,
) {
}

public function set(OrderInterface $order, int $discount): void
{
$channel = $order->getChannel();
$items = $order->getItems();

$distributedPrices = $this->minimumPriceDistributor->distribute($items->toArray(), $discount, $channel, true);

foreach ($distributedPrices as $i => $distribution) {
$this->orderItemDiscountAdjustmentAdder->add(
$items->get($i),
AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT,
-$distribution,
);
}
}
}
12 changes: 12 additions & 0 deletions src/Setter/OrderDiscountAdjustmentSetterInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Setter;

use Sylius\Component\Core\Model\OrderInterface;

interface OrderDiscountAdjustmentSetterInterface
{
public function set(OrderInterface $order, int $discount): void;
}
8 changes: 3 additions & 5 deletions tests/Functional/OrderUpdateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public function testItAllowsToAddAndRemoveDiscountsForTheWholeOrderMultipleTimes
/** @var EditableOrderInterface $order */
$order = $this->getOrderRepository()->findOneBy(['tokenValue' => 'TOKEN']);
self::assertSame($initialOrderTotalWithoutTaxes - 200, $this->getResultTotal($order));
self::assertSame(-200, $order->getAdjustmentsTotal(AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT));
self::assertSame(-200, $order->getAdjustmentsTotalRecursively(AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT));
}

public function testItDoesNotAllowToExceedTheInitialOrderTotal(): void
Expand Down Expand Up @@ -157,10 +157,9 @@ public function testItAllowsToAddDiscountsForTheSpecificOrderItem(): void
/** @var OrderInterface $order */
$order = $this->getOrderRepository()->findOneBy(['tokenValue' => 'TOKEN']);
self::assertSame($initialOrderTotalWithoutTaxes - 100, $this->getResultTotal($order));
self::assertSame(0, $order->getAdjustmentsTotal(AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT));
self::assertSame(
-100,
$order->getItems()->first()->getAdjustmentsTotal(AdjustmentTypes::SETONO_ADMIN_ORDER_ITEM_DISCOUNT),
$order->getItems()->first()->getAdjustmentsTotalRecursively(AdjustmentTypes::SETONO_ADMIN_ORDER_ITEM_DISCOUNT),
);
}

Expand All @@ -183,10 +182,9 @@ public function testItAllowsToAddAndRemoveDiscountsForTheOrderItemMultipleTimes(
/** @var EditableOrderInterface $order */
$order = $this->getOrderRepository()->findOneBy(['tokenValue' => 'TOKEN']);
self::assertSame($initialOrderTotalWithoutTaxes - 200, $this->getResultTotal($order));
self::assertSame(0, $order->getAdjustmentsTotal(AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT));
self::assertSame(
-200,
$order->getItems()->first()->getAdjustmentsTotal(AdjustmentTypes::SETONO_ADMIN_ORDER_ITEM_DISCOUNT),
$order->getItems()->first()->getAdjustmentsTotalRecursively(AdjustmentTypes::SETONO_ADMIN_ORDER_ITEM_DISCOUNT),
);
}

Expand Down
63 changes: 63 additions & 0 deletions tests/Unit/Adder/DiscountAdjustmentsAdderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Tests\Unit\Adder;

use Doctrine\Common\Collections\ArrayCollection;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Setono\SyliusOrderEditPlugin\Adder\DiscountAdjustmentsAdder;
use Setono\SyliusOrderEditPlugin\Model\AdjustmentTypes;
use Sylius\Component\Core\Distributor\IntegerDistributorInterface;
use Sylius\Component\Core\Model\AdjustmentInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Core\Model\OrderItemUnitInterface;
use Sylius\Component\Order\Factory\AdjustmentFactoryInterface;

final class DiscountAdjustmentsAdderTest extends TestCase
{
use ProphecyTrait;

public function testItAddsDiscountAdjustmentsOnOrderItemUnits(): void
{
$integerDistributor = $this->prophesize(IntegerDistributorInterface::class);
$adjustmentFactory = $this->prophesize(AdjustmentFactoryInterface::class);

$item = $this->prophesize(OrderItemInterface::class);
$firstUnit = $this->prophesize(OrderItemUnitInterface::class);
$secondUnit = $this->prophesize(OrderItemUnitInterface::class);
$thirdUnit = $this->prophesize(OrderItemUnitInterface::class);

$adder = new DiscountAdjustmentsAdder(
$integerDistributor->reveal(),
$adjustmentFactory->reveal(),
);

$item->getQuantity()->willReturn(3);
$item->getUnits()->willReturn(new ArrayCollection(
[$firstUnit->reveal(), $secondUnit->reveal(), $thirdUnit->reveal()]
));

$integerDistributor->distribute(-1000, 3)->willReturn([-333, -333, -334]);

$firstAdjustment = $this->prophesize(AdjustmentInterface::class);
$secondAdjustment = $this->prophesize(AdjustmentInterface::class);
$thirdAdjustment = $this->prophesize(AdjustmentInterface::class);

$adjustmentFactory
->createWithData(AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT, 'Custom discount', -333)
->willReturn($firstAdjustment->reveal(), $secondAdjustment->reveal())
;
$adjustmentFactory
->createWithData(AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT, 'Custom discount', -334,)
->willReturn($thirdAdjustment->reveal())
;

$firstUnit->addAdjustment($firstAdjustment->reveal())->shouldBeCalled();
$secondUnit->addAdjustment($secondAdjustment->reveal())->shouldBeCalled();
$thirdUnit->addAdjustment($thirdAdjustment->reveal())->shouldBeCalled();

$adder->add($item->reveal(), AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT, -1000);
}
}
Loading

0 comments on commit b052d06

Please sign in to comment.