При построении аналитических отчетов часто требуется сопоставить данные текущего периода с аналогичным периодом прошлого месяца или года. Например, сравнить продажи с понедельника по среду текущей недели с аналогичными тремя днями той же недели прошлого месяца. Попытка выполнить расчет границ периода непосредственно внутри параметров виртуальной таблицы с помощью вложенных функций даты нередко приводит к ошибке «Неверные параметры». Рассмотрим, почему возникает данная ошибка, какие логические нюансы важно учитывать при смещении дат и как правильно решить эту задачу.
Проанализируем типичный пример запроса, вызывающий ошибку платформы:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ПродажиОбороты.Магазин КАК Магазин,
СУММА(ПродажиОбороты.СтоимостьОборот) КАК TotalLastMonth_RUB,
СУММА(ПродажиОбороты.КоличествоОборот) КАК TotalLastMonth_PCS
ИЗ
РегистрНакопления.Продажи.Обороты(
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОтчета, МЕСЯЦ, -1), НЕДЕЛЯ),
ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(&ДатаОтчета, МЕСЯЦ, -1), ДЕНЬ, ДЕНЬНЕДЕЛИ(&ДатаОтчета)),
День,
) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
ПродажиОбороты.Магазин
Выясним причину появления ошибки «Неверные параметры». В языке запросов 1С параметры виртуальных таблиц (таких как .Обороты(...) или .Остатки(...)) обрабатываются специализированным препроцессором платформы. На эти параметры накладываются строгие ограничения:
ВЫБРАТЬ, в аргументах виртуальных таблиц нельзя использовать сложные составные конструкции, такие как вызов функции ДЕНЬНЕДЕЛИ() в качестве аргумента функции ДОБАВИТЬКДАТЕ(). Препроцессор ожидает простые параметры запроса, константы или базовые поддерживаемые выражения.ДЕНЬНЕДЕЛИ() возвращает числовое значение от 1 (понедельник) до 7 (воскресенье). Если прибавить результат напрямую к началу недели (понедельник 00:00:00), то для понедельника дата сместится на +1 день и превратится во вторник. Чтобы смещение работало корректно, нужно вычитать единицу: ДЕНЬНЕДЕЛИ(...) - 1.КОНЕЦПЕРИОДА(..., ДЕНЬ).Разберем основные практические способы решения этой задачи.
Согласно стандартам разработки 1С, наиболее надежный и производительный способ — рассчитать необходимые граничные даты во встроенном языке и передать их в запрос в виде готовых параметров типа Дата. Это снижает нагрузку на СУБД и исключает синтаксические ошибки.
// Определяем начало недели прошлого месяца
НачалоПрошлогоМесяца = ДобавитьМесяц(ДатаОтчета, -1);
НачалоПериода = НачалоНедели(НачалоПрошлогоМесяца);
// Смещаем на нужное количество дней и берем конец дня
СмещениеДней = ДеньНедели(ДатаОтчета) - 1;
КонецПериода = КонецДня(НачалоПериода + СмещениеДней * 86400);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| ПродажиОбороты.Магазин КАК Магазин,
| СУММА(ПродажиОбороты.СтоимостьОборот) КАК TotalLastMonth_RUB,
| СУММА(ПродажиОбороты.КоличествоОборот) КАК TotalLastMonth_PCS
|ИЗ
| РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК ПродажиОбороты
|СГРУППИРОВАТЬ ПО
| ПродажиОбороты.Магазин";
Запрос.УстановитьПараметр("НачалоПериода", НачалоПериода);
Запрос.УстановитьПараметр("КонецПериода", КонецПериода);
Если отчет разрабатывается в системе компоновки данных (СКД), нет необходимости писать циклический обход параметров в модуле. Платформа позволяет декларативно настроить формулы вычисления параметров на вкладке «Параметры»:
&ДатаОтчета.&НачалоПериода и &КонецПериода, включив для них флаг «Ограничение доступности».Для параметра &НачалоПериода:
НачалоПериода(ДобавитьКДате(&ДатаОтчета, "Месяц", -1), "Неделя")
Для параметра &КонецПериода:
КонецПериода(ДобавитьКДате(НачалоПериода(ДобавитьКДате(&ДатаОтчета, "Месяц", -1), "Неделя"), "День", ДеньНедели(&ДатаОтчета) - 1), "День")
Обратите внимание: в языке выражений СКД тип периода в функции ДобавитьКДате указывается строковым литералом в кавычках ("Месяц", "День", "Неделя"), в то время как в тексте запроса используются ключевые слова без кавычек.
Если требуется сохранить логику смещения внутри текста запроса, вычисление номера дня недели можно вынести в отдельный числовой параметр, избавившись от недопустимой вложенности функций:
// Передаем параметры в запрос:
Запрос.УстановитьПараметр("ДатаОтчета", ДатаОтчета);
Запрос.УстановитьПараметр("СмещениеДней", ДеньНедели(ДатаОтчета) - 1);
Текст запроса в таком случае примет корректный вид:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ПродажиОбороты.Магазин КАК Магазин,
СУММА(ПродажиОбороты.СтоимостьОборот) КАК TotalLastMonth_RUB,
СУММА(ПродажиОбороты.КоличествоОборот) КАК TotalLastMonth_PCS
ИЗ
РегистрНакопления.Продажи.Обороты(
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОтчета, МЕСЯЦ, -1), НЕДЕЛЯ),
КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОтчета, МЕСЯЦ, -1), НЕДЕЛЯ), ДЕНЬ, &СмещениеДней), ДЕНЬ),
Авто,
) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
ПродажиОбороты.Магазин
Такой подход устраняет синтаксическую ошибку препроцессора и гарантирует точный отбор записей регистра по дням недели.