Purpose of OrderAdjustmentClearer

I am trying to implement a PromotionAction that adds a number of products to an order for free, and I cannot seem to implement correctly the revert() method of the Action.

This causes that a free product is added to the Order for every item the user added to its cart.

I am using “sylius/sylius”: “~1.5.0@rc”

Here is part of the class, I have omitted the _construct(), and isConfigurationValid() methods, and the class constant ADJUSTMENT_TYPE equals to AdjustmentInterface::ORDER_PROMOTION_ADJUSTMENT:

public function revert(PromotionSubjectInterface $subject, array $configuration, PromotionInterface $promotion): void
{
    /** @var OrderInterface $subject */
    Assert::isInstanceOf($subject, OrderInterface::class);

    try {
        $this->isConfigurationValid($configuration);

        $productVariant = $this->productVariantRepository->findOneByCode($configuration['variant']);

        Assert::notNull($productVariant);
    } catch (\InvalidArgumentException $exception) {
        return;
    }

    $quantity = $configuration['quantity'];
    foreach ($subject->getItems() as $item) {
        if ($item->getVariant()->getCode() !== $configuration['variant']) {
            continue;
        }

        // Find the OrderItemUnits that have adjustments from this promotion
        // so we can remove them
        $promoCode = $promotion->getCode();

        $unitsToRemove = [];
        foreach ($item->getUnits() as $unit) {
            $adjustments = $unit->getAdjustments(self::ADJUSTMENT_TYPE)->filter(
                function (AdjustmentInterface $adjustment) use ($promoCode) {
                    return $promoCode === $adjustment->getOriginCode();
                }
            );

            $count = $adjustments->count(); // THIS ALWAYS RETURNS 0
            if ($count > 0) {
                $unitsToRemove[] = $unit;
            }
        }

        foreach ($unitsToRemove as $unit) {
            $item->removeUnit($unit);
        }
    }

    return;
}

public function execute(PromotionSubjectInterface $subject, array $configuration, PromotionInterface $promotion): bool
{
    /** @var OrderInterface $subject */
    Assert::isInstanceOf($subject, OrderInterface::class);

    if (0 === $subject->countItems()) {
        return false;
    }

    try {
        $this->isConfigurationValid($configuration);

        $productVariant = $this->productVariantRepository->findOneByCode($configuration['variant']);

        Assert::notNull($productVariant);
    } catch (\InvalidArgumentException $exception) {
        return false;
    }

    $orderItem = null;
    foreach ($subject->getItems() as $item) {
        if ($item->getVariant()->getCode() === $productVariant->getCode()) {
            $orderItem = $item;
            break;
        }
    }

    $unitPrice = $this->productVariantPriceCalculator->calculate(
        $productVariant,
        ['channel' => $subject->getChannel()]
    );

    if (is_null($orderItem)) {
        $orderItem = $this->orderItemFactory->createNew($subject);
        $orderItem->setVariant($productVariant);

        $orderItem->setUnitPrice($unitPrice);

        $subject->addItem($orderItem);
    }

    $quantity = $this->calculateQuantity($subject, $configuration);
    if ($quantity <= 0) {
        return false;
    }

    $this->createUnits($quantity, $orderItem, $promotion);

    return true;
}

private function createUnits(int $quantity, OrderItemInterface $orderItem, PromotionInterface $promotion): void
{
    for ($i = 0; $i < $quantity; $i++) {
        $unit = new OrderItemUnit($orderItem);

        $this->addAdjustment($promotion, $unit, -$orderItem->getUnitPrice());
    }
}

private function calculateQuantity(OrderInterface $order, array $configuration): int
{
    if ($configuration['type'] === self::TYPE_FIXED) {
        return $configuration['quantity'];
    }

    if ($configuration['itemQuantity'] == 0) {
        return 0;
    }

    foreach ($order->getItems() as $item) {
        if ($item->getVariant()->getCode() === $configuration['variant']) {
            $quantity = intval(floor($item->getQuantity() / $configuration['itemQuantity']));

            return $quantity * $configuration['quantity'];
        }
    }

    return 0;
}

private function addAdjustment(PromotionInterface $promotion, OrderItemUnitInterface $unit, int $amount): void
{
    $adjustment = $this->adjustmentFactory
        ->createWithData(self::ADJUSTMENT_TYPE, $promotion->getName(), $amount)
    ;
    $adjustment->setOriginCode($promotion->getCode());

    $unit->addAdjustment($adjustment);
}

The

// This always returns an empty array, even if I remove the filter() call
$adjustments = $unit->getAdjustments(self::ADJUSTMENT_TYPE)->filter(
    function (AdjustmentInterface $adjustment) use ($promoCode) {
        return $promoCode === $adjustment->getOriginCode();
    }
);

in the revert() method should return the adjustments added in the execute() function, but it always returns an empty collection. If I use the AdjustmentRepository I can retrieve them, so they are added to database.

I took a look at the OrderProcessors and I found that the OrderAdjustmentsClearer removes all adjustments made after the call to “execute”, so I cannot find them in the “revert” call.

What is the purpose of this processor? How can I know which adjustments has created the Action if they are removed before executing “rever”?

Thank you very much, hope I have explained myself.