<?php declare(strict_types=1);
namespace Uandi\UandiEfbErpSynchronization\Subscriber;
use Monolog\Logger;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Content\Product\ProductDefinition;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Product\ProductEvents;
use Shopware\Core\Framework\Api\Context\SalesChannelApiSource;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\PlatformRequest;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Uandi\UandiEfbErpSynchronization\Service\ApiConnectionServiceInterface;
use Uandi\UandiEfbErpSynchronization\Service\Price\Import as PriceImportService;
class ProductSubscriber implements EventSubscriberInterface
{
/**
* Entity updated
*/
const REQUEST_NAME = 'PriceRequest';
private RequestStack $requestStack;
private EntityRepository $customerPriceRepository;
private ApiConnectionServiceInterface $soapService;
private PriceImportService $priceImportService;
private Logger $logger;
public function __construct(
RequestStack $requestStack,
EntityRepository $customerPriceRepository,
ApiConnectionServiceInterface $soapService,
PriceImportService $priceImportService,
Logger $logger
) {
$this->requestStack = $requestStack;
$this->customerPriceRepository = $customerPriceRepository;
$this->soapService = $soapService;
$this->priceImportService = $priceImportService;
$this->logger = $logger;
}
/**
* @return string[]
*/
public static function getSubscribedEvents(): array
{
return [
ProductEvents::PRODUCT_LOADED_EVENT => 'onProductLoaded'
];
}
/**
* Updates prices per customer on product load
*
* @param EntityLoadedEvent $event
*/
public function onProductLoaded(EntityLoadedEvent $event): void
{
if ($event->getDefinition() instanceof ProductDefinition
&& $this->requestStack->getCurrentRequest() instanceof Request
) {
$source = $event->getContext()->getSource();
if ($source instanceof SalesChannelApiSource) {
/** @var SalesChannelContext $salesChannelContext */
$salesChannelContext = $this->requestStack->getCurrentRequest()->attributes->get(
PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT
);
// check if customer is logged in
if ($salesChannelContext instanceof SalesChannelContext
&& $salesChannelContext->getCustomer() instanceof CustomerEntity
) {
$currentCustomer = $salesChannelContext->getCustomer();
$customerPricesResult = $this->customerPriceRepository
->search(
(new Criteria())
->addFilter(
new EqualsFilter('customerId', $currentCustomer->getId()),
new EqualsAnyFilter('productId', $event->getIds())
),
$event->getContext()
);
// get products which needs to be updated
$productsToUpdate = $this->getProductsToUpdate($customerPricesResult, $event->getEntities());
if ($productsToUpdate === []) {
return;
}
// create request array
$priceRequestArray = $this->createPriceRequestArray($productsToUpdate, $currentCustomer->getCustomerNumber());
// request prices from the ERP
$newCustomerPrices = $this->requestErpPrice($priceRequestArray, $salesChannelContext->getSalesChannelId());
//import prices to CustomerPriceEntity
$this->priceImportService->execute($newCustomerPrices, $currentCustomer->getId(), $productsToUpdate, $event->getContext());
/**
* @see \VioCustomerPrice\Subscriber\ProductSubscriber
*/
$customerPricesResult = $this->customerPriceRepository
->search(
(new Criteria())
->addFilter(
new EqualsFilter('customerId', $salesChannelContext->getCustomer()->getId()),
new EqualsAnyFilter('productId', array_keys($productsToUpdate))
),
$event->getContext()
);
/** @var ProductEntity $product */
foreach ($event->getEntities() as $product) {
if (!array_key_exists($product->getId(), $productsToUpdate)) {
continue;
}
$customerPrices = $customerPricesResult->filterByProperty('productId', $product->getId());
if ($product->hasExtension('customerPrices')) {
$product->removeExtension('customerPrices');
}
$product->addExtension('customerPrices', $customerPrices->getEntities());
}
}
}
}
}
/**
* Returns an array of products needs to be updated
* productId=>productNumber
*
* @param $customerPricesResult
* @param $productsLoaded
*
* @return array
*/
private function getProductsToUpdate($customerPricesResult, $productsLoaded): array
{
$productNumbersToUpdate = [];
$customerPrices = $customerPricesResult->getElements();
$customerPriceProductIds = [];
foreach ($customerPrices as $customerPrice) {
$customerPriceProductIds[] = $customerPrice->getProductId();
}
// products with graduated prices
$customerPriceProductIds = array_unique($customerPriceProductIds);
foreach ($productsLoaded as $product) {
//request price only for variants
if (null === $product->getParentId()) {
continue;
}
if (!in_array($product->getId(), $customerPriceProductIds)) {
$productNumbersToUpdate[$product->getId()] = $product->getProductNumber();
}
}
return $productNumbersToUpdate;
}
/**
* Creates price request array
*
* $value =[
* "customer_number" => 11129100,
* "product_list" => [
* 'K5510.2',
* 'K5510.10',
* 'E-20071'
* ]
* ];
*
* @param $productNumbers
* @param $customerNumber
*
* @return array
*/
public function createPriceRequestArray($productNumbers, $customerNumber): array
{
if (empty($productNumbers) || empty($customerNumber)) {
return [];
}
$requestArray['customer_number'] = $customerNumber;
foreach ($productNumbers as $productNumber) {
$requestArray['product_list'][] = $productNumber;
}
return $requestArray;
}
/**
* Request price from the ERP
*
* @param array $requestArray
* @param string $salesChannelId
* @return array
*/
public function requestErpPrice(array $requestArray, string $salesChannelId): array
{
return $this->soapService->execute($requestArray, self::REQUEST_NAME, $salesChannelId);
}
}