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

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

При разработке внешних обработок или документов на управляемых формах 1С:Предприятие часто возникает задача: пользователь вводит значение в одну колонку табличной части (например, «Номер»), и система должна автоматически подтянуть данные в другую колонку (например, «Значение») из регистра сведений или другого источника. Начинающие программисты часто сталкиваются с ошибкой «Поле объекта не обнаружено», пытаясь обратиться к колонке напрямую через Объект.Место.Номер. В этой статье мы подробно разберем, почему так происходит, и рассмотрим три эффективных способа решения этой задачи.

Почему возникает ошибка «Поле объекта не обнаружено»?

Разберем причину ошибки. В системе 1С Объект.Место (где «Место» — это табличная часть) является коллекцией (типа ДанныеФормыКоллекция). У коллекции нет свойства «Номер», это свойство есть только у конкретной строки этой коллекции. Когда вы пишете Запрос.УстановитьПараметр("Номер", Объект.Место.Номер), система не понимает, из какой именно строки (первой, пятой или текущей) нужно взять этот номер. Именно поэтому возникают трудности с передачей параметра.

Для решения нам нужно либо четко указать конкретную строку, либо обработать всю таблицу целиком. Рассмотрим варианты реализации.

Вариант 1. Заполнение при изменении конкретной строки

Этот метод наиболее удобен для пользователя: как только он ввел номер и нажал Enter, значение заполняется мгновенно. Мы будем использовать событие ПриИзменении для элемента формы, связанного с колонкой «Номер».

Рассмотрим пошаговый алгоритм:

  1. На форме выбираем поле ввода колонки «Номер».
  2. В свойствах создаем событие ПриИзменении на клиенте.
  3. В обработчике получаем текущую строку через Элементы формы.

Посмотрим на пример кода:


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

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

Преимущество: Высокая оперативность. Пользователь сразу видит результат своей работы.

Вариант 2. Массовое заполнение всех строк по кнопке

Иногда пользователю удобнее сначала заполнить весь список номеров, а затем нажать одну кнопку «Получить данные» или «Заполнить». В этом случае нам нужно перебрать все строки табличной части. Проанализируем, как это сделать правильно, избегая запросов в цикле (это критически важно для производительности).

Рассмотрим пример реализации команды «Показать»:


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

Важный нюанс: Обратите внимание на использование ЛЕВОЕ СОЕДИНЕНИЕ в запросе. Это гарантирует, что даже если для какого-то номера в регистре нет значения, строка в вашей табличной части не пропадет, а получит значение по умолчанию (например, «Не задано»).

Вариант 3. Использование метода Загрузить() и его опасности

В обсуждениях часто предлагают метод Объект.Место.Загрузить(РезультатЗапроса.Выгрузить()). Проанализируем эту ситуацию подробнее. Метод Загрузить() полностью очищает табличную часть и заменяет её содержимое данными из переданной Таблицы Значений.

Почему это может быть плохо:

  1. Если в табличной части были другие колонки, которые не заполняются запросом (например, комментарий пользователя или ручные пометки), они будут стерты.
  2. Нарушается связь идентификаторов строк. Если у вас открыты какие-то связанные окна или формы, они могут начать работать некорректно.
  3. Если количество строк очень большое, метод может существенно нагрузить память сервера.

Когда это допустимо: Если ваша табличная часть является исключительно «результативной» (то есть полностью формируется программно) и пользователь не вводит в неё никаких дополнительных данных, кроме тех, что участвуют в запросе.

Оптимизация производительности (Temporary Tables)

Если в вашей табличной части сотни или тысячи строк, обычная передача параметра через массив или ТЗ может замедлить работу. Рассмотрим профессиональный подход с индексацией временных таблиц:

  1. Выгрузите табличную часть в ТаблицуЗначений.
  2. Обязательно типизируйте колонки в ТаблицеЗначений (особенно если «Номер» имеет составной тип).
  3. В запросе после помещения данных во временную таблицу используйте инструкцию ИНДЕКСИРОВАТЬ ПО.

Пример фрагмента запроса с индексацией:


ВЫБРАТЬ
    ТЗ.Номер КАК Номер
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    &ТЗ КАК ТЗ
ИНДЕКСИРОВАТЬ ПО
    Номер
;

Индексирование временной таблицы значительно ускоряет операцию СОЕДИНЕНИЕ с физическими таблицами базы данных (регистрами), так как СУБД не придется выполнять полный перебор строк (Table Scan).

Нюанс с «Грязными данными» на управляемых формах

Выясним причину еще одной частой проблемы: пользователь ввел номер, нажал кнопку «Показать», но значение не подтянулось. Это происходит потому, что данные в ячейке, которую пользователь только что редактировал, еще не успели «зафиксироваться» в реквизите Объект на сервере.

Решение: В коде команды на клиенте, перед вызовом серверной процедуры, рекомендуется принудительно завершить редактирование. Часто для этого достаточно вызвать ЭтаФорма.ПроверитьЗаполнение() или просто убедиться, что фокус ввода покинул ячейку. Однако самым надежным способом является использование данных, переданных непосредственно из элементов формы через ТекущиеДанные.

Итоги

Мы рассмотрели, как правильно передавать параметры из табличной части в запрос. Подведем краткие итоги:

Применяя эти методы, вы создадите отзывчивый и производительный интерфейс для ваших пользователей.

← На главную