Как использовать динамические параметры в запросах к виртуальным таблицам 1С

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

При разработке отчетов и запросов в системе 1С:Предприятие 8 часто возникает задача получить остатки или обороты на определенную дату, которая рассчитывается индивидуально для каждой строки. Например, когда дата среза или контроля долга зависит от срока оплаты, указанного в конкретном договоре или свойствах субконто.

Попытка передать такое динамическое поле непосредственно в параметры виртуальной таблицы (например, в &НачалоПериода таблицы РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты) приводит к ошибке компиляции запроса. В этой статье мы подробно разберем, почему возникает эта ошибка и какими способами можно решить эту задачу на платформе 1С.

Почему возникает ошибка (техническая суть ограничения)

Виртуальные таблицы 1С (такие как Остатки, Обороты, ОстаткиИОбороты) физически не существуют в СУБД. Платформа 1С:Предприятие транслирует обращение к ним во вложенные SQL-запросы к реальным таблицам движений и итогов.

Параметры виртуальной таблицы определяют границы выборки данных на самом первом этапе работы СУБД — до того, как начнется соединение таблиц и расчет строк. СУБД должна заранее знать эти границы. Передать в качестве параметра значение поля, которое выбирается из этой же (или другой) таблицы в процессе выполнения запроса (например, Субконто2.СрокОплаты), технически невозможно. Параметры виртуальной таблицы обязаны быть константами на момент начала трансляции запроса к СУБД. Попытка написать код в параметрах таблицы наподобие:


РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(&НачалоПериода - ХозрасчетныйОстаткиИОбороты.Субконто2.СрокОплаты, &КонецПериода, ...)

всегда будет вызывать ошибку синтаксиса.

Дополнительно отметим, что в запросах 1С нельзя выполнять прямое вычитание дат с помощью оператора «минус». Для работы с датами необходимо использовать специальные встроенные функции:

Ниже мы подробно рассмотрим три основных метода решения задачи, когда требуется получить остатки на динамические даты.

---

Решение 1. Перенос логики расчета в поля запроса (классический метод)

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

Рассмотрим пример, когда нам нужно рассчитать разницу во времени между началом периода и датой оплаты из субконто:


ВЫБРАТЬ
    ХозрасчетныйОстаткиИОбороты.Субконто2.СрокОплаты КАК СрокОплаты,
    РАЗНОСТЬДАТ(ХозрасчетныйОстаткиИОбороты.Субконто2.СрокОплаты, &НачалоПериода, ДЕНЬ) КАК РазницаВремени,
    ХозрасчетныйОстаткиИОбороты.СуммаНачальныйОстатокДт КАК БУНачальныйОстатокДт
ИЗ
    РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, Месяц, , Счет В (&Счета), , ) КАК ХозрасчетныйОстаткиИОбороты

Преимущества: Простой и понятный запрос, высокая скорость работы при наличии индексов по аналитике и периоду.

---

Решение 2. Использование связей наборов данных в СКД (рекомендуемый метод)

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

Разберем по шагам, как настроить такую схему:

  1. Создадим Набор данных - запрос №1 (назовем его ДанныеСубконто). В нем мы выберем исходную информацию и вычислим нашу индивидуальную дату для каждой строки:
    
    ВЫБРАТЬ
        ХозрасчетныйДвижения.Субконто1 КАК Контрагент,
        ХозрасчетныйДвижения.Субконто2 КАК Договор,
        ДОБАВИТЬКДАТЕ(ХозрасчетныйДвижения.Субконто2.СрокОплаты, ДЕНЬ, 1) КАК ИндивидуальнаяДата
    ИЗ
        РегистрБухгалтерии.Хозрасчетный Движения КАК ХозрасчетныйДвижения
    
  2. Создадим Набор данных - запрос №2 (назовем его ОстаткиНаДату). Этот запрос будет получать остатки на один конкретный момент времени, переданный через параметр:
    
    ВЫБРАТЬ
        ХозрасчетныйОстатки.Субконто1 КАК Контрагент,
        ХозрасчетныйОстатки.Субконто2 КАК Договор,
        ХозрасчетныйОстатки.СуммаОстатокДт КАК ОстатокДт
    ИЗ
        РегистрБухгалтерии.Хозрасчетный.Остатки(&ПериодОстатков, Счет В (&Счета), , ) КАК ХозрасчетныйОстатки
    
  3. Перейдем на вкладку Связи наборов данных и добавим новую строку:
    • Источник связи: ДанныеСубконто
    • Приемник связи: ОстаткиНаДату
    • Условие связи: Контрагент = Контрагент И Договор = Договор
    • Параметры: В этой колонке укажем, что в параметр приемника ПериодОстатков мы передаем значение поля источника ИндивидуальнаяДата.

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

---

Решение 3. Расчет остатков на динамические даты через нарастающий итог

Если отчет строится без использования СКД (например, в коде модуля обработки или динамическом списке), то для расчета остатков на динамические даты можно использовать классический алгоритм нарастающих итогов через левое соединение с таблицей движений.

Рассмотрим структуру такого запроса:

  1. Сначала мы получаем временную таблицу всех необходимых нам аналитик и их индивидуальных расчетных дат оплаты (назовем её ВТ_ОпорныеДаты).
  2. Затем мы получаем все детальные движения по регистру за весь возможный период во временную таблицу ВТ_Движения.
  3. Соединяем ВТ_ОпорныеДаты с таблицей движений по условию: ВТ_Движения.Период <= ВТ_ОпорныеДаты.ИндивидуальнаяДата.
  4. Группируем результат по аналитике и рассчитываем остаток как сумму всех движений до этой даты.

Пример реализации запроса:


ВЫБРАТЬ
    ВТ_ОпорныеДаты.Контрагент КАК Контрагент,
    ВТ_ОпорныеДаты.Договор КАК Договор,
    ВТ_ОпорныеДаты.ИндивидуальнаяДата КАК ИндивидуальнаяДата,
    СУММА(ВЫБОР
            КОГДА ВТ_Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
                ТОГДА ВТ_Движения.Сумма
            ИНАЧЕ -ВТ_Движения.Сумма
        КОНЕЦ) КАК ОстатокНаДату
ИЗ
    ВТ_ОпорныеДаты КАК ВТ_ОпорныеДаты
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Движения КАК ВТ_Движения
        ПО ВТ_ОпорныеДаты.Контрагент = ВТ_Движения.Контрагент
            И ВТ_ОпорныеДаты.Договор = ВТ_Движения.Договор
            И ВТ_Движения.Период <= ВТ_ОпорныеДаты.ИндивидуальнаяДата

СГРУППИРОВАТЬ ПО
    ВТ_ОпорныеДаты.Контрагент,
    ВТ_ОпорныеДаты.Договор,
    ВТ_ОпорныеДаты.ИндивидуальнаяДата

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

Важный нюанс: Учет временных границ

При расчете остатков на динамические даты всегда помните о границах периодов. Виртуальная таблица остатков 1С по умолчанию вычисляет остаток на начало указанной секунды периода. Если дата оплаты в вашем договоре заполнена без учета времени (например, 15.01.2023 00:00:00), движения, проведенные в течение этого дня, могут не попасть в расчет.

Для гарантированного включения всех движений дня используйте функцию КОНЕЦПЕРИОДА() или прибавляйте одну секунду/день к вашей расчетной дате с помощью функции ДОБАВИТЬКДАТЕ, как было продемонстрировано в примерах выше.

← На главную