Перейти к содержимому

Синхронизация пользователей с внешней системой - ключ, группы, увольнения

Настраиваем регулярный обмен учётными записями с кадровой или учётной системой: ключ сопоставления, обновление, группы и уволенные сотрудники.

Что нужно знать заранее

Ключом сопоставления служит внешний код, а не адрес почты. Почта меняется у живых людей регулярно, и синхронизация по ней заводит второго пользователя вместо обновления первого.

Запись групп заменяет весь набор групп пользователя. Выданные вручную права после такой записи исчезают, и сотрудник теряет доступ к разделам, которые внешняя система не знает.

Ушедших сотрудников выключают, а не удаляют. Удаление рвёт связи с заказами, заявками и историей действий, а выключенная запись сохраняет всё это и не пускает человека на сайт.

Шаги

  1. Договориться о ключе сопоставления и получать его в каждой выгрузке.
  2. Сохранять внешний код при первом создании пользователя на сайте.
  3. Обновлять только изменившиеся записи, сверяя данные перед записью.
  4. Собирать группы из внешних и своих ролей, а не затирать их выгрузкой.
  5. Выключать отсутствующих в выгрузке вместо удаления учётных записей.

Решение

Ищем пользователя по внешнему коду:

$user = \Bitrix\Main\UserTable::getRow(['filter' => ['=XML_ID' => $row['code']],
'select' => ['ID', 'ACTIVE', 'EMAIL', 'NAME', 'LAST_NAME']]);
// внешний код - единственный надёжный ключ регулярной синхронизации
// поиск по почте заводит дубль, как только сотрудник её сменит

Внешний код приходит из системы-источника и там же не меняется. Поэтому смена фамилии, почты или логина на сайте не мешает найти ту же самую учётную запись при следующем обмене.

Создаём или обновляем запись:

$cuser = new \CUser();
$fields = ['NAME' => $row['name'], 'LAST_NAME' => $row['surname'],
'EMAIL' => $row['email'], 'XML_ID' => $row['code'], 'ACTIVE' => 'Y'];
$id = $user ? ($cuser->Update($user['ID'], $fields) ? $user['ID'] : 0)
: $cuser->Add($fields + ['LOGIN' => $row['code'], 'PASSWORD' => randString(16)]);
if (!$id) { $log[] = $cuser->LAST_ERROR; } // текст ошибки нужен в журнале

Ошибку записи обязательно кладут в журнал обмена. Молчаливый пропуск строки превращается в вопрос «почему у половины отдела нет доступа» через неделю после запуска.

Меняем группы, не теряя своих:

$current = \CUser::GetUserGroup($id); // что есть сейчас
$manual = array_intersect($current, [7, 12]); // роли, выданные руками
$cuser->Update($id, ['GROUP_ID' => array_unique(array_merge($manual, $fromSource))]);
// запись групп заменяет набор целиком, поэтому свои роли добавляют явно

Свои роли перечисляют списком в самом коде обмена. Так права, выданные администратором сайта, переживают каждую синхронизацию и не требуют повторной выдачи.

Выключаем ушедших сотрудников:

$seen = array_column($rows, 'code'); // коды из этой выгрузки
$rs = \Bitrix\Main\UserTable::getList(['filter' => ['=ACTIVE' => 'Y', '!=XML_ID' => false],
'select' => ['ID', 'XML_ID']]);
foreach ($rs as $u) {
if (!in_array($u['XML_ID'], $seen, true)) { $cuser->Update($u['ID'], ['ACTIVE' => 'N']); }
}

Выключение вместо удаления сохраняет заказы, заявки и историю действий человека. Оно же обратимо: вернувшийся сотрудник получает прежнюю учётную запись, а не новую с нуля.

Обрабатываем выгрузку порциями:

foreach (array_chunk($rows, 200) as $i => $chunk) {
processChunk($chunk);
\Bitrix\Main\Diag\Debug::writeToFile(['порция' => $i], date('H:i:s'), 'users-sync.log');
}

Типичные проблемы

После обмена в списке появились дубли сотрудников.

Сопоставление идёт по адресу почты, а он у людей меняется. Ключом синхронизации делают внешний код системы-источника и сохраняют его при создании.

У менеджеров пропали права после ночного обмена.

Запись групп заменила весь набор групп пользователя данными из выгрузки. Роли, выданные вручную, добавляют к внешним списком прямо в коде обмена.

Заказы потеряли покупателя после чистки уволенных.

Учётные записи удалялись, а не выключались, и связи с заказами оборвались. Отсутствующих в выгрузке переводят в неактивные и оставляют в базе.

Обмен падает по времени на середине выгрузки.

Все записи обрабатываются одним проходом без порций и без сохранения места. Выгрузку режут на порции по паре сотен строк и ведут журнал прохода.

Каждый обмен рассылает сотрудникам письма о смене данных.

Запись выполняется для всех строк подряд, включая неизменившиеся. Перед записью сверяют поля и обновляют только тех, у кого данные действительно изменились.

Частые вопросы

Что брать ключом сопоставления?

Внешний код из системы-источника, сохранённый в поле внешнего кода пользователя. Почта и логин меняются, а код остаётся прежним всю жизнь записи.

Как быть с паролями при создании?

Генерировать случайный пароль и отправлять человеку ссылку на его смену. Пароль из выгрузки нельзя ни хранить, ни пересылать письмом.

Почему обмен стал долгим со временем?

Обновляются все записи подряд, включая неизменившиеся, и на каждой срабатывают события. Сверка перед записью убирает большую часть работы.

Удалять ли учётные записи уволенных?

Нет, их выключают: удаление рвёт связи с заказами и историей. Полное удаление делают только по отдельному запросу на удаление персональных данных.

Куда девать сотрудников без внешнего кода?

Оставлять как есть и не трогать обменом: это ручные учётные записи. Фильтр по непустому внешнему коду отделяет их от синхронизируемых.

Смежное

Первоисточник