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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions