Автоматизируем HR-процессы в компании через систему управления проектами
Чем больше и сложнее бизнес, тем больше нюансов и хитрых настроек нужно реализовать в корпоративной СУП. Мы - компания-интегратор Powertask, и основа нашего бизнеса - не просто продать и установить заказчику коробочную систему, но настроить ее так, чтобы с ее помощью бизнес-процессы в компании работали еще лучше, чем раньше. И чтобы сотрудников не пришлось заново переучивать на новые процессы, теряя производительность.
Хочется поделиться опытом недавнего внедрения для телекоммуникационной компании, в которой мы кастомизировали систему управления проектами на базе отечественной экосистемы EVA. Вообще, EVA хороша тем, что помимо огромного числа предустановленных настроек многое в ней можно дополнительно сделать, написав прямо в системе автоматизацию на bzPython. Это коллекция обвязок над стандартными библиотеками для обеспечения типовых действий и упрощения кодирования бизнес-логики. Полностью поддерживает использование языка python3 в чистом виде.
Систему у заказчика мы реализовали так, чтобы для каждого структурного подразделения компании был настроен отдельный проект, в котором можно вести задачи, хранить базу знаний, формировать спринты, канбан-доски и прочее. Это удобно - у каждой команды есть свое отдельное пространство, куда чужие не ходят (доступ настроен через группы в Active Directory) и которое можно настроить так, как привыкли в этой команде. Но взаимодействие между отделами никто не отменял, и первыми про это вспомнили сотрудники кадровой службы. Ведь в каждой крупной организации выход на работу нового человека обычно запускает много процессов: его нужно оформить, выдать ноутбук, наушники и мышь, организовать рабочее место, создать учетку для корпоративных систем (и ладно еще, если одну единую), выдать пропуск, занести в зарплатную ведомость и много чего еще. Увольнение сотрудника отматывает те же процессы, но в обратном направлении - вычеркнуть из зарплатной ведомости, отобрать ноутбук и т.п.
Чтобы это все нормально работало, задание от заказчика звучало так - при создании в пространстве HR-службы задачи на прием нового сотрудника система должна автоматически создавать логически связанные тикеты в пространствах других отделов с формулировками, соответствующими этим отделам. То есть, для ИТ-службы тикеты на выдачу техники и создание учетной записи, тикет для безопасников на выдачу пропуска, для финслужбы поставить на довольствие и так далее. При этом данные нового сотрудника в описании всех тикетов должны быть одни и те же, поскольку речь идет об одном человеке.
EVA обладает широкими возможностями кастомизации: при помощи скриптов можно автоматизировать рабочие процессы, запускать триггеры по происходящим событиям с объектами системы и кроны по расписанию, разворачивать вебхуки для интеграций с другими системами. В зависимости от поставленной задачи, может понадобиться использовать конкретный тип автоматизации: в нашем случае это были триггеры. Но перед тем, как более подробно поговорить о них, давайте сначала расскажем про шаблоны.
Механизм шаблонов в EVA позволяет пользователю заполнять, редактировать и использовать в повседневной работе заготовки повторяющихся задач и документов. Мы предварительно создали следующие шаблоны задач: Выход нового сотрудника, Выдача пропуска, Создание учетной записи, Оборудование для нового сотрудника. В каждом шаблоне мы указали логический тип задачи, описание, тип обращения, приоритет, группу соисполнителей и сделали привязку к конкретному проекту. Шаблоны (для удобства вызова пользователем) можно объединять в группы.
Теперь напишем немного кода в триггере. Для удобства начнем с определения переменных, которые будем использовать в скрипте.
# шаблон задачи на выход нового сотрудника
start_new_employee_template_task_code='HR1-2'
# шаблоныгенерируемыхсвязанныхзадач
start_template_task_code_list= ['ITHE-8', 'ITHE-7', 'SB-1']
task_relation_type='system.link'
system_user=models.CmfPerson.get(code='PRS-000001')
cmf_author=system_user
cmf_owner=system_userТриггер будет запускаться по событию «Создание задачи», поэтому self - это объект заачи. В этом объекте помимо системных полей, имеются кастомные пользовательские поля, которые сотрудник HR-службы заполняет при создании задачи: ФИО сотрудника, телефон, адрес электронной почты и т.п. Их имена начинаются на cf_.
Далее перейдем к основной логике. Сначала проверяем, что задача создана из определенного нами шаблона «Выход нового сотрудника», после чего пробегаемся по списку связанных задач: генерируем их с определенным набором значений, определяемых в словаре, и добавляем для каждой связь с родительской задачей. В скрипте это выглядит примерно так:
# Проверяем что задача создана из нашего шаблона
if self.cloned_from.code == start_new_employee_template_task_code:
# Переопределяем имя задачи
self.name = ' | '.join([
self.cloned_from.name.value,
self.cf_data_nr.value.strftime('%d.%m.%Y'),
self.cf_fio_sotrudnika.value])
# Создаем новые связанные задачи по списку
for task_code in start_template_task_code_list:
# Находимшаблон
template = models.CmfTask.get(code=task_code)
if template:
new_project = template.project
# Создаем словарь с нужными полями и значениями
dict_params = {'parent': new_project, 'project': new_project,
'cmf_author': cmf_author, 'cmf_owner': cmf_owner}
# Проверяем наличие пользовательских полей в шаблоне и добавляем в словарь
if hasattr(template, 'cf_fio_sotrudnika'):
dict_params.setdefault('cf_fio_sotrudnika', self.cf_fio_sotrudnika)
if hasattr(template, 'cf_telefon'):
dict_params.setdefault('cf_telefon', self.cf_telefon.load())
if hasattr(template, 'cf_lichnyj_email'):
dict_params.setdefault('cf_lichnyj_email', self.cf_lichnyj_email.load())
...
# Создаемзадачу
new_task_id = models.CmfTask.create_task_from_template(template, params=dict_params)
new_task = models.CmfTask.get(id=new_task_id)
if self.cf_data_nr and self.cf_fio_sotrudnika:
new_task.name.value = ' | '.join([
template.name.value,
self.cf_data_nr.value.strftime('%d.%m.%Y'),
self.cf_fio_sotrudnika.value])
new_task.save()
# и добавляем связь между созданной задачей и родительской
rlo = models.CmfRelationOption(
out_link=self,
in_link=new_task,
relation_type=task_relation_type
).save()По большому счету, это все что нужно - задачи по новым шаблонам автоматически созданы и либо назначены на пользователей по умолчанию, либо встали в очередь, откуда их заберут те сотрудники, которым это положено по должности. Нужно только обратить внимание, что в нашем примере задачи создавались в разных проектах системы, соответственно требовалось немного поколдовать с пользовательскими правами, чтобы у автора исходной задачи они были в соответствующих пространствах.
Чего мы добились благодаря этой автоматизации? HR-специалистам нашего клиента теперь не нужно писать отдельные задачи в смежные подразделения - они создаются автоматически. Не нужно опасаться забыть создать задачу для одной из служб. Наконец, благодаря связям между тикетами, удобно на странице корневой задачи видеть статусы всех, что с ней связаны, и получать уведомления об изменении этих задач.