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

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

При разработке конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача предотвращения появления дублей в справочниках. Особенно это актуально для справочников «Контактные лица», «Контрагенты» или «Номенклатура», где человеческий фактор часто приводит к созданию одинаковых записей. В этой статье мы подробно разберем несколько способов реализации такой проверки: от простого запроса до использования механизмов полнотекстового поиска.

Метод 1: Проверка с помощью запроса в модуле объекта

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

Проанализируем пример кода, который можно разместить в модуле объекта:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Всегда проверяем свойство ОбменДанными.Загрузка, чтобы не блокировать импорт данных
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |	КонтактныеЛица.Ссылка КАК Ссылка
        |ИЗ
        |	Справочник.КонтактныеЛица КАК КонтактныеЛица
        |ГДЕ
        |	КонтактныеЛица.Наименование = &Наименование
        |	И КонтактныеЛица.Ссылка <> &Ссылка";

    Запрос.УстановитьПараметр("Наименование", Наименование);
    Запрос.УстановитьПараметр("Ссылка", Ссылка);

    РезультатЗапроса = Запрос.Выполнить();

    Если Не РезультатЗапроса.Пустой() Тогда
        ТекстСообщения = "Контактное лицо с таким наименованием уже существует в базе!";
        ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения, , "Наименование", , Отказ);
    КонецЕсли;

КонецПроцедуры

Разберем важный нюанс: почему мы используем условие КонтактныеЛица.Ссылка <> &Ссылка? Если мы создаем новый элемент, его ссылка еще пустая, и запрос будет искать любое совпадение по наименованию. Если же мы редактируем существующий элемент, это условие позволит запросу проигнорировать саму редактируемую запись, иначе система никогда не даст сохранить изменения, так как «дублем» будет выступать сам этот объект.

Метод 2: Проверка на стороне формы (интерактивный подход)

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


&НаСервере
Функция ЕстьДубликатНаСервере()
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	КонтактныеЛица.Ссылка
        |ИЗ
        |	Справочник.КонтактныеЛица КАК КонтактныеЛица
        |ГДЕ
        |	КонтактныеЛица.Наименование = &Наименование
        |	И КонтактныеЛица.Ссылка <> &Ссылка";
    
    Запрос.УстановитьПараметр("Наименование", Объект.Наименование);
    Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);
    
    Возврат Не Запрос.Выполнить().Пустой();
КонецФункции

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

Важный совет: При использовании этого метода помните, что проверка на форме защищает только от действий пользователя в данной конкретной форме. Если запись будет программно создана через обмен данными или обработку, проверка на форме не сработает. Поэтому для максимальной надежности всегда дублируйте логику в модуле объекта.

Метод 3: Использование полнотекстового поиска (нечеткое сравнение)

Обычный запрос ищет полное совпадение строки. Но что, если один пользователь ввел «Иванов Иван», а другой «Иванов Иван » (с пробелом в конце)? Или «Иван Иванов»? В таких случаях стандартный запрос может не найти дубль. Здесь на помощь приходит механизм полнотекстового поиска (ППД).

Рассмотрим, как можно организовать поиск «похожих» записей:

  1. Выясним, разрешено ли использование полнотекстового поиска в системе.
  2. Создадим список поиска по метаданным нужного справочника.
  3. Установим порог нечеткости (процент совпадения).
  4. Выведем пользователю список найденных совпадений.

// Пример упрощенной логики поиска похожих записей
СтрПоиска = СокрЛП(Объект.Наименование);
СписокПоиска = ПолнотекстовыйПоиск.СоздатьСписок(СтрПоиска, 10);
СписокПоиска.ПорогНечеткости = 15; // Чем выше число, тем меньше точность
СписокПоиска.ОбластьПоиска.Добавить(Метаданные.Справочники.КонтактныеЛица);

СписокПоиска.ПерваяЧасть();

Для Каждого Результат Из СписокПоиска Цикл
    Сообщить("Найден похожий объект: " + Результат.Значение);
КонецЦикла;

У полнотекстового поиска есть одна особенность: индекс поиска обновляется регламентным заданием. Если вы только что создали элемент и тут же пытаетесь создать его копию, ППД может не найти свежую запись, пока не обновится индекс. Для критически важных проверок этот метод лучше комбинировать со строгим запросом.

Архитектурные нюансы и рекомендации

Чтобы ваша проверка работала эффективно и не замедляла систему, проанализируем следующие рекомендации:

  1. Индексация: Обязательно проверьте в конфигураторе, что у реквизита Наименование установлено свойство Индексировать. Без этого при большом количестве записей (например, более 50 000) проверка будет приводить к существенным задержкам при сохранении.
  2. Нормализация данных: Перед поиском и сравнением полезно очищать строку от лишних символов. Используйте функцию СокрЛП() для удаления пробелов по краям, а для продвинутой очистки можно удалять кавычки, точки и лишние пробелы внутри строки.
  3. Транзакционные блокировки: В высоконагруженных системах возможна ситуация, когда два пользователя одновременно записывают одинаковое имя. Оба запроса в начале транзакции покажут, что дублей нет. Чтобы этого избежать, можно использовать объект БлокировкаДанных для исключительной блокировки проверяемого значения.
  4. Использование БСП: Если в вашей конфигурации установлена Библиотека стандартных подсистем (БСП), обратите внимание на подсистему «Поиск и удаление дублей». В ней есть встроенные механизмы программного поиска похожих записей, которые учитывают перестановку слов и созвучие.

Подводя итог, можно сказать, что выбор метода зависит от бизнес-задачи. Для жесткого запрета на дубли достаточно запроса в ПередЗаписью. Для помощи пользователю в корректном вводе данных лучше использовать мягкие проверки на форме с выводом списка похожих записей через полнотекстовый поиск или встроенные инструменты БСП.

← На главную