Skip to content

Порча данных клиентов при синхронизации истории #415

@drNovikov

Description

@drNovikov

При совпадении нескольких факторов синхронизация истории пользователей агентом модуля приводит к порче данных:

  1. Имя перезаписывается именем предыдущего (в ответе API) пользователя.
  2. Логин и email перезаписываются сгенерированным значением вида user_***@example.com

Порча данных происходит при совпадении нескольких факторов:

  1. В Битриксе включена обязательность номера телефона для регистрации аккаунта.
  2. В CRM создан (например, при создании диалога из мессенджера) пользователь без email и номера телефона, которого нет в битриксе (поэтому нет externalId)
  3. Агент модуля получает историю существующего в битриксе пользователя после истории указанного выше несуществующего в битриксе пользователя без поля create.

Проблема в RetailCrmHistory->customerHistory() и переиспользовании объекта CustomerBuilder

$customerBuilder = new CustomerBuilder();

  1. Eсли из CRM прилетает клиент без поля create, то пропускается проверка в начале цикла.
    if (isset($customer['create']) && empty($customer['phones'])) {
  2. Если нет email, то CustomerBuilder->build() генерирует его в виде user_***@example.com.
    $customerBuilder->setDataCrm($customer)->build();
  3. Далее так как нет externalId модуль пытается создать пользователя, но без телефона получает ошибку и выходит из цикла, не дойдя до $customerBuilder->reset().
  4. Состояние билдера теперь содержит имя, полученное из API, и сгенерированный логин и email, которые достаются следующему -- уже реальному пользователю -- и портят учетную запись.
  5. После этого испорченные данные улетают уже из Битрикса в Retail CRM и другие системы.
$customerBuilder = new CustomerBuilder(); // Переиспользуется в цикле, внутри содержит Customer

            foreach ($customers as $customer) {
                if (function_exists('retailCrmBeforeCustomerSave')) {
                    $newResCustomer = retailCrmBeforeCustomerSave($customer);
                    if (is_array($newResCustomer) && !empty($newResCustomer)) {
                        $customer = $newResCustomer;
                    } elseif ($newResCustomer === false) {
                        RCrmActions::eventLog('RetailCrmHistory::customerHistory', 'retailCrmBeforeCustomerSave()', 'UserCrmId = ' . $customer['id'] . '. Sending canceled after retailCrmBeforeCustomerSave');

                        continue; 
                    }
                }

                if (isset($customer['deleted'])) {
                    continue;
                }

                if (RetailcrmConfigProvider::isPhoneRequired()) { // Эта проверка пропускается, если не приходит поле create
                    if (isset($customer['create']) && empty($customer['phones'])) {
                        Logger::getInstance()->write('$customer["phones"] is empty. Customer ' . $customer['id'] . ' cannot be created', 'createCustomerError');
                        continue; // Если обязателен номер телефона, а из CRM приходит без него и без email, то здесь переходит на следующую итерацию без сброса билдера 
                    }
                }

В качестве хотфикса предлагаю прекратить переиспользовать экземпляр CustomerBuilder и инициализировать его каждый раз в начале цикла.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions