Когда обсуждается работа по расписанию, в указанный день и время, обычно имеется в виду установка регламентного задания на это время. Регламентные задания можно создавать программно, с разными расписаниями.
Возникла задача использовать расписания отдельно от регламента для следующих целей.
1) рассылка разнообразных отчетов по расписанию, в разные отделы и внешним контрагентам. Какие-то отчеты требуются ежедневно, какие-то в течение рабочих дней недели, раз в неделю, месяц. Заведение регламентного задания для каждого отчета не представляется целесообразным.
2) существуют задачи, которые невозможно выполнять по регламенту, но которые тем не менее требуют расписания. Например, задача отгрузки товара в определенные дни недели или месяца, либо например более гибкие настройки расписания срабатывания скидок или акций, чем день недели.
3) возможны требования использования расписания, которое невозможно реализовать через меню расписания регламентного задания - например "понедельник четной недели года", "15е и посл.день месяца" и т.п.
Недостатком данного метода является то, что у вас не будет привычного журналирования выполнения регламентных заданий. Также подразумевается, что у вас все эти задачи привязываются ко дню без учета времени.
Решить данную задачу предлагается созданием простенького справочника, содержащего либо строку с сериализованным объектом "Расписание регламентного задания", который превосходно работает и отдельно от самого регламентного задания, либо строку с формулой на языке 1с.
Далее - для рассылки вы добавляете поле "Расписание" в справочник настройки рассылки отчетов, устанавливаете регламент этой рассылки в ежедневный и при срабатывании регламента дополнительно проверяете срабатывание условия соответствия расписанию и только после этого высылаете отчет.
В случае с датой отгрузки можно искать следующую "правильную" дату после даты отгрузки указанной в заказе, и принудительно ставить дату отгрузки на нее.
В случае со скидками требуется аккуратная "прошивка" поля "Расписание" от документа установки скидки до функции вычисления результата скидки или акции.
Справочник у меня будет с табличной частью с двумя реквизитами - оба строковые, длиной 0 (неограниченнаяя длина, чтобы сериализацию хранить).
Табличная часть сделана для того чтобы можно было соединять несколько расписаний. В программе они соединяются через "или", но конечно можно настроить какие угодно соединения.
Модуль обработки расписания я для простоты выложил в менеджер объекта:
// Преобразует объект расписание в сериализованную строку, для хранения в справочнике
Функция РасписаниеВСтроку(Расписание) Экспорт
Если Строка(Расписание) = "один день; один раз в день" Тогда
Возврат "";
Иначе
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьXML,Расписание);
Возврат ЗаписьXML.Закрыть();
КонецЕсли;
КонецФункции
//В реквизите расписания будем хранить либо сериализованный объект либо формулу для вычисления
Функция ЭтоОбъектРасписание(стрРасписание) Экспорт
Возврат Лев(стрРасписание,12) = "<JobSchedule"
КонецФункции
//Обратное преобразование строки в объект
Функция СтрокаВРасписание(стрРасписание0) Экспорт
Если ТипЗнч(стрРасписание0) = Тип("СправочникСсылка.Расписания") Тогда
стрРасписание = стрРасписание0.Расписание;
Иначе
стрРасписание = стрРасписание0;
КонецЕсли;
Если стрРасписание = "" Тогда Возврат Новый РасписаниеРегламентногоЗадания КонецЕсли;
Попытка
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(стрРасписание);
Расписание = СериализаторXDTO.ПрочитатьXML(ЧтениеXML,Тип("РасписаниеРегламентногоЗадания"));
Исключение
Расписание = Новый РасписаниеРегламентногоЗадания;
КонецПопытки;
Возврат Расписание;
КонецФункции
//Функция проверки.
//Тестовая строка должна выглядеть как обычное логическое выражение над переменной "Дата".
Функция ТребуетсяВыполнение(Расписание, тестДата)
Если ЭтоОбъектРасписание(Расписание) Тогда
обРасписание = СтрокаВРасписание(Расписание);
Возврат обРасписание.ТребуетсяВыполнение(тестДата);
Иначе
стрРасписание = СтрЗаменить(Расписание,"Дата","ТестДата");
Возврат Вычислить(стрРасписание);
КонецЕсли;
КонецФункции
//Интерфейс справочника. Предоставляем элемент справочника, и дату,
//Возвращаем истина если дата подходит под условия расписания.
Функция ВыполнятьЗадание(Расписание, ТестДата) Экспорт
Если НЕ ЗначениеЗаполнено(Расписание) Тогда Возврат Истина КонецЕсли;
Если ТипЗнч(Расписание) = Тип("Строка") Тогда
Если Расписание = "" Тогда Возврат Истина КонецЕсли;
Возврат ТребуетсяВыполнение(Расписание, тестДата);
Иначе
обРасписание = Расписание;
фВыполнять = Ложь;
Для каждого р из обРасписание.ДетальноеРасписание Цикл
Если Не ЗначениеЗаполнено(р.Расписание) Тогда Продолжить КонецЕсли;
фВыполнять = фВыполнять или ТребуетсяВыполнение(р.Расписание, тестДата);
КонецЦикла;
Возврат фВыполнять;
КонецЕсли;
КонецФункции
//Выводит представление сериализованного расписания в виде стандартной строки его представления
Функция ПолучитьПредставление(стрРасписание) Экспорт
Если ТипЗнч(стрРасписание) = Тип("Строка") Тогда
Если стрРасписание = "" Тогда Возврат "Расписание отсутствует" КонецЕсли;
Если ЭтоОбъектРасписание(стрРасписание) Тогда
обРасписание = СтрокаВРасписание(стрРасписание);
Иначе
обРасписание = стрРасписание;
КонецЕсли;
ИначеЕсли ТипЗнч(стрРасписание) = Тип("РасписаниеРегламентногоЗадания") Тогда
обРасписание = стрРасписание;
Иначе
обРасписание = СтрокаВРасписание(стрРасписание.Расписание);
КонецЕсли;
Возврат Строка(обРасписание);
КонецФункции
//Переборка дат для получения следующей даты.
//рекомендуется вынести в модуль с повторным использованием если быстродействие
//будет снижаться
Функция ПолучитьСледующуюДату(Расписание, ДатаОтгрузки) Экспорт
Если НЕ ЗначениеЗаполнено(Расписание) Тогда Возврат ДатаОтгрузки КонецЕсли;
//Условно считаем что хотя бы раз в месяц должно выполняться
максДата = ДобавитьМесяц(ДатаОтгрузки, 1);
ТекДата = ДатаОтгрузки;
Пока ТекДата<=максДата Цикл
Если ВыполнятьЗадание(Расписание,ТекДата) Тогда Возврат ТекДата КонецЕсли;
ТекДата = ТекДата + 86400;
КонецЦикла;
Возврат ДатаОтгрузки; // Не нашли - грузим по факту.
КонецФункции
Для формы элемента предлагается такая начинка
При двойном клике на колонке расписания будет вызван стандартный диалог расписания регламентного задания (естественно без самого задания).
При редактировании расписания учтите что в нем не будут работать настройки меньше чем то, что вы поставите в регламенте, кроме того не будет проверяться предыдущее выполнение, поэтому "один день" и "каждый день" при проверке будут выдавать один и тот же результат, настройка "повторять каждые" тоже не работает, так как в данном случае 1с эти самые каждые не хранит. А вот настройка периода с какой даты и по какую - работает.
Другой вариант - задание строки формулы. Для этого вам надо будет обойти двойной клик - сначала войти в редактирование строки скажем в колонке Период, а затем перейти в колонку Расписание и внести корректную формулу. Например - понедельник нечетной недели: ДеньНедели(Дата)=1 и НеделяГода(Дата)%2=1
Тестирование даты на картинке выше:
Сообщить(Расписание.Наименование + ", " + пТестДаты + " - " + Справочники.Расписания.ВыполнятьЗадание(Расписание,пТестДаты));
где пТестДаты - поле тестовой даты.
Чтобы вернуться обратно в режим открытия окна расписания, нужно сначала стереть эту формулу и выйти из редактирования.
Примеры использования.
1) для рассылки моя компания использует замечательную разработку Автоматическая рассылка отчетов из 1С: Управление торговлей 10.3 и 1С:УПП по электронной почте.
В справочник "Настройка рассылки отчетов" я поместил реквизит Расписание, поместил его на форме и для его обработки добавил в модуль РассылкаОтчетов в процедуру РассылкаОтчетов (которая вызывается по регламентному заданию) следующий фрагмент
//...........начало пропущено.
//далее идет цикл выборки по записям справочника настроек отчетов для рассылки.
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Попытка
НастройкаРассылки = ВыборкаДетальныеЗаписи.НастройкаРассылки;
//+++++ Расписание в расписании
Если НЕ Справочники.Расписания.ВыполнятьЗадание(НастройкаРассылки.Расписание,ТекущаяДата()) Тогда
Продолжить;
КонецЕсли;
//+++++ Конец вставки
Расписание для регламентного задания РассылкаОтчетов подразумевается ежедневное один раз в день. Время отправки вы настраиваете именно в этом расписании. Отдельные регламенты для каждого отчета создавать после этой добавки не нужно.
2) Для корректировки даты отгрузки в модуль ПередЗаписью, или на форму в событие заполнения/изменения даты/даты отгрузки достаточно добавить строчку
Если (ЭтоНовый() или Не Проведен) Тогда
ДатаОтгрузки = Справочники.Расписания.ПолучитьСледующуюДату(Контрагент.Расписание,ДатаОтгрузки);
КонецЕсли;
- это при условии что у вас реквизит Расписание добавлен в справочник Контрагенты.
Естественно что вам необходимо тщательно продумать проверку, в каких случаях можно автоматически переставлять дату отгрузки в соответствии с бизнес-условиями вашего предприятия. Желательно проверять наличие уже выданной накладной и закрытие заказа.
Если вашему бизнесу требуется возможность корректировки даты отгрузки, скажем если у вас машина отправляется по средам но на следующей неделе вам необходимо ее отправить в четверг, то придется заводить дополнительный документ, записывающий соответствие типовой следующей даты и той, которая должна быть.
3) для проставления расписания для скидки вам потребуется:
- внести реквизит Расписание в документ "Установка скидок номенклатуры", вывести его на форму;
- Заведем также реквизит в регистре сведений СкидкиНаценкиНоменклатуры, если вы добавляли реквизит расписания в основные реквизиты документа, то в модуле проведения добавляется строчка записи в регистр из СтруктураШапкиДокумента.
- в модуле ОбработкаТабличныхЧастей в запросе в функции ЗапросПоСкидкам в запрос вписывается реквизит Расписание,
- в функцию РассчитатьСкидкиПриПродаже вставляется проверка расписания, такая же как для рассылки отчетов.
Upd 24.07.2023 - Версия для управляемых форм
- Отличается только модулем элемента формы. Модуль менеджера объекта остается такой же.
(форму предлагается скачать по ссылке внизу).
P.S. Естественно при необходимости можно создавать Календари, События, добавить в справочник время и т.п. У меня пока таких задач не стояло.
P.P.S. Примеры применения для УТ11 будут до конца года.
P.P.P.S. Добавлен файл для скачивания - расширение для Управляемых Форм. Вы можете его скачать для поддержания разработчика, а также воспользоваться привязкой событий к элементам формы. Все тексты модуля опубликованы в статье.