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