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