<?php declare(strict_types=1);
namespace Asbs\StockWatcher\Subscriber;
use Psr\Log\LoggerInterface;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class StockChangeSubscriber implements EventSubscriberInterface
{
private EntityRepository $productRepository;
private LoggerInterface $logger;
public function __construct(EntityRepository $productRepository, LoggerInterface $logger)
{
$this->productRepository = $productRepository;
$this->logger = $logger;
}
public static function getSubscribedEvents(): array
{
return [
'product.written' => 'onProductWritten',
];
}
public function onProductWritten(EntityWrittenEvent $event): void
{
$payloads = $event->getPayloads();
$context = $event->getContext();
foreach ($payloads as $payload) {
if (isset($payload['stock'])) {
try {
$this->logger->info('AsbsStockWatcher: Processing stock change', [
'productId' => $payload['id'],
'newStock' => $payload['stock']
]);
$this->updateAvailablePseudo($payload['id'], $payload['stock'], $context);
} catch (\Exception $e) {
$this->logger->error('AsbsStockWatcher: Error updating pseudo sales', [
'productId' => $payload['id'],
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
}
}
}
}
private function updateAvailablePseudo(string $productId, int $stock, Context $context): void
{
// Lade das Produkt mit seinen Custom Fields (ohne Parent-Association)
$criteria = new Criteria([$productId]);
$criteria->addAssociation('customFields');
$product = $this->productRepository->search($criteria, $context)->first();
if (!$product) {
return;
}
$customFields = $product->getCustomFields() ?? [];
$parentCustomFields = [];
// Wenn das Produkt ein Parent hat, lade es separat
if ($product->getParentId()) {
$parentCriteria = new Criteria([$product->getParentId()]);
$parentCriteria->addAssociation('customFields');
$parent = $this->productRepository->search($parentCriteria, $context)->first();
if ($parent) {
$parentCustomFields = $parent->getCustomFields() ?? [];
}
}
$this->logger->info('AsbsStockWatcher: Product data loaded', [
'productId' => $productId,
'hasParent' => $product->getParentId() !== null,
'parentId' => $product->getParentId(),
'productCustomFields' => $customFields,
'parentCustomFields' => $parentCustomFields
]);
// Shopware Vererbungslogik: Erst das Produkt selbst prüfen, dann Parent
$pseudoSales = null;
// Prüfe zuerst die Custom Fields des Produkts (dort sind auch vererbte Werte)
if (isset($customFields['pseudoSales']) && $customFields['pseudoSales'] !== null && $customFields['pseudoSales'] !== '') {
$pseudoSales = $customFields['pseudoSales'];
$this->logger->info('AsbsStockWatcher: Using product own pseudoSales', [
'productId' => $productId,
'ownPseudoSales' => $pseudoSales
]);
}
// Wenn nicht vorhanden und Parent Custom Fields geladen wurden
elseif (!empty($parentCustomFields)) {
$pseudoSales = $parentCustomFields['pseudoSales'] ?? 0;
$this->logger->info('AsbsStockWatcher: Using parent pseudoSales', [
'productId' => $productId,
'parentId' => $product->getParentId(),
'inheritedPseudoSales' => $pseudoSales
]);
}
else {
$pseudoSales = 0;
$this->logger->info('AsbsStockWatcher: No pseudoSales found, using default', [
'productId' => $productId,
'defaultPseudoSales' => $pseudoSales
]);
}
// Berechne neuen Wert für doncarne_available_pseudo
$doncarneAvailablePseudo = $stock > 0 ? 10000 + $pseudoSales : $pseudoSales;
// Update nur wenn sich der Wert geändert hat
if (($customFields['doncarne_available_pseudo'] ?? 0) !== $doncarneAvailablePseudo) {
$this->logger->info('AsbsStockWatcher: Updating doncarne_available_pseudo', [
'productId' => $productId,
'oldValue' => $customFields['doncarne_available_pseudo'] ?? 0,
'newValue' => $doncarneAvailablePseudo,
'pseudoSales' => $pseudoSales,
'stock' => $stock
]);
// Nur das doncarne_available_pseudo Feld updaten
$updateData = [
'id' => $productId,
'customFields' => array_merge($customFields, [
'doncarne_available_pseudo' => $doncarneAvailablePseudo
])
];
// Erstelle einen neuen Context ohne Validierung für interne Updates
$updateContext = clone $context;
$updateContext->addState(Context::SKIP_TRIGGER_FLOW);
$this->productRepository->update([$updateData], $updateContext);
}
}
}