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