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