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