Що було: як збирали розклад?
Навчальний центр працює звичним для галузі циклом: до 15-го числа поточного місяця треба сформувати групи на наступний. Картки курсів, груп і підгруп уже були в CRM як смарт-процеси — з тривалістю, типом курсу, лімітами учасників і зв'язками з угодами зарахованих клієнтів. Даних вистачало. Не вистачало інструмента, який з цих даних складе календар.
Тому методист складав його в голові й на аркуші. Правил, які треба тримати одночасно, набирається чимало: робочий день — вісім годин, вихідні не плануються, дві групи одного курсу не можуть займати ті самі години (крім курсів, де паралельні групи дозволені явно), у змішаних курсів короткі й довгі групи мають чергуватися рівномірно, остання група місяця може закінчуватися вже в наступному, а нумерація з початку року починається заново.
Ціна помилки тут відкладена. Накладка двох груп не підсвічується в момент, коли її роблять, — вона просто стає видимою, коли слухачі вже прийшли на заняття. І виправляють її потім люди, а не система.
Чому не «доналаштувати CRM», а писати модуль?
Коробкова CRM добре робить те, для чого вона зроблена: картки, списки, канбан, права, бізнес-процеси. Але вона не вміє розкладати години по робочих днях за галузевими правилами конкретного навчального центру — і жодне налаштування полів цього не дасть. Тут потрібен не конструктор, а код.
Ключове обмеження ми поставили собі самі: жодних змін в ядрі платформи. Увесь модуль живе у власному ізольованому каталозі й користується штатними механізмами — власними таблицями, контролерами, розширеннями інтерфейсу, API смарт-процесів. Це нудна на вигляд вимога, від якої залежить усе: модуль ізольований від ядра, тому його можна супроводжувати окремо і перенести разом із даними, якщо клієнт колись мігруватиме на іншу платформу.
Що побудували?
У головному меню CRM з'явився пункт «Розклад груп» із двома основними екранами — календарем і чернеткою.
Календар показує сітку робочих днів із плашками груп: назва курсу, тип (довга або коротка), номер групи, кількість слухачів. Якщо в один день припадає більше двох груп, зайві згортаються в бейдж «+N». Групи, що почалися минулого місяця й тривають далі, позначаються маркером «ПР». Групи, які після ручних правок почали накладатися одна на одну всупереч правилам, підсвічуються червоним — одразу, а не заднім числом.
- Фільтри: період (місяць, тиждень, довільний діапазон), курси мультивибором, тип курсу, стан сертифікатів — сформовані чи ні.
- Друк розкладу за поточний період із урахуванням застосованих фільтрів, у верстці під A4.
- Клік по курсу або номеру групи відкриває відповідну картку в бічній панелі — без переходу на іншу сторінку.
- Три ролі: інспектор бачить і друкує, методист формує й налаштовує, адміністратор може ще й перегенерувати вже фіналізований розклад.
Другий екран — чернетка. Методист натискає «Сформувати», і система за налаштуваннями курсів розкладає групи на наступний місяць. Це поки що чернетка, яку видно й можна поправити: мікроблок групи перетягується на інший день, після чого перераховуються дати початку й завершення, зайнятість годин і конфлікти. Коли картина влаштовує — «Сформувати» вдруге. Аж тепер система призначає номери у форматі «рік/номер», фіксує час і створює реальні елементи смарт-процесу «Групи».
Налаштування — окремий екран, де для кожного курсу задається кількість коротких і довгих груп на місяць, загальна кількість і сам факт участі курсу в автоформуванні. Це те, що перетворює модуль з разової автоматизації на інструмент: правила змінюються протягом року, з'являються нові курси, і методист міняє їх сам, без нас.
Від яких двох деталей залежить, чи буде такий модуль працювати?
Перша — планувальник «хвиль». Групи одного курсу розбиваються на хвилі: паралельні йдуть в одному часовому слоті, послідовні — одна за одною. Хвиля заповнює восьмигодинний день; якщо не вміщається — переходить на наступний робочий день, а якщо завершується опівдні — залишок дня віддається наступній хвилі. Вихідні пропускаються. Планування ведеться в годинах із підтримкою дробових значень — це знадобиться, коли дійде черга до поурочного плану підготовки.
Друга — двофазна фіналізація. Створення елементів CRM не загорнеш у транзакцію бази даних, тож збій посередині залишив би половину груп створеними, а половину — ні. Тому модуль спершу створює елементи в CRM, потім у транзакції оновлює власні таблиці, і якщо друга фаза падає — відкочує першу, видаляючи щойно створені групи. Повторне натискання «Сформувати» не плодить дублів. Кожна значуща дія — генерація, перетягування, фіналізація — пишеться в журнал: хто, що і коли.
Скільки це зайняло?
Оцінка на старті була 60 годин розробки і 2,5–3 місяці календарного часу. Робоча версія стояла на сервері клієнта за два тижні від першого коміту — далі йшло приймання, правки за зауваженнями методистів і фінальне закриття завдання. Ми не називаємо це «зробили за два тижні»: писати код і здати систему в експлуатацію — різні за тривалістю речі, і чесніше показувати обидві.
- Близько 6 500 рядків власного коду — бекенд, інтерфейс, стилі.
- 73 юніт-тести на планувальник хвиль, детектор конфліктів, нумерацію груп і перевірку прав.
- Три власні таблиці бази під налаштування, запуски формування й елементи чернетки.
- Нуль змін в ядрі платформи — модуль ізольований і переноситься при міграції.
Робота на цьому не закінчилася: далі в черзі підгрупи з літерними індексами й окрема вкладка розкладу для методистів із розбивкою годин на теорію, практику та іспити. Модуль від початку проєктувався під це — тому наступні блоки додаються, а не переписують попередні.
Що з цього виносити?
Коли бізнес упирається в те, що CRM «майже підходить», рішень зазвичай розглядають два: терпіти або міняти систему. Є третє — дописати відсутню частину поверх наявної. Дані, права, картки клієнтів, угоди й звички команди залишаються на місці; додається рівно той шматок, якого бракувало. Auspex робить такі доробки як окремі модулі — саме щоб їх можна було розвивати далі й не втратити при зміні платформи.