Перенос встроенных печатных форм во внешние обработки — одна из самых частых задач, с которой сталкивается разработчик 1С. В процессе адаптации кода под формат внешней печатной формы (ВПФ) нередко возникают досадные ошибки, способные поставить в тупик начинающего специалиста. Одной из таких классических проблем является ошибка несовместимости типов в запросе при сравнении ссылки на документ с параметром.
В этой статье мы подробно разберем, почему возникает ошибка «Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов», проанализируем внутренние механизмы работы платформы 1С на уровне СУБД и рассмотрим правильные методы оптимизации кода печатных форм.
Рассмотрим ситуацию: при выполнении запроса во внешней печатной форме на обычных формах система аварийно завершает работу и выдает следующее сообщение об ошибке:
{ВнешняяОбработка.СписаниеТоваровВнешн.Форма.ФормаДляПроцедурМодуляДокумента.Форма(94)}: Ошибка при вызове метода контекста (Выполнить) Шапка = Запрос.Выполнить().Выбрать(); по причине: {(17, 25)}: Неверные параметры в операции сравнения. Нельзя сравнивать поля неограниченной длины и поля несовместимых типов. СписаниеТоваров.Ссылка <<?>>= &ТекущийДокумент
Давайте выясним причину такого поведения системы. В тексте запроса мы видим стандартное условие фильтрации по конкретному документу:
ГДЕ
СписаниеТоваров.Ссылка = &ТекущийДокумент
Поле Ссылка в таблице базы данных имеет строго определенный тип — ДокументСсылка.СписаниеТоваров (на уровне СУБД это уникальный 128-битный идентификатор GUID). Для того чтобы сравнение прошло успешно, в параметр запроса &ТекущийДокумент также должна быть передана ссылка.
Однако в коде печатной формы автора (Сообщение 6) параметр первого запроса заполняется следующим образом:
Запрос.УстановитьПараметр("ТекущийДокумент", ЭтаОбработка.СсылкаНаОбъект.ПолучитьОбъект());
Метод ПолучитьОбъект() обращается к базе данных, считывает абсолютно все данные документа (включая все его табличные части) и конструирует в оперативной памяти тяжелый тип данных — ДокументОбъект.СписаниеТоваров. СУБД «не умеет» сравнивать системный GUID записи таблицы со сложной структурой объекта в памяти. Из-за этого несовпадения типов СУБД генерирует ошибку несовместимых типов.
При этом во втором запросе этой же процедуры автор устанавливает параметр корректно:
Запрос.УстановитьПараметр("ТекущийДокумент", ЭтаОбработка.СсылкаНаОбъект.Ссылка);
Именно из-за невнимательности при копировании кода в первом запросе остался вызов ПолучитьОбъект(), что и привело к падению программы.
Для исправления этой ошибки нам необходимо исключить передачу объекта в параметры запроса. Пройдем по шагам процесс исправления и оптимизации:
ПечатьНакладной()).Запрос."ТекущийДокумент".ПолучитьОбъект(), оставив передачу чистой ссылки.Давайте сравним ошибочный и правильный варианты кода.
Ошибочный код:
Запрос = Новый Запрос;
// ОШИБКА: Передается объект, а не ссылка
Запрос.УстановитьПараметр("ТекущийДокумент", ЭтаОбработка.СсылкаНаОбъект.ПолучитьОбъект());
Правильный код:
Запрос = Новый Запрос;
// ПРАВИЛЬНО: Передается чистая ссылка на документ
Запрос.УстановитьПараметр("ТекущийДокумент", ЭтаОбработка.СсылкаНаОбъект);
После этого простого изменения СУБД получит в параметре &ТекущийДокумент значение типа ДокументСсылка, сравнение отработает корректно, и печатная форма успешно сформируется.
На форумах часто советуют использовать конструкцию вида СсылкаНаОбъект.Ссылка (как показано в Сообщении 3). Разберем, почему это избыточно и потенциально опасно.
Реквизит внешней обработки СсылкаНаОбъект уже сам по себе является ссылкой (тип ДокументСсылка.СписаниеТоваров). Обращение через точку СсылкаНаОбъект.Ссылка возвращает ту же самую ссылку. С точки зрения стандартов разработки фирмы «1С» это избыточный код (bad practice), ухудшающий читаемость программы.
Кроме того, если в процессе работы переменная СсылкаНаОбъект окажется незаполненной (значение Неопределено), то при попытке выполнить код СсылкаНаОбъект.Ссылка система вызовет критическую ошибку выполнения: «Поле объекта не обнаружено (Ссылка)». Если же вы передадите просто СсылкаНаОбъект, запрос выполнится (хотя и вернет пустой результат), не вызывая падения системы.
Внимательно проанализировав исходный код функции ПечатьНакладной() из Сообщения 6, можно заметить еще несколько серьезных архитектурных ошибок, которые сильно замедляют работу системы, особенно под высокой нагрузкой.
Рассмотрим следующую строку:
Склад = ЭтаОбработка.СсылкаНаобъект.склад;
Когда вы обращаетесь к реквизитам через точку от ссылки (СсылкаНаОбъект.Склад), платформа 1С неявно для разработчика выполняет отдельный SQL-запрос к базе данных, чтобы извлечь значение этого поля. В рамках одной печатной формы таких обращений «через точку» может быть множество, что порождает десятки лишних запросов к СУБД.
Еще более тяжелой является следующая конструкция:
ТаблицаСуммСписания = БухгалтерскийУчет.ПолучитьСуммуСписанияАктивов(ЭтаОбработка.СсылкаНаОбъект.ПолучитьОбъект(), ВестиУчетПоДопРазрезамРегл);
Здесь принудительно создается копия объекта в памяти ради передачи его в экспортный метод. Это приводит к избыточному потреблению оперативной памяти сервера приложений 1С.
Как сделать правильно:
Все необходимые реквизиты шапки документа (такие как Склад, Организация, Подразделение, Основание) нужно получать одним единственным запросом. Поскольку в коде печатной формы уже есть запрос для получения данных шапки, все нужные реквизиты следует поместить в список его выборки. Это позволит получить все данные за одно обращение к базе данных.
Если в будущем вам потребуется переписать эту внешнюю печатную форму под управляемые формы для конфигураций на базе БСП (Библиотека стандартных подсистем), вам придется полностью пересмотреть архитектуру работы с параметрами.
В современных конфигурациях внешняя печатная форма регистрируется в системе и использует стандартный интерфейс БСП. Экспортная процедура печати в модуле объекта обработки выглядит следующим образом:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Здесь пишется код формирования печатных форм
КонецПроцедуры
Обратите внимание на ключевые отличия БСП от старых обычных форм:
МассивОбъектов, содержащий список ссылок на документы. БСП спроектирована так, чтобы пользователь мог выделить в списке несколько документов и распечатать их групповым способом за один раз.= в запросе станет невозможным, так как мы работаем с массивом. Условие ГДЕ обязательно должно использовать оператор В:
ГДЕ
СписаниеТоваров.Ссылка В (&МассивОбъектов)
Если вы попытаетесь оставить сравнение через равенство СписаниеТоваров.Ссылка = &МассивОбъектов, платформа снова выдаст ошибку несовместимости типов в операции сравнения, поскольку невозможно сравнить одиночную ссылку с целым массивом.
Разрабатывая внешние печатные формы, всегда помните о различиях между ссылочными и объектными типами данных в 1С. Чистые ссылки ДокументСсылка оптимальны для использования в параметрах запросов и логических проверках. Объектные сущности ДокументОбъект требуются исключительно тогда, когда вам необходимо программно изменить данные документа и записать их в базу данных.