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