Как запретить ввод дублей по нескольким полям в табличной части документа 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

В процессе разработки конфигураций на платформе 1С часто возникает задача контроля уникальности строк в табличной части документа. Типичный пример — документ Поступление товаров, где необходимо исключить ситуацию, когда одна и та же номенклатура с одинаковой характеристикой (например, цветом) вводится в разные строки. Это может привести к ошибкам в учете и неудобству при проведении документа. В этой статье мы подробно разберем несколько эффективных способов решения этой задачи, проанализируем ошибки начинающих разработчиков и выберем оптимальный подход.

Анализ проблемы и типичные ошибки

Рассмотрим ситуацию, с которой столкнулся автор темы. Попытка использовать метод НайтиСтроки в событии ПередЗаписью на клиенте часто приводит к тому, что проверка срабатывает даже тогда, когда в таблице всего одна строка. Это происходит потому, что метод НайтиСтроки находит в том числе и ту строку, которую мы в данный момент проверяем. Если результат поиска Количество() больше 0, это еще не значит, что есть дубль — это может быть сама текущая строка.

Кроме того, выполнение сложных проверок на стороне клиента — не самая лучшая практика с точки зрения производительности и безопасности данных. Давайте разберем правильные методы реализации этой логики.

Способ 1. Использование запроса (Рекомендуемый метод)

Использование запроса считается наиболее надежным и "чистым" способом проверки данных. Мы передаем всю табличную часть в запрос, группируем данные по ключевым полям и используем агрегатные функции для поиска повторений. Проанализируем, как это работает на практике.

Нам потребуется создать функцию на сервере, которая будет принимать таблицу товаров и возвращать результат проверки.


&НаСервере
Функция ТЧСодержитДубли(ДокументОбъект) Экспорт
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ТЧ.Номенклатура КАК Номенклатура,
        |    ТЧ.Цвет КАК Цвет
        |ПОМЕСТИТЬ ВТ_Данные
        |ИЗ
        |    &ТЧ КАК ТЧ
        |;
        |
        |////////////////////////////////////////////////////////////////////////////////
        |ВЫБРАТЬ
        |    Номенклатура,
        |    Цвет
        |ИЗ
        |    ВТ_Данные
        |СГРУППИРОВАТЬ ПО
        |    Номенклатура,
        |    Цвет
        |ИМЕЮЩИЕ
        |    КОЛИЧЕСТВО(*) > 1";
    
    Запрос.УстановитьПараметр("ТЧ", ДокументОбъект.Товары.Выгрузить());
    
    РезультатЗапроса = Запрос.Выполнить();
    
    Возврат НЕ РезультатЗапроса.Пустой();
    
КонецФункции

В этом решении мы используем конструкцию ИМЕЮЩИЕ КОЛИЧЕСТВО(*) > 1. Это ключевой момент: запрос вернет только те комбинации Номенклатура + Цвет, которые встречаются в таблице более одного раза. Если результат запроса не пустой, значит, дубли найдены.

Способ 2. Метод «Свернуть» таблицы значений

Если вы хотите реализовать проверку максимально быстро и с минимальным количеством кода, можно воспользоваться методом Свернуть объекта ТаблицаЗначений. Этот алгоритм основан на сравнении количества строк в исходной таблице и в таблице после удаления дублей.

Рассмотрим по шагам, как это работает:

  1. Выгружаем табличную часть в объект ТаблицаЗначений.
  2. Запоминаем исходное количество строк.
  3. Применяем метод Свернуть по ключевым полям (Номенклатура, Цвет).
  4. Сравниваем новое количество строк с исходным.

&НаСервереБезКонтекста
Функция ЕстьДублирующиеСтроки(ДанныеФормы)
    
    // Выгружаем данные из коллекции формы в таблицу значений
    Таблица = ДанныеФормы.Выгрузить();
    ИсходноеКоличество = Таблица.Количество();
    
    // Сворачиваем таблицу по проверяемым полям
    // Вторым параметром можно указать суммируемые поля (например, "Количество"), но для проверки дублей это не обязательно
    Таблица.Свернуть("Номенклатура, Цвет");
    
    КоличествоПослеСвертки = Таблица.Количество();
    
    Возврат ИсходноеКоличество <> КоличествоПослеСвертки;
    
КонецФункции

Этот способ очень эффективен, так как метод Свернуть реализован на уровне платформы и работает крайне быстро даже с большими массивами данных.

Способ 3. Проверка «на лету» (UX-подход)

Чтобы пользователь не ждал момента записи документа для обнаружения ошибки, мы можем проверять дубли сразу при изменении строки. Рассмотрим пример использования метода НайтиСтроки, но с правильной логикой исключения текущей строки.

Для этого в событии ПриИзменении поля Номенклатура или Цвет мы можем написать следующий код:


&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    
    СтрокаТЧ = Элементы.Товары.ТекущиеДанные;
    Если СтрокаТЧ = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Формируем структуру поиска
    ПараметрыПоиска = Новый Структура("Номенклатура, Цвет", СтрокаТЧ.Номенклатура, СтрокаТЧ.Цвет);
    
    // Ищем все подходящие строки
    НайденныеСтроки = Объект.Товары.НайтиСтроки(ПараметрыПоиска);
    
    // Если найдено больше одной строки, значит, текущая комбинация уже где-то есть
    Если НайденныеСтроки.Количество() > 1 Тогда
        ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Внимание! Такая номенклатура с этим цветом уже есть в списке.");
        // Здесь можно очистить значение или подсветить строку
    КонецЕсли;
    
КонецПроцедуры

Оптимизация производительности: использование «Соответствия»

Если в вашем документе тысячи строк, перебор или постоянная выгрузка таблицы в запрос может замедлить работу. В таких случаях программисты 1С используют объект Соответствие (аналог Map в других языках). Поиск по ключу в Соответствии выполняется значительно быстрее, чем НайтиСтроки в коллекции.

Разберем пример реализации через один цикл:


&НаСервере
Функция ПроверитьУникальностьЧерезСоответствие(Товары)
    
    ПроверенныеДубли = Новый Соответствие;
    
    Для Каждого СтрокаТЧ Из Товары Цикл
        // Создаем уникальный ключ из полей (можно через структуру или строку)
        Ключ = Строка(СтрокаТЧ.Номенклатура.УникальныйИдентификатор()) + "_" + Строка(СтрокаТЧ.Цвет);
        
        Если ПроверенныеДубли.Получить(Ключ) <> Неопределено Тогда
            Возврат Истина; // Дубль найден
        КонецЕсли;
        
        ПроверенныеДубли.Вставить(Ключ, Истина);
    КонецЦикла;
    
    Возврат Ложь;
    
КонецФункции

Важные нюансы при работе с дублями

Проанализируем несколько важных моментов, которые часто упускают из виду:

  1. Пустые значения: Нужно ли считать дублями две строки, в которых Номенклатура не заполнена? Как правило, проверку стоит запускать только если ключевое поле (Номенклатура) заполнено. Добавьте условие ЗначениеЗаполнено(СтрокаТЧ.Номенклатура) в ваш цикл или запрос.
  2. Визуальное выделение: Вместо простого сообщения об ошибке, попробуйте использовать Условное оформление формы. Вы можете добавить скрытый реквизит в ТЧ "ЭтоДубль" и программно помечать его. Настройте оформление так, чтобы строки с этим флагом окрашивались в светло-красный цвет. Это значительно повышает удобство работы пользователя.
  3. Регистры сведений: Если уникальность должна соблюдаться не в рамках одного документа, а во всей системе, лучше использовать Регистр сведений с соответствующим набором измерений. Платформа сама не даст записать дубли на уровне базы данных.

Подведем итог: для большинства задач в 1С 8.3 оптимальным является Способ 1 (Запрос), так как он наиболее универсален и позволяет легко расширять условия проверки (например, проверять дубли только в рамках конкретных складов или упаковок). Если же требуется максимальная простота кода — используйте Метод Свернуть.

← На главную