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

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

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

Анализ типичных ошибок при проверке уникальности

Разберем распространенные ошибки, которые часто допускаются при попытке реализовать проверку уникальности вручную:

  1. Неправильное обращение к метаданным: в коде часто ошибочно обращаются к коллекции как Справочник вместо правильного системного имени Справочники.
  2. Использование методов прямого поиска: метод НайтиПоНаименованию() выполняет поиск по наименованию самого элемента справочника, а не по значению реквизита привязки. Кроме того, при повторном сохранении уже существующего элемента такой метод вернет текущую ссылку, что приведет к ложному срабатыванию блокировки.
  3. Размещение проверки только в модуле формы: если разместить проверку в обработчиках формы, контроль сработает только при интерактивных действиях пользователя. При программном создании или изменении объектов, обмене данными или выполнении групповых обработок такая проверка будет полностью проигнорирована.

Реализация проверки запросом в событии ПередЗаписью

Проанализируем корректный способ контроля. Основную бизнес-логику проверки целостности данных следует размещать в модуле объекта справочника в процедуре-обработчике события ПередЗаписью. Это гарантирует срабатывание проверки как при интерактивной, так и при программной записи объекта.

Рассмотрим по шагам, как строится логика такой проверки:

  1. Проверяем, заполнено ли поле владельца с помощью функции ЗначениеЗаполнено(). Если поле не заполнено, выполнять проверку запросом нет необходимости.
  2. Формируем запрос к таблице справочника, в котором проверяем наличие записей с таким же значением реквизита.
  3. Обязательно исключаем из выборки сам текущий записываемый элемент через условие Ссылка <> &ТекущаяСсылка, чтобы не блокировать повторное сохранение уже существующего объекта.
  4. Используем конструкцию ВЫБРАТЬ ПЕРВЫЕ 1 для оптимизации скорости выполнения запроса к СУБД.
  5. Если результат запроса не пустой, устанавливаем параметр Отказ = Истина и выводим понятное сообщение пользователю.

Посмотрим на пример реализации кода в модуле объекта:


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

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

Обеспечение целостности в многопользовательском режиме

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

Чтобы гарантировать абсолютную уникальность в транзакции, перед выполнением проверочного запроса необходимо установить исключительную управляемую блокировку с помощью объекта БлокировкаДанных:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Справочник.КарточкиНоменклатуры");
ЭлементБлокировки.УстановитьЗначение("ВладелецКарточки", ВладелецКарточки);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();

Альтернативные архитектурные решения

Рассмотрим альтернативные способы организации структуры метаданных для связи «один к одному»:

  1. Использование регистра сведений: вместо создания отдельного реквизита в справочнике можно создать непериодический независимый регистр сведений. Номенклатура помещается в Измерение, а карточка — в Ресурс. Платформа 1С на уровне СУБД контролирует уникальность измерений регистра, исключая появление дублей без необходимости писать запросы и ручные блокировки.
  2. Размещение реквизитов непосредственно в номенклатуре: если между объектами строгое соответствие «один к одному», наиболее простым и надежным решением часто является отказ от дополнительного справочника и перенос необходимых реквизитов напрямую в справочник Номенклатура или использование механизма дополнительных реквизитов и сведений.
← На главную