Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distribute discount over units #24

Merged
merged 5 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/Adder/DiscountAdjustmentsAdder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Adder;

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 DiscountAdjustmentsAdder implements DiscountAdjustmentsAdderInterface
{
public function __construct(
private readonly IntegerDistributorInterface $integerDistributor,
private readonly AdjustmentFactoryInterface $adjustmentFactory,
) {
}

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

/** @var int $discount */
foreach ($discounts as $i => $discount) {
/** @var AdjustmentInterface $adjustment */
$adjustment = $this->adjustmentFactory->createWithData(
$adjustmentType,
$label,
$discount,
);
$adjustment->setOriginCode($originCode);

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

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Adder;

use Sylius\Component\Core\Model\OrderItemInterface;

interface DiscountAdjustmentsAdderInterface
{
public function add(
OrderItemInterface $orderItem,
string $adjustmentType,
string $originCode,
string $label,
int $discount,
): void;
}
47 changes: 30 additions & 17 deletions src/Form/Type/CustomDiscountCollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

namespace Setono\SyliusOrderEditPlugin\Form\Type;

use Doctrine\Common\Collections\Collection;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\OrderItemInterface;
use Sylius\Component\Order\Factory\AdjustmentFactoryInterface;
use Sylius\Component\Order\Model\AdjustableInterface;
use Sylius\Component\Order\Model\AdjustmentInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Webmozart\Assert\Assert;

abstract class CustomDiscountCollectionType extends AbstractType
{
Expand All @@ -36,26 +40,35 @@ public function configureOptions(OptionsResolver $resolver): void
'entry_options' => [
'label' => false,
],
'getter' => function (AdjustableInterface &$adjustable): array {
$adjustments = $adjustable->getAdjustments($this->adjustmentType)->toArray();
'getter' =>
/** @param OrderItemInterface|OrderInterface $adjustable */
function (AdjustableInterface &$adjustable): array {
Assert::isInstanceOfAny($adjustable, [OrderInterface::class, OrderItemInterface::class]);
/** @var Collection $adjustments */
$adjustments = $adjustable->getAdjustmentsRecursively($this->adjustmentType);

return array_map(function (AdjustmentInterface $adjustment): int {
return -1 * $adjustment->getAmount();
}, $adjustments);
},
$notDistributedAdjustments = [];
/** @var AdjustmentInterface $adjustment */
foreach ($adjustments as $adjustment) {
/** @var string $originCode */
$originCode = $adjustment->getOriginCode();

if (isset($notDistributedAdjustments[$originCode])) {
$notDistributedAdjustments[$originCode] += ($adjustment->getAmount()) * -1;

continue;
}

$notDistributedAdjustments[$originCode] = ($adjustment->getAmount()) * -1;
}

return $notDistributedAdjustments;
},
'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;
}
22 changes: 20 additions & 2 deletions src/Form/Type/OrderDiscountCollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,30 @@
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(

Check failure on line 16 in src/Form/Type/OrderDiscountCollectionType.php

View workflow job for this annotation

GitHub Actions / Backwards Compatibility Check

The number of required arguments for Setono\SyliusOrderEditPlugin\Form\Type\OrderDiscountCollectionType#__construct() increased from 1 to 2
AdjustmentFactoryInterface $adjustmentFactory,
private readonly OrderDiscountAdjustmentSetterInterface $orderDiscountAdjustmentSetter,
) {
parent::__construct($adjustmentFactory, 'Custom discount', AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT);
}

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);
}
}
}
31 changes: 29 additions & 2 deletions src/Form/Type/OrderItemDiscountCollectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,40 @@

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(

Check failure on line 16 in src/Form/Type/OrderItemDiscountCollectionType.php

View workflow job for this annotation

GitHub Actions / Backwards Compatibility Check

The number of required arguments for Setono\SyliusOrderEditPlugin\Form\Type\OrderItemDiscountCollectionType#__construct() increased from 1 to 2
AdjustmentFactoryInterface $adjustmentFactory,
private readonly DiscountAdjustmentsAdderInterface $discountAdjustmentsAdder,
) {
parent::__construct($adjustmentFactory, 'Custom item discount', AdjustmentTypes::SETONO_ADMIN_ORDER_ITEM_DISCOUNT);
}

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

$adjustable->removeAdjustmentsRecursively($this->adjustmentType);
/** @var int $orderItemId */
$orderItemId = $adjustable->getId();

/** @var int $discount */
foreach ($discounts as $discount) {
$this->discountAdjustmentsAdder->add(
$adjustable,
$this->adjustmentType,
$this->adjustmentType . '_' . $orderItemId,
'Custom item discount',
-$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.integer_distributor" />
<argument type="service" id="setono_sylius_order_edit.adder.discount_adjustments" />
</service>
</services>
</container>
42 changes: 42 additions & 0 deletions src/Setter/OrderDiscountAdjustmentSetter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Setono\SyliusOrderEditPlugin\Setter;

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

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

public function set(OrderInterface $order, int $discount): void
{
$items = $order->getItems();
/** @var int $orderId */
$orderId = $order->getId();

$distributedPrices = $this->integerDistributor->distribute($discount, $items->count());

/** @var int $distribution */
foreach ($distributedPrices as $i => $distribution) {
/** @var OrderItemInterface $item */
$item = $items->get($i);
$this->orderItemDiscountAdjustmentAdder->add(
$item,
AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT,
AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT . '_' . $orderId,
'Custom 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

{% set orderPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT') %}
{% set unitPromotionAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::ORDER_UNIT_PROMOTION_ADJUSTMENT') %}
{% set adminOrderDiscountAdjustment = constant('Setono\\SyliusOrderEditPlugin\\Model\\AdjustmentTypes::SETONO_ADMIN_ORDER_DISCOUNT') %}
{% set adminOrderItemDiscountAdjustment = constant('Setono\\SyliusOrderEditPlugin\\Model\\AdjustmentTypes::SETONO_ADMIN_ORDER_ITEM_DISCOUNT') %}
{% set shippingAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::SHIPPING_ADJUSTMENT') %}
{% set taxAdjustment = constant('Sylius\\Component\\Core\\Model\\AdjustmentInterface::TAX_ADJUSTMENT') %}
Expand All @@ -23,13 +24,22 @@
{{ money.format(item.unitPrice, order.currencyCode) }}
</td>
<td class="right aligned unit-discount">
{{ money.format(item.units.first.adjustmentsTotal(unitPromotionAdjustment), order.currencyCode) }}
{{ money.format(
item.units.first.adjustmentsTotal(unitPromotionAdjustment) + item.units.first.adjustmentsTotal(adminOrderItemDiscountAdjustment),
order.currencyCode
) }}
</td>
<td class="right aligned unit-order-discount">
<span style="font-style: italic;">~ {{ money.format(item.units.first.adjustmentsTotal(orderPromotionAdjustment), order.currencyCode) }}</span>
<span style="font-style: italic;">~ {{ money.format(
item.units.first.adjustmentsTotal(orderPromotionAdjustment) + item.units.first.adjustmentsTotal(adminOrderDiscountAdjustment),
order.currencyCode
) }}</span>
</td>
<td class="right aligned discounted-unit-price">
{{ money.format(item.fullDiscountedUnitPrice, order.currencyCode) }}
{{ money.format(
item.fullDiscountedUnitPrice + item.units.first.adjustmentsTotal(adminOrderItemDiscountAdjustment) + item.units.first.adjustmentsTotal(adminOrderDiscountAdjustment),
order.currencyCode
) }}
</td>
<td class="right aligned quantity">
{{ item.quantity }}
Expand All @@ -49,14 +59,3 @@
{{ money.format(item.total, order.currencyCode) }}
</td>
</tr>
{% set discounts = item.getAdjustments(adminOrderItemDiscountAdjustment) %}
{% if discounts is not empty %}
<tr>
<td colspan="9">
<strong>{{ 'setono_sylius_order_edit.ui.discounts'|trans }}:</strong>
{% for discount in discounts %}
{{ money.format(discount.amount, order.currencyCode) }}{% if not loop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
Loading
Loading