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