<?php declare(strict_types=1);
namespace Proc\ProcLiveOrderHistory\Subscriber;
use Proc\ProcFoundation\Helper\ErrorHandler;
use Proc\ProcFoundation\Helper\RequestHandler;
use Proc\ProcFoundation\Service\ProcFoundationService;
use Proc\ProcLiveOrderHistory\Helper\OrderHistoryStruct;
use Proc\ProcLiveOrderHistory\Helper\BuildRequestHelper;
use Shopware\Storefront\Page\Account\Order\AccountOrderPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use DateTime;
use Exception;
/**
* Class LiveOrderHistorySubscriber
* @package Proc\ProcLiveOrderHistory\Subscriber
*/
class LiveOrderHistorySubscriber implements EventSubscriberInterface
{
private const PATH_HEADLIST = '/iman/order-history-headlist';
private const PATH_DETAILS = '/iman/order-history-details';
/**
* @todo
* Richtig muss es '/iman/order-history-document' heißen. Dies muss aber noch im iMan angepasst werden.
*/
private const PATH_DOCUMENTS = '/iman/order-history-documents';
// private const PATH_DOCUMENTS = '/iman/order-history-document';
/**
* @var RequestHandler
*/
private $requestHandler;
/**
* @var ErrorHandler
*/
private $errorHandler;
/**
* @var string
*/
private $host,
$port,
$customerNumber,
$fromDate,
$toDate;
private $error = null;
/**
* @var ProcFoundationService
*/
private $foundationService;
/**
* @var AccountOrderPageLoadedEvent
*/
private $event;
/**
* @var int
*/
private $elementsPerPage,
$page;
/**
* @var EntityRepositoryInterface
*/
private $productRepository;
/**
* @var Context
*/
private $context;
/**
* @inheritDoc
*/
public static function getSubscribedEvents()
{
return [
AccountOrderPageLoadedEvent::class => ['onAccountOrderPageLoaded' ,1]
];
}
/**
* LiveOrderHistorySubscriber constructor.
* @param ProcFoundationService $foundationService
* @param RequestHandler $requestHandler
* @param ErrorHandler $errorHandler
* @throws Exception
*/
public function __construct(ProcFoundationService $foundationService, RequestHandler $requestHandler, ErrorHandler $errorHandler, EntityRepositoryInterface $productRepository)
{
$this->foundationService = $foundationService;
$this->requestHandler = $requestHandler;
$this->errorHandler = $errorHandler;
$this->productRepository = $productRepository;
}
/**
* @param AccountOrderPageLoadedEvent $event
*/
public function onAccountOrderPageLoaded(AccountOrderPageLoadedEvent $event)
{
$this->event = $event;
/**
* Prüfung ob Benutzer angemeldet ist, sonst keine Live-Abfrage.
*/
if (!($this->checkLogin()) || !($this->checkConfig()))
{
$orderNotComplete['fromDate'] = (new DateTime())->format('Y-m-d');
$orderNotComplete['toDate'] = (new DateTime())->format('Y-m-d');
$orderNotComplete['numElements'] = 0;
$orderNotComplete['order-history-list'] = '';
$orderNotCompleteStruct = new OrderHistoryStruct();
$orderNotCompleteStruct->setOrderHistory($orderNotComplete);
$this->event->getPage()->addExtension('order_history', $orderNotCompleteStruct);
return;
}
$this->handleOrderHistory();
}
private function checkLogin()
{
$this->customerNumber = $this->foundationService->checkLogin($this->event->getSalesChannelContext(), (string)get_class($this));
$this->context = $this->event->getSalesChannelContext()->getContext();
if (!($this->customerNumber)) return false;
return true;
}
/**
* Hauptfunktion welche das einlesen der OrderHistory primär steuert.
* Die OrderHistory wird anhand vorhandener Daten eingelesen. Wird kein Datum ermittelt werden alle Order von 1.1.2021 bis zum aktuellen Datum vom iMan geholt.
* Standard Anzahl der Elemente pro Seite ist bei 10, kann aber über das Menü der OrderHistory auch anders ausgewählt werden.
*/
public function handleOrderHistory()
{
$request = $this->event->getRequest();
if ($request->getQueryString() == '') {
$orderNotComplete['fromDate'] = (new DateTime())->modify('-1 month')->format('Y-m-d');
$orderNotComplete['toDate'] = (new DateTime())->format('Y-m-d');
$orderNotComplete['numElements'] = 10;
$orderNotComplete['order-history-list'] = '';
}
$fromDate = $request->get('fromDate') ?? (new DateTime())->modify('-1 month')->format('Y-m-d');;
$toDate = $request->get('toDate') ?? (new DateTime())->format('Y-m-d');;
$this->elementsPerPage = $request->get('numElements') ? (int)$request->get('numElements') : null ?? 10;
$this->page = $request->get('page') ? (int)$request->get('page') : null ?? 1;
$orderHistory = $this->getOrderHistory($fromDate, $toDate);
if ($orderHistory['head']['status'] === 'NOK') {
$this->error = $this->errorHandler->error($this->errorHandler::SAP_RESPONSE_ERROR, get_class($this), 'SAP Status Fehler.' . print_r($orderHistory, true));
$this->errorHandler->writeErrorLog($this->error);
$orderNotComplete['fromDate'] = $request->get('fromDate');
$orderNotComplete['toDate'] = $request->get('toDate');
$orderNotComplete['numElements'] = 0;
$orderNotComplete['order-history-list'] = '';
$orderComplete['currentPage'] = $this->page;
$orderNotCompleteStruct = new OrderHistoryStruct();
$orderNotCompleteStruct->setOrderHistory($orderNotComplete);
$this->event->getPage()->addExtension('order_history', $orderNotCompleteStruct);
} else {
$orderHistory = $this->formatOrderHistory($orderHistory);
$pageOrders = $this->getOrdersPerPage($orderHistory);
$orderComplete = $this->getOrderDetails($pageOrders);
// $numElements = count($orderComplete['order-history-list']['order']);
$numElements = $this->elementsPerPage;
$orderComplete['numElements'] = $numElements;
if (null !== $qs = $this->event->getRequest()->getQueryString()) {
$qs = '?'.$qs;
}
$orderComplete['baseUri'] = $this->event->getSalesChannelContext()->getSalesChannel()->getDomains()->first()->getUrl() .$this->event->getRequest()->getPathInfo().$qs;
$orderComplete['fromDate'] = $this->fromDate;
$orderComplete['toDate'] = $this->toDate;
$orderComplete['currentPage'] = $this->page;
$orderCompleteStruct = new OrderHistoryStruct();
$orderCompleteStruct->setOrderHistory($orderComplete);
$this->event->getPage()->addExtension('order_history', $orderCompleteStruct);
}
}
/**
* Nimm die Liste der OrderHistory-Elemente für die Zeit von-bis, wie beim Aufruf der getOrderHistory definiert
* schneidet" die Elemente für die gewünschte Seite aus und gibt das gekürzte Array zurück.
* @param $orderHistory
* @return array
*/
protected function getOrdersPerPage($orderHistory) : array
{
$numElements = count($orderHistory['order-history-list']['order']);
if ($this->page === '' || $this->elementsPerPage === '' || $this->elementsPerPage === null) {
$this->page = 1;
$this->elementsPerPage = 10;
}
if ($numElements <= $this->elementsPerPage) {
return $orderHistory;
}
$start = ($this->page - 1) * $this->elementsPerPage;
$end = ($this->page * $this->elementsPerPage) - 1;
$resultArray['head'] = $orderHistory['head'];
$resultArray['order-history-list']['order'] = array_slice($orderHistory['order-history-list']['order'], $start, $this->elementsPerPage);
$resultArray['numPages'] = (int)ceil($numElements / $this->elementsPerPage);
$resultArray['currentPage'] = $this->page;
return $resultArray;
}
/**
* Liest die OrderHistory aus und die hinterlegten Dokumente
* füllte das Basis-Array mit diesen Daten und gibt das angereicherte Array zurück.
* @param array $iterationArray
* @param int $shopInstance
* @param string $language
* @return array
*/
protected function getOrderDetails(array $iterationArray, int $shopInstance = 1, string $language = 'DE') : array
{
foreach ($iterationArray['order-history-list']['order'] as $key => $orderElement)
{
$params = [
'shop-instance' => $shopInstance,
'sap-order-id' => $orderElement['sap-order-id'],
'language' => $language
];
$buildRequestHelper = new BuildRequestHelper($params, 'details', $this->errorHandler);
/**
* @var string $request
*/
$request = $this->requestHandler->buildRequest($buildRequestHelper, (string)get_class($this));
if ($request !== '') {
/**
* @var string $detailResult
*/
$detailResult = $this->requestHandler->sendRequest($this->host, $this->port, self::PATH_DETAILS, $request);
} else {
$this->error = $this->errorHandler->error($this->errorHandler::UNKNOWN_REQUEST, get_class($this), 'Fehler im Aufbau des Request');
$this->errorHandler->writeErrorLog($this->error);
continue;
}
$detailArray = RequestHandler::parseResult($detailResult);
if ($detailArray['head']['status'] === 'NOK' || count($detailArray) === 0) {
continue;
}
/**
* Holen der Dokumente zu den einzelnen Orders
* @todo Deaktiviert, da SAP noch keine Dokumente liefern kann. Muss noch getestet werden, sobald SAP Dokumente liefern kan.
*/
if (array_key_exists('sap-order-id', $detailArray['document-list']['document'])) {
$detailIterationArray['head'] = $detailArray['head'];
$detailIterationArray['document-list']['document'][0] = $detailArray['document-list']['document'];
} else {
$detailIterationArray = $detailArray;
}
/**
* @todo Die Foreach war für um nur Rechunungen heraus zu filtern. Hier sollte es konfigurierbar gemacht werden.
*/
foreach ($detailIterationArray['document-list']['document'] as $key2 => $documentElement)
{
if (($documentElement['document-type-code'] != 'M') && ($documentElement['document-type-code'] != 'C'))
{
unset($detailIterationArray['document-list']['document'][$key2]);
}
}
// $detailCompleteArray = $this->getOrderDocuments($detailIterationArray);
$iterationArray['order-history-list']['order'][$key]['document-list'] = $detailIterationArray['document-list'];
$iterationArray['order-history-list']['order'][$key]['head'] = $detailArray['head'];
if ($iterationArray['order-history-list']['order'][$key]['customer-order-id'] === []) {
$iterationArray['order-history-list']['order'][$key]['customer-order-id'] = 'keine Kunden-Bestellnummer';
}
$totalAmount = 0;
//check if an array of $detailArray['articles']['article'] is assocciative
if(array_key_exists('number', $detailArray['articles']['article'])) {
//means that the order have only one article
$additionaDataFromSW = $this->fetchProductByReferenceNumber($detailArray['articles']['article']['number'], $detailArray['articles']['article']['short-text']);
if($additionaDataFromSW ) {
$detailArray['articles']['article'] = array_merge( $detailArray['articles']['article'],$additionaDataFromSW);
}
$iterationArray['order-history-list']['order'][$key]['articles'][] = $detailArray['articles']['article'];
$iterationArray['order-history-list']['order'][$key]['totalAmount'] = $detailArray['articles']['article']['net-position-sum'];
} else {
foreach($detailArray['articles']['article'] as $lineItemKey => $lineItem) {
$additionaDataFromSW = $this->fetchProductByReferenceNumber($lineItem['number'], $lineItem['short-text']);
if($additionaDataFromSW ) {
$detailArray['articles']['article'][$lineItemKey] = array_merge( $lineItem, $additionaDataFromSW);
}
$totalAmount += $lineItem['net-position-sum'];
}
$iterationArray['order-history-list']['order'][$key]['articles'] = $detailArray['articles']['article'];
$iterationArray['order-history-list']['order'][$key]['totalAmount'] = $totalAmount;
}
// @fts - Add discounts to array
$iterationArray['order-history-list']['order'][$key]['discounts'] = $detailArray['discounts'] ?? [];
}
return $iterationArray;
}
/**
* Liest die OrderDetails aus und die hinterlegten Dokumente
* füllte das Details-Array mit den Dokumenten und gibt das angereicherte Array zurück.
* @param array $detailsArray
* @param int$shopInstance
* @return array
* @todo In Controller auslagern, um die document.php im Plugin zu halten
*/
public function getOrderDocuments(array $detailsArray, int $shopInstance = 1)
{
foreach ($detailsArray['document-list']['document'] as $key => $documentElement)
{
/**
* @todo
* Hier muss noch wegen der Dokumententypen angepasst werden.
*/
// if ($documentElement['document-type-code'] == 'M') {
if (true) {
$params = [
'shop-instance' => $shopInstance,
'customer' => $this->customerNumber,
'document-number' => $documentElement['document-number'],
'document-type-code' => $documentElement['document-type-code']
];
$buildRequestHelper = new BuildRequestHelper($params, 'documents', $this->errorHandler);
/**
* @var string $request
*/
$request = $this->requestHandler->buildRequest($buildRequestHelper, (string)get_class($this));
if ($request !== '') {
/**
* @var string $documentResult
*/
$documentResult = $this->requestHandler->sendRequest($this->host, $this->port, self::PATH_DOCUMENTS, $request);
} else {
$this->error = $this->errorHandler->error($this->errorHandler::UNKNOWN_REQUEST, get_class($this), 'Fehler im Aufbau des Request');
$this->errorHandler->writeErrorLog($this->error);
continue;
}
$documentArray = RequestHandler::parseResult($documentResult);
if ($documentArray['head']['status'] === 'NOK' || count($documentArray) === 0) {
continue;
}
if (array_key_exists('data', $documentArray['head'])) {
$documentIterationArray['data'] = $documentArray['head']['data'];
} else {
$documentIterationArray['data'] = $documentArray['head']['status'];
}
$detailsArray['document-list']['document'][$key]['document-data'] = $documentIterationArray['data'];
/**
* @todo Hier werden alle nicht Rechnungen entfernt. Muss raus genommen werden, wenn andere Dokumente gehen
*/
} else {
unset($detailsArray['document-list']['document'][$key]);
}
}
return $detailsArray;
}
/**
* Bereitet mit dem Requesthandler die Request auf und holt die Daten vom iMan
*
* @param string|null $fromDate
* @param string|null $toDate
* @param int $shopInstance
* @param string $language
* @return array
*/
protected function getOrderHistory(string $fromDate = null,
string $toDate = null,
int $shopInstance = 1,
string $language = 'DE') : array
{
if ($fromDate !== null && $toDate !== null) {
$this->checkDateRange($fromDate, $toDate);
}
if ($fromDate === null) {
$fromDate = '2021-01-01';
}
if ($toDate === null) {
$toDate = (new DateTime())->format('Y-m-d');
}
$this->fromDate = $fromDate;
$this->toDate = $toDate;
$params = [
'shop-instance' => $shopInstance,
'customer' => $this->customerNumber,
'date-from' => $this->fromDate,
'date-to' => $this->toDate,
'language' => $language
];
$buildRequestHelper = new BuildRequestHelper($params, 'headlist', $this->errorHandler);
/**
* @var string $request
*/
$request = $this->requestHandler->buildRequest($buildRequestHelper, (string)get_class($this));
if ($request !== '') {
/**
* @var string $result
*/
$result = $this->requestHandler->sendRequest($this->host, $this->port, self::PATH_HEADLIST, $request);
$this->error = $this->errorHandler->error($this->errorHandler::UNKNOWN_REQUEST, get_class($this), 'Result: ' . print_r($result, true));
$this->errorHandler->writeErrorLog($this->error);
} else {
/**
* @todo Fehlerbehandlung
*/
Return array();
}
return $this->requestHandler->parseResult($result);
}
/**
* Prüft die übergebenen Daten auf ihren Wert und bei vertauschte Angabe werden diese ausgewechselt.
*
* @param string|null $fromDate
* @param string|null $toDate
*/
protected function checkDateRange(string &$fromDate, string &$toDate) : void
{
try {
$startDate = new DateTime($fromDate);
$endDate = new DateTime($toDate);
} catch (Exception $e) {
$this->error = $this->errorHandler->error($this->errorHandler::GENERAL_ERROR, get_class($this), 'Eine Datumsangabe entspricht nicht dem vorgegeben Format. - ' . $e->getCode() . ' - ' . $e->getMessage());
$this->errorHandler->writeErrorLog($this->error);
$fromDate = '';
$toDate = '';
}
if ($endDate < $startDate)
{
$fromDate = $endDate->format('Y-m-d');
$toDate = $startDate->format('Y-m-d');
}
}
/**
* Array des Response wird in ein einheitliches Format gebracht zur Vereinfachung der weiteren Handhabung
* @param array $orderHistory
* @return array
*/
protected function formatOrderHistory(array $orderHistory) : array
{
if (array_key_exists('sap-order-id', $orderHistory['order-history-list']['order'])) {
$iterationArray['head'] = $orderHistory['head'];
$iterationArray['order-history-list']['order'][0] = $orderHistory['order-history-list']['order'];
} else {
$iterationArray = $orderHistory;
}
if (empty($iterationArray['head']['customer']) || is_array($iterationArray['head']['customer'])) {
$iterationArray['head']['customer'] = $this->customerNumber;
}
return $iterationArray;
}
/**
* @return bool
*/
private function checkConfig() : bool
{
if ($this->foundationService->getConfigStatus()) {
$this->host = $this->foundationService->getHost();
$this->port = $this->foundationService->getPort();
return true;
}
$this->error = $this->errorHandler->error($this->errorHandler::CONFIG_NOT_VALID, get_class($this), 'Konnte die Konfiguration nicht ermitteln');
$this->errorHandler->writeErrorLog($this->error);
return false;
}
public function fetchProductByReferenceNumber(string $referenceNumber, string $orginalName)
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('product.productNumber', $referenceNumber));
$product = $this->productRepository->search($criteria, $this->context)->first();
/** @var ProductEntity $product */
if (!$product) {
// throw new NotFoundException("Product with reference number {$referenceNumber} not found.");
return ['name' => $orginalName];
}
$result = [
'identifier' => $product->getId(),
'name' => $product->getTranslated()['name']
];
return $result;
}
}