301 lines
10 KiB
PHP
301 lines
10 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace External\HannahBundle\SAP\Synchronizer;
|
|
|
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
|
use External\HannahBundle\SAP\Exception\SAPException;
|
|
use External\HannahBundle\SAP\Exception\SAPManyException;
|
|
use External\HannahBundle\SAP\MappingType;
|
|
use External\HannahBundle\SAP\Util\DataFactory\UserDataFactory;
|
|
use External\HannahBundle\SAP\Util\SAPClient;
|
|
use KupShop\AdminBundle\Util\ActivityLog;
|
|
use Query\Operator;
|
|
|
|
class UserSynchronizer extends BaseSynchronizer
|
|
{
|
|
private const RETRY_COUNT_MAX = 4;
|
|
|
|
protected static $type = 'user';
|
|
|
|
/** @var SAPClient */
|
|
private $sapClient;
|
|
|
|
/** @var UserDataFactory */
|
|
private $userDataFactory;
|
|
|
|
/**
|
|
* @required
|
|
*/
|
|
public function setUserDataFactory(UserDataFactory $userDataFactory): void
|
|
{
|
|
$this->userDataFactory = $userDataFactory;
|
|
}
|
|
|
|
/**
|
|
* @required
|
|
*/
|
|
public function setSAPClient(SAPClient $sapClient): void
|
|
{
|
|
$this->sapClient = $sapClient;
|
|
}
|
|
|
|
public function processToSAP(): void
|
|
{
|
|
if (isLocalDevelopment()) {
|
|
return;
|
|
}
|
|
|
|
$lastSyncTime = $this->getLastSyncTime();
|
|
|
|
$orX = ['su.id_sap IS NULL'];
|
|
if ($lastSyncTime) {
|
|
$orX[] = 'u.date_updated IS NOT NULL AND u.date_updated >= :lastSync';
|
|
} else {
|
|
$orX[] = 'u.date_updated IS NOT NULL';
|
|
}
|
|
|
|
$qb = sqlQueryBuilder()
|
|
->select('u.id, su.id_sap')
|
|
->from('users', 'u')
|
|
->leftJoin('u', 'sap_users', 'su', 'u.id = su.id_user')
|
|
->andWhere(Operator::equals(['u.figure' => 'Y']))
|
|
// pokud neni vyplnene jmeno a prijmeni, tak SAP hazi chybu a nechce ho zapsat
|
|
->andWhere('u.name != "" AND u.surname != ""')
|
|
->andWhere(Operator::orX($orX));
|
|
|
|
if ($lastSyncTime) {
|
|
$qb->addParameters(
|
|
[
|
|
'lastSync' => date('Y-m-d H:i:s', $lastSyncTime),
|
|
]
|
|
);
|
|
}
|
|
|
|
$newlyAddedUsers = [];
|
|
|
|
foreach ($qb->execute() as $item) {
|
|
if ($result = $this->updateUserToSAP($item)) {
|
|
[$user, $isNewlyAddedUser] = $result;
|
|
|
|
if ($isNewlyAddedUser) {
|
|
$newlyAddedUsers[] = $user;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->updateLastSyncTime();
|
|
|
|
if (!empty($newlyAddedUsers)) {
|
|
sleep(3);
|
|
// update data of newly added users to SAP (bcs of price level definition)
|
|
foreach ($newlyAddedUsers as $user) {
|
|
$this->updateNewlyAddedUserFromSAP($user);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zalozi nebo aktualizuje uzivatele v SAPu.
|
|
*/
|
|
public function updateUserToSAP(array $item, bool $force = false): ?array
|
|
{
|
|
$user = \User::createFromId($item['id']);
|
|
|
|
if ($force === false && $this->getUserTryCount($user) > self::RETRY_COUNT_MAX) {
|
|
return null;
|
|
}
|
|
|
|
$isNewlyAddedUser = false;
|
|
|
|
try {
|
|
$passwordInSap = $user->getCustomData()['passwordInSap'] ?? null;
|
|
if ($item['id_sap']) {
|
|
$this->logUserUpdate($user, 'updateUserToSAP::customerUpdate');
|
|
// obecny update uzivatele
|
|
$this->sapClient->customerUpdate(
|
|
$item['id_sap'],
|
|
$this->userDataFactory->getData(['user' => $user, 'type' => UserDataFactory::DATA_UPDATE]),
|
|
$this->configuration->getProcessByUser($user)
|
|
);
|
|
|
|
// aktualizovat heslo uzivatele, protoze si ho na shopu zmenil
|
|
if (!empty($user->passw) && $user->passw !== $passwordInSap) {
|
|
$this->logUserUpdate($user, 'updateUserToSAP::customerChangePassword');
|
|
$this->sapClient->customerChangePassword($item['id_sap'], $user->passw);
|
|
$user->setCustomData('passwordInSap', $user->passw);
|
|
}
|
|
|
|
// resetovat counter pro sapUpdateTry
|
|
$user->setCustomData('sapUpdateTry', null);
|
|
} else {
|
|
// zalozeni uzivatele
|
|
try {
|
|
$this->logUserUpdate($user, 'updateUserToSAP::customerCreate');
|
|
$result = $this->sapClient->customerCreate(
|
|
$this->userDataFactory->getData(['user' => $user]),
|
|
$this->configuration->getProcessByUser($user)
|
|
);
|
|
// resetovat counter pro sapUpdateTry
|
|
$user->setCustomData('sapUpdateTry', null);
|
|
} catch (SAPManyException $e) {
|
|
if ($e->hasExceptionWithCode(['477'])) {
|
|
$sapUserData = $user->getCustomData()['sap'] ?? [];
|
|
$sapUserData['cards'] = [];
|
|
$user->setCustomData('sap', $sapUserData);
|
|
}
|
|
|
|
// zalogovat chybu do Activity logu
|
|
if (!$e->hasExceptionWithCode(['020', '095'])) {
|
|
$this->handleActivityLogError(
|
|
$user,
|
|
sprintf('[SAP] Chyba během zakládání uživatele "%s" v SAPu: %s', $user->email, $e->getMessage())
|
|
);
|
|
|
|
return null;
|
|
}
|
|
|
|
// pokusit se nacist uz existujiciho uzivatele
|
|
$this->logUserUpdate($user, 'updateUserToSAP::customerValidate');
|
|
if ($result = $this->sapClient->customerValidate($user->email, $user->passw, $this->configuration->getProcessByUser($user))) {
|
|
$result = $result->CustomerData;
|
|
}
|
|
}
|
|
|
|
if (!empty($result->CustomerId)) {
|
|
$this->createUserMapping($user, $result);
|
|
$isNewlyAddedUser = true;
|
|
}
|
|
|
|
$user->setCustomData('passwordInSap', $user->passw);
|
|
}
|
|
} catch (SAPException $e) {
|
|
if (isLocalDevelopment()) {
|
|
throw $e;
|
|
}
|
|
|
|
if ($e instanceof SAPManyException) {
|
|
// autofix duplicated sap_users
|
|
if ($item['id_sap'] && $e->hasExceptionWithCode(['020'])) {
|
|
$sapUsersCount = sqlQueryBuilder()
|
|
->select('id_sap')
|
|
->from('sap_users')
|
|
->where(Operator::equals(['id_user' => $user->id]))
|
|
->execute()->rowCount();
|
|
|
|
if ($sapUsersCount > 1) {
|
|
sqlQueryBuilder()
|
|
->delete('sap_users')
|
|
->where(Operator::equals(['id_user' => $user->id, 'id_sap' => $item['id_sap']]))
|
|
->execute();
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->handleActivityLogError(
|
|
$user,
|
|
sprintf('[SAP] Nepodařilo se synchronizovat uživatele "%s": %s', $user->email, $e->getMessage())
|
|
);
|
|
} catch (\Throwable $e) {
|
|
$this->sentryLogger->captureException($e);
|
|
}
|
|
|
|
return [$user, $isNewlyAddedUser];
|
|
}
|
|
|
|
public function updateNewlyAddedUserFromSAP(\User $user): void
|
|
{
|
|
try {
|
|
$this->logUserUpdate($user, 'updateNewlyAddedUserFromSAP::customerValidate');
|
|
$result = $this->sapClient->customerValidate(
|
|
mb_strtolower($user->email),
|
|
$user->passw,
|
|
$this->configuration->getProcessByUser($user)
|
|
);
|
|
} catch (SAPManyException $e) {
|
|
$this->sentryLogger->captureException($e);
|
|
|
|
return;
|
|
} finally {
|
|
$this->logger->notice('updateNewlyAddedUserFromSAP result', ['UserId' => $user->id, 'UserData' => $result ?? null]);
|
|
}
|
|
|
|
$customer = $result->CustomerData;
|
|
|
|
$this->sapUtil->updateUser($user, $customer);
|
|
}
|
|
|
|
protected function getHandledFields(): array
|
|
{
|
|
return [];
|
|
}
|
|
|
|
private function createUserMapping(\User $user, object $sapCustomer): void
|
|
{
|
|
try {
|
|
$this->sapUtil->createMapping(MappingType::USERS, $sapCustomer->CustomerId, (int) $user->id);
|
|
} catch (UniqueConstraintViolationException $e) {
|
|
// pokud SAP ID pro daneho uzivatele uz ma nastavene nekdo jiny
|
|
$duplicatedUser = sqlQueryBuilder()
|
|
->select('u.id, u.email')
|
|
->from('users', 'u')
|
|
->join('u', 'sap_users', 'su', 'su.id_user = u.id')
|
|
->andWhere(Operator::equals(['su.id_sap' => $sapCustomer->CustomerId]))
|
|
->execute()->fetchAssociative();
|
|
|
|
if (!$duplicatedUser) {
|
|
throw $e;
|
|
}
|
|
|
|
if ($duplicatedUser['email'] !== $sapCustomer->PersonalData->Email) {
|
|
sqlGetConnection()->transactional(function () use ($duplicatedUser, $user, $sapCustomer) {
|
|
$this->sapUtil->deleteMapping(MappingType::USERS, $duplicatedUser['id']);
|
|
$this->createUserMapping($user, $sapCustomer);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private function handleActivityLogError(\User $user, string $message): void
|
|
{
|
|
$tryCount = $this->getUserTryCount($user);
|
|
|
|
// prestavam zapisivat activity log, dokud se nepodari zapis, aby to nespamovalo silene moc
|
|
if ($tryCount > self::RETRY_COUNT_MAX) {
|
|
return;
|
|
}
|
|
|
|
// zapisu do activity logu
|
|
$this->activityLog->addActivityLog(
|
|
ActivityLog::SEVERITY_ERROR,
|
|
ActivityLog::TYPE_SYNC,
|
|
$message
|
|
);
|
|
|
|
// updatuju counter na uzivateli
|
|
$user->setCustomData('sapUpdateTry', ++$tryCount);
|
|
}
|
|
|
|
private function getUserTryCount(\User $user): int
|
|
{
|
|
return (int) ($user->getCustomData(true)['sapUpdateTry'] ?? 1);
|
|
}
|
|
|
|
private function logUserUpdate(\User $user, string $source): void
|
|
{
|
|
if (isLocalDevelopment()) {
|
|
return;
|
|
}
|
|
|
|
$this->logger->notice(
|
|
message: 'SAP: User update to SAP; ID: '.$user->id,
|
|
context: [
|
|
'userId' => $user->id,
|
|
'email' => $user->email,
|
|
'source' => $source,
|
|
]
|
|
);
|
|
}
|
|
}
|