Warning: Cannot modify header information - headers already sent by (output started at /home/c/cl52239/wordpress_5cpkj/public_html/wp-content/themes/cp_theme/functions.php:279) in /home/c/cl52239/wordpress_5cpkj/public_html/wp-includes/feed-rss2.php on line 8
Разработка под Битрикс24: API, интеграции и кастомизация https://grazy-chipmunk.ru/category/develop/ Все про Битрикс24 Sun, 31 Aug 2025 10:02:23 +0000 ru-RU hourly 1 https://wordpress.org/?v=7.1.3 https://grazy-chipmunk.ru/wp-content/uploads/2025/04/cropped-chipmunk-svgrepo-com-32x32.png Разработка под Битрикс24: API, интеграции и кастомизация https://grazy-chipmunk.ru/category/develop/ 32 32 Создание своей сущности. Упаковываем модуль, создаем таблицы и ORM-сущности. Часть 1. https://grazy-chipmunk.ru/crud-entity-part-2/ https://grazy-chipmunk.ru/crud-entity-part-2/#respond Sun, 31 Aug 2025 08:27:07 +0000 https://grazy-chipmunk.ru/?p=331 В первой части статьи разберем создание модуля от проектирования правильной структуры до реализации миграций БД и работы с ORM. Подробно рассмотрим механизм обновлений через транзакции и откат миграций.

Сообщение Создание своей сущности. Упаковываем модуль, создаем таблицы и ORM-сущности. Часть 1. появились сначала на Crazy Chipmunk.

]]>
Начнем с создания модуля. Структура модуля планируется следующая:

├── .settings.php
├── install
│   ├── files
│   ├── index.php
│   └── version.php
├── lang
│   └── ru
└── lib
├── Access
├── Controller
├── Dto
├── Entities
├── Handler
├── Migrations
├── Service
├── TagSelector
└── Validator

Рассмотрим структуру модуля подробнее:

  • Файл .settings.php —  файл настроек, описывающий настройки модуля.
  • Папка install содержит файлы, необходимые для установки модуля.
    • install.php — файл с описанием модуля. Также содержит необходимые инструкции для установки.
    • папка files содержит файлы компонентов, js расширений, а также файлы для публичной части. Эти файлы будут скопированы в bitrix/components, bitrix/js и в публичную часть портала соответственно.
    • version.php — файл с версией и датой выхода модуля.
  • Папка lang содержит языковые файлы.
  • Папка lib содержит классы модуля. Например, в Controller будут расположены контроллеры модуля, в Handler — обработчики событий, в Entities — ORM-модели для таблиц модуля, Migrations — классы для создания таблиц и демо-данных.

Прочитать настройки из .settings.php можно следующим образом:


\Bitrix\Main\Config\Configuration::getInstance($module)

Файл install.php будет состоять из нижеперечисленных методов:

<?php  
  
use Bitrix\Main\Loader;  
use Bitrix\Main\Localization\Loc;  
use Bitrix\Main\ModuleManager;  
use Chipmunk\News\Migrations\AddTestNews;  
use Chipmunk\News\Migrations\CreateNewsTable;  
use Chipmunk\News\Migrations\CreateNewsTagsTable;  
use Chipmunk\News\Migrations\CreateTagsTable;  
  
defined('B_PROLOG_INCLUDED') || die;  
  
final class chipmunk_news extends CModule  
{  
    public function __construct()  
    {  
        $this->MODULE_ID = 'chipmunk.news';  
        $this->MODULE_NAME = Loc::getMessage('CHIPMUNK.NEWS_NAME');  
        $this->MODULE_DESCRIPTION = Loc::getMessage('CHIPMUNK.NEWS_DESCR');  
        $this->PARTNER_NAME = Loc::getMessage('CHIPMUNK.NEWS_PARTNER_NAME');  
        $this->PARTNER_URI = Loc::getMessage('CHIPMUNK.NEWS_PARTNER_URL');  
  
        $version = include __DIR__ . '/version.php';  
  
        $this->MODULE_VERSION = $version['MODULE_VERSION'];  
        $this->MODULE_VERSION_DATE = $version['MODULE_VERSION_DATE'];  
    }  
  
    public function DoInstall(): bool  
    {  
        global $APPLICATION;  
  
        $application = \Bitrix\Main\Application::getInstance();  
        $connection = $application->getConnection();  
        try {  
            $connection->startTransaction();  
            ModuleManager::registerModule($this->MODULE_ID);  
            Loader::requireModule($this->MODULE_ID);  
            $this->installMigrations();  
            $connection->commitTransaction();  
        } catch (\Exception $e) {  
            $connection->rollbackTransaction();  
            $this->unInstallMigrations();  
            $APPLICATION->ThrowException(Loc::getMessage('CHIPMUNK.NEWS_INSTALL_ERROR') . $e->getMessage());  
            ModuleManager::unRegisterModule($this->MODULE_ID);  
            return false;  
        }  
        return true;  
    }  
  
    public function installMigrations(): void  
    {  
        foreach ($this->getMigrations() as $migration) {  
            $migration->up();  
        }  
    }  
  
    public function DoUninstall(): bool  
    {  
        global $APPLICATION;  
        Loader::requireModule($this->MODULE_ID);  
  
        $application = \Bitrix\Main\Application::getInstance();  
        $connection = $application->getConnection();  
        try {  
            $connection->startTransaction();  
            $this->unInstallMigrations();  
            $connection->commitTransaction();  
        } catch (\Exception $e) {  
            $connection->rollbackTransaction();  
            $APPLICATION->ThrowException("Ошибка удаления модуля: " . $e->getMessage());  
            return false;  
        } finally {  
            ModuleManager::unRegisterModule($this->MODULE_ID);  
            Loader::clearModuleCache($this->MODULE_ID);  
        }  
        return true;  
    }  
  
    public function unInstallMigrations(): void  
    {  
        $migrations = array_reverse($this->getMigrations());  
        foreach ($migrations as $migration) {  
            $migration->down();  
        }  
    }  
  
    public function UnInstallDB(): void  
    {  
        Loader::requireModule($this->MODULE_ID);  
  
        $this->unInstallMigrations();  
  
        ModuleManager::unRegisterModule($this->MODULE_ID);  
  
    }  
    public function getMigrations(): array  
    {  
        return [  
            new CreateNewsTable(),  
            new CreateTagsTable(),  
            new CreateNewsTagsTable(),  
            new AddTestNews()  
        ];  
    }  
}

Пошаговое описание каждого метода класса chipmunk_news:

  • __construct() — конструктор класса, инициализирует основные свойства модуля
    • MODULE_ID – идентификатор модуля (chipmunk.news)
    • MODULE_NAME – название модуля (из языковых файлов)
    • MODULE_DESCRIPTION – описание модуля (из языковых файлов)
    • PARTNER_NAME – название партнёра (из языковых файлов)
    • PARTNER_URI – URL партнёра ( из языковых файлов)
    • MODULE_VERSION и MODULE_VERSION_DATE – версия и дата версии модуля (из файла version.php)
  • DoInstall(): bool — основной метод установки модуля
    • Начинает транзакцию БД
    • Регистрирует модуль в системе через ModuleManager::registerModule()
    • Подключает модуль через Loader::requireModule()
    • Вызывает installMigrations() для применения миграций
    • Фиксирует транзакцию (commitTransaction())
    • В случае ошибки:
      • Откатывает транзакцию (rollbackTransaction())
      • Отменяет миграции (unInstallMigrations())
      • Выводит сообщение об ошибке
      • — Удаляет модуль (ModuleManager::unRegisterModule())
  • installMigrations(): void — накатывает миграции
    • Получает список миграций через getMigrations()
    • Для каждой миграции вызывает метод up() (создание таблиц/заполнение данными)
  • DoUninstall(): bool — основной метод удаления модуля
    • Подключает модуль через Loader::requireModule()
    • Начинает транзакцию БД
    • Вызывает unInstallMigrations() для отката миграций
    • Фиксирует транзакцию (commitTransaction())
    • В случае ошибки:
      • Откатывает транзакцию (rollbackTransaction())
      • Выводит сообщение об ошибке
      • Возвращает false
    • В любом случае:
      • Удаляет модуль (ModuleManager::unRegisterModule())
  • unInstallMigrations(): void — откатывает все миграции
    • Получает список миграций через getMigrations() (в обратном порядке)
    • Для каждой миграции вызывает метод down() (удаление таблиц/данных)
  • getMigrations(): array — возвращает массив объектов миграций в порядке их выполнения
    • CreateNewsTable – создание таблицы новостей
    • CreateTagsTable – создание таблицы тегов
    • CreateNewsTagsTable – создание связующей таблицы новости-теги
    • AddTestNews – добавление тестовых данных

Для начала работы над модулем нам понадобятся 3 таблицы и ORM модели к ним:

1. Таблица «Новости»

Класс-миграция для создания таблицы:

<?php  
  
namespace Chipmunk\News\Migrations;  
  
use Bitrix\Main\Application;  
use Bitrix\Main\Entity\Base;  
use Chipmunk\News\Entities\NewsTable;  
  
class CreateNewsTable  
{  
    public static function up(): void  
    {  
        $connection = Application::getConnection();  
  
        if (!$connection->isTableExists(NewsTable::getTableName())) {  
            try {  
                Base::getInstance(NewsTable::class)->createDbTable();  
            } catch (\Exception $e) {  
                throw new \Exception($e);  
            }  
        }  
    }  
  
    public static function down(): void  
    {  
        $connection = Application::getConnection();  
        try {  
            $connection->queryExecute('DROP TABLE IF EXISTS ' . NewsTable::getTableName());  
        }  
        catch (\Exception $e) {  
            throw new \Exception($e);  
        }  
    }  
}

Описание класса CreateNewsTable и его методов:

up(): void

Назначение:
Создает таблицу новостей в БД, если она не существует.

Логика работы:

  1. Получает подключение к БД через Application::getConnection().
  2. Проверяет существование таблицы с помощью $connection->isTableExists(), используя имя таблицы из NewsTable::getTableName().
  3. Если таблицы нет:
    • Создает ее через ORM-механизм
    • Base::getInstance(NewsTable::class)->createDbTable().
  4. В случае ошибки пробрасывает исключение.

Пример вызова:
Выполняется при установке модуля или применении миграций.

down(): void

Назначение:
Удаляет таблицу новостей из БД.

Логика работы:

  1. Получает подключение к БД.
  2. Выполняет SQL-запрос на удаление таблицы:
    DROP TABLE IF EXISTS <имя_таблицы>.
  3. В случае ошибки пробрасывает исключение.

Пример вызова:
Выполняется при удалении модуля или откате миграций.

ORM класс таблицы «Новости»

<?  
  
namespace Chipmunk\News\Entities;  
  
use Bitrix\Main\Entity;  
use Bitrix\Main\Type;  
use Bitrix\Main\ORM\Fields\Relations\ManyToMany;  
use Bitrix\Main\UserTable;  
  
class NewsTable extends Entity\DataManager  
{  
    public static function getTableName()  
    {  
        return 'b_chipmunk_news';  
    }  
  
    public static function getMap()  
    {  
        return [  
            (new Entity\IntegerField('ID'))  
                ->configureAutocomplete()  
                ->configurePrimary(),  
  
            (new Entity\StringField('TITLE', [  
                'validation' => function () {  
                    return [  
                        new Entity\Validator\Length(null, 255)  
                    ];  
                }  
            ]))->configureRequired(),  
  
            (new Entity\TextField('TEXT'))->configureRequired(),  
  
            (new Entity\IntegerField('IMAGE_ID'))->configureRequired(),  
  
            (new Entity\IntegerField('AUTHOR_ID'))->configureRequired(),  
  
            new Entity\ReferenceField(  
                'AUTHOR',  
                UserTable::class,  
                ['=this.AUTHOR_ID' => 'ref.ID'],  
                ['join_type' => 'LEFT']  
            ),  
  
            (new Entity\DatetimeField('DATE_CREATE'))  
                ->configureDefaultValue(new Type\DateTime),  
  
            (new Entity\DatetimeField('DATE_UPDATE')),  
            (new ManyToMany('TAGS', TagTable::class))  
                ->configureTableName('b_chipmunk_news_tags')  
                ->configureLocalPrimary('ID', 'NEWS_ID')  
                ->configureLocalReference('NEWS')  
                ->configureRemotePrimary('ID', 'TAG_ID')  
                ->configureRemoteReference('TAG'),  
  
        ];  
    }  
}

Ниже детальное описание класса NewsTable и его методов:

getTableName(): string

Назначение:
Возвращает имя таблицы в базе данных.

Возвращаемое значение:
'b_chipmunk_news' — таблица будет создана с этим именем в БД.

getMap(): array

Назначение:
Определяет структуру таблицы и связи между сущностями.

Возвращаемое значение:
Массив с описанием полей таблицы и связей.

Описание полей:

ID:

  • Тип: IntegerField
  • Настройки:
    • configureAutocomplete() — автоинкремент
    • configurePrimary() — первичный ключ

TITLE:

  • Тип: StringField
  • Настройки:
    • Валидация длины до 255 символов
    • configureRequired() — обязательное поле

TEXT:

  • Тип: TextField
  • Настройки: configureRequired()

IMAGE_ID:

  • Тип: IntegerField
  • Настройки: configureRequired()

AUTHOR_ID:

  • Тип: IntegerField
  • Настройки: configureRequired()

Связь AUTHOR:

  • Тип: ReferenceField
  • Связывает с таблицей пользователей (UserTable)
  • Условие связи: AUTHOR_ID = ID из UserTable
  • Тип соединения: LEFT JOIN

DATE_CREATE:

  • Тип: DatetimeField
  • Настройки:
    • Значение по умолчанию: текущая дата/время

DATE_UPDATE:

  • Тип: DatetimeField
  • Без значения по умолчанию

Связь TAGS (ManyToMany):

  • Связь многие-ко-многим с таблицей тегов (TagTable)
  • Использует промежуточную таблицу b_chipmunk_news_tags
  • Локальный ключ: ID (из NewsTable) → NEWS_ID (в промежуточной таблице)
  • Внешний ключ: ID (из TagTable) → TAG_ID (в промежуточной таблице)

2. Таблица «Теги»

Класс-миграция для создания таблицы

<?php  
  
namespace Chipmunk\News\Migrations;  
  
use Bitrix\Main\Application;  
use Bitrix\Main\Entity\Base;  
use Chipmunk\News\Entities\TagTable;  
  
class CreateTagsTable  
{  
    public static function up(): void  
    {  
        $conn = Application::getConnection();  
  
        if (!$conn->isTableExists(TagTable::getTableName())) {  
            try {  
                Base::getInstance(TagTable::class)->createDbTable();  
            } catch (\Exception $e) {  
                throw new \Exception($e->getMessage());  
            }  
        }  
    }  
  
    public static function down(): void  
    {  
        $conn = Application::getConnection();  
        try {  
            $conn->queryExecute('DROP TABLE IF EXISTS ' . TagTable::getTableName());  
        } catch (\Exception $e) {  
            throw new \Exception($e->getMessage());  
        }  
    }  
}

ORM класс таблицы

<?  
  
namespace Chipmunk\News\Entities;  
  
use Bitrix\Main\Entity;  
use Bitrix\Main\ORM\Fields\Relations\ManyToMany;  
  
class TagTable extends Entity\DataManager  
{  
    public static function getTableName()  
    {  
        return 'b_chipmunk_tags';  
    }  
  
    public static function getMap()  
    {  
        return [  
            (new Entity\IntegerField('ID'))  
                ->configurePrimary()  
                ->configureAutocomplete(),  
  
            (new Entity\StringField('NAME', [  
                 'validation' => function() {  
                    return [  
                        new Entity\Validator\Length(null, 255)  
                    ];  
                }  
            ]))->configureRequired(),  
  
            (new ManyToMany('NEWS', NewsTable::class))  
                ->configureTableName('b_chipmunk_news_tags')  
                ->configureLocalPrimary('ID', 'TAG_ID')  
                ->configureLocalReference('TAG')  
                ->configureRemotePrimary('ID', 'NEWS_ID')  
                ->configureRemoteReference('NEWS')  
  
        ];  
    }  
}

3. Таблица «Новости и теги»

Класс-миграция для создания таблицы

<?php  
  
namespace Chipmunk\News\Migrations;  
  
use Bitrix\Main\Application;  
  
class CreateNewsTagsTable  
{  
    protected const TABLE_NAME = 'b_chipmunk_news_tags';  
  
    public static function up(): void  
    {  
        $conn = Application::getConnection();  
  
        if (!$conn->isTableExists(self::TABLE_NAME)) {  
            try {  
                $conn->queryExecute("  
                    CREATE TABLE " . self::TABLE_NAME . " (  
                        NEWS_ID INT NOT NULL,                        TAG_ID INT NOT NULL,                        PRIMARY KEY (NEWS_ID, TAG_ID),  
                        FOREIGN KEY (NEWS_ID) REFERENCES b_chipmunk_news(ID) ON DELETE CASCADE,  
                        FOREIGN KEY (TAG_ID) REFERENCES b_chipmunk_tags(ID) ON DELETE CASCADE  
                    )                ");  
            } catch (\Exception $e) {  
                throw new \Exception($e);  
            }  
        }  
    }  
  
    public static function down(): void  
    {  
        $conn = Application::getConnection();  
        try {  
            $conn->queryExecute('DROP TABLE IF EXISTS ' . self::TABLE_NAME);  
        } catch (\Exception $e) {  
            throw new \Exception($e);  
        }  
    }  
}

up(): void

  1. Получает соединение с БД
  2. Проверяет отсутствие таблицы (isTableExists())
  3. Создает таблицу с указанной структурой

Структура таблицы:

  • NEWS_ID: INT, NOT NULL (внешний ключ к таблице новостей)
  • TAG_ID: INT, NOT NULL (внешний ключ к таблице тегов)
  • Составной первичный ключ: (NEWS_ID, TAG_ID)
  • Внешние ключи с каскадным удалением:
    • NEWS_ID → b_chipmunk_news(ID)
    • TAG_ID → b_chipmunk_tags(ID)

down(): void

  1. Получает соединение с БД
  2. Удаляет таблицу (с проверкой существования)
  3. Обрабатывает возможные ошибки

Скачать код из статьи

Сообщение Создание своей сущности. Упаковываем модуль, создаем таблицы и ORM-сущности. Часть 1. появились сначала на Crazy Chipmunk.

]]>
https://grazy-chipmunk.ru/crud-entity-part-2/feed/ 0
Создание своей сущности в Битрикс24 https://grazy-chipmunk.ru/crud-entity/ https://grazy-chipmunk.ru/crud-entity/#respond Thu, 08 May 2025 10:20:02 +0000 https://grazy-chipmunk.ru/?p=288 Cоздадим свою сущность в Битрикс24 с гридом, CRUD, своим провайдером для tagSelector, массовым редактирование записей и не только

Сообщение Создание своей сущности в Битрикс24 появились сначала на Crazy Chipmunk.

]]>
Cоздадим свою сущность в Битрикс24 с гридом, CRUD, своим провайдером для tagSelector, массовым редактирование записей и не только.

В этой статье рассмотрим создание своей сущности в Битрикс24.

Для решения задачи будем использовать следующие инструменты:

  • Компонент main.ui.grid
  • Компонент main.ui.filter
  • SidePanel — Слайдер
  • Виджет TagSelector
  • Диалог выбора сущностей
  • Диалог для пошагового процесса
  • Виджет для загрузки файлов
  • Компонент BX.UI.AccessRights

По ходу решения задачи разработаем:

  • Модуль
  • Таблицы для сущности новостей и тегов
  • Провайдер данных для тегов для TagSelector
  • ES6 расширение для детальной страницы новости
  • Валидаторы
  • Интерфейс для настройки прав доступа к редактированию новостей

Результат:

Сообщение Создание своей сущности в Битрикс24 появились сначала на Crazy Chipmunk.

]]>
https://grazy-chipmunk.ru/crud-entity/feed/ 0
Api модуля humanresources. Сотрудники. Часть 3 https://grazy-chipmunk.ru/api-humanresources-members-part-3/ https://grazy-chipmunk.ru/api-humanresources-members-part-3/#respond Sun, 27 Apr 2025 13:41:54 +0000 https://grazy-chipmunk.ru/?p=141 В этой статье рассмотрим основные методы работы с сотрудниками в подразделениях через API модуля Humanresources Битрикс24.
Примеры кода демонстрируют получение списка сотрудников с постраничной навигацией, определение их ролей в подразделении, вычисление иерархических отношений, а также операции добавления, перемещения и удаления сотрудников.

Сообщение Api модуля humanresources. Сотрудники. Часть 3 появились сначала на Crazy Chipmunk.

]]>
Получить список сотрудников отдела

Существует 2 метода: getAllEmployees и getPagedEmployees.
Отличие в том, что метод getAllEmployees вернёт список всех сотрудников подразделения, а метод getPagedEmployees— только ограниченное количество сотрудников с постраничной навигацией, что удобно при работе с большими структурами. Параметр $withAllChildNodes позволяет учесть подчиненные отделы.

Метод getById класса RoleHelperService вернет роль сотрудника в данном подразделении. Роли хранятся в таблице b_hr_structure_role:

  1. HEAD — руководитель
  2. EMPLOYEE — сотрудник
  3. DEPUTY_HEAD — заместитель

use Bitrix\HumanResources\Service\NodeMemberService;
use Bitrix\HumanResources\Service\RoleHelperService;

$departmentId = 3;
$modeMemberService = new NodeMemberService();
$roleHelperService = new RoleHelperService();
// Получить всех сотрудников
$result = $modeMemberService
    ->getAllEmployees(
        $departmentId,
        false,
        false
    )
    ->getItemMap();
// Получить сотрудников с пагинацией
$result = $modeMemberService
    ->getPagedEmployees(
        $departmentId,
        false,
        0,
        100,
        true
    )
    ->getItemMap();
foreach ($result as $item) {
    $memberId = $item->id;
    foreach ($item->roles as $role) {
        // получить роль сотрудника в данном подразделении
        $role = $roleHelperService->getById($role);
    }
}

Результирующий объект представляет собой экземпляр класса Bitrix\HumanResources\Item\NodeMember.

Свойство id этого объекта указывает на идентификатор сотрудника в контексте конкретного подразделения(memberId).

В свойстве entityId хранится id пользователя.

Этот id отличается от стандартного id пользователя.

Рассмотрим объект Bitrix\HumanResources\Item\NodeMember

СвойствоОписание
entityTypeТип сущности, в данном случае это перечисление Bitrix\HumanResources\Type\MemberEntityType
entityIdИдентификатор сущности (пользователя)
nodeIdИдентификатор отдела
activeСтатус активности (1 — активен, 0 — неактивен)
rolesСписок ролей
iconИконка
idУникальный идентификатор сотрудника
addedByИдентификатор пользователя, который добавил сотрудника
createdAtДата и время создания сущности (DateTime)
updatedAtДата и время последнего обновления сущности (DateTime)

Вычислить отношение между сотрудниками


use Bitrix\HumanResources\Service\NodeMemberService;

$memberId = 3;
$targetMemberId = 11;
$result = $modeMemberService
    ->getMemberSubordination(
        $memberId,
        $targetMemberId
    );

Отношения между сотрудниками в Битрикс24 хранятся в перечислении (enum) Bitrix\HumanResources\Type\MemberSubordinateRelationType.

EnumЗначениеОписание
RELATION_ITSELF0Один и тот же человек
RELATION_HIGHER1Сотрудник выше по иерархии (руководитель)
RELATION_EQUAL2Сотрудники на одном уровне (коллеги)
RELATION_LOWER3Сотрудник ниже по иерархии (подчинённый)
RELATION_OTHER4Связь не найдена
RELATION_OTHER_STRUCTURE5Сотрудники из разных структур

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


use Bitrix\HumanResources\Service\NodeMemberService;

$departmentId = 3;
$result = $nodeMemberService
    ->getDefaultHeadRoleEmployees($departmentId)
    ->getItemMap();

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


use Bitrix\HumanResources\Exception\CreationFailedException;
use Bitrix\HumanResources\Item\NodeMember;
use Bitrix\HumanResources\Repository\NodeMemberRepository;
use Bitrix\HumanResources\Type\MemberEntityType;

$userId = 1;
$targetDepartmentId = 3;
$member = new NodeMember(
    MemberEntityType::USER,
    $userId,
    $targetDepartmentId,
    true,
    role: 1 // руководитель
);
$nodeMemberRepository = new NodeMemberRepository();
try {
    $nodeMemberRepository->create($member);
} catch (CreationFailedException $e) {
    print_r($e->getErrors()->toArray());
}

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


use Bitrix\HumanResources\Repository\NodeMemberRepository;
use Bitrix\HumanResources\Repository\NodeRepository;
use Bitrix\HumanResources\Service\NodeMemberService;
use Bitrix\Intranet\Exception\UpdateFailedException;

$modeMemberService = new NodeMemberService();
$nodeRepository = new NodeRepository();
$memberRepository = new NodeMemberRepository();
$targetDepartmentId = 3;
$memberId = 9;
$node = $nodeRepository->getById($targetDepartmentId);
$member = $memberRepository->findById($memberId);
// изменить роль на заместителя если требуется
$member->role = 3;
try {
    $result = $modeMemberService->moveMember($member, $node);
} catch (UpdateFailedException $e) {
    print $e->getErrors()->toArray();
}

Удалить сотрудника из подразделения

При удалении сотрудник будет перемещён в корневое подразделение.


use Bitrix\HumanResources\Exception\WrongStructureItemException;
use Bitrix\HumanResources\Repository\NodeMemberRepository;
use Bitrix\HumanResources\Repository\NodeRepository;
use Bitrix\HumanResources\Service\NodeMemberService;
use Bitrix\Intranet\Exception\UpdateFailedException;

$modeMemberService = new NodeMemberService();
$nodeRepository = new NodeRepository();
$memberRepository = new NodeMemberRepository();
$targetDepartmentId = 3;
$memberId = 9;
$member = $memberRepository->findById($memberId);
try {
    $result = $modeMemberService->removeUserMemberFromDepartment($member);
} catch (UpdateFailedException $e) {
    print_r($e->getErrors()->toArray());
} catch (WrongStructureItemException $e) {
    print_r($e->getErrors()->toArray());
}

Полезные ссылки

Сообщение Api модуля humanresources. Сотрудники. Часть 3 появились сначала на Crazy Chipmunk.

]]>
https://grazy-chipmunk.ru/api-humanresources-members-part-3/feed/ 0
Api модуля humanresources. Отделы. Часть 2. https://grazy-chipmunk.ru/api-humanresources-deparements-part-2/ https://grazy-chipmunk.ru/api-humanresources-deparements-part-2/#respond Sun, 27 Apr 2025 13:21:02 +0000 https://grazy-chipmunk.ru/?p=121 В этом материале подробно рассмотрим работу с отделами через API модуля humanresources для Битрикс24. Вы узнаете, как получать список подразделений, создавать, редактировать и удалять отделы, а также работать с их связями и структурами.

Сообщение Api модуля humanresources. Отделы. Часть 2. появились сначала на Crazy Chipmunk.

]]>
Получить список отделов

use Bitrix\HumanResources\Service\NodeService;

$nodeService = new NodeService();
$nodeId = 1;
$nodes = $nodeService->getNodeChildNodes($nodeId)->getItemMap();

$arResult = [];
foreach ($nodes as $node) {
    $arResult[$node->id] = $node->name;
}

Метод getNodeChildNodes()Bitrix\HumanResources\Item\Collection\NodeCollection

Описание методов коллекции

МетодОписание
getItemMap()массив объектов Bitrix\HumanResources\Item\Node
getIterator()вернет экземпляр класса ArrayIterator
getFirst()вернет первый объект коллекции Bitrix\HumanResources\Item\Node
getItemById($id)вернет объект коллекции Bitrix\HumanResources\Item\Node по id
filter(\Closure $rule)вернет отфильтрованную коллекцию
count()количество элементов в коллекции

$nodeService
   ->getNodeChildNodes($nodeId)
   ->filter(fn($node) => $node->id === 1);

Рассмотрим объект класса Bitrix\HumanResources\Item\Node

СвойствоОписание
nameНазвание отдела
typeТип, перечисление Bitrix\HumanResources\Type\NodeEntityType
structureIdИдентификатор структуры, к которому относится отдел
accessCodeКод доступа
idУникальный идентификатор
parentIdИдентификатор родительского узла. 0 указывает на корневую сущность
depthГлубина структуры. 0 указывает на верхний уровень
createdByИдентификатор пользователя, создавшего отдел
createdAtДата и время создания
updatedAtДата и время последнего обновления
xmlIdВнешний идентификатор
activeАктивность (1 — активен, 0 — неактивен)
globalActiveГлобальный статус активности (1 — активен, 0 — неактивен)
sortПорядок сортировки
descriptionОписание

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


use Bitrix\HumanResources\Model\NodeTable;

$nodeId = 3;
$structureId = 1;
$nodes = NodeTable::getList([
    'select' => ['*'],
    'filter' => ['=PARENT_ID' => $nodeId, '=STRUCTURE_ID' => $structureId],
])->fetchAll();

Получить отделы, в которых работает сотрудник


use Bitrix\HumanResources\Model\NodeMemberTable;  
use Bitrix\HumanResources\Model\NodeTable;  
use Bitrix\Main\Engine\CurrentUser;  
use Bitrix\Main\ORM\Query\Join;  
use Bitrix\HumanResources\Service\NodeService;  

// через api
$nodeService = new NodeService();  
$result = $nodeService->getNodesByUserId(1);

// через таблицы
$nodes = NodeMemberTable::getList([  
    'filter' => ['ENTITY_ID' => CurrentUser::get()->getId(), 'ACTIVE' => 'Y'],  
    'select' => ['ID', 'DEPARTMENT'],  
    'runtime' => [  
        'DEPARTMENT' =>  
            [  
                'data_type' => NodeTable::class,  
                'reference' => ['=this.NODE_ID' => 'ref.ID'],  
                'join_type' => Join::TYPE_LEFT  
            ]  
    ]  
])->FetchCollection();  
  
foreach ($nodes as $node) {  
    echo $node->get('DEPARTMENT')->getName();  
    echo '<br>';  
}

Создать отдел


use Bitrix\HumanResources\Exception\CreationFailedException;  
use Bitrix\HumanResources\Item\Node;  
use Bitrix\HumanResources\Service\NodeService;  
use Bitrix\HumanResources\Type\NodeEntityType;

/** на вход свойства объекта Bitrix\HumanResources\Item\Node
 * @param string $name  
 * @param NodeEntityType = DEPARTMENT  
 * @param int $structureId =1  
 * @param ?string $accessCode  
 * @param ?int $id  
 * @param ?int $parentId  
 */  
$node = new Node(  
    'It-отдел',  
    NodeEntityType::DEPARTMENT,  
    1,  
    null,  
    null,  
    1  
);  
try {  
    $nodeService->insertNode($node);  
} catch (Exception $e) {  
    print_r($e->getErrors()->toArray());  
}

Обновить описание и переместить отдел в подчинение другому отделу


use Bitrix\HumanResources\Repository\NodeRepository;  
use Bitrix\HumanResources\Service\NodeService;  

$nodeService = new NodeService();  
$nodeRepository = new NodeRepository();  

$departmentId = 9;  
$node = $nodeRepository->getById($departmentId);  
$node->description = "Просто какое-то описание";  
$node->parentId = 2;

try {  
    $nodeService->updateNode($node);  
} catch (Exception $e) {  
    print_r($e->getErrors()->toArray());  
}

Удалить отдел


use Bitrix\HumanResources\Exception\DeleteFailedException;  
use Bitrix\HumanResources\Repository\NodeRepository;  
use Bitrix\HumanResources\Service\NodeService;  

$nodeService = new NodeService();  
$nodeRepository = new NodeRepository();  

$departmentId = 9;  
$node = $nodeRepository->getById($departmentId);  

try {  
    $nodeService->removeNode($node);  
} catch (DeleteFailedException $e) {  
    print $e->getErrors()->toArray();  
}

Полезные ссылки

Сообщение Api модуля humanresources. Отделы. Часть 2. появились сначала на Crazy Chipmunk.

]]>
https://grazy-chipmunk.ru/api-humanresources-deparements-part-2/feed/ 0
Новая структура компании в Битрикс24. Часть 1 https://grazy-chipmunk.ru/bitrix24-new-structure/ https://grazy-chipmunk.ru/bitrix24-new-structure/#respond Wed, 23 Apr 2025 19:01:55 +0000 https://cl52239-wordpress-5cpkj.tw1.ru/?p=6 Переход от Nested Sets к комбинации Closure Table и Adjacency List — важное обновление архитектуры структуры компании в Битрикс24. В статье разбираем, почему традиционные инфоблоки стали узким местом, какие преимущества принесла новая модель хранения и как устроены ключевые таблицы модуля humanresources

Сообщение Новая структура компании в Битрикс24. Часть 1 появились сначала на Crazy Chipmunk.

]]>
Новая структура в Битрикс24 реализована модулем humanresources.

Ранее данные об организационной структуре хранились в инфоблоках, но в текущей версии модуля реализовано хранение в собственных таблицах.

Возможности модуля

Модуль позволяет:

  • создавать подразделения;
  • добавлять сотрудников в подразделения;
  • назначать несколько руководителей и заместителей;
  • поддерживать произвольный уровень вложенности;
  • работать с несколькими структурами в рамках одного Битрикс24 (еще не реализовано)

Как выглядит новая структура

Страница создания и изменения структуры компании расположена по адресу: /hr/structure/

Новая структура компании Битрикс24 -- Так выглядит новая структура компании в Битрикс24
Так выглядит новая структура компании в Битрикс24

На странице также можно гибко настроить права доступа к редактированию структуры.

Страница настройки прав доступа

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

Страница настроек прав доступа к редактированию структуры -- Страница настроек прав доступа к редактированию структуры
Так выглядит страница настроек прав доступа для редактирования структуры

Совместимость

На данный момент (апрель 2025) реализована обратная совместимость модуля HR: При добавлении подразделения через модуль, подразделение добавляется и в инфоблоки (и наоборот). Важно: В будущем планируется полный переход на новую модель без обратной совместимости.

Архитектура

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

Nested Sets — это один из методов хранения иерархических данных (например, деревьев или структур с родительскими и дочерними элементами) в базе данных. Этот подход основан на использовании двух числовых значений (обычно называемых LEFT_MARGIN и RIGHT_MARGIN) для каждого узла в дереве.

Как работает Nested Sets?

  • LEFT_MARGIN и RIGHT_MARGIN — это числовые значения, которые обозначают границы диапазона для каждого узла в дереве.
  • Каждый узел имеет два числа, которые определяют его позицию в дереве, включая все дочерние элементы.
  • Если узел является родительским, то его дочерние узлы будут иметь значения, которые находятся внутри диапазона этого родителя (между LEFT_MARGIN и RIGHT_MARGIN родительского узла).
Преимущества:
  • Быстрый поиск потомков: Чтобы найти всех потомков определенного узла, достаточно выполнить запрос, проверив, находятся ли LEFT_MARGIN и RIGHT_MARGIN дочерних узлов внутри диапазона родительского узла.
  • Хорошая производительность при чтении: Чтение иерархии происходит быстро, так как все связи между родителями и потомками заранее определены.
Недостатки:
  • Медленные операции изменения: Изменение структуры, например, добавление, удаление или перемещение узлов, требует пересчета значений LEFT_MARGIN и RIGHT_MARGIN для множества других узлов, что делает эти операции достаточно дорогими по времени.
  • Меньшая гибкость: Из-за необходимости пересчитывать диапазоны для многих узлов, сложно динамично изменять структуру дерева, особенно на больших объемах данных.

В новой версии используется комбинация методов Closure Table и Adjacency List.

Adjacency List (Список смежности)

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

Каждый узел хранит свой уникальный идентификатор (например, id) и идентификатор родительского узла (parent_id), который указывает на родительский элемент в дереве. Для корневых узлов значение parent_id будет равно NULL или 0.

Преимущества:
  • Простота реализации: Требуется только одно поле для хранения идентификатора родителя.
  • Легкость в добавлении и удалении узлов: Нужно просто указать родителя для нового узла или удалить узел и его поддерево.
Недостатки:
  • Для получения всех потомков узла требуется выполнить рекурсивный запрос или несколько последовательных запросов.
  • Затруднено получение глубоко вложенных элементов (например, всех потомков определенного узла).
  • Модификация структуры (например, перемещение узла или изменение родителя) требует дополнительных операций для обработки зависимостей.

Closure Table (Таблица замыканий)

Хранит не только родителя каждого узла, но и все возможные отношения между узлами. То есть для каждого узла сохраняются все его предки и сам он. Это достигается за счет использования дополнительной таблицы, которая хранит пары «потомок-родитель» для всех уровней иерархии.

В Closure Table создается таблица, где каждая строка содержит два столбца: ancestor (предок) и descendant (потомок). Таблица хранит все возможные пути от каждого потомка к его предкам.

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

Таблицы модуля HR

ТаблицаНазначение
b_hr_access_permissionПрава доступа, привязанные к ролям
b_hr_access_roleСписок всех ролей в системе (например, Администратор, Руководитель, Сотрудник). Создать роль можно на странице настроек прав доступа
b_hr_access_role_relationСвязь ролей с пользователями, подразделениями, группами
b_hr_structureСтруктуры компании (задел на будущее, на данный момент не реализовано)
b_hr_structure_nodeКонкретные узлы структуры (отделы), реализация метода Adjacency List
b_hr_structure_node_memberСотрудники, прикрепленные к отделам
b_hr_structure_node_pathДля хранения узлов дерева, реализация метода Closure Table
b_hr_structure_node_relationДля хранения всех возможных путей между узлами
b_hr_logЛоги ошибок
b_hr_structure_node_member_roleКонкретные роли сотрудников в рамках их подразделений.

Полезные ссылки

Сообщение Новая структура компании в Битрикс24. Часть 1 появились сначала на Crazy Chipmunk.

]]>
https://grazy-chipmunk.ru/bitrix24-new-structure/feed/ 0