При работе с отчетами и выборками данных в 1С:Предприятие одной из наиболее частых задач является правильное ограничение периода. Особенно часто возникает потребность рассчитать интервал динамически относительно текущей даты — например, получить обороты с начала текущего года по завершение прошлого месяца. В этой статье мы подробно разберем, как реализовать такой расчет программно, как использовать возможности языка запросов и какие технические нюансы следует учитывать для обеспечения точности данных.
Рассмотрим ситуацию, когда нам необходимо передать параметры НачалоПериода и КонецПериода в запрос. Самый простой и распространенный способ — это вычисление дат на стороне встроенного языка перед выполнением запроса. Разберем по шагам логику, предложенную в обсуждении.
Для получения начала года используется стандартная функция НачалоГода(). Чтобы получить конец прошлого месяца, нам нужно сначала найти начало текущего месяца, отнять от него одну секунду (что даст нам последнюю секунду предыдущего месяца) и, для надежности, применить функцию КонецМесяца().
Проанализируем пример кода:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ПродажиОбороты.ВыручкаОборот КАК ВыручкаОборот
|ИЗ
| РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , Мастер = &Мастер) КАК ПродажиОбороты";
// ВыбДата — это точка отсчета, заданная пользователем или системой
Запрос.УстановитьПараметр("Мастер", Мастер);
Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(ВыбДата));
// Вычисляем конец прошлого месяца
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(НачалоМесяца(ВыбДата) - 1));
РезультатЗапроса = Запрос.Выполнить();
Почему мы используем КонецМесяца(НачалоМесяца(ВыбДата) - 1)? Вычитание единицы из даты 01.05.2023 00:00:00 даст нам 30.04.2023 23:59:59. В большинстве случаев этого достаточно. Однако использование функции КонецМесяца() считается правилом «хорошего тона» в программировании на 1С. Это гарантирует, что мы всегда попадаем в последнюю доступную точку времени периода, даже если в будущих версиях платформы изменится точность хранения данных.
В современной платформе 1С (начиная с версии 8.2) многие расчеты можно перенести непосредственно в текст запроса. Это делает код более лаконичным, так как не требует предварительной подготовки параметров на встроенном языке. Выясним, как это сделать с помощью функций языка запросов НАЧАЛОПЕРИОДА и ДОБАВИТЬКДАТЕ.
Посмотрим на пример реализации прямо в тексте запроса:
ВЫБРАТЬ
ПродажиОбороты.ВыручкаОборот КАК ВыручкаОборот
ИЗ
РегистрНакопления.Продажи.Обороты(
НАЧАЛОПЕРИОДА(&ЗаданнаяДата, ГОД),
ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&ЗаданнаяДата, МЕСЯЦ), СЕКУНДА, -1),
,
Мастер = &Мастер) КАК ПродажиОбороты
В данном случае мы передаем только один параметр &ЗаданнаяДата, а система самостоятельно вычисляет границы периода. Функция НАЧАЛОПЕРИОДА с параметром ГОД возвращает 1 января, а комбинация ДОБАВИТЬКДАТЕ и НАЧАЛОПЕРИОДА позволяет точно вычислить последнюю секунду предыдущего месяца.
При работе с виртуальными таблицами (особенно Остатки или ОстаткиИОбороты) критически важно понимать разницу между простой датой и объектом Граница. Хотя для таблицы Обороты это менее критично, использование Границы является «золотым стандартом» разработки.
Для чего это нужно? В 1С дата включает время. Если мы передаем дату 31.03.2023 23:59:59, то записи, сделанные ровно в эту секунду, могут быть не включены или включены неоднозначно в зависимости от контекста. Чтобы гарантированно включить все движения до конца указанной секунды, мы используем объект Граница с видом Включая.
Рассмотрим, как правильно установить такой параметр:
КонецПериодаДата = КонецМесяца(НачалоМесяца(ВыбДата) - 1);
ГраницаПериода = Новый Граница(КонецПериодаДата, ВидГраницы.Включая);
Запрос.УстановитьПараметр("КонецПериода", ГраницаПериода);
Это исключает любые ошибки при получении данных на стыке периодов. Проанализируем ситуацию: если документ проведен временем 23:59:59, простая передача даты может его «потерять» в некоторых видах итогов, в то время как Граница отработает корректно.
Если в вашей обработке или отчете период задается пользователем на форме через стандартное поле, вы, скорее всего, столкнетесь с типом данных СтандартныйПериод. Рассмотрим нюанс: свойство ДатаОкончания этого объекта обычно содержит начало дня (00:00:00) последней даты периода.
Чтобы данные за последний день периода попали в запрос, обязательно преобразуйте дату окончания:
// ПериодНаФорме — это реквизит типа СтандартныйПериод
Запрос.УстановитьПараметр("НачалоПериода", ПериодНаФорме.ДатаНачала);
Запрос.УстановитьПараметр("КонецПериода", КонецДня(ПериодНаФорме.ДатаОкончания));
Без вызова КонецДня() вы получите данные только по 00:00:00 последнего дня, и все продажи, совершенные в течение этого дня, не попадут в выборку.
Важно помнить, что в 1С параметры периода нужно передавать именно в параметры виртуальной таблицы (в скобках после имени таблицы), а не в секцию ГДЕ. Проанализируем, почему это так.
Обороты(&Начало, &Конец), система обращается к заранее рассчитанным таблицам итогов. Это происходит практически мгновенно.ГДЕ Период МЕЖДУ &Начало И &Конец, платформа будет вынуждена прочитать все записи из основной таблицы регистра (физической таблицы движений). На больших базах это приведет к колоссальным задержкам и нагрузке на СУБД.Всегда проверяйте параметры на заполненность. Если в НачалоПериода передать Неопределено или пустую дату, запрос начнет выборку с 01.01.0001, что может быть не тем результатом, на который вы рассчитывали.
Подводя итог, мы разобрали основные способы установки периода. Оптимальным вариантом для задачи «от начала года до конца прошлого месяца» будет использование встроенных функций 1С для расчета точных дат и их передача в параметры виртуальной таблицы запроса с использованием объекта Граница для максимальной точности.