При работе с системой 1С часто возникает необходимость в автоматической рассылке различных отчетов по расписанию. Однако стандартные механизмы рассылки отчетов могут не обладать достаточной гибкостью для реализации сложных условий, таких как перенос даты рассылки на предыдущий рабочий день, если заданная дата выпадает на выходной или праздничный день. В данной статье мы подробно рассмотрим, как можно реализовать такую функциональность, используя возможности платформы 1С.
Прежде всего, давайте выясним, возможно ли это сделать стандартными средствами. К сожалению, стандартная настройка рассылки отчетов не предусматривает такую логику. Механизм расписания в типовых конфигурациях 1С позволяет настроить периодичность (ежедневно, еженедельно, ежемесячно, по определенным дням), но не умеет автоматически корректировать дату рассылки на основе производственного календаря, перенося ее на предыдущий рабочий день. Поэтому для решения этой задачи нам потребуются дополнительные доработки. Для решения этой проблемы можно использовать расширение для настройки расписания рассылок по рабочим дням.
Рассмотрим несколько подходов, которые помогут нам автоматизировать рассылку с учетом производственного календаря.
Один из наиболее гибких и рекомендуемых подходов — это создание собственного регламентного задания, которое будет запускаться по расписанию и проверять условия для рассылки. Для этого мы можем создать внешнюю обработку, которая будет содержать всю необходимую логику, а затем настроить ее запуск через регламентное задание. Для организации подобных запусков можно использовать подсистему регулярных заданий по графикам производственного календаря.
РегистрСведений.РегламентированныйПроизводственныйКалендарь или РегистрСведений.ДанныеПроизводственногоКалендаря, в зависимости от конфигурации и версии платформы.РассылкаОтчетов.ВыполнитьРассылкуОтчетов или аналогичная. Это позволит нам использовать уже существующие настройки рассылки, заданные пользователями, но инициировать их выполнение в нужный момент времени.
// Внешняя обработка, которая будет запускаться регламентным заданием ежедневно
Процедура ВыполнитьРассылкуПоРасписанию() Экспорт
// Определяем целевую дату для рассылки (например, 10-е число каждого месяца)
ЦелеваяДатаРассылки = НачалоМесяца(ТекущаяДата());
ЦелеваяДатаРассылки = ДобавитьМесяц(ЦелеваяДатаРассылки, 1); // Следующий месяц
ЦелеваяДатаРассылки = ДобавитьДень(ЦелеваяДатаРассылки, 9); // 10-е число следующего месяца
// Сегодняшняя дата
ДатаСегодня = ТекущаяДата();
// Проверяем, нужно ли нам обрабатывать рассылку в этот день
// Например, если целевая дата уже прошла, или еще слишком рано
Если ДатаСегодня < ЦелеваяДатаРассылки - 10 Тогда // Например, начинаем проверять за 10 дней до 10-го числа
Возврат;
КонецЕсли;
// Определяем рабочий день для рассылки, если целевая дата выпадает на выходной
РабочийДеньДляРассылки = ОпределитьРабочийДеньДляРассылки(ЦелеваяДатаРассылки);
// Проверяем, является ли сегодняшний день тем самым рабочим днем для рассылки
Если День(ДатаСегодня) = День(РабочийДеньДляРассылки) И Месяц(ДатаСегодня) = Месяц(РабочийДеньДляРассылки) И Год(ДатаСегодня) = Год(РабочийДеньДляРассылки) Тогда
Сообщить("Сегодня " + Формат(ДатаСегодня, "ДЛФ=Д") + ". Найдена подходящая дата для рассылки: " + Формат(РабочийДеньДляРассылки, "ДЛФ=Д"));
// Здесь можно получить нужные настройки рассылки, например, по наименованию
// Пример (псевдокод):
// Запрос = Новый Запрос;
// Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ РегистрСведений.НастройкиРассылокОтчетов ГДЕ Наименование = &НаименованиеРассылки";
// Запрос.УстановитьПараметр("НаименованиеРассылки", "Ежемесячный_Отчет");
// Выборка = Запрос.Выполнить().Выбрать();
// Если Выборка.Следующий() Тогда
// ОбъектРассылки = Выборка.Ссылка.ПолучитьОбъект();
// РассылкаОтчетов.ВыполнитьРассылкуОтчета(ОбъектРассылки); // Вызов функции общего модуля
// КонецЕсли;
Сообщить("Рассылка отчетов выполнена!");
КонецЕсли;
КонецПроцедуры
Функция ОпределитьРабочийДеньДляРассылки(ИсходнаяДата)
ДатаДляПроверки = ИсходнаяДата;
Пока Истина Цикл
// Проверяем, является ли день рабочим
Если Не РассылкаОтчетовКлиентСервер.ЭтоВыходнойИлиПраздник(ДатаДляПроверки) Тогда // Предполагается наличие функции проверки в общем модуле
Возврат ДатаДляПроверки;
КонецЕсли;
// Если день нерабочий, переходим на предыдущий день
ДатаДляПроверки = ДобавитьДень(ДатаДляПроверки, -1);
КонецЦикла;
КонецФункции
// Пример функции для определения выходного/праздника (может быть в общем модуле или здесь для примера)
Функция ЭтоВыходнойИлиПраздник(ДатаДляПроверки)
// Необходимо получить данные производственного календаря
// Примерный код (может отличаться в зависимости от конфигурации):
// Запрос = Новый Запрос;
// Запрос.Текст = "ВЫБРАТЬ
// | ДанныеПроизводственногоКалендаря.ТипДня
// |ИЗ
// | РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеПроизводственногоКалендаря
// |ГДЕ
// | ДанныеПроизводственногоКалендаря.Дата = &Дата";
// Запрос.УстановитьПараметр("Дата", ДатаДляПроверки);
// Результат = Запрос.Выполнить().Выбрать();
// Если Результат.Следующий() Тогда
// Если Результат.ТипДня = Перечисления.ТипыДнейПроизводственногоКалендаря.Выходной
// Или Результат.ТипДня = Перечисления.ТипыДнейПроизводственногоКалендаря.Праздник Тогда
// Возврат Истина;
// КонецЕсли;
// КонецЕсли;
// Возврат Ложь;
// В качестве упрощенного примера, проверяем только дни недели
ДеньНедели = ДеньНедели(ДатаДляПроверки);
Если ДеньНедели = 6 Или ДеньНедели = 7 Тогда // Суббота или Воскресенье
Возврат Истина;
Иначе
Возврат Ложь;
КонецЕсли;
КонецФункции
Обратите внимание, что функция РассылкаОтчетовКлиентСервер.ЭтоВыходнойИлиПраздник или аналогичная, использующая регистры производственного календаря, должна быть доступна и корректно реализована. В типовых конфигурациях обычно есть функции, позволяющие работать с производственным календарем.
Другой эффективный метод — использование расширений конфигурации. Этот подход позволяет модифицировать поведение типовых объектов без снятия конфигурации с поддержки, что значительно упрощает процесс обновления.
РегистрСведений.ДанныеПроизводственногоКалендаря для определения типа дня и корректировки даты рассылки.Предположим, существует типовая процедура, которая запускает рассылку: РассылкаОтчетов.ЗапуститьРассылку(). Мы можем переопределить её в расширении:
// Модуль расширения, переопределяющий процедуру запуска рассылки
&Перед("ЗапуститьРассылку")
Процедура РассылкаОтчетов_ЗапуститьРассылкуПеред(СсылкаНаРассылку)
// Здесь можем получить объект рассылки и его расписание
// ОбъектРассылки = СсылкаНаРассылку.ПолучитьОбъект();
// РасписаниеРассылки = ОбъектРассылки.Расписание; // Получаем текущее расписание
// Если рассылка настроена на 10-е число каждого месяца,
// то мы можем пересчитать дату запуска.
// Эта логика будет аналогична описанной выше в ОпределитьРабочийДеньДляРассылки.
// Например, если текущая дата - это и есть тот самый пересчитанный рабочий день,
// тогда мы позволяем типовой процедуре выполниться.
// Если же текущая дата не совпадает с пересчитанным рабочим днем,
// мы можем отменить выполнение типовой процедуры (например, через Отказ = Истина)
// или изменить параметры, если типовая процедура это позволяет.
// Для более полного контроля, возможно, потребуется не просто &Перед,
// а полностью переопределить процедуру или функцию, которая принимает решение о запуске рассылки,
// и внедрить туда всю логику.
КонецПроцедуры
// Более сложный вариант - если есть функция, которая вычисляет следующую дату запуска
// &Вместо("ПолучитьСледующуюДатуЗапускаРассылки")
// Функция Расширение_ПолучитьСледующуюДатуЗапускаРассылки(Расписание)
// // Здесь полностью переписываем логику определения следующей даты запуска,
// // учитывая производственный календарь и перенос на предыдущий рабочий день.
// // Используем функцию ОпределитьРабочийДеньДляРассылки, как описано выше.
// Возврат ОпределитьРабочийДеньДляРассылки(ИсходнаяДатаНаОсновеРасписания);
// КонецФункции
Преимущества расширения:
Некоторые пользователи предлагают менее автоматизированный подход, который может быть полезен в очень редких случаях или при малом количестве таких рассылок. Этот метод подразумевает ручное создание нескольких настроек рассылки.
Таким образом, ручной метод является скорее обходным путем, чем полноценным решением для автоматизации, и не рекомендуется для регулярных задач.
Ключевым элементом всех программных решений является производственный календарь. В 1С он представлен регистрами сведений, которые хранят информацию о типах дней (рабочий, выходной, праздничный) для каждого года. Мы должны уметь получать эти данные и использовать их для определения, является ли конкретная дата рабочим днем.
Для работы с производственным календарем мы будем использовать следующие объекты:
РегистрСведений.ДанныеПроизводственногоКалендаря: Этот регистр хранит информацию о каждом дне в году.Перечисления.ТипыДнейПроизводственногоКалендаря: Содержит значения, такие как РабочийДень, Выходной, Праздник, Предпраздничный.Пример получения типа дня из производственного календаря (для серверного модуля):
// Функция для определения типа дня по дате
Функция ПолучитьТипДняПоДате(ДатаПроверки) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеПроизводственногоКалендаря.ТипДня
|ИЗ
| РегистрСведений.ДанныеПроизводственногоКалендаря КАК ДанныеПроизводственногоКалендаря
|ГДЕ
| ДанныеПроизводственногоКалендаря.Дата = &Дата";
Запрос.УстановитьПараметр("Дата", НачалоДня(ДатаПроверки));
РезультатЗапроса = Запрос.Выполнить().Выбрать();
Если РезультатЗапроса.Следующий() Тогда
Возврат РезультатЗапроса.ТипДня;
Иначе
// Если данных нет, по умолчанию считаем рабочим (или добавляем свою логику)
Возврат Перечисления.ТипыДнейПроизводственногоКалендаря.РабочийДень;
КонецЕсли;
КонецФункции
// Функция для проверки, является ли день выходным или праздничным
Функция ЭтоВыходнойИлиПраздникПоКалендарю(ДатаПроверки) Экспорт
ТипДня = ПолучитьТипДняПоДате(ДатаПроверки);
Возврат ТипДня = Перечисления.ТипыДнейПроизводственногоКалендаря.Выходной
Или ТипДня = Перечисления.ТипыДнейПроизводственногоКалендаря.Праздник;
КонецФункции
Используя эти функции, мы можем эффективно определять рабочие и нерабочие дни для расчета корректной даты рассылки.
Хотя стандартные настройки рассылки отчетов в 1С не предоставляют необходимой гибкости для автоматического переноса даты на предыдущий рабочий день при попадании на выходной или праздник, эта задача вполне реализуема с помощью программных доработок. Мы рассмотрели два основных подхода: использование регламентного задания с внешней обработкой и доработку через расширение конфигурации. Оба метода позволяют создать надежный и автоматизированный механизм рассылки, учитывающий производственный календарь. Выбор конкретного метода зависит от предпочтений разработчика, сложности требуемой логики и политики поддержки конфигурации.
Рекомендуем использовать программные решения (регламентные задания или расширения), так как они обеспечивают автоматизацию, точность и избавляют от рутинных проверок и настроек, характерных для ручного метода.