При разработке сложных отчетов или журналов документов в 1С программисты часто сталкиваются с необходимостью динамического формирования текста запроса. Одной из самых коварных ловушек в этом процессе является попытка вставить значение переменной (ссылки, числа или даты) напрямую в строку запроса через оператор конкатенации (+). В данной статье мы подробно разберем, почему возникает ошибка "Ожидается выражение )", проанализируем механизм работы ПостроителяОтчета и научимся правильно передавать параметры в запрос.
Рассмотрим ситуацию, с которой столкнулся автор темы. В коде была предпринята попытка добавить условие ГДЕ следующим образом:
ТекстЗапроса = ТекстЗапроса + "
| И (__Документ.Касса = "+ОсновнаяКасса+"
| ИЛИ __Документ.КассаОтправитель = "+ОсновнаяКасса+")";
Проанализируем, что происходит в этот момент внутри системы. Переменная ОсновнаяКасса содержит ссылку на объект справочника. Когда мы пытаемся сложить строку и ссылку, 1С автоматически преобразует ссылку в строку (вызывает строковое представление объекта). Если касса называется, например, "Основная касса GSL", то итоговый текст запроса превратится в:
И (__Документ.Касса = Основная касса GSL ИЛИ ...
Для встроенного языка запросов 1С текст Основная касса GSL является бессмысленным набором слов, так как он не заключен в кавычки, не является функцией или параметром. Система ожидает завершения логического выражения (закрывающей скобки), но встречает непонятный текст, что и приводит к ошибке "Ожидается выражение )". Кроме того, даже если бы мы добавили кавычки, запрос сравнивал бы ссылку с текстом, что логически неверно.
Правильный подход заключается в использовании параметров. Параметр в языке запросов помечается символом амперсанда (&). Это позволяет отделить логику запроса от конкретных данных. Перепишем фрагмент добавления условия, заменив прямую вставку на имя параметра:
ТекстЗапроса = ТекстЗапроса + "
| И (__Документ.Касса = &ПараметрОсновнаяКасса
| ИЛИ __Документ.КассаОтправитель = &ПараметрОсновнаяКасса)";
Важный момент: Обратите внимание на пробелы при склейке строк. Мы всегда рекомендуем начинать новую строку в текстовом блоке с символа вертикальной черты |, что гарантирует наличие необходимых отступов и предотвращает "слипание" ключевых слов запроса (например, ГДЕ__Документ вместо ГДЕ __Документ).
После того как мы указали параметр в тексте запроса, его необходимо "пробросить" в объект, который выполняет этот запрос. В рассматриваемом примере используется ПостроительОтчета. Разберем типичную ошибку: попытку вызвать метод УстановитьПараметр() у самой строки ТекстЗапроса.
Поскольку ТекстЗапроса — это переменная типа Строка, у нее нет и не может быть метода УстановитьПараметр. Этот метод принадлежит объекту Запрос, но у ПостроителяОтчета работа с параметрами реализована через специальное свойство Параметры, которое является структурой.
Рассмотрим правильный код для передачи значения:
// Сначала присваиваем текст построителю
ПостроительОтчета.Текст = ТекстЗапроса;
// Затем устанавливаем параметры через коллекцию параметров построителя
ПостроительОтчета.Параметры.Вставить("ПараметрОсновнаяКасса", ОсновнаяКасса);
// Только после этого заполняем настройки
ПостроительОтчета.ЗаполнитьНастройки();
Использование метода Вставить() для структуры параметров гарантирует, что если параметр с таким именем уже существовал, он будет обновлен, а если нет — создан.
Проанализируем еще одну потенциальную проблему, которая часто всплывает при динамической сборке — использование псевдонимов в секции УПОРЯДОЧИТЬ ПО. В исходном сообщении автора фигурировала строка УПОРЯДОЧИТЬ ПО По_документ.дата. Это также может вызвать ошибку, если в секции ВЫБРАТЬ поле не было названо именно так.
Рекомендуем придерживаться следующей структуры обращения к полям:
УПОРЯДОЧИТЬ ПО __Документ.Дата (используя псевдоним таблицы).КАК ДатаДокумента), то: УПОРЯДОЧИТЬ ПО ДатаДокумента.Посмотрим на исправленный и дополненный фрагмент кода целиком, чтобы закрепить материал:
// Формируем текст запроса
ТекстЗапроса = "ВЫБРАТЬ
| __Документ.Ссылка,
| __Документ.Дата КАК Дата,
| __Документ.Номер КАК Номер
|ИЗ
| Документ." + ИмяДокумента + " КАК __Документ
|ГДЕ
| __Документ.Дата >= НАЧАЛОПЕРИОДА(&ДатаНачала, ДЕНЬ)
| И (__Документ.Дата <= КОНЕЦПЕРИОДА(&ДатаОкончания, ДЕНЬ) ИЛИ &ДатаОкончания = ДАТАВРЕМЯ(1,1,1))";
// Добавляем наше специфическое условие
ТекстЗапроса = ТекстЗапроса + "
| И (__Документ.Касса = &ОсновнаяКасса
| ИЛИ __Документ.КассаОтправитель = &ОсновнаяКасса)";
// Упорядочивание
ТекстЗапроса = ТекстЗапроса + "
|УПОРЯДОЧИТЬ ПО Дата";
// Работа с Построителем
ПостроительОтчета.Текст = ТекстЗапроса;
ПостроительОтчета.Параметры.Вставить("ДатаНачала", ДатаНачалаПериодаЖурнала);
ПостроительОтчета.Параметры.Вставить("ДатаОкончания", ДатаОкончанияПериодаЖурнала);
ПостроительОтчета.Параметры.Вставить("ОсновнаяКасса", ОсновнаяКасса);
ПостроительОтчета.ЗаполнитьНастройки();
Проанализируем ситуацию, когда параметр ОсновнаяКасса может быть не заполнен. Если мы просто оставим условие ГДЕ, а пользователь не выберет кассу, запрос может ничего не вернуть или вернуть ошибку. В профессиональной разработке принято использовать проверку на пустую ссылку прямо в запросе.
Рассмотрим пример универсального условия:
|ГДЕ
| (&ОсновнаяКасса = ЗНАЧЕНИЕ(Справочник.Кассы.ПустаяСсылка)
| ИЛИ __Документ.Касса = &ОсновнаяКасса)
Такой подход позволяет запросу игнорировать фильтр, если параметр не задан, что делает ваш код более надежным и удобным для пользователя.
Подведем итоги:
+.& для обеспечения безопасности и корректности типов.ПостроителемОтчета устанавливайте значения через ПостроительОтчета.Параметры.Вставить().Соблюдение этих правил позволит вам избежать трудночитаемых ошибок компиляции запроса и значительно упростит отладку кода в будущем.