Пользователи и управленческая иерархия

Сервисы пользователей определяют место пользователя в структуре по его идентификатору. С их помощью можно проверить роль, найти руководителей и подчиненных, получить управляемые подразделения и команды.

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

Подготовить сервисы

Подготовьте сервисы для сценариев в два шага.

  1. Выберите сервис по задаче:

    • сервис пользователей узлов Bitrix\HumanResources\Public\Service\Node\UserService — для подразделений и команд в одном сценарии,

    • сервис пользователей подразделений Bitrix\HumanResources\Public\Service\Department\UserService — только для подразделений,

    • сервис пользователей команд Bitrix\HumanResources\Public\Service\Team\UserService — только для команд,

    • сервис доступа Bitrix\HumanResources\Public\Service\AccessService — для поиска пользователей, которым роль дает право увольнения.

  2. Подключите модуль humanresources и получите выбранные сервисы через контейнер сервисов Bitrix\HumanResources\Public\Service\Container.

Если сценарий начинается с известного узла, используйте сервис участников. Если сначала нужно найти подразделение или команду, используйте сервис узлов.

Каждый сценарный блок содержит необходимые директивы use, входные данные и получение сервиса. Замените числовые идентификаторы на идентификаторы существующих пользователей, узлов и структур.

Пример. Код подключает модуль, получает сервисы пользователей и доступа.

use Bitrix\HumanResources\Public\Service\Container;
use Bitrix\Main\Loader;

// Подключение модуля перед получением сервисов
if (!Loader::includeModule('humanresources'))
{
    throw new \RuntimeException('Модуль humanresources не установлен');
}

$userService = Container::getUserService();
$departmentUserService = Container::getUserDepartmentService();
$teamUserService = Container::getUserTeamService();
$accessService = Container::getAccessService();

Сервисы пользователей подразделений и команд повторяют часть операций сервиса пользователей узлов, но сами выбирают тип узла. Например, Department\UserService::getUserHeads() соответствует вызову метода Node\UserService::getUserHeads() с типом DEPARTMENT.

Построить управленческую цепочку

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

Проверить отношение руководитель — сотрудник

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

Метод сервиса пользователей узлов isManagerForEmployee() проверяет, является ли один пользователь руководителем другого.

Передайте параметры:

  • $userId — идентификатор предполагаемого руководителя,

  • $employeeId — идентификатор сотрудника,

  • $nodeTypes — типы узлов для проверки. Передайте подразделения, команды, оба типа или null, чтобы проверить все типы.

Пример. Код проверяет, является ли пользователь 10 руководителем пользователя 20 в иерархии подразделений.

use Bitrix\HumanResources\Public\Service\Container;
use Bitrix\HumanResources\Type\NodeEntityType;

// Идентификаторы предполагаемого руководителя и сотрудника
$managerId = 10;
$employeeId = 20;

$userService = Container::getUserService();
$isManager = $userService->isManagerForEmployee(
    userId: $managerId,
    employeeId: $employeeId,
    nodeTypes: [NodeEntityType::DEPARTMENT],
);

Метод сопоставляет положение пользователей в иерархии и числовой приоритет их ролей. Он возвращает true при двух условиях:

  • пользователи находятся в одном узле либо предполагаемый руководитель состоит в родительском узле,

  • роль предполагаемого руководителя имеет приоритет не ниже роли сотрудника.

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

Метод определяет управленческую связь, но не подтверждает наличие конкретной роли. Например, заместитель вышестоящего узла не считается руководителем для руководителя нижестоящего узла.

Методы isManagerForEmployee() сервисов пользователей подразделений и команд принимают только идентификаторы двух пользователей. Каждый сервис ограничивает проверку своим типом узла.

Получить подчиненных

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

Метод сервиса пользователей узлов getSubordinateUserIds() возвращает массив идентификаторов подчиненных. Передайте в него параметры:

  • $userId — идентификатор руководителя,

  • $nodeEntityType — тип узлов DEPARTMENT или TEAM,

  • $direct — режим обхода. Значение true возвращает ближайший уровень, а false — всех участников поддерева.

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

Пример. Код получает непосредственных и всех подчиненных руководителя 10 в иерархии подразделений.

use Bitrix\HumanResources\Public\Service\Container;
use Bitrix\HumanResources\Type\NodeEntityType;

// Идентификатор руководителя
$managerId = 10;

$userService = Container::getUserService();
$directUserIds = $userService->getSubordinateUserIds(
    userId: $managerId,
    nodeEntityType: NodeEntityType::DEPARTMENT,
    direct: true,
);

$allUserIds = $userService->getSubordinateUserIds(
    userId: $managerId,
    nodeEntityType: NodeEntityType::DEPARTMENT,
    direct: false,
);

Сам руководитель не входит в результат. Если пользователь не руководит узлами выбранного типа, метод возвращает пустой массив.

Методы сервисов пользователей подразделений и команд getSubordinateUserIds() принимают идентификатор руководителя и необязательный параметр $direct. По умолчанию $direct равен true, поэтому методы возвращают непосредственных подчиненных. Тип узла передавать не нужно.

Получить руководителей и заместителей

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

Методы сервиса пользователей узлов getUserHeads() и getUserDeputies() принимают идентификатор пользователя и тип узла. Для подразделений передайте NodeEntityType::DEPARTMENT, для команд — NodeEntityType::TEAM. Оба метода возвращают NodeMemberCollection, а при ошибке — пустую коллекцию.

Пример. Код получает руководителей и заместителей пользователя 25 в иерархии подразделений.

use Bitrix\HumanResources\Public\Service\Container;
use Bitrix\HumanResources\Type\NodeEntityType;

// Идентификатор пользователя
$userId = 25;

$userService = Container::getUserService();
$heads = $userService->getUserHeads(
    $userId,
    NodeEntityType::DEPARTMENT,
);

$deputies = $userService->getUserDeputies(
    $userId,
    NodeEntityType::DEPARTMENT,
);

Методы сервисов пользователей подразделений и команд getUserHeads() и getUserDeputies() не принимают тип узла, потому что каждый сервис уже зафиксировал свой тип.

Получить руководителей нескольких пользователей

Пакетный поиск связывает каждого пользователя со своей коллекцией руководителей. Используйте его для списка заявок или сотрудников, чтобы не собирать результат отдельными вызовами getUserHeads() и getUserDeputies().

Метод сервиса пользователей узлов getUsersManagers() принимает параметры:

  • $userIds — идентификаторы пользователей,

  • $nodeEntityTypes — типы узлов, в которых нужно искать руководителей,

  • $includeDeputies — признак включения заместителей. По умолчанию метод их не включает.

Метод возвращает ассоциативный массив array<int, NodeMemberCollection>. Ключ содержит идентификатор пользователя, значение — коллекцию его руководителей.

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

use Bitrix\HumanResources\Public\Service\Container;
use Bitrix\HumanResources\Type\NodeEntityType;

// Идентификаторы пользователей
$userIds = [10, 20, 30];

$userService = Container::getUserService();
$managersByUser = $userService->getUsersManagers(
    userIds: $userIds,
    nodeEntityTypes: [
        NodeEntityType::DEPARTMENT,
        NodeEntityType::TEAM,
    ],
    includeDeputies: true,
);

$managerIdsByUser = [];
foreach ($managersByUser as $userId => $managers)
{
    $managerIdsByUser[$userId] = $managers->getUniqueEntityIds();
}

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

Методы getUsersManagers() сервисов пользователей подразделений и команд используют фиксированный тип узла. Выбирайте их, если нужны руководители только подразделений или только команд.

Определить положение пользователя

Положение пользователя определяет, какие узлы он возглавляет, где замещает руководителя и к каким административным подразделениям относится. Для простого условия получите bool. Для последующего запроса к узлам получите их идентификаторы или полные связи NodeMember.

Проверить роль в подразделении или команде

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

Проверка

Подразделения

Команды

Пользователь руководит хотя бы одним узлом

isHeadOfDepartment()

isHeadOfTeam()

Пользователь замещает руководителя хотя бы одного узла

isDeputyOfDepartment()

isDeputyOfTeam()

Пользователь руководит или замещает руководителя

isHeadOrDeputyOfDepartment()

isHeadOrDeputyOfTeam()

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

Пример. Код проверяет, руководит ли пользователь 25 подразделением или командой либо замещает их руководителя.

use Bitrix\HumanResources\Public\Service\Container;

// Идентификатор пользователя
$userId = 25;

$departmentUserService = Container::getUserDepartmentService();
$teamUserService = Container::getUserTeamService();

$isDepartmentManager = $departmentUserService
    ->isHeadOrDeputyOfDepartment($userId)
;

$isTeamManager = $teamUserService
    ->isHeadOrDeputyOfTeam($userId)
;

Сервис пользователей подразделений проверяет роли руководителя и заместителя подразделения. Сервис пользователей команд проверяет соответствующие командные роли.

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

Для поиска первой или всех связей пользователя по ролям используйте методы findByUserIdAndRoleXmlIds() и findAllByUserIdAndRoleXmlIds().

Подробнее о параметрах и результате методов читайте в статье Участники и роли.

Получить управляемые узлы

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

Все методы принимают идентификатор пользователя. Чтобы получить идентификаторы управляемых узлов, вызовите:

  • getDepartmentIdsWhereUserIsHead() — для подразделений, где пользователь — руководитель,

  • getDepartmentIdsWhereUserIsDeputy() — для подразделений, где пользователь — заместитель,

  • getTeamIdsWhereUserIsHead() — для команд, где пользователь — руководитель,

  • getTeamIdsWhereUserIsDeputy() — для команд, где пользователь — заместитель.

Методы возвращают array<int> или пустой массив, если совпадений нет.

Чтобы получить связи с узлами, где пользователь — руководитель или заместитель, вызовите getNodeMembersWhereUserIsHeadOrDeputy() у сервиса подразделений или команд. Метод возвращает NodeMemberCollection.

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

Пример. Код получает управляемые узлы пользователя 25. Первый вызов возвращает подразделения, где пользователь — руководитель. Второй — команды, где он руководитель или заместитель.

use Bitrix\HumanResources\Public\Service\Container;

// Идентификатор пользователя
$userId = 25;

$departmentUserService = Container::getUserDepartmentService();
$teamUserService = Container::getUserTeamService();

$departmentIds = $departmentUserService
    ->getDepartmentIdsWhereUserIsHead($userId)
;

$managedTeamMembers = $teamUserService
    ->getNodeMembersWhereUserIsHeadOrDeputy($userId)
;
$teamIds = $managedTeamMembers->getNodeIds();

Получить цепочки команд пользователя

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

Метод сервиса пользователей команд getTeamChainsByUserId() принимает идентификатор пользователя и направление сортировки $orderDirection. Результат имеет тип list<NodeCollection>: каждый элемент содержит одну цепочку узлов.

Параметр $orderDirection принимает значения перечисления Bitrix\HumanResources\Enum\Direction:

  • ROOT — от текущей команды к корню, это значение метод выбирает по умолчанию,

  • CHILD — от корня к текущей команде.

Чтобы вывести каждую цепочку от корня к текущей команде, передайте Direction::CHILD.

Пример. Код получает цепочки команд пользователя 25 и выводит названия команд от корня к текущей команде.

use Bitrix\HumanResources\Enum\Direction;
use Bitrix\HumanResources\Public\Service\Container;

// Идентификатор пользователя
$userId = 25;

$teamUserService = Container::getUserTeamService();
$chains = $teamUserService->getTeamChainsByUserId(
    userId: $userId,
    orderDirection: Direction::CHILD,
);

foreach ($chains as $chain)
{
    foreach ($chain as $team)
    {
        echo $team->name . PHP_EOL;
    }
}

Если идентификатор пользователя меньше или равен нулю, метод возвращает пустой массив.

Проверить пользователей административной структуры

Сервис пользователей подразделений помогает отделить сотрудников административной структуры от остальных пользователей. Он проверяет одного пользователя, фильтрует массив идентификаторов и считает сотрудников в структуре по умолчанию. Командные связи не влияют на эти результаты.

Проверить одного сотрудника

Проверьте участие пользователя после назначения или перед действием, доступным только сотрудникам административной структуры. Метод isEmployee() принимает идентификатор пользователя и возвращает true, если пользователь связан хотя бы с одним существующим подразделением.

Пример. Код проверяет, входит ли пользователь 25 в административную структуру, и выводит сообщение при отрицательном результате.

use Bitrix\HumanResources\Public\Service\Container;

// Идентификатор пользователя
$userId = 25;

$departmentUserService = Container::getUserDepartmentService();
$isEmployee = $departmentUserService->isEmployee($userId);

if (!$isEmployee)
{
    echo 'Пользователь не входит в административную структуру';
}

Отсутствие связи возвращает false. Метод не проверяет участие в командах.

Отфильтровать идентификаторы сотрудников

Отфильтруйте идентификаторы перед пакетной операцией, которая должна обрабатывать только сотрудников административной структуры. Метод filterEmployeeIds() принимает массив идентификаторов и оставляет пользователей, связанных хотя бы с одним существующим подразделением. Нечисловые значения и числа меньше или равные нулю не попадают в результат. Пустой входной массив возвращает пустой массив.

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

use Bitrix\HumanResources\Public\Service\Container;

// Идентификаторы пользователей
$userIds = [1, 2, 3, 100500];

$departmentUserService = Container::getUserDepartmentService();
$employeeIds = $departmentUserService->filterEmployeeIds($userIds);

Результат содержит только идентификаторы пользователей из административной структуры. Метод не гарантирует порядок элементов и сохранение исходных ключей. Сравнивайте идентификаторы по значениям.

Получить количество сотрудников

Получите общее количество сотрудников, если нужно показать показатель для всей административной структуры. Метод getTotalEmployeeCount() начинает с корневого узла структуры по умолчанию и учитывает уникальных участников всех дочерних узлов. Метод возвращает одно число без разбивки по подразделениям.

Пример. Код получает количество уникальных сотрудников в структуре по умолчанию.

use Bitrix\HumanResources\Public\Service\Container;

$departmentUserService = Container::getUserDepartmentService();
$employeeCount = $departmentUserService->getTotalEmployeeCount();

Метод возвращает 0, если не находит структуру по умолчанию или ее корневой узел.

Назначить пользователя в подразделения

Сервис пользователей подразделений Department\UserService назначает пользователя в одно или несколько подразделений. Такое назначение не изменяет связи с командами. Для новой связи с подразделением сервис назначает роль сотрудника. При повторной активации существующей связи роль сохраняется, а в режиме замены исключенные связи удаляются вместе с ролями.

Перед вызовом подготовьте:

  • положительный идентификатор существующего пользователя,

  • внутренние идентификаторы узлов типа DEPARTMENT, а не идентификаторы подразделений классического API,

  • идентификатор структуры, если назначение относится не к структуре по умолчанию.

Методы назначения не принимают действие доступа и идентификатор текущего пользователя. Проверьте право на изменение состава подразделений на уровне вызывающего сценария до обращения к сервису.

Добавить подразделения к текущим

Добавьте подразделения к текущим, если пользователь должен состоять в нескольких подразделениях одновременно. Метод assignToDepartments() сохраняет существующие связи и активирует неактивную связь, если пользователь уже был связан с целевым подразделением.

Передайте параметры:

  • $userId — идентификатор пользователя,

  • $departmentIds — непустой массив внутренних идентификаторов подразделений,

  • $replaceExisting — режим замены, значение false сохраняет текущие подразделения,

  • $structureId — идентификатор структуры. Значение null выбирает структуру по умолчанию.

Пример. Код сохраняет текущие связи пользователя 25 и добавляет подразделения 15 и 27 из структуры 2.

use Bitrix\HumanResources\Public\Service\Container;

// Идентификатор пользователя, структуры и подразделений
$userId = 25;
$structureId = 2;
$departmentIds = [15, 27];

$departmentUserService = Container::getUserDepartmentService();
$departmentUserService->assignToDepartments(
    userId: $userId,
    departmentIds: $departmentIds,
    replaceExisting: false,
    structureId: $structureId,
);

Каждый элемент $departmentIds должен обозначать подразделение структуры $structureId. Повторный вызов с теми же данными не создает еще одну связь.

Заменить текущие подразделения

Оставьте пользователю одно подразделение, если кадровый сценарий требует однозначного положения в административной структуре. Метод assignToSingleDepartment() принимает идентификаторы пользователя, подразделения и структуры. Он удаляет остальные связи пользователя с подразделениями, но не изменяет команды.

Пример. Код оставляет пользователю 25 только подразделение 15 из структуры 2.

use Bitrix\HumanResources\Public\Service\Container;

// Идентификатор пользователя, структуры и подразделения
$userId = 25;
$structureId = 2;
$departmentId = 15;

$departmentUserService = Container::getUserDepartmentService();
$departmentUserService->assignToSingleDepartment(
    userId: $userId,
    departmentId: $departmentId,
    structureId: $structureId,
);

Для замены текущих связей несколькими подразделениями вызовите assignToDepartments() с параметром replaceExisting: true. В $departmentIds передайте полный набор подразделений, которые нужно сохранить.

Пример. Код заменяет подразделения пользователя 25 на подразделения 15 и 27 из структуры 2.

use Bitrix\HumanResources\Public\Service\Container;

// Идентификатор пользователя, структуры и подразделений
$userId = 25;
$structureId = 2;
$departmentIds = [15, 27];

$departmentUserService = Container::getUserDepartmentService();
$departmentUserService->assignToDepartments(
    userId: $userId,
    departmentIds: $departmentIds,
    replaceExisting: true,
    structureId: $structureId,
);

Режим замены учитывает связи пользователя во всех структурах, а не только в структуре из $structureId. Он удаляет все подразделения, которых нет в $departmentIds. Передавайте полный набор связей, который нужно сохранить.

Проверить назначение

Проверьте назначение через API модуля humanresources, потому что методы назначения ничего не возвращают. Метод сервиса узлов findAllByMemberEntityId() принимает идентификатор пользователя, структуру, тип узлов и фильтр активности. Для проверки подразделений передайте тип DEPARTMENT.

Чтобы учесть активные и неактивные подразделения, передайте фильтр NodeActiveFilter::ALL. Затем сравните полученные идентификаторы с массивом $departmentIds.

Пример. Код проверяет, что пользователь 25 связан с подразделениями 15 и 27 из структуры 2.

use Bitrix\HumanResources\Enum\NodeActiveFilter;
use Bitrix\HumanResources\Public\Service\Container;
use Bitrix\HumanResources\Type\NodeEntityType;

// Идентификатор пользователя, структуры и ожидаемых подразделений
$userId = 25;
$structureId = 2;
$departmentIds = [15, 27];

$nodeService = Container::getNodeService();
$departments = $nodeService->findAllByMemberEntityId(
    memberEntityId: $userId,
    structureId: $structureId,
    nodeTypes: [NodeEntityType::DEPARTMENT],
    nodeActiveFilter: NodeActiveFilter::ALL,
);

$missingDepartmentIds = array_diff(
    $departmentIds,
    $departments->getIds(),
);

if ($missingDepartmentIds !== [])
{
    throw new \RuntimeException('Не все подразделения назначены');
}

В режиме замены дополнительно сравните полный массив $departments->getIds() с ожидаемым набором.

Сервис не записывает пользовательское поле UF_DEPARTMENT напрямую. После изменения связей модуль синхронизирует поле в фоновом задании.

До завершения фонового задания проверяйте назначение через API модуля humanresources. Поле UF_DEPARTMENT читайте после завершения задания.

Обработать ошибку назначения

Методы назначения выбрасывают \InvalidArgumentException в следующих случаях:

  • массив подразделений пуст,

  • идентификатор не относится к узлу типа DEPARTMENT,

  • подразделение не принадлежит выбранной структуре.

Перехватите исключение на уровне приложения, чтобы добавить в сообщение идентификатор пользователя и набор подразделений.

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

use Bitrix\HumanResources\Public\Service\Container;

// Идентификатор пользователя, структуры и подразделений
$userId = 25;
$structureId = 2;
$departmentIds = [15, 27];

$departmentUserService = Container::getUserDepartmentService();

try
{
    $departmentUserService->assignToDepartments(
        userId: $userId,
        departmentIds: $departmentIds,
        structureId: $structureId,
    );
}
catch (\InvalidArgumentException $exception)
{
    throw new \RuntimeException(
        sprintf(
            'Не удалось назначить пользователя %d в подразделения [%s]',
            $userId,
            implode(', ', $departmentIds),
        ),
        previous: $exception,
    );
}

Проверить право увольнения

Получите пользователей с правом увольнения, чтобы проверить назначения ролей. Метод сервиса доступа getUserIdsWithFirePermission() не принимает параметров и возвращает идентификаторы активных пользователей, которым роль дает право HUMAN_RESOURCES_FIRE_EMPLOYEE.

Пример. Код получает идентификаторы активных пользователей, которым роль дает право увольнения.

use Bitrix\HumanResources\Public\Service\Container;

$accessService = Container::getAccessService();
$userIdsWithFirePermission = $accessService
    ->getUserIdsWithFirePermission()
;

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

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

Обработать пустой результат и исключения

Если данных нет, сервисы возвращают пустые значения. Некоторые ошибки они сообщают исключениями. Способ обработки зависит от конкретного метода.

Проверить пустой результат

При отсутствии данных результат зависит от метода:

  • проверки ролей и отношения «руководитель — сотрудник» возвращают false,

  • метод isEmployee() возвращает false, если у пользователя нет связи с существующим подразделением,

  • методы идентификаторов и цепочек возвращают пустой массив,

  • методы getUserHeads() и getUserDeputies() при ошибке возвращают пустую NodeMemberCollection,

  • методы получения связей возвращают пустую NodeMemberCollection, если совпадений нет,

  • метод getUsersManagers() пропускает идентификаторы пользователей меньше или равные нулю,

  • метод getTotalEmployeeCount() возвращает 0, если не находит структуру по умолчанию или ее корневой узел,

  • метод getUserIdsWithFirePermission() возвращает пустой массив, если подходящих назначений ролей нет.

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

Обработать исключения

На уровне приложения обрабатывайте исключения.

Исключение

Возможный сценарий

Bitrix\Main\DB\SqlQueryException

Проверка отношения руководитель — сотрудник

Bitrix\HumanResources\Exception\WrongStructureItemException

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

\InvalidArgumentException

Назначение в пустой или неподходящий набор подразделений

Методы getUserHeads() и getUserDeputies() перехватывают ошибки внутри сервиса и возвращают пустую коллекцию. Если нужно отличить ошибку от отсутствия руководителей, одной проверки коллекции недостаточно. Добавьте журналирование контекста запроса на уровне приложения.

Продолжить изучение