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

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

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

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

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

Для начала нам необходимо определить критерий уникальности. Чаще всего это Наименование, ИНН или комбинация нескольких реквизитов. Разберем пример проверки по наименованию. Проанализируем следующий код:


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

    // Выполняем поиск дубля только для новых элементов или при изменении ключевого реквизита
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |	Номенклатура.Ссылка КАК Ссылка
        |ИЗ
        |	Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |	Номенклатура.Наименование = &Наименование
        |	И Номенклатура.Ссылка <> &Ссылка";

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

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

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

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

Разберем по шагам важные детали этого кода. Во-первых, мы используем СокрЛП(), чтобы исключить дублирование из-за лишних пробелов. Во-вторых, условие Номенклатура.Ссылка <> &Ссылка критически важно: если мы редактируем уже существующий элемент, запрос не должен найти «сам себя» и заблокировать сохранение. В-третьих, мы проверяем свойство ОбменДанными.Загрузка, чтобы не нарушать работу механизмов синхронизации.

Метод 2: Использование подписки на событие

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

Рассмотрим ситуацию, когда нам нужно проверять уникальность по реквизиту ИНН для справочника «Контрагенты». Создадим процедуру в общем модуле:


Процедура ПроверитьУникальностьИННПередЗаписью(Источник, Отказ) Экспорт

    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    Если ПустаяСтрока(Источник.ИНН) Тогда
        Возврат;
    КонецЕсли;

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

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

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

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

При использовании этого метода объект передается в параметре Источник. Преимущество такого подхода — централизованное управление проверками для всей системы в одном месте.

Метод 3: Оптимизация UX — проверка «на лету»

Как справедливо заметили участники обсуждения, пользователю будет обидно заполнить 20 реквизитов и только в самом конце при нажатии кнопки «Записать» получить отказ. Чтобы сделать интерфейс более дружелюбным, мы можем добавить проверку в событие ПриИзменении ключевого реквизита на форме.

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


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

Метод 4: Модуль менеджера и обработка получения данных выбора

Проанализируем ситуацию, когда пользователь только начинает вводить название элемента в поле ввода. Мы можем предотвратить создание дубля еще на этапе выбора, используя механизм «ввода по строке». В модуле менеджера справочника можно переопределить обработчик ОбработкаПолученияДанныхВыбора.

Это позволит системе сразу предлагать пользователю существующие элементы, даже если он думал, что такого товара еще нет. Таким образом, мы подталкиваем пользователя выбрать старую карточку вместо создания новой.

Важные нюансы реализации

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

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

Резюме

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

← На главную