При разработке систем планирования (например, графиков обучения, доставок или сервисных работ) часто возникает потребность предоставить пользователю гибкий инструмент настройки расписания. Использовать стандартный объект 1С РасписаниеРегламентногоЗадания — логичное решение, так как он обладает мощным встроенным функционалом. Однако создавать под каждый элемент справочника реальное регламентное задание в списке «Регламентные и фоновые задания» — архитектурная ошибка, которая приведет к деградации производительности системы.
В данной статье мы разберем, как использовать объект расписания автономно: хранить его настройки прямо в справочнике, вызывать стандартное окно настройки и интерпретировать эти данные в конкретный список дат для записи в регистр или вывода в отчеты.
Прежде чем переходить к коду, проанализируем оптимальную схему реализации. Чтобы система работала быстро и была удобной для запросов, мы разделим данные на две части:
РегистрСведений. Это результат работы «формулы», который легко использовать в запросах, диаграммах Ганта и календарях.Поскольку тип данных РасписаниеРегламентногоЗадания не может быть типом реквизита справочника напрямую, мы будем хранить его в виде строки. Рассмотрим два способа сериализации.
Способ 1. ЗначениеВСтрокуВнутр (быстрый, но платформозависимый)
Этот метод прост в реализации и часто используется для внутренних нужд системы. Рассмотрим пример записи и чтения:
// Запись расписания в реквизит справочника
Объект.РасписаниеСтрокой = ЗначениеВСтрокуВнутр(ОбъектРасписания);
// Восстановление объекта из строки
ОбъектРасписания = ЗначениеИзСтрокиВнутр(Объект.РасписаниеСтрокой);
Способ 2. XDTO (рекомендуемый и надежный)
Использование СериализаторXDTO делает данные более универсальными и читаемыми. Это гарантирует, что при обновлении платформы структура данных останется корректной.
Для настройки расписания пользователем воспользуемся стандартным объектом ДиалогРасписанияРегламентногоЗадания. Разберем программный код вызова этого диалога в управляемой форме справочника.
Сначала подготовим серверную процедуру для получения объекта расписания:
&НаСервере
Функция ПолучитьОбъектРасписания()
Если ЗначениеЗаполнено(Объект.РасписаниеХранилище) Тогда
Возврат ЗначениеИзСтрокиВнутр(Объект.РасписаниеХранилище);
Иначе
Возврат Новый РасписаниеРегламентногоЗадания;
КонецЕсли;
КонецФункции
Теперь реализуем команду открытия диалога на клиенте с использованием описания оповещения:
&НаКлиенте
Процедура НастроитьРасписание(Команда)
ТекущееРасписание = ПолучитьОбъектРасписания();
Диалог = Новый ДиалогРасписанияРегламентногоЗадания(ТекущееРасписание);
Оповещение = Новый ОписаниеОповещения("ПослеНастройкиРасписания", ЭтотОбъект);
Диалог.Показать(Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеНастройкиРасписания(ВыбранноеРасписание, ДополнительныеПараметры) Экспорт
Если ВыбранноеРасписание <> Неопределено Тогда
Объект.ПредставлениеРасписания = Строка(ВыбранноеРасписание);
СохранитьРасписаниеНаСервере(ВыбранноеРасписание);
ЭтаФорма.Модифицированность = Истина;
КонецЕсли;
КонецПроцедуры
Самый важный этап — превращение «абстрактного» расписания в конкретные моменты времени. Для этого используется метод ТребуетсяВыполнение(). Рассмотрим, как заполнить таблицу дат на определенный период (например, на месяц вперед).
Проанализируем алгоритм: мы будем перебирать временные интервалы с определенным шагом и проверять, должно ли в этот момент запуститься задание. Важно помнить: метод проверяет точное соответствие времени. Если расписание настроено на запуск раз в день в 10:00:00, то проверка в 10:00:01 вернет Ложь.
&НаСервере
Процедура РассчитатьДатыВыполнения(ОбъектРасписания, ДатаНачала, ДатаОкончания)
ТаблицаДат = Новый ТаблицаЗначений;
ТаблицаДат.Колонки.Добавить("ДатаВыполнения");
// Определяем шаг проверки. Если расписание может быть секундным, шаг - 1 сек.
// Для большинства бизнес-задач достаточно шага в 60 секунд (1 минута).
ШагПроверки = 60;
ТекущийМомент = НачалоМинуты(ДатаНачала);
Пока ТекущийМомент <= ДатаОкончания Цикл
Если ОбъектРасписания.ТребуетсяВыполнение(ТекущийМомент) Тогда
НоваяСтрока = ТаблицаДат.Добавить();
НоваяСтрока.ДатаВыполнения = ТекущийМомент;
// Если в одну минуту не может быть двух запусков,
// можно ускорить цикл, перейдя к следующей минуте
ТекущийМомент = ТекущийМомент + 60;
Иначе
ТекущийМомент = ТекущийМомент + ШагПроверки;
КонецЕсли;
КонецЦикла;
// Полученную таблицу записываем в РегистрСведений
ЗаписатьПланВРегистр(ТаблицаДат);
КонецПроцедуры
Хранение всех дат в регистре позволяет легко строить отчеты. Однако не стоит рассчитывать план на 10 лет вперед. Рассмотрим стратегию «Планирования на лету»:
ЗначениеВСтрокуВнутр), чтобы не выполнять тяжелые операции записи в БД без необходимости.1. Использование БСП (Библиотека стандартных подсистем)
Если в вашей конфигурации есть БСП, обратите внимание на общий модуль ОбщегоНазначения и функции работы со строковыми представлениями. Также полезно изучить модуль РегламентныеЗаданияСлужебный, где реализованы механизмы обработки расписаний.
2. Проблема часовых поясов
Объект РасписаниеРегламентногоЗадания всегда ориентируется на время сервера. Если ваши пользователи распределены географически, при расчете дат для оповещений обязательно учитывайте смещение часового пояса пользователя или храните время в формате UTC.
3. Исключения (Праздники)
Стандартное расписание не знает о производственном календаре. После того как вы получили таблицу дат методом ТребуетсяВыполнение(), проанализируйте полученные даты вторым проходом: удалите те, что выпадают на государственные праздники или выходные, если это требуется бизнес-логикой.
Подводя итог, можно сказать, что отделение объекта расписания от физических регламентных заданий — это правильный путь для создания масштабируемых систем планирования в 1С. Сочетание строкового хранения настроек и трансляции их в физические записи регистра сведений обеспечивает идеальный баланс между гибкостью настроек и скоростью работы отчетов.