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