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

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

При разработке документов в системе 1С:Предприятие мы часто сталкиваемся с задачей автоматического заполнения полей. Классический сценарий: пользователь вводит заводской номер или артикул в строке табличной части, а программа должна автоматически подставить наименование, производителя, тип оборудования и другие характеристики.

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

Постановка задачи

Представим ситуацию: у нас есть Документ с табличной частью, например, ПереченьОборудования. В этой таблице есть колонка ЗаводскойНомер (строка) и колонки для описания: ТипОборудования, Предназначение, СтранаПроизводитель.

Также существует Справочник Оборудование, где хранятся эталонные данные. Наша цель — при вводе пользователем заводского номера найти соответствующий элемент в справочнике и перенести его данные в текущую строку документа.

Типичные ошибки при работе с табличной частью

Давайте проанализируем, почему код, работающий для "шапки" документа, перестает работать для табличной части. Самая распространенная ошибка выглядит так:


&НаСервере
Процедура ЗаводскойНомерПриИзмененииНаСервере()
    // ОШИБКА: Объект.ЗаводскойНомер обращается к реквизиту шапки, 
    // которого может не быть, или берет не то значение.
    ИскомыйНомер = Объект.ЗаводскойНомер; 
    
    // Выполнение запроса...
    
    // ОШИБКА: Запись идет в реквизиты шапки, а не в строку таблицы
    Объект.ТипОборудования = Выборка.Тип; 
КонецПроцедуры

В чем здесь проблема? Переменная Объект содержит данные всего документа. Когда мы редактируем строку табличной части, нам нужно обращаться именно к текущим данным этой строки, а не к документу в целом.

Вторая частая ошибка — попытка обратиться к табличной части через коллекцию Объект.ИмяТЧ без указания индекса строки. Система выдаст ошибку "Поле объекта не обнаружено", так как табличная часть — это коллекция строк, а не одна запись.

Правильное решение: использование ТекущихДанных

Для корректной работы нам необходимо разделить логику на клиентскую и серверную части. На клиенте мы определяем, какую строку редактирует пользователь, а на сервере получаем данные.

Шаг 1. Клиентская процедура

В обработчике события ПриИзменении поля ввода на форме мы должны получить доступ к текущей строке. Для этого используется коллекция Элементы.


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

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

КонецПроцедуры

Обратите внимание: мы не передаем весь Объект на сервер. Мы передаем только критерий поиска — ЗаводскойНомер.

Шаг 2. Серверная функция без контекста

Для оптимизации производительности лучше использовать модуль формы с директивой компиляции &НаСервереБезКонтекста. Это позволяет избежать передачи "тяжелой" формы документа на сервер и обратно.


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

Оптимизация кода: ЗаполнитьЗначенияСвойств

В примере выше мы использовали функцию ЗаполнитьЗначенияСвойств(Приемник, Источник). Это мощный инструмент платформы 1С. Рассмотрим его подробнее.

Вместо того чтобы писать "лесенку" присваиваний:


ТекущаяСтрока.ТипОборудования = Данные.ТипОборудования;
ТекущаяСтрока.Предназначение = Данные.Предназначение;
ТекущаяСтрока.СтранаПроизводитель = Данные.СтранаПроизводитель;

Мы используем одну команду. Главное условие — имена полей в структуре (которую мы сформировали из запроса, используя псевдонимы КАК) должны совпадать с именами колонок табличной части документа. Это делает код чище и упрощает добавление новых полей в будущем: достаточно добавить их в запрос и в табличную часть, код заполнения менять не придется.

Архитектурный вопрос: денормализация данных

Важно обсудить архитектурный аспект, поднятый в обсуждении. Стоит ли хранить в табличной части документа текстовые поля "Тип", "Производитель", если они уже есть в Справочнике?

Копирование данных (денормализация) имеет свои минусы:

  1. Если в справочнике исправят ошибку в названии страны, в старых документах останется старое значение.
  2. Увеличивается объем базы данных.
  3. Усложняется логика проведения документа (данные разрознены).

Рекомендуемый подход:

В табличную часть документа лучше добавить реквизит типа СправочникСсылка.Оборудование. При вводе заводского номера мы находим ссылку на элемент справочника и записываем её в табличную часть.

А как отобразить пользователю тип и производителя? Для этого не обязательно создавать реквизиты документа. Можно использовать свойства реквизита на форме или добавить колонки, которые ссылаются на данные через точку: Объект.ПереченьОборудования.Оборудование.Тип.

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

Альтернативный вариант поиска

Если поиск осуществляется не по стандартному реквизиту Код или Наименование, а по отдельному реквизиту (в нашем случае ЗаводскойНомер), важно помнить про производительность.

Убедитесь, что для реквизита ЗаводскойНомер в справочнике установлено свойство Индексировать (Индексировать или Индексировать с доп. упорядочиванием). Без индекса, по мере роста справочника, поиск будет работать всё медленнее, вызывая задержки при заполнении документа пользователем.

Заключение

Подведем итог решения задачи:

  1. Используйте событие ПриИзменении элемента формы.
  2. Обращайтесь к данным редактируемой строки через Элементы.ИмяТЧ.ТекущиеДанные.
  3. Используйте серверные вызовы без контекста (&НаСервереБезКонтекста) для экономии ресурсов.
  4. Формируйте результат запроса в виде Структуры.
  5. Используйте ЗаполнитьЗначенияСвойств для переноса данных из структуры в строку таблицы.
  6. Следите за индексацией реквизитов поиска.
← На главную