Логика построения универсальных графиков работ
Графики работ сотрудников неотъемлемая часть любой CRM системы. Но в зависимости от специфики бизнеса клиента они могут очень отличаться. В клинике это графики приема пациентов, в телекоме – графики подключения клиентов, в школе – графики занятий. Все они отличаются по структуре и сути. У них разные шапки, разный набор полей, разные сетки.
Наша ERP-Платформа ориентирована на быструю разработку любой нишевой конфигурации и мы задумались, а как бы это все сделать удобно. В первую очередь для себя. Чтобы наши трудозатраты на разработку графика для специфической ниши занимало не более человеко-часа.
До этого графики были некой штукой разработанной на php. Но боже! Карл! Каждый раз, когда нужен новый график под какое-то нишевое решение, надо этот код копировать, перерабатывать. Индивидуальность графиков не позволяла что-то централизованно изменить. Да и в целом подрывало концепцию нашей облачной платформы в том, что клиент может самостоятельно ее конфигурировать.
В итоге мы сформировали следующие требования к графикам работ
-
Конфигурируемость
Они должны полностью конфигурироваться из веб интерфейса. -
Переменный шаг
У них должен быть переменный шаг сетки.
В одной клинике сетка приема врача может быть 30 минут на пациента, а в другой 20, в третьей час -
Произвольная шапка
Шапка графика может быть уникальной, т.е. это может быть набор врачей, объектов, преподавателей, бригад и т.п. В общем что угодно из того что у клиента есть в системе. Даже из того, о чем мы не можем предположить. -
В записи графика – произвольный набор полей
Набор полей можно задавать произвольно, из любой информации, которая есть в системе. А записи расставляться в соответствии с шапкой. -
CRUD
Записи графика должны быть СRUD (create, read, update, delete), не смотря на произвольные наборы полей. Причем не просто только это, а с использованием процедур, чтобы организовать сложные взаимодействия.
Пример: есть график работ по задачам.
В записи графика выводится кто выполняет, где проводятся работы и т.п. + какие-то данные по задаче (например ее номер и название).
Доп.данные по записи графика хранятся в одной таблице, а данные задачи в другой.
При создании указывается номер задачи, кто и где выполняет. А выводиться данные будут из двух таблиц.
Редактировать можно только данные записи графика, но нельзя данные задачи.
Удаляться должна запись графика, но не задача.
Надо вести логи кто-что делал с этой записью. Т.к. она может быть удалена и не обязательно будет видна в графике, то до логов как-то надо добраться. Их логичнее вести в самой задаче, т.е. при создании-модификации-удалении должно писаться в отдельную таблицу и выводиться в задаче.
Таких вещи могут решаться только хранимыми процедурами, чтобы решать можно было и простые и сложные ситуации. -
Поля могут быть списками
Конечно поля не должны быть только текстовыми. Они могут быть и всплывающими списками из каких-то данных в системе. -
Поля могут быть ссылками
Удобно, когда например из графика работ по задачам можно попасть в саму задачу. -
Скрытие полей
Если полей в записи много, имеет смысл сворачивать под кат часть из них для удобства восприятия. -
Триггеры
Необходимо настраивать реакцию системы на происходящее с записью. Удобнее всего это делать триггерами.
Например если пользователю понадобиться писать какие-то логи по действиям над записью, или слать уведомления сотрудникам, или даже клиенту sms послать или вообще сделать то, что мы и предположить не можем.
-
Кросс-записи
Кросс-записи – это записи из разных графиков сведенные в единый график. Или же появление записей в других графиках, при появлении записей в текущем.
Например, бывают такие ситуации что одни и те же бригады выполняют работы, например и по заявками и по задачам. Т.е. им удобен будет некий общий график, где будут отображаться задачи с разных графиков.
Или необходим личный график сотрудника, с записями из всех графиков где он участвует.
Такие задачи немного сложнее чем типовые, но вполне решаются при помощи триггеров и процедур получения данных. Т.е. путем п.9 и п.5.
а) на записи нужных графиков создается триггера, которые создают запись в личном графике пользователя в случае если пользователь там есть (соответственно в случае изменения изменяют, в случае удаления удаляют)
б) в графике пользователя создаются дополнительные поля, которые будут включать нужные данные, а так же дорабатываются процедуры вывода записи, которые будут по этим идентификаторам подтягивать данные из таблиц других графиков.
Такие задачи могут быть решены и обратным путем, при формировании личного графика сотрдуника делается более хитрая процедура, которая выведет данные не только из личных таблиц, но из таблиц иных графиков где присутствует сотрудник в данных день. Такие решения на базе этой конструкции тоже возможны. -
Накладки времени
Система должна корректно отображать пересечения записей по времени, а не как у большинства систем, где записи наезжают друг на друга и нет даже возможности прочитать нижние слои.
Типа такого, это же печаль…
-
Отчеты
Чтобы по данным графика можно было строить отчеты. Система хранения данных должна быть организована так, чтобы ее можно было подцеплять в конфигураторе отчетов.
Например, понадобилось создать отчет, кто из сотрудников сколько времени провел в разъездах по задачам – вуаля! Идем в конфигуратор отчетов, там выбираем таблицу, где хранятся данные записей нужного графика и делаем отчет при помощи стандартного конфигуратора по данным из графика.
-
Встраиваемость
График должен иметь возможность легко встраиваться в любое место любой страницы – т.е. это должен быть стандартный input элемент, такой же как текстовое поле, или кнопка.
Благо в разработке сложных стандартных элементов у нас был опыт. В частности у нас есть стандартный элемент комментарий, форма приложения файла, форма контактов, таблицы. Все они добавляются одним щелчком как обычные элементы и без проблем настраиваются. -
Доступ и безопасность
Права доступа к графику в целом должны регулироваться общий системой прав, а внутри графика права доступа к записям должны регулироваться настройками графика.
Например указать что только автор может редактировать свою запись, или его группа, или подразделение.
-
Удобство конфигурирования
Ну и конечно создание и конфигурирование нового графика должно делаться быстро и не вызывать никаких проблем. В частности для нас, для типового случая (понятно что бывает конечно и очень сложные) эта работа должна занимать не более человеко-часа.
Мы считаем, что у нас достаточно изящно получилось реализовать данные требования и в этой статье будет описано, как это сделано.
На текущий момент я не знаю систем с аналогичными по универсальности графиками (если Вы знаете, напишите в комментариях, интересно посмотреть)
Общая схема конфигурирования графика выглядит примерно так (не судите строго, рисовал сам). Каждый элемент здесь нужен, лишнего нет.
Вся работа начинается с понимания, какой набор данных должен быть в записи и создания таблицы в БД под данный график.
К примеру, для графика работ по задачам в телеком компании важно знать, когда будет проводиться работа, по какой задаче, по какому объекту, и какая группа инженеров будет проводить эту работу.
Поэтому формируем таблицу, где есть вся эта инфа + служебные поля с ID записи графика. (PS: таблицы у нас можно редактировать прямо в облаке из браузера).
Это простой случай, но можно и намного сложнее, где больше полей или несколько таблиц.
Все, структура хранения сформирована и теперь графику надо знать, как с этим работать. Каждая запись может добавляться, выводиться, изменяться и удаляться.
Т.к. набор данных и структура хранения может быть произвольная, то с этой структурой надо работать через хранимую процедуру.
Сначала надо процедуры создать. У нас в примере случай простой, и их можно в конфигураторе создать одной кнопкой при помощи функции “Создать типовые процедуры”. Система самостоятельно по этой таблице создаст процедуры внесения, изменения, удаления, и различных выводов.
Например, фрагмент автоматически созданной процедуры добавления выгляди так
Все процедуры редактируются прямо из веб-интерфейса. Логика составления аналогична PL-SQL.
Если что-то сложное, то эти процедуры могут редактироваться в зависимости от структуры хранения и можно задать различные условия, циклы, select-ы, update-ы, insert-ы и т.п. В общем, нарисовать любую структуру обработки.
В графике в каждом действии указывается, какая процедура отвечает за это действие.
“Вшитые” настройки графика
Но все же есть и “вшитые” настройки, которые можно только настраивать, но не изменять структуру. В этом просто нет необходимости.
— Координаты даты-времени – это неотъемлемое свойство любой записи
— Местоположение графика в системе. Оно редактируется средствами встроенного редактора интерфейса и нет необходимости делать это из самого графика.
— Память того где был пользователь – нет необходимости напрямую конфигурировать это свойство.
В настройках могут так же задаваться Шаг (например, сетка в 20 минут, или в 30) и Диапазон рабочего времени (например, с 8 до 20).
Диапазон времени нужен, чтобы отсечь лишнее, например если все сотрудники работают 8 часовой стандартный рабочий день, то выводить ночные часы смысла нет.
Шаг – определяет сетку графика. Например, в одной клинике может быть сетка приема врача 20 минут, в другой 15, в третей 30. Это все настраивается.
Однако это не значит, что запись нельзя делать вне рамок сетки. Можно делать любые записи, но отображаться они будут в рамках сетки.
Диапазон шага у нас можно задавать 5, 10, 15, 30, 60 минут.
Вывод записи
Мало создать и указать процедуру. Надо чтобы график понимал, как с этим работать со своей стороны.
Шапка
Самое важное – указать шапку графика. Какое из полей вывода процедуры должно быть шапкой.
Рассмотрим логику построения расписания занятий в образовательной организации. Допустим, в записи есть набор Преподаватель-Группа.
Здесь можно сделать шапку по Преподавателям, тогда график будет формироваться так:
а можно по Группам, тогда так:
а можно вообще сделать 2 разных графика и выводить и так и так, кому как нравится, пользователи сами выберут. При этом будут использоваться одни и те же данные, одни и те же процедуры. Изменения в одном графике будут изменяться и в другом.
Но в целом выбор зависит от вопросов целесообразности. Шапка должна быть фундаментальной мало меняющейся информацией. Если вы обслуживаете фиксированное количество объектов – то это должны быть они. Если у вас фиксированное количество групп – то они.
С шапкой можно творить вообще интересные вещи. Например, если в качестве шапки указать статус задачи, то при изменении статуса в задаче – запись задачи в графике перескочит сама под соответствующий статус. Т.е. по сути чуть ли не канбан доски можно здесь делать и т.п. вещи. Когда мы привязываем через процедуры данные записи к живой информации в системе – в графиках оно тоже может оживать. Очень гибкий механизм.
Так же если в процедуре задать условия фильтрации, в графике можно формировать данные шапки с учетом произвольных фильтров.
Что не выводить
Так же можно указывать, что выводить в записи, а что нет. Например, в процедуре в качестве результатов могут быть выведены какие-то служебные идентификаторы для связок. Эту не значимую для пользователя информацию можно при выводе игнорировать.
Что скрывать
А можно выводить, но сворачивать под кат. Информацию, которая не очень важна, но иногда нужна. Например, кто и когда создал данную запись, может понадобиться только в случае каких-то разборок, но не в оперативной работе.
Ссылки
Так же очень удобно, когда какое-то поле может быть ссылкой.
Например, если мы делаем график по задачам, почему бы не кликнув по ее номеру попасть в саму задачу.
У нас в конфигураторе есть стандартный механизм формирования ссылок. Можно сделать их там, и связать с полем в графике. Все работает.
Название
Конечно, поле должно как-то называться. По умолчанию система возьмет название из поля процедуры, но оно бывает не всегда корректно для восприятия. Поэтому иногда нужно вводить альтернативные названия.
Например, все вышеперечисленное у нас выглядит так. Напротив полей указанной процедуры ставятся галочки.
Вывод записи при этом будет выглядеть так:
Добавление новой записи
Поля добавления новой записи должны определяться процедурой добавления. В каждый входной параметр процедуры надо подать информацию.
Но есть фишка. Поля могут быть не только текстом, но и списками.
Возьмем наш пример для телекома, где есть задача, группа, объект. Мы же не будем пользователя заставлять искать идентификаторы объектов и их вбивать. Нужно чтобы был всплывающий список с нужной информацией. А где его взять?
Для этого поле ввода процедуры должна дернуть другую процедуру, которая выдаст к примеру список актуальных задач, или список нужных объектов и т.п. При этом нельзя просто указать поля какой-то таблицы т.к. могут быть сложные фильтры. Тех же задач может быть уже 10000 в системе наштамповано, а надо вывести список из 100 актуальных сейчас.
В общем, процедура, которая дергается по полю другой процедуры.
На самом деле только страшно звучит. Данные вещи хорошо автоматизированы, процедуры по стандартным справочникам получаются одной кнопкой. Процедуры для всевозможных списков групп, задач, заявок и т.п. давно реализованы для работы всевозможных CRM модулей. Их надо только выбрать. Если уже есть процедура с подобной функциональностью, она копируется и доредактируется.
Так же надо указать какое из добавляемых полей будет шапкой. Это нам понадобится в системе редактирования.
Выглядит у нас это так:
Редактирование записи
Тут все еще интереснее. Мало вывести все поля списки как в добавлении. Надо в этих списках выбрать текущие позиции (selected). Мы же редактируем.
Для этого надо связать поля создания с полями вывода. А в случае поля списка не просто бахнуть инфу из вывода, а знать именно идентификатор этой информации.
Т.е. тут надо:
а) указать процедуру редактирования
б) ее поля связать с полями добавления
в) ее поля связать с полями вывода данных записи, чтобы в списках поставить текущие данные
Каких-то других особенностей тут указывать не надо, т.к. у нас уже есть сконфигурированные Вывод и Добавление.
При выводе окошка редактирования система самостоятельно по каждому полю вызовет нужную процедуру, получит данные, сопоставит с данными из вывода записи, и в списках проставит selected, а в простых полях вставит текущие значения.
Выглядит у нас так
Удаление записи
Удаление это самое простое. Просто указывается процедура удаления с одним входным параметром: ID записи графика.
Обработка событий при добавлении/модификации/удалении записей. Триггеры. Кросс-записи.
Только добавлять/модифицировать записи в графиках мало. Нужно еще управлять событиями. Это открывает неограниченный простор для конфигурирования.
Управлять событиями на самом деле очень просто. У нас уже ведь есть таблицы, в которых хранятся данные записей графика. На эти таблицы можно ставить любые по сложности триггеры. Наш облачный конфигуратор позволяет легко такое делать.
Например, понадобились логи, кто что делал в записи. Все просто. Создается таблица для логов, и на таблицу графика ставятся триггера, которые пишут данные модификации в таблицу с логами.
Так же благодаря этому доступны кросс-записи. Например, когда в нашем примере ставится задача в группу, можно определить всех участников данной группы и поставить им дубль этой записи в личные графики. Поставить в личные графики в данном случае означает – сделать запись в таблицу данных личного графика. Он тоже график в этой системе, только сконфигурирован по-другому и выведен в другое место. В нем эта запись появится.
Или надо послать пользователям группы уведомление. Или послать клиенту смс, что к нему придут проводить работы по такой-то задаче тогда-то. Для этого просто делаются триггера, которые делают записи в таблицы уведомлений или смс.
Отчеты
Отчеты по графикам тоже нужны, и при такой структуре их можно делать.
Например, понадобилось сделать отчет, сколько времени были в разъездах сотрудники в предыдущем месяце. Идем в конфигуратор отчетов, выбираем нужную таблицу, где хранятся записи графика, ставим нужные фильтры и агрегационные функции – и вауля, отчет готов.
Другое использование данных графика
Данные графика можно использовать где угодно, имея развитый конфигуратор системы. Например, в задачах мы легко смогли сделать вкладку Графики и одной простой процедурой выводить информацию, на какое время и у кого стоит данная задача к исполнению.
Накладки времени
Они случаются. И нет ничего хуже смотрящегося, чем записи наезжающие друг на друга и не дающие прочитать нужную запись. Эта проблема тоже решена. Система пересекающиеся записи автоматически размещает рядом. При этом используется только простой html, просто структура таблицы строится нужным для этого образом. Системой предусмотрен уход на любую глубину, но больше 2 записей встречается редко.
Конечно, если время накладывается, скорее всего, что-то не так запланировано. Человек не может одновременно в двух местах быть, но даже такие ситуации надо отображать корректно.
Доступ и безопасность
Доступ разделяется на 2 уровня. Системный и Личный графика. Системный определяется в целом редактором Ролей и правами по умолчанию заданными в конфигураторе интерфейса. Например, если на данную ячейку страницы (или наследуемые выше, вкладки, страницы, меню) будет стоять запрет доступа на чтение, то никакие пользователи не будут видеть этого графика на уровне отображения интерфейса, пока в Роли пользователей не назначат права на чтение этой области.
Далее вступают внутренние права графика. Они могут быть
1) Редактирование
— только автор
— только группа автора (все сотрудники входящие в группы, в которых состоит автор записи)
— только подразделение автора (все сотрудники входящие в подразделение автора, согласно штатке)
PS: рабочая группа и штатка могут у нас отличаться. Например, в одном подразделении может быть несколько рабочих групп, или сотрудники из разных подразделений могут быть в одной группе. Так же один и тот же сотрудник может состоять одновременно в разных группах. Все это система учитывает.
2) Удаление аналогично.
— только автор
— только группа
— только подразделение
Для чего нужны внутренние права?
Если это личный график, или есть какие-то особенности, то имеет смысл поставить только автора.
Но бывает сменный персонал и ситуации, когда запись надо отредактировать, а сотрудника нет на работе. Что делать? Таким графикам имеет смысл поставить редактирование группой или подразделением, чтобы другие сотрудники смены могли работать с этими записями.
Так же в графике может быть указана Роль, которая имеет право администрировать. Сотрудники с этой Ролью будут иметь права и на редактирование и на удаление, вне зависимости от групп и подразделений.
Вот такая получилась у нас классная штука, очень упрощающая жизнь, когда умеешь ее юзать конечно. Сходу неискушенному человеку может будет сложновато разобраться. Но у нас накидать такой график в несложном варианте, составляет около человеко-часа.
Помимо этого еще получаем глубочайшие конфигурационные возможности и возможности взаимодействия с другими элементами системы.