Files
kupshop/bundles/External/HannahBundle/SAP/Synchronizer/BaseSynchronizer.php
2025-08-02 16:30:27 +02:00

245 lines
7.0 KiB
PHP

<?php
declare(strict_types=1);
namespace External\HannahBundle\SAP\Synchronizer;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use External\HannahBundle\SAP\Exception\SAPException;
use External\HannahBundle\SAP\Util\SAPUtil;
use External\HannahBundle\Util\Configuration;
use KupShop\AdminBundle\Util\ActivityLog;
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
use Psr\Log\LoggerInterface;
abstract class BaseSynchronizer implements SynchronizerInterface
{
protected static $type;
protected $logging = true;
protected bool $allowFullSync = false;
/** @required */
public Configuration $configuration;
protected $sapUtil;
protected $activityLog;
protected $sentryLogger;
protected string $currentImportType = 'DIFF';
protected bool $isFileImport = false;
protected array $context = [];
protected $logger;
protected $processItemCallback;
public function __construct(
SAPUtil $sapUtil,
LoggerInterface $logger,
ActivityLog $activityLog,
SentryLogger $sentryLogger,
) {
$this->sapUtil = $sapUtil;
$this->activityLog = $activityLog;
$this->logger = $logger;
$this->sentryLogger = $sentryLogger;
}
public static function getType(): string
{
if (empty(static::$type)) {
throw new SAPException('Missing required property "$type" of synchronizer service');
}
return static::$type;
}
public function setProcessItemCallback(?callable $callback): void
{
$this->processItemCallback = $callback;
}
public function setAllowFullSync(bool $allowFullSync): void
{
$this->allowFullSync = $allowFullSync;
}
public function process(array $data): void
{
$this->createContext($data);
foreach ($this->getHandledFields() as $field => $methodName) {
// missing data, or not expected structure
if (!isset($data[$field]['item'])) {
if (!array_key_exists($field, $data)) {
// kibana log
$this->log($data, sprintf('Unexpected data of "%s"', static::getType()));
// activity log
$this->activityLog->addActivityLog(
ActivityLog::SEVERITY_WARNING,
ActivityLog::TYPE_SYNC,
sprintf('Data has unexpected format! Missing "%s" field of type "%s".', $field, static::getType()),
$this->currentImportType === 'FULL' ? [] : ['data' => $data]
);
}
continue;
}
$method = 'process'.ucfirst($methodName);
if (!method_exists($this, $method)) {
throw new SAPException(
sprintf('Missing "%s:%s" method', static::class, $method)
);
}
// process data
foreach (((array) $data[$field]['item']) as $item) {
// zalogovat prichozi data
$this->log((array) $item);
// process data
try {
$this->{$method}((array) $item);
} catch (LockWaitTimeoutException|DeadlockException $e) {
if ($this->currentImportType === 'FULL' && $this->isFileImport) {
$this->onDeadLockFailure((array) $item);
continue;
}
throw $e;
}
if ($this->processItemCallback) {
call_user_func($this->processItemCallback, (array) $item);
}
}
}
try {
$this->postprocess();
if ($this->context['isFileImport'] ?? false) {
// prejmenuju soubor na FTP, aby koncil na ".done"
if (!empty($this->context['filename'])) {
$this->sapUtil->updateJSONFileAsDone($this->context['filename']);
}
$this->sapUtil->addActivityLog('Úspěšně zpracován JSON: '.$this->context['filename'], severity: ActivityLog::SEVERITY_SUCCESS);
}
} catch (\Throwable $e) {
if (isDevelopment()) {
throw $e;
}
$this->sentryLogger->captureException($e);
}
}
public function processToSAP(): void
{
throw new SAPException('Process to SAP method is not implemented');
}
protected function postprocess(): void
{
}
protected function onDeadlockFailure(array $item): void
{
// v BaseSynchronizer se nic nestane -> pouze az v CatalogSynchronizer
}
abstract protected function getHandledFields(): array;
protected function createContext(array $data): void
{
$this->currentImportType = $data['ImportType'] ?? 'DIFF';
$this->isFileImport = $data['isFileImport'] ?? false;
$context = [
'importType' => $data['ImportType'] ?? 'DIFF',
'isFileImport' => $data['isFileImport'] ?? false,
'process' => $data['Process'] ?? 'WPJPL',
];
$context['language'] = $this->configuration->getLanguageByProcess($context['process']);
if ($context['isFileImport']) {
$context['filename'] = $data['filename'] ?? null;
}
$this->context = $context;
}
public function getContext(): array
{
return $this->context;
}
protected function log(array $item, ?string $message = null): void
{
if (isLocalDevelopment()) {
return;
}
if (!$this->isLoggingEnabled()) {
return;
}
if (!$message) {
$message = sprintf('SAP: Processing change of \'%s\'', static::getType());
}
$this->logger->notice(
$message,
[
'Type' => static::getType(),
'Item' => $item,
]
);
}
protected function isLoggingEnabled(): bool
{
// vypinam logovani, protoze ho ted nevyuzivama a generuje to obrovske mnozstvi dat do Kibany
return false;
// plne logovani pouze pro monobrandy
if (in_array($this->configuration->getShopId(), [Configuration::SHOP_KEEN, Configuration::SHOP_HANNAH, Configuration::SHOP_RAFIKI])) {
return true;
}
return $this->logging;
}
private $tmpLastSync;
protected function getLastSyncTime(?string $type = null): ?int
{
$dbcfg = \Settings::getDefault();
$sap = $dbcfg->loadValue('SAP');
$this->tmpLastSync = time() - (60 * 2);
return $sap[$type ?: static::getType()] ?? null;
}
protected function updateLastSyncTime(): void
{
$dbcfg = \Settings::getDefault();
if (!($sap = $dbcfg->loadValue('SAP'))) {
$sap = [];
}
$sap[static::getType()] = $this->tmpLastSync ?: (time() - (60 * 2));
$dbcfg->saveValue('SAP', $sap, false);
}
}