Раздвоение периодов в отчете 1С и ошибка несовместимости типов с Границей: подробное руководство по решению

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

При разработке сложных отчетов в системе «1С:Предприятие» программисты часто сталкиваются с неочевидным поведением виртуальных таблиц и механизмов компоновки данных. Две наиболее коварные проблемы возникают при объединении запросов с использованием разных источников данных и при передаче параметров периода в физические таблицы СУБД. В данной статье мы детально разберем обе проблемы, выясним причины их возникновения и найдем элегантные способы их решения.

Проблема первая: Раздвоение колонок периодов при произвольном начале интервала отчета

Рассмотрим ситуацию: у нас есть отчет, написанный на базе встроенного инструмента ПостроительОтчета (или Универсального отчета), который объединяет данные из виртуальной таблицы оборотов регистра накопления Продажи.Обороты и физической таблицы документов ОтчетКомиссионераОПродажах.Товары.

Когда мы формируем отчет за полный календарный период (например, с 1-го числа месяца), все данные выводятся корректно. Однако стоит нам указать произвольную дату начала периода (например, 9-е число), как колонка или группировка по периоду (например, по месяцам) неожиданно раздваивается. В отчете появляются две отдельные колонки для одного и того же месяца.

Выясним причину этого поведения:

Это стандартная особенность работы виртуальных таблиц оборотов СУБД в платформе 1С. Когда в параметрах виртуальной таблицы периодичность установлена в значение Авто, и в качестве начала периода передается дата, не совпадающая с началом календарного периода (например, 09.11.2023 00:00:00), первая запись, возвращаемая виртуальной таблицей в поле периода, примет значение именно фактической даты начала интервала запроса.

То есть для первого месяца в выборке поле ПериодМесяц вернет значение 09.11.2023 00:00:00. Все последующие записи за будущие периоды будут возвращаться со стандартным началом периода — 1-м числом (например, 01.12.2023 00:00:00).

В то же время во второй части запроса (после конструкции ОБЪЕДИНИТЬ ВСЕ) мы выбираем данные из физической таблицы документов, где поле периода рассчитывается жестко через стандартную функцию:

НАЧАЛОПЕРИОДА(ОтчетКомиссионераОПродажахТовары.Ссылка.Дата, МЕСЯЦ)

Для любого документа, проведенного в ноябре, эта функция всегда вернет чистое календарное начало месяца — 01.11.2023 00:00:00.

При объединении этих двух выборок система группировки получает для одного и того же месяца две совершенно разные даты: 09.11.2023 и 01.11.2023. Поскольку значения различаются, система не может их схлопнуть в одну группировку, что и приводит к визуальному раздвоению колонок в отчете.

Решение проблемы раздвоения периодов

Чтобы исключить раздвоение данных, нам необходимо принудительно привести периоды, возвращаемые виртуальной таблицей, к календарному началу периода. Для этого мы оборачиваем поля периодов в первом запросе объединения в функцию НАЧАЛОПЕРИОДА.

Проанализируем пример корректировки исходного запроса. Было:


ВЫБРАТЬ
    ПродажиОбороты.Номенклатура КАК Номенклатура,
    ...
    ПродажиОбороты.ПериодДень,
    ПродажиОбороты.ПериодНеделя,
    ПродажиОбороты.ПериодМесяц,
    ПродажиОбороты.ПериодКвартал,
    ПродажиОбороты.ПериодГод
ИЗ
    РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон, Авто, ...) КАК ПродажиОбороты

Стало (правильный вариант с округлением периодов):


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

При таком подходе первая запись из виртуальной таблицы за ноябрь (с датой 09.11.2023) будет преобразована функцией к 01.11.2023. Данные из обеих частей запроса успешно объединятся и сгруппируются без дублирования колонок.

---

Проблема вторая: Ошибка «Неверные параметры в операции сравнения» при использовании Границы

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

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

Эта ошибка указывает на строку в секции ГДЕ второго запроса (выборки по документам):

И ОтчетКомиссионераОПродажахТовары.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон

Разберем причину ошибки подробнее:

В Универсальном отчете параметры начала и конца периода заполняются объектами специального типа Граница (свойство ВидГраницы.Включая). Это делается для того, чтобы виртуальные таблицы учитывали движения документов, записанных на самую последнюю секунду периода (включая эту секунду).

Объект типа Граница не является примитивным типом данных СУБД. Платформа 1С умеет прозрачно обрабатывать его **только при передаче в качестве параметров виртуальных таблиц** (таких как Обороты или ОстаткиИОбороты).

Когда СУБД выполняет запрос ко второй части объединения (к физической таблице документов), она встречает условие сравнения даты документа с параметром &ДатаНач, в который передан объект типа Граница. СУБД не умеет сравнивать примитивный тип Дата с объектным типом Граница, в результате чего транслятор запросов выдает ошибку несовместимости типов.

Решение проблемы сравнения с объектом Граница

Для решения этой проблемы в платформе 1С и Универсальном отчете предусмотрено разделение параметров на две независимые пары:

  1. Для виртуальных таблиц — используются параметры &ДатаНач и &ДатаКон, которые содержат объекты типа Граница.
  2. Для физических таблиц (условий в секции ГДЕ) — используются параметры &ДатаНачала и &ДатаКонца, которые содержат чистые значения типа Дата (обработанные функциями НачалоДня и КонецДня).

Посмотрим на код инициализации параметров в Универсальном отчете:


ПостроительОтчета.Параметры.Вставить("ДатаНач", ?(ДатаНач = Дата('00010101000000'), ДатаНач, Новый Граница(НачалоДня(ДатаНач), ВидГраницы.Включая)));
ПостроительОтчета.Параметры.Вставить("ДатаКон", ?(ДатаКон = Дата('00010101000000'), ДатаКон, Новый Граница(КонецДня(ДатаКон), ВидГраницы.Включая)));

// Чистые даты для физических таблиц:
ПостроительОтчета.Параметры.Вставить("ДатаНачала", ?(ДатаНач = Дата('00010101000000'), ДатаНач, НачалоДня(ДатаНач)));
ПостроительОтчета.Параметры.Вставить("ДатаКонца", ?(ДатаКон = Дата('00010101000000'), Дата("39991231"), КонецДня(ДатаКон)));

Таким образом, чтобы избавиться от ошибки несовместимости типов, нам достаточно изменить параметры фильтрации во втором запросе (выборке документов). Вместо &ДатаНач и &ДатаКон необходимо использовать &ДатаНачала и &ДатаКонца.

Было:


ГДЕ
    ОтчетКомиссионераОПродажахТовары.Ссылка.ОтражатьВУправленческомУчете
    И ОтчетКомиссионераОПродажахТовары.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон
    И ОтчетКомиссионераОПродажахТовары.Ссылка.Проведен

Стало (правильный вариант):


ГДЕ
    ОтчетКомиссионераОПродажахТовары.Ссылка.ОтражатьВУправленческомУчете
    И ОтчетКомиссионераОПродажахТовары.Ссылка.Дата МЕЖДУ &ДатаНачала И &ДатаКонца
    И ОтчетКомиссионераОПродажахТовары.Ссылка.Проведен

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

---

Подведем итоги работы

Мы успешно устранили две критические ошибки в самописном отчете. Давайте зафиксируем ключевые правила для успешной разработки аналогичных отчетов в будущем:

  1. При использовании конструкции ОБЪЕДИНИТЬ ВСЕ с участием виртуальных таблиц оборотов всегда принудительно приводите возвращаемые периоды к их календарному началу с помощью функции НАЧАЛОПЕРИОДА(). Это защитит отчет от раздвоения колонок при произвольных интервалах дат.
  2. Никогда не сравнивайте реквизиты дат физических таблиц (например, Ссылка.Дата) с параметрами типа Граница. Сравнивайте их только с параметрами примитивного типа Дата.
  3. Используйте предопределенные раздельные параметры Универсального отчета (&ДатаНач/&ДатаКон — для виртуальных таблиц, &ДатаНачала/&ДатаКонца — для физических таблиц документов и справочников).
← На главную