Как правильно передать параметр из текущей строки табличной части в запрос на сервере 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на управляемых формах 1С:Предприятия 8.3 программисты часто сталкиваются с задачей: при выборе значения в одной колонке табличной части (например, ссылки на документ или справочник) необходимо автоматически заполнить другие колонки данными из этого объекта. Основная сложность здесь заключается в правильной организации взаимодействия между клиентом и сервером, а также в понимании того, какие типы данных доступны в различных контекстах выполнения кода.

В этой статье мы подробно разберем, почему возникает ошибка при попытке прямого обращения к элементам формы на сервере, и рассмотрим наиболее эффективные способы передачи параметров в запрос для автоматического заполнения реквизитов.

Анализ проблемы: Почему сервер «не видит» текущую строку

Типичная ошибка начинающего разработчика заключается в попытке обратиться к свойству ТекущиеДанные элемента формы непосредственно в серверном коде. Рассмотрим ситуацию: у нас есть табличная часть Авансы и реквизит КСФВыданныйАванс. Мы хотим в обработчике события ПриИзменении получить данные этого аванса (сумму, ставку НДС) и записать их в ту же строку.

На сервере код ЭтаФорма.Элементы.Авансы.ТекущиеДанные зачастую либо вызывает ошибку, либо возвращает объект, который невозможно передать в параметр запроса. Это происходит потому, что управляемый интерфейс жестко разделяет данные и их представление. Коллекция Элементы предназначена для управления визуальным отображением, а на сервер передаются данные (объект РеквизитФормыВЗначение или через контекст формы).

Более того, в запросе 1С нельзя использовать объекты типа ДанныеФормыЭлементКоллекции. Параметр запроса должен быть простым типом (строка, число) или ссылкой (справочник, документ).

Решение 1: Передача ссылки через параметры функции (Рекомендуемое)

Наиболее правильным и производительным способом является использование бесконтекстного серверного вызова. Мы получаем нужную ссылку на клиенте и передаем её в функцию на сервере. Проанализируем этот подход по шагам.

Шаг 1. Создаем клиентскую процедуру

В этой процедуре мы получаем доступ к данным текущей строки через Элементы.Авансы.ТекущиеДанные. На клиенте это работает быстро и корректно.


&НаКлиенте
Процедура АвансыКСФВыданныйАвансПриИзменении(Элемент)
    
    ТекДанные = Элементы.Авансы.ТекущиеДанные;
    
    // Проверяем, заполнена ли ссылка, чтобы не выполнять лишний вызов сервера
    Если НЕ ЗначениеЗаполнено(ТекДанные.КСФВыданныйАванс) Тогда
        Возврат;
    КонецЕсли;
    
    // Вызываем серверную функцию и передаем только ссылку
    ДанныеСФ = ПолучитьДанныеСчетаФактурыНаСервере(ТекДанные.КСФВыданныйАванс);
    
    // Заполняем реквизиты строки полученными данными
    ЗаполнитьЗначенияСвойств(ТекДанные, ДанныеСФ);
    
КонецПроцедуры

Шаг 2. Реализуем серверную логику

Использование директивы &НаСервереБезКонтекста позволяет не передавать всю форму на сервер, что значительно ускоряет работу программы.


&НаСервереБезКонтекста
Функция ПолучитьДанныеСчетаФактурыНаСервере(СсылкаСФ)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	СчетФактураВыданныйАванс.Сумма КАК Сумма,
        |	СчетФактураВыданныйАванс.СуммаНДС КАК СуммаНДС,
        |	СчетФактураВыданныйАванс.НалогообложениеНДС КАК СтавкаНДС
        |ИЗ
        |	Документ.СчетФактураВыданныйАванс КАК СчетФактураВыданныйАванс
        |ГДЕ
        |	СчетФактураВыданныйАванс.Ссылка = &Ссылка";
    
    Запрос.УстановитьПараметр("Ссылка", СсылкаСФ);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    // Подготавливаем структуру для возврата данных на клиент
    Данные = Новый Структура("Сумма, СтавкаНДС, СуммаНДС", 0, Неопределено, 0);
    
    Выборка = РезультатЗапроса.Выбрать();
    Если Выборка.Следующий() Тогда
        ЗаполнитьЗначенияСвойств(Данные, Выборка);
    КонецЕсли;
    
    Возврат Данные;
    
КонецФункции

Решение 2: Использование идентификатора строки на сервере

Если по каким-то причинам вам необходимо работать в контексте всей формы (директива &НаСервере), вы можете использовать идентификатор строки. Это полезно, когда нужно обращаться к другим реквизитам документа, не передавая их в параметрах. Выясним, как это работает.

Каждая строка табличной части в управляемой форме имеет уникальный числовой идентификатор (свойство ТекущаяСтрока у элемента таблицы). По этому идентификатору можно найти нужную строку в коллекции Объект.ИмяТабличнойЧасти на сервере.


&НаКлиенте
Процедура АвансыПриИзменении(Элемент)
    // Передаем идентификатор текущей строки на сервер
    ЗаполнитьСтрокуНаСервере(Элементы.Авансы.ТекущаяСтрока);
КонецПроцедуры

&НаСервере
Процедура ЗаполнитьСтрокуНаСервере(ИдентификаторСтроки)
    
    // Находим строку в данных объекта по идентификатору
    ТекущиеДанныеСтроки = Объект.Авансы.НайтиПоИдентификатору(ИдентификаторСтроки);
    
    Если ТекущиеДанныеСтроки <> Неопределено Тогда
        // Теперь мы можем использовать данные строки для запроса
        Запрос = Новый Запрос("ВЫБРАТЬ ... ГДЕ Ссылка = &Ссылка");
        Запрос.УстановитьПараметр("Ссылка", ТекущиеДанныеСтроки.КСФВыданныйАванс);
        
        // Выполняем запрос и заполняем ТекущиеДанныеСтроки напрямую
        // ...
    КонецЕсли;
    
КонецПроцедуры

Важные замечания по оптимизации и качеству кода

Проанализируем несколько приемов, которые сделают ваш код более профессиональным:

  1. Использование метода ЗаполнитьЗначенияСвойств. Как мы увидели в примерах, этот метод позволяет избежать ручного присваивания каждого поля. Главное условие — имена полей в структуре-источнике (или псевдонимы в запросе после слова КАК) должны совпадать с именами реквизитов табличной части.
  2. Обработка очистки поля. Рассмотрим ситуацию, когда пользователь удалил ссылку из поля. В этом случае запрос вернет пустой результат или ошибку. Всегда проверяйте ЗначениеЗаполнено() перед вызовом сервера. Если ссылка пуста — просто обнулите зависимые поля на клиенте.
  3. Типовые функции. Если ваша конфигурация построена на БСП (Библиотека стандартных подсистем), вместо написания своего запроса для получения одного-двух полей лучше использовать функцию ОбщегоНазначения.ЗначениеРеквизитаОбъекта(). Это сократит объем кода и уменьшит вероятность ошибок.
  4. Массовая обработка. Если вам нужно заполнить данные не для одной строки, а, например, при загрузке целого списка, никогда не делайте запрос в цикле. Соберите все ссылки в массив, передайте его на сервер и выполните один запрос с условием ГДЕ Ссылка В (&МассивСсылок).

Посмотрим на пример правильной очистки данных при пустой ссылке:


&НаКлиенте
Процедура АвансыКСФВыданныйАвансПриИзменении(Элемент)
    ТекДанные = Элементы.Авансы.ТекущиеДанные;
    
    Если ЗначениеЗаполнено(ТекДанные.КСФВыданныйАванс) Тогда
        // Выполняем логику получения данных через сервер
        ЗаполнитьДанныеИзСервера(ТекДанные);
    Иначе
        // Очищаем зависимые поля без вызова сервера
        ТекДанные.Сумма = 0;
        ТекДанные.СуммаНДС = 0;
        ТекДанные.СтавкаНДС = ПредопределенноеЗначение("Перечисление.СтавкиНДС.ПустаяСсылка");
    КонецЕсли;
КонецПроцедуры

Подведем итог: Для передачи параметра в запрос из табличной части всегда извлекайте ссылку на клиенте через Элементы.ИмяТаблицы.ТекущиеДанные.ИмяРеквизита и передавайте её как обычный параметр в серверную функцию. Это обеспечит стабильную работу кода и высокую скорость работы интерфейса.

← На главную