I implemented a price collector / processor exactly as described in the docs (https://developer.shopware.com/docs/guides/plugins/plugins/checkout/cart/change-price-of-item).
For testing purposes every line item product in the cart has a price of 100€.
When applying a discount (10%) that I have created in the admin, the discount is applied on the original price of the product but not on the actual new cart price. What am I missing here? My OverwritePriceCollector.php looks as following:
<?php declare(strict_types=1);
namespace Test\TestPlugin\Core\Checkout\Cart;
use Shopware\Core\Checkout\Cart\Cart;
use Shopware\Core\Checkout\Cart\CartBehavior;
use Shopware\Core\Checkout\Cart\CartDataCollectorInterface;
use Shopware\Core\Checkout\Cart\CartProcessorInterface;
use Shopware\Core\Checkout\Cart\LineItem\CartDataCollection;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
use Shopware\Core\Checkout\Cart\Price\AbsolutePriceCalculator;
use Shopware\Core\Checkout\Cart\Price\PercentagePriceCalculator;
use Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator;
use Shopware\Core\Checkout\Cart\Price\Struct\AbsolutePriceDefinition;
use Shopware\Core\Checkout\Cart\Price\Struct\PercentagePriceDefinition;
use Shopware\Core\Checkout\Cart\Price\Struct\QuantityPriceDefinition;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
class OverwritePriceCollector implements CartDataCollectorInterface, CartProcessorInterface
{
/**
* @var QuantityPriceCalculator
*/
private $calculator;
public function __construct(QuantityPriceCalculator $calculator) {
$this->calculator = $calculator;
}
public function collect(CartDataCollection $data, Cart $original, SalesChannelContext $context, CartBehavior $behavior): void
{
// get all product ids of current cart
$productIds = $original->getLineItems()->filterType(LineItem::PRODUCT_LINE_ITEM_TYPE)->getReferenceIds();
// remove all product ids which are already fetched from the database
$filtered = $this->filterAlreadyFetchedPrices($productIds, $data);
// Skip execution if there are no prices to be saved
if (empty($filtered)) {
return;
}
foreach ($filtered as $id) {
$key = $this->buildKey($id);
// Needs implementation, just an example
$newPrice = 100;
// we have to set a value for each product id to prevent duplicate queries in next calculation
$data->set($key, $newPrice);
}
}
public function process(CartDataCollection $data, Cart $original, Cart $toCalculate, SalesChannelContext $context, CartBehavior $behavior): void
{
// get all product line items
$products = $toCalculate->getLineItems()->filterType(LineItem::PRODUCT_LINE_ITEM_TYPE);
foreach ($products as $product) {
$key = $this->buildKey($product->getReferencedId());
// no overwritten price? continue with next product
if (!$data->has($key) || $data->get($key) === null) {
continue;
}
$newPrice = $data->get($key);
// build new price definition
$definition = new QuantityPriceDefinition(
$newPrice,
$product->getPrice()->getTaxRules(),
$product->getPrice()->getQuantity()
);
// build CalculatedPrice over calculator class for overwritten price
$calculated = $this->calculator->calculate($definition, $context);
// set new price into line item
$product->setPrice($calculated);
$product->setPriceDefinition($definition);
}
}
private function filterAlreadyFetchedPrices(array $productIds, CartDataCollection $data): array
{
$filtered = [];
foreach ($productIds as $id) {
$key = $this->buildKey($id);
// already fetched from database?
if ($data->has($key)) {
continue;
}
$filtered[] = $id;
}
return $filtered;
}
private function buildKey(string $id): string
{
return 'price-overwrite-'.$id;
}
}
I think your problem is that you registered your Collector with the same priority as in documentation (4500).
Shopware PromotionProcessor
is registered with priority 4900, so your code is called after PromotionProcessor
.
So what do you need is to register your OverwritePriceCollector
with higher priority e.g. 5000.