Как программно заполнить колонку табличной части из регистра сведений в управляемых формах 1С?

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

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

Разбор основной задачи и выбор контекста исполнения

Представим ситуацию: у нас есть документ «Реализация товаров и услуг», в табличной части Товары добавлена колонка IDТовара (тип Число). Нам нужно по нажатию кнопки заполнить эту колонку значениями из регистра сведений, сопоставляя данные по Номенклатуре и Характеристике. Первое, что необходимо запомнить: любые манипуляции с данными и запросами должны происходить на сервере. Клиентская часть 1С (тонкий или веб-клиент) не имеет прямого доступа к базе данных и не предназначена для тяжелых вычислений.

Проанализируем типичную ошибку: попытку сначала получить данные на сервере в виде таблицы значений, а затем обходить её на клиенте. Это приводит к лишним пересылкам данных между сервером и клиентом, что замедляет работу интерфейса.

Метод 1: Прямой обход табличной части (классический подход)

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

Разберем по шагам правильный алгоритм:

  1. Очищаем колонку IDТовара в табличной части.
  2. Формируем запрос к регистру сведений, передавая в него массив номенклатуры из ТЧ.
  3. Обходим строки ТЧ документа и для каждой строки ищем значение в результате запроса.

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


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

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

Метод 2: Оптимизация через объект «Соответствие»

Проанализируем ситуацию, когда в документе сотни или тысячи строк. Поиск методом НайтиСтроки() внутри цикла может работать медленно. Опытные разработчики используют объект Соответствие (Map). Это позволяет обращаться к данным по ключу практически мгновенно.

Рассмотрим, как это работает: Мы один раз проходим по результату запроса и заполняем Соответствие, где ключом является Номенклатура, а значением — IDТовара. Затем при обходе ТЧ мы просто «спрашиваем» у соответствия значение по ключу.


// ... код запроса аналогичен предыдущему ...
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

ДанныеID = Новый Соответствие;
Пока Выборка.Следующий() Цикл
    ДанныеID.Вставить(Выборка.Номенклатура, Выборка.IDТовара);
КонецЦикла;

// Теперь заполнение ТЧ происходит молниеносно
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
    ЗначениеID = ДанныеID.Получить(СтрокаТЧ.Номенклатура);
    Если ЗначениеID <> Неопределено Тогда
        СтрокаТЧ.IDТовара = ЗначениеID;
    КонецЕсли;
КонецЦикла;

Метод 3: Обработка несохраненных данных (Временные таблицы)

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

Разберем этот продвинутый алгоритм:

  1. Выгружаем текущую ТЧ Товары в ТаблицуЗначений.
  2. Передаем эту таблицу в запрос как параметр.
  3. В тексте запроса создаем временную таблицу из этого параметра.
  4. Соединяем временную таблицу с регистром сведений (Левое соединение).
  5. Получаем готовый результат и загружаем его обратно.

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

Важные замечания по характеристикам

Если в вашей конфигурации используются характеристики номенклатуры, обязательно учитывайте их в связке. IDТовара в регистре обычно привязан к паре Номенклатура + Характеристика. В запросе при соединении таблиц следует использовать функцию ЕСТЬNULL для полей характеристик, так как они могут быть пустыми, что приведет к некорректному сопоставлению (NULL не равен пустой ссылке).

Также помните, что после программного изменения данных на сервере, управляемая форма в большинстве случаев обновится сама (так как мы меняем реквизиты Объект.Товары). Однако, если вы меняли данные непосредственно в базе через ОбъектЗаписать(), на форме потребуется вызвать метод ЭтаФорма.Прочитать() или ОповеститьОбИзменении().

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

← На главную