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