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