<?php
namespace Wabs\ProductPreorder\Subscriber;
use Shopware\Core\Checkout\Cart\Event\BeforeLineItemAddedEvent;
use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent;
use Shopware\Core\Content\Product\ProductCollection;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Product\ProductEvents;
use Shopware\Core\Content\Product\SalesChannel\Price\AbstractProductPriceCalculator;
use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionEntity;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Struct\ArrayStruct;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Wabs\ProductPreorder\Service\Install\CustomFieldSetService\CustomFieldSetService;
use Wabs\ProductPreorder\Service\Product\ProductPreorderService;
class FrontendSubscriber implements EventSubscriberInterface
{
private EntityRepositoryInterface $orderRepository;
private EntityRepositoryInterface $productRepository;
private EntityRepository $wareneingangRepository;
private ProductPreorderService $productPreorderService;
private AbstractProductPriceCalculator $preorderPriceCalculator;
/**
* @param EntityRepositoryInterface $orderRepository
* @param EntityRepositoryInterface $productRepository
* @param ProductPreorderService $productPreorderService
* @param AbstractProductPriceCalculator $preorderPriceCalculator
* @param EntityRepository $wareneingangRepository
*/
public function __construct(
EntityRepositoryInterface $orderRepository,
EntityRepositoryInterface $productRepository,
ProductPreorderService $productPreorderService,
AbstractProductPriceCalculator $preorderPriceCalculator,
\Shopware\Core\Framework\DataAbstractionLayer\EntityRepository $wareneingangRepository
) {
$this->orderRepository = $orderRepository;
$this->productRepository = $productRepository;
$this->productPreorderService = $productPreorderService;
$this->preorderPriceCalculator = $preorderPriceCalculator;
$this->wareneingangRepository = $wareneingangRepository;
}
public static function getSubscribedEvents(): array
{
return [
CheckoutOrderPlacedEvent::class => 'onOrderPlaced',
BeforeLineItemAddedEvent::class => 'onBeforeLineItemAdded',
ProductEvents::PRODUCT_LOADED_EVENT => 'onProductLoaded',
ProductEvents::PRODUCT_LISTING_CRITERIA => 'addPropertiesAssociation'
];
}
public function onOrderPlaced(CheckoutOrderPlacedEvent $event)
{
$order = $event->getOrder();
if ($this->isPreorder($order, $event->getContext())) {
$this->orderRepository->update([
[
'id' => $order->getId(),
'customFields' => [
CustomFieldSetService::WABS_PRODUCT_PREORDER_ORDER_IS_PREORDER_FIELD => true
]
]
], $event->getContext());
}
}
private function isPreorder(OrderEntity $order, Context $context)
{
$orderLineItems = $order->getLineItems();
$isPreorder = true;
$productIds = [];
foreach ($orderLineItems as $orderLineItem) {
$productIds[] = $orderLineItem->getProductId();
}
try {
/** @var ProductCollection $products */
$products = $this->productRepository->search(
(new Criteria($productIds))->addAssociation('properties')->addAssociation('properties.group')
, $context);
} catch (\Exception $e) {
return false;
}
if ($products->count()) {
foreach ($products as $product) {
/** @var PropertyGroupOptionEntity $property */
$productPreorder = $this->productPreorderService->isPreorder($product);
if (!$productPreorder) {
$isPreorder = false;
break;
}
}
}
return $isPreorder;
}
public function onBeforeLineItemAdded(BeforeLineItemAddedEvent $event)
{
$lineItem = $event->getLineItem();
if ($lineItem->getType() === 'product') {
/** @var ProductEntity $product */
$product = $this->productRepository->search((new Criteria([$lineItem->getReferencedId()]))->addAssociation('properties')->addAssociation('properties.group'),
$event->getContext())->first();
if ($product && $product->getActive() &&
(!$product->getReleaseDate() or $product->getReleaseDate() > new \DateTime("now"))) {
$lineItem->setPayloadValue('isPreorder', $this->productPreorderService->isPreorder($product));
}
}
}
public function onProductLoaded(EntityLoadedEvent $event): void
{
if ($event->getContext()->getScope() !== Context::USER_SCOPE) {
return;
}
/** @var ProductEntity $product */
foreach ($event->getEntities() as $product) {
if (!$this->productPreorderService->isPreorder($product)) {
continue;
}
$customFields = $product->getCustomFields();
$totalOrderAmount = $this->productPreorderService->getTotalOrderAmount($product, $event->getContext());
$newPrice = $this->productPreorderService->getNewPrice($customFields, $totalOrderAmount);
if ($newPrice) {
$product->setPrice($newPrice);
$product->setCheapestPrice(null);
}
$product->addExtension('preorderData', new ArrayStruct([
'totalOrderAmount' => $totalOrderAmount,
'hasWareneingang' => $this->hasWareneingang($product, $event->getContext())
]));
}
}
public function addPropertiesAssociation(ProductListingCriteriaEvent $event)
{
$criteria = $event->getCriteria();
$criteria->addAssociation('properties.group');
}
private function hasWareneingang(ProductEntity $product, Context $context): bool
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('igmProduktNummer', $product->getProductNumber()));
$criteria->addFilter(new EqualsFilter('isSync', 1));
return $this->wareneingangRepository->search($criteria, $context)->count() > 0;
}
}