B2BPRO.KZ | Рекламное агентство в Алматы

Кастомные бизнес-процессы на PHP в коробочном Битрикс24

Кастомные бизнес-процессы на PHP в коробочном Битрикс24: схема ветвлений и серверные блоки в стиле чертежа

Разговор про доработки почти всегда начинается одинаково. Компания живёт в Битрикс24 второй или третий год, стандартные роботы и бизнес-процессы расставлены, а потом появляется задача, под которую готового действия просто нет. Согласование закупки должно уходить в 1С и ждать оттуда ответа. Заявка должна проверяться по внешнему реестру. Договор должен собираться из шаблона с расчётом по своей формуле, а не по той, что заложена в стандартном действии.

В облаке такие вещи делают через вебхуки и приложения, и часто этого хватает. Но когда логика тяжёлая, данных много, а результат нужен внутри самого процесса, а не рядом с ним, речь заходит о коробке и о собственном коде на PHP. Коробочная версия отличается от облачной именно этим: у вас есть доступ к исходному коду продукта, и дорабатывать его можно почти без ограничений.

Ниже устройство кастомных бизнес-процессов в Битрикс24 на уровне файлов и классов: где проходит граница между настройкой и разработкой, как действие встраивается в дизайнер и что чаще всего ломается после обновления коробки.

Почему эта тема существует только для коробки

Облачный Битрикс24 закрыт для правки кода по своей архитектуре. Вы работаете с интерфейсом, роботами, триггерами и REST API, но не можете положить свой PHP-файл внутрь портала. Всё, что выходит за рамки штатных возможностей, живёт снаружи: приложение или вебхук получает событие, обрабатывает его на своём сервере и возвращает результат обратно через API.

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

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

Три уровня доработки, и только один из них про PHP

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

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

Второй уровень это обработчики событий. Модули Битрикс24 генерируют события при создании и изменении сущностей, и на них можно повесить свой код. Регистрация обработчиков традиционно живёт в файле init.php, который начиная с версии 14.0.1 рекомендуется размещать в папке /local/php_interface/. Уровень подходит, когда логику нужно выполнить в ответ на изменение данных, но показывать её в дизайнере не требуется.

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

Из чего состоит своё действие

С точки зрения кода действие бизнес-процесса это PHP-класс, который наследуется от абстрактного класса CBPActivity. Имя класса обязано начинаться с префикса CBP и состоять из латинских букв и цифр. Для контейнерных действий, внутрь которых вкладываются другие, используется CBPCompositeActivity.

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

Внутри каталога обычно лежит такой набор:

  • Файл .description.php с описанием действия: название, категория, значения, которые действие возвращает, поведение JS-класса в конструкторе.
  • Основной файл с классом и всей логикой.
  • Каталог lang с языковыми файлами.
  • Иконка icon.gif размером 24 на 24 пикселя для отображения в конструкторе, необязательная.

В самом классе реализуются методы жизненного цикла. Execute() выполняет работу и возвращает статус. Cancel() отвечает за отмену, HandleFault() за обработку ошибки. Форму настроек рисует GetPropertiesDialog(), а проверяет и сохраняет введённое GetPropertiesDialogValues().

Отдельно стоит сказать про результат. Начиная с версии 17.0.3 модуля бизнес-процессов действие может передавать итог своей работы другим действиям процесса. Значения описываются в .description.php ключом RETURN, а если состав результата заранее неизвестен, например это ответы на анкету, используется ADDITIONAL_RESULT. Практический смысл простой: вместо того чтобы писать данные куда-то вбок и потом их оттуда доставать, действие честно отдаёт значение следующему шагу.

Где размещать код: /bitrix/activities/custom против /local

Штатные действия лежат в /bitrix/activities/bitrix/. Свои исторически размещали рядом, в каталоге /bitrix/activities/custom/, и такой путь до сих пор описан в учебных материалах.

Более аккуратный вариант это /local/activities/. Разница принципиальная и объясняется тем, как работает обновление: папка /bitrix относится к продукту и при обновлении ядра перезаписывается, а /local остаётся нетронутой. Весь кастомный код, включая действия, обработчики и свои модули, лучше держать именно там.

Мы встречали порталы, где половина доработок жила в /bitrix, и каждое обновление превращалось в спецоперацию: сначала архив, потом обновление, потом ручное возвращение файлов на место. Стоимость такой схемы не в самом переносе, а в том, что рано или поздно кто-то забывает вернуть один файл, и процесс тихо перестаёт работать. Заметят это через неделю, когда выяснится, что согласования не уходят.

Когда доработок становится больше нескольких файлов, их имеет смысл собрать в собственный модуль и положить туда же, в /local/modules. Тогда у кода появляется понятная граница, установка и удаление, а сами действия перестают быть россыпью папок, происхождение которых через год никто не вспомнит. Для одного-двух действий это избыточно, для десятка уже нет.

Перенос старых доработок в /local это отдельная работа, и делать её лучше на тестовом контуре, а не на продакшене. Такой перенос обычно входит в внедрение и настройку Битрикс24 при передаче портала на сопровождение, потому что без него дальнейшие обновления остаются лотереей.

Действия, которые умеют ждать

Часть логики выполняется мгновенно: посчитать, записать, отправить. Но в реальных процессах много шагов, где система должна остановиться и дождаться чего-то извне. Это может быть ответ из 1С, подпись руководителя или оплата по выставленному счёту.

Для таких случаев действие реализует интерфейсы IBPEventActivity и IBPActivityExternalEventListener. Первый говорит движку бизнес-процессов, что действие ждёт внешнего события и процесс нужно приостановить. Второй обрабатывает пришедшее событие и решает, что делать дальше: продолжить, завершить с ошибкой или ждать снова.

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

Бизнес-процесс по собственной сущности

Самый глубокий сценарий доработки это когда бизнес-процессы нужно запускать не по сделке или списку, а по вашей собственной сущности. Например, по документу склада или заявке в своём модуле.

Связующим звеном выступает класс документа, реализующий интерфейс IBPWorkflowDocument. Он объясняет движку, что такое ваша сущность и что с ней можно делать. Базовый набор методов описывает тип и поля: GetDocumentType() возвращает код типа документа, GetDocumentFields() перечисляет поля с типами данных, CanUserOperateDocument() и CanUserOperateDocumentType() отвечают за права.

Рабочие методы обеспечивают чтение и запись: GetDocument(), CreateDocument(), UpdateDocument(), DeleteDocument(). Вспомогательные закрывают частности вроде логических групп пользователей через GetAllowableUserGroups() и GetUsersFromUserGroup() или ссылки на карточку через GetDocumentAdminPage().

Идентификатор типа документа состоит из трёх элементов: код модуля, класс-реализация и код типа. Интерфейс собирается из штатных компонентов: bizproc.workflow.list показывает список шаблонов, bizproc.workflow.edit это сам конструктор, bizproc.document управляет запущенными процессами. Дополнительно понадобятся три служебных скрипта в /bitrix/admin, отвечающих за настройки действия, селектор и настройки процесса.

Запуск процесса при создании или изменении документа делается через CBPDocument::AutoStartWorkflows() с константами CBPDocumentEventType::Create и CBPDocumentEventType::Edit.

Объём работы здесь заметно больше, чем при написании одного действия, поэтому решение оправдано только тогда, когда сущность действительно ваша и живёт долго. Если речь про разовую таблицу, дешевле обойтись универсальными списками или смарт-процессами.

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

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

Стандартными средствами закрывается маршрут: ветвление по сумме, задачи согласующим, уведомления. Не закрывается расчёт, потому что формула обращается к внешним данным, и не закрывается обмен, потому что процесс должен приостановиться до ответа.

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

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

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

Что ломается при обновлении

Первая и самая частая причина поломок это правка файлов ядра. Достаточно один раз поменять что-то внутри /bitrix, и обновление вернёт файл к исходному виду вместе с потерей логики. Лечится переносом в /local и работой через события.

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

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

Как отлаживать то, что выполняется внутри процесса

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

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

Второе это отдельный простой шаблон для проверки. Не гоняйте боевой процесс согласования из десяти шагов ради теста одного действия. Соберите шаблон из двух шагов, где ваше действие получает нужные параметры и сразу отдаёт результат. Цикл проверки сокращается с получаса до минуты.

Третье касается формы настроек. Ошибки в GetPropertiesDialog() и GetPropertiesDialogValues() выглядят как проблемы конструктора: диалог не открывается или введённые значения не сохраняются. Проверяйте форму на всех типах параметров, которые действие принимает, включая пустые значения и подстановку значений из других шагов процесса.

Документация, которая экономит деньги через год

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

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

Полезно вести и общий список кастомизаций портала: что именно доработано, кем и когда. При обновлении такой список превращается в чек-лист проверок и экономит часы на выяснении, что вообще было сделано за прошедшие годы.

Когда кастом на PHP не нужен

Честный ответ на вопрос «нужна ли нам разработка» чаще всего отрицательный. Прежде чем писать действие, стоит проверить несколько вещей.

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

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

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

Если после этих вопросов задача всё ещё требует своего действия, значит, случай настоящий, и разработка окупится.

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

Можно ли написать своё действие бизнес-процесса в облачном Битрикс24?
В том виде, в каком это делается в коробке, нет: доступа к файлам портала в облаке не бывает. Расширять логику в облаке можно через REST API и приложения, включая действия, которые вызывают внешний сервис. Собственный PHP-класс внутри портала доступен только в коробочной версии.

Куда класть файлы действия, в /bitrix/activities/custom или в /local/activities?
Практичнее в /local/activities. Каталог /bitrix относится к продукту и перезаписывается при обновлении ядра, а /local остаётся нетронутым. Вариант с /bitrix/activities/custom рабочий и описан в учебных материалах, но требует помнить о переносе файлов при каждом обновлении.

Почему моё действие не появилось в дизайнере?
Чаще всего дело в именовании. Класс обязан начинаться с CBP, а каталог должен повторять имя класса без этого префикса и строчными буквами. Вторая частая причина в ошибке внутри .description.php, из-за которой описание не читается.

Как действие может дождаться ответа от внешней системы?
Через интерфейсы IBPEventActivity и IBPActivityExternalEventListener. Действие сообщает движку, что ждёт внешнего события, процесс приостанавливается и продолжается только после того, как событие придёт. Опрашивать внешнюю систему в цикле не нужно.

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

Что ещё, кроме действий, можно держать в /local?
Папка /local обрабатывается системой целиком и поддерживает те же разделы, что и /bitrix: activities для действий бизнес-процессов, components для компонентов, templates для шаблонов, modules для модулей, gadgets для гаджетов рабочего стола и php_interface для init.php. При обработке приоритет всегда у /local, поэтому там же удобно переопределять стандартные элементы системы своими версиями.

Переживёт ли доработка обновление коробки?
Если код лежит в /local, использует документированные классы и не правит файлы ядра, шансы высокие. Проверять это всё равно нужно на тестовой копии портала до того, как обновление уйдёт на боевой сервер.

Прокрутить вверх