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, ] ); } }