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