В процессе разработки и поддержки внешних обработок (особенно механизмов обмена данными, таких как Клиент-Банк) программисты 1С часто сталкиваются с неочевидными ошибками в запросах. Две наиболее коварные проблемы связаны с некорректной типизацией параметров в условиях СУБД и изменением структуры метаданных при обновлении типовых конфигураций. В этой статье мы подробно разберем, почему возникают эти ошибки и как их правильно исправить.
Данная ошибка возникает в тот момент, когда СУБД пытается выполнить логическую операцию фильтрации в секции ГДЕ, но переданный в запрос параметр имеет тип, отличный от строгого Булево (Истина / Ложь). Проанализируем исходную ситуацию.
Разработчик пытается использовать в запросе условие фильтрации по параметру &ВыгружатьПлатПоручения:
|ГДЕ
| ПлатежноеПоручение.Дата МЕЖДУ &ДатаНачало И &ДатаОкончание
| И (НЕ ПлатежноеПоручение.ПометкаУдаления)
| И ПлатежноеПоручение.СчетОрганизации В(&МассивСчетов)
| И &ВыгружатьПлатПоручения
При этом сам параметр в коде инициализируется следующим образом:
ВыгружатьПлатПоручения = ТаблицаДокументов.Найти("Платежное поручение", "Название");
Выясним причину сбоя. Метод таблицы значений Найти() возвращает либо объект типа СтрокаТаблицыЗначений (если строка найдена), либо значение Неопределено (если совпадений нет). СУБД при обработке запроса ожидает в секции ГДЕ логическое выражение, но получает либо строку таблицы, либо Неопределено. Поскольку неявное приведение этих типов к булеву на уровне базы данных невозможно, платформа возвращает ошибку выполнения запроса.
Рассмотрим варианты решения этой проблемы.
Способ 1. Приведение результата поиска к типу Булево в коде
Перед передачей параметра в запрос мы должны гарантировать, что он содержит строго Истина или Ложь. Сделать это можно с помощью тернарного оператора:
ВыгружатьПлатПоручения = ?(ТаблицаДокументов.Найти("Платежное поручение", "Название") = Неопределено, Ложь, Истина);
Или, используя более лаконичную и изящную конструкцию сравнения:
ВыгружатьПлатПоручения = НЕ (ТаблицаДокументов.Найти("Платежное поручение", "Название") = Неопределено);
В этом случае переменная ВыгружатьПлатПоручения гарантированно примет логическое значение, и запрос отработает без ошибок.
Способ 2. Явное сравнение параметра внутри запроса
Если вы хотите перестраховаться (например, при работе на старых версиях платформы или при включенных режимах совместимости, где прямое использование булевых параметров в секции ГДЕ могло вызывать проблемы), рекомендуется использовать явное сравнение в тексте запроса:
|ГДЕ
| ПлатежноеПоручение.Дата МЕЖДУ &ДатаНачало И &ДатаОкончание
| И &ВыгружатьПлатПоручения = ИСТИНА
Способ 3. Оптимизация архитектуры кода (Рекомендуемый подход)
Проанализируем ситуацию с точки зрения производительности системы. Если параметр ВыгружатьПлатПоручения равен Ложь, то выполнение всего запроса не имеет практического смысла, так как результат выборки гарантированно будет пустым. Зачем нагружать СУБД лишней работой и создавать блокировки в базе данных?
Правильным архитектурным решением будет вынесение этой проверки на уровень встроенного языка 1С еще до инициализации и выполнения запроса:
ВыгружатьПлатПоручения = НЕ (ТаблицаДокументов.Найти("Платежное поручение", "Название") = Неопределено);
Если ВыгружатьПлатПоручения Тогда
Запрос = Новый Запрос;
// Настраиваем параметры и выполняем запрос
// ...
Выборка = Запрос.Выполнить().Выбрать();
КонецЕсли;
Этот подход экономит ресурсы сервера и существенно повышает общую производительность системы.
Вторая проблема часто возникает при обновлении конфигураций или при попытке запустить старые внешние обработки в новых редакциях программ (например, при переходе с «1С:Бухгалтерии 7.7» или БП 1.6 на редакцию БП 2.0 и БП 3.0).
Пользователь сталкивается с ошибкой следующего вида:
Поле не найдено "ПлатежноеПоручение.ДатаВыписки"
Разберем методологическую причину этого изменения. В старых редакциях конфигураций документы движения безналичных денежных средств содержали реквизиты ДатаВыписки или ДатаОплаты, которые заполнялись при фактическом проведении платежа банком. Однако начиная с редакции «Бухгалтерия предприятия 2.0» концепция учета безналичных денежных средств была полностью переработана:
Платежное поручение теперь выполняет исключительно сервисную функцию — он предназначен для подготовки печатной формы и выгрузки файла в Клиент-Банк. Он не делает никаких движений (проводок) по счетам учета.ДатаВыписки был полностью удален из метаданных этого документа. Теперь у платежного поручения есть только стандартный реквизит Дата (дата составления документа).Списание с расчетного счета и Поступление на расчетный счет. Именно в них фиксируется дата проведения операции банком, которая совпадает со стандартной датой этих документов.Посмотрим на ошибочный запрос, представленный автором темы:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ПлатежноеПоручение.Ссылка,
ПлатежноеПоручение.ДатаВыписки,
ПлатежноеПоручение.СчетОрганизации.НомерСчета
ИЗ
Документ.ПлатежноеПоручениу КАК ПлатежноеПоручение
ГДЕ
ПлатежноеПоручение.ДатаВыписки >= &ДатаНач
И ПлатежноеПоручение1.ДатаВходящегоДокумента <= &ДатаКон
Помимо отсутствия реквизита ДатаВыписки, в этом запросе допущены критические синтаксические ошибки, из-за которых платформа в любом случае выдала бы отказ:
Документ.ПлатежноеПоручение написано Документ.ПлатежноеПоручениу (с буквой «у» на конце). Такой таблицы метаданных не существует.ИЗ таблице назначен псевдоним ПлатежноеПоручение, однако далее в условиях ГДЕ автор обращается к несуществующей таблице ПлатежноеПоручение1.ДатаВходящегоДокумента для исходящих платежных поручений ошибочна.Для того чтобы исправить запрос и адаптировать его под современную структуру метаданных, мы должны заменить обращение к несуществующему реквизиту ДатаВыписки на стандартный реквизит Дата, а также исправить все синтаксические опечатки.
Приведем корректный вариант построения запроса:
ДокументыНаУдаление = Новый Запрос;
ДокументыНаУдаление.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| ПлатежноеПоручение.Ссылка КАК Ссылка,
| ПлатежноеПоручение.Дата КАК Дата,
| ПлатежноеПоручение.СчетОрганизации.НомерСчета КАК НомерСчета
|ИЗ
| Документ.ПлатежноеПоручение КАК ПлатежноеПоручение
|ГДЕ
| ПлатежноеПоручение.Дата МЕЖДУ &ДатаНач И &ДатаКон
| И ПлатежноеПоручение.СчетОрганизации.НомерСчета В(&НомераСчетов)";
ДокументыНаУдаление.УстановитьПараметр("ДатаНач", НачалоИнтервалаЗагрузки);
ДокументыНаУдаление.УстановитьПараметр("ДатаКон", КонецИнтервалаЗагрузки);
ДокументыНаУдаление.УстановитьПараметр("НомераСчетов", СписокСчетов);
ВыборкаДокументов = ДокументыНаУдаление.Выполнить().Выбрать();
Если же логика вашей внешней обработки требует анализа именно фактической оплаты (проведения банком), то вам необходимо полностью перенаправить запрос на выборку данных из документов списания и поступления денег:
ЗапросПоВыписке = Новый Запрос;
ЗапросПоВыписке.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| СписаниеСРасчетногоСчета.Ссылка КАК Ссылка,
| СписаниеСРасчетногоСчета.Дата КАК ДатаПроведения
|ИЗ
| Документ.СписаниеСРасчетногоСчета КАК СписаниеСРасчетногоСчета
|ГДЕ
| СписаниеСРасчетногоСчета.Проведено = ИСТИНА
| И СписаниеСРасчетногоСчета.Дата МЕЖДУ &ДатаНач И &ДатаКон";
Таким образом, мы детально разобрали обе проблемы. Систематический контроль типов передаваемых параметров и своевременная актуализация запросов под особенности метаданных используемой конфигурации избавят вас от ошибок СУБД и обеспечат стабильную работу ваших интеграционных решений.