Как в запросе 1С добавить к дате день недели и избежать ошибки неверных параметров?

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При построении аналитических отчетов часто требуется сопоставить данные текущего периода с аналогичным периодом прошлого месяца или года. Например, сравнить продажи с понедельника по среду текущей недели с аналогичными тремя днями той же недели прошлого месяца. Попытка выполнить расчет границ периода непосредственно внутри параметров виртуальной таблицы с помощью вложенных функций даты нередко приводит к ошибке «Неверные параметры». Рассмотрим, почему возникает данная ошибка, какие логические нюансы важно учитывать при смещении дат и как правильно решить эту задачу.

Проанализируем типичный пример запроса, вызывающий ошибку платформы:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ПродажиОбороты.Магазин КАК Магазин,
    СУММА(ПродажиОбороты.СтоимостьОборот) КАК TotalLastMonth_RUB,
    СУММА(ПродажиОбороты.КоличествоОборот) КАК TotalLastMonth_PCS
ИЗ
    РегистрНакопления.Продажи.Обороты(
        НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОтчета, МЕСЯЦ, -1), НЕДЕЛЯ),
        ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(&ДатаОтчета, МЕСЯЦ, -1), ДЕНЬ, ДЕНЬНЕДЕЛИ(&ДатаОтчета)),
        День,
    ) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
    ПродажиОбороты.Магазин

Причины возникновения ошибки и логические нюансы

Выясним причину появления ошибки «Неверные параметры». В языке запросов 1С параметры виртуальных таблиц (таких как .Обороты(...) или .Остатки(...)) обрабатываются специализированным препроцессором платформы. На эти параметры накладываются строгие ограничения:

  1. Ограничение на вложенность функций: В отличие от секции ВЫБРАТЬ, в аргументах виртуальных таблиц нельзя использовать сложные составные конструкции, такие как вызов функции ДЕНЬНЕДЕЛИ() в качестве аргумента функции ДОБАВИТЬКДАТЕ(). Препроцессор ожидает простые параметры запроса, константы или базовые поддерживаемые выражения.
  2. Смещение на один лишний день: Функция ДЕНЬНЕДЕЛИ() возвращает числовое значение от 1 (понедельник) до 7 (воскресенье). Если прибавить результат напрямую к началу недели (понедельник 00:00:00), то для понедельника дата сместится на +1 день и превратится во вторник. Чтобы смещение работало корректно, нужно вычитать единицу: ДЕНЬНЕДЕЛИ(...) - 1.
  3. Учет времени суток (граница периода): Конечная дата периода оборотов должна включать движения последнего дня целиком. Если передать просто дату с временем 00:00:00, обороты за последний день могут не войти в отчет. Правую границу необходимо приводить к КОНЕЦПЕРИОДА(..., ДЕНЬ).

Разберем основные практические способы решения этой задачи.

Способ 1. Расчет границ периода во встроенном языке (рекомендуемый)

Согласно стандартам разработки 1С, наиболее надежный и производительный способ — рассчитать необходимые граничные даты во встроенном языке и передать их в запрос в виде готовых параметров типа Дата. Это снижает нагрузку на СУБД и исключает синтаксические ошибки.


// Определяем начало недели прошлого месяца
НачалоПрошлогоМесяца = ДобавитьМесяц(ДатаОтчета, -1);
НачалоПериода = НачалоНедели(НачалоПрошлогоМесяца);

// Смещаем на нужное количество дней и берем конец дня
СмещениеДней = ДеньНедели(ДатаОтчета) - 1;
КонецПериода = КонецДня(НачалоПериода + СмещениеДней * 86400);

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	ПродажиОбороты.Магазин КАК Магазин,
    |	СУММА(ПродажиОбороты.СтоимостьОборот) КАК TotalLastMonth_RUB,
    |	СУММА(ПродажиОбороты.КоличествоОборот) КАК TotalLastMonth_PCS
    |ИЗ
    |	РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Авто, ) КАК ПродажиОбороты
    |СГРУППИРОВАТЬ ПО
    |	ПродажиОбороты.Магазин";

Запрос.УстановитьПараметр("НачалоПериода", НачалоПериода);
Запрос.УстановитьПараметр("КонецПериода", КонецПериода);

Способ 2. Настройка вычисляемых параметров в СКД без программного кода

Если отчет разрабатывается в системе компоновки данных (СКД), нет необходимости писать циклический обход параметров в модуле. Платформа позволяет декларативно настроить формулы вычисления параметров на вкладке «Параметры»:

  1. Оставим доступным для пользователя только один параметр — &ДатаОтчета.
  2. Создадим служебные параметры &НачалоПериода и &КонецПериода, включив для них флаг «Ограничение доступности».
  3. В колонке «Выражение» зададим формулы на языке выражений СКД:

    Для параметра &НачалоПериода:

    НачалоПериода(ДобавитьКДате(&ДатаОтчета, "Месяц", -1), "Неделя")

    Для параметра &КонецПериода:

    
    КонецПериода(ДобавитьКДате(НачалоПериода(ДобавитьКДате(&ДатаОтчета, "Месяц", -1), "Неделя"), "День", ДеньНедели(&ДатаОтчета) - 1), "День")
    

Обратите внимание: в языке выражений СКД тип периода в функции ДобавитьКДате указывается строковым литералом в кавычках ("Месяц", "День", "Неделя"), в то время как в тексте запроса используются ключевые слова без кавычек.

Способ 3. Передача номера дня недели через отдельный параметр

Если требуется сохранить логику смещения внутри текста запроса, вычисление номера дня недели можно вынести в отдельный числовой параметр, избавившись от недопустимой вложенности функций:


// Передаем параметры в запрос:
Запрос.УстановитьПараметр("ДатаОтчета", ДатаОтчета);
Запрос.УстановитьПараметр("СмещениеДней", ДеньНедели(ДатаОтчета) - 1);

Текст запроса в таком случае примет корректный вид:


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ПродажиОбороты.Магазин КАК Магазин,
    СУММА(ПродажиОбороты.СтоимостьОборот) КАК TotalLastMonth_RUB,
    СУММА(ПродажиОбороты.КоличествоОборот) КАК TotalLastMonth_PCS
ИЗ
    РегистрНакопления.Продажи.Обороты(
        НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОтчета, МЕСЯЦ, -1), НЕДЕЛЯ),
        КОНЕЦПЕРИОДА(ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&ДатаОтчета, МЕСЯЦ, -1), НЕДЕЛЯ), ДЕНЬ, &СмещениеДней), ДЕНЬ),
        Авто,
    ) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
    ПродажиОбороты.Магазин

Такой подход устраняет синтаксическую ошибку препроцессора и гарантирует точный отбор записей регистра по дням недели.

← На главную