При доработке нетиповых отчетов, созданных без использования Системы компоновки данных (СКД), часто возникает задача предоставить пользователю возможность гибкой фильтрации данных (например, исключить определенный список контрагентов). В процессе реализации разработчики нередко сталкиваются с двумя распространенными проблемами: пользовательский отбор в объекте ПостроительОтчета не влияет на результат выборки, а при попытке переписать запрос возникает ошибка вида «Неверные параметры "-"». Разберем по шагам причины такого поведения платформы 1С и рассмотрим надежные варианты решения.
Проанализируем типичную ситуацию: на форму выведено табличное поле с типом данных Отбор, связанное с реквизитом Построитель (тип ПостроительОтчета). Пользователь интерактивно указывает условие (например, Контрагент Не в списке), однако отчет все равно формируется по всем записям без учета фильтра.
Выясним причину такого поведения:
Построитель.Текст = Запрос.Текст, коллекция Построитель.Отбор полностью сбрасывается и переинициализируется исходными значениями. Все параметры, флаги использования и значения, которые пользователь установил на форме до нажатия кнопки формирования, при этом стираются.{ГДЕ ...}. Например, синтаксис {(Контрагент).* КАК Контрагент} в параметрах виртуальной таблицы или {ГДЕ Контрагент.*} в основном запросе.Запрос.Выполнить(), а затем данные выводятся вручную, построитель никак не участвует в фильтрации. Для применения условий построителя необходимо либо использовать метод Построитель.Выполнить() с последующей выборкой Построитель.Результат.Выбрать(), либо применять специализированный объект ПостроительЗапроса.Рассмотрим, как правильно организовать работу с объектом ПостроительОтчета, чтобы пользовательские отборы не затирались при формировании данных.
Текст запроса с секциями отбора должен задаваться построителю один раз при инициализации формы. В обработчике открытия вызывается метод ЗаполнитьНастройки(), который формирует доступный список полей на форме:
Процедура ПриОткрытии()
// Задаем основной текст запроса с секцией расширения построителя {ГДЕ ...}
Построитель.Текст日益 =
"ВЫБРАТЬ
| Взаиморасчеты.Контрагент КАК Контрагент,
| Взаиморасчеты.ДоговорКонтрагента КАК ДоговорКонтрагента,
| Взаиморасчеты.СуммаВзаиморасчетовОстаток КАК ОстатокДолга
|ИЗ
| РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(&Период, ) КАК Взаиморасчеты
|{ГДЕ
| Взаиморасчеты.Контрагент.* КАК Контрагент,
| Взаиморасчеты.ДоговорКонтрагента.* КАК ДоговорКонтрагента}";
// Заполняем состав доступных настроек
Построитель.ЗаполнитьНастройки();
КонецПроцедуры
В процедуре формирования отчета мы не перезаписываем текст запроса заново, а лишь передаем актуальные значения параметров и выполняем выборку:
Процедура СформироватьОтчет()
// Заполняем параметры построителя
Построитель.Параметры.Вставить("Период", КонецДня(ДатаОтчета));
// Выполняем построитель с учетом наложенных пользователем отборов
Построитель.Выполнить();
Выборка = Построитель.Результат.Выбрать();
Пока Выборка.Следующий() Цикл
// Обработка и вывод данных в отчет
КонецЦикла;
КонецПроцедуры
Если задача заключается исключительно в том, чтобы исключить список контрагентов-взаимозачетчиков, можно отказаться от механизма построителя и реализовать отбор напрямую через параметры обычного объекта Запрос.
Разберем реализацию: разместим на форме отчета реквизит СписокИсключений с типом данных СписокЗначений. В тексте запроса добавим проверку условия вхождения:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Взаиморасчеты.Контрагент КАК Контрагент,
| Взаиморасчеты.СуммаВзаиморасчетовОстаток КАК СуммаДолга
|ИЗ
| РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(&ДатаОтчета, ) КАК Взаиморасчеты
|ГДЕ
| ВЫБОР
| КОГДА &ИспользоватьИсключения
| ТОГДА НЕ Взаиморасчеты.Контрагент В (&СписокИсключений)
| ИНАЧЕ ИСТИНА
| КОНЕЦ";
Запрос.УстановитьПараметр("ДатаОтчета", КонецДня(ДатаДолга));
Запрос.УстановитьПараметр("СписокИсключений", СписокИсключений);
Запрос.УстановитьПараметр("ИспользоватьИсключения", СписокИсключений.Количество() > 0);
РезультатЗапроса = Запрос.Выполнить();
Такой подход упрощает программный код, делает поведение формы предсказуемым и избавляет от необходимости синхронизировать свойства табличного поля с коллекцией отборов построителя.
Рассмотрим вторую проблему, возникающую при выполнении запросов с временными таблицами: сообщение об ошибке {(..., ...)}: Неверные параметры "-" в строке с вычислением разности дат и числовых полей.
Посмотрим на пример выражения, вызывающего ошибку:
ГДЕ
РАЗНОСТЬДАТ(КОНЕЦПЕРИОДА(ВтДолги.Период, ДЕНЬ), КОНЕЦПЕРИОДА(&ДатаОтчета, ДЕНЬ), ДЕНЬ) - ВтДолги.ДопустимоеЧислоДнейЗадолженности >= -3
Выясним причину: во временной таблице поле ДопустимоеЧислоДнейЗадолженности формировалось с помощью конструкции ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ. В платформе 1С результирующее поле условной конструкции во временной таблице может получить составной или неопределенный тип данных, если значение одного из реквизитов не заполнено, возвращает NULL или параметр &ЧислоДнейПоУмолчанию не имеет строго зафиксированного типа.
СУБД не может применить арифметическую операцию вычитания (-) к нетипизированной колонке. Для устранения ошибки применим явное приведение типов с помощью функции ВЫРАЗИТЬ и обработаем возможные пустые значения через ЕСТЬNULL.
Разберем по шагам правильное формирование поля во временной таблице:
ВЫБРАТЬ
ВтВзаиморасчеты.Период КАК Период,
ВтВзаиморасчеты.Контрагент КАК Контрагент,
ВЫРАЗИТЬ(
ВЫБОР
КОГДА ЕСТЬNULL(ВтВзаиморасчеты.ДоговорКонтрагента.Дней, 0) > 0
ТОГДА ВтВзаиморасчеты.ДоговорКонтрагента.Дней
ИНАЧЕ &ЧислоДнейПоУмолчанию
КОНЕЦ КАК ЧИСЛО(10, 0)
) КАК ДопустимоеЧислоДнейЗадолженности
ПОМЕСТИТЬ ВтДолгиПоДокументам
ИЗ
ВтВзаиморасчеты КАК ВтВзаиморасчеты
;
После явного указания типа ЧИСЛО(10, 0) в секции формирования временной таблицы арифметические операции в последующих пакетах запроса и условиях ГДЕ будут выполняться без ошибок.