Как проверить дубли контрагентов по наименованию и ИНН перед записью в 1С

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

В типовых конфигурациях и при самостоятельной разработке на платформе 1С часто возникает задача предотвратить появление дубликатов в справочнике Контрагенты. Стандартный механизм платформы поддерживает автоматический контроль уникальности только для кодов и номеров, поэтому контроль наименования, ИНН или других реквизитов разработчик должен реализовать программно.

Разберем по шагам, где правильнее размещать код проверки, какие методы платформы использовать и каких типичных ошибок следует избегать, чтобы не заблокировать работу пользователей и штатные механизмы обмена данными.

Типичные ошибки при реализации контроля уникальности

Проанализируем распространенные ошибки, которые часто допускают при решении данной задачи:

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

Способ 1. Реализация через процедуру ОбработкаПроверкиЗаполнения в модуле объекта

Наиболее правильным и стандартизированным местом для контроля данных перед фиксацией является событие ОбработкаПроверкиЗаполнения в модуле объекта справочника. Платформа вызывает этот обработчик автоматически при интерактивной записи из формы или при вызове метода ПроверитьЗаполнение().

Посмотрим на пример кода, где поиск дублей по наименованию и ИНН выполняется с помощью запроса, а при обнаружении совпадений выводится структурированное сообщение, привязанное к конкретному полю на форме:


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

Способ 2. Проверка в событии ПередЗаписью модуля объекта

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

Рассмотрим подробнее реализацию функции проверки, охватывающей проверку как по наименованию, так и по связке реквизитов ИНН и КПП:


Процедура ПередЗаписью(Отказ)
	
	Если ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;
	
	Если ЕстьДубликатыВБазе() Тогда
		Отказ = Истина;
		
		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = "Запись отменена: найден контрагент с совпадающим наименованием или ИНН/КПП.";
		Сообщение.Поле = "Наименование";
		Сообщение.УстановитьДанные(ЭтотОбъект);
		Сообщение.Сообщить();
	КонецЕсли;
	
КонецПроцедуры

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

Способ 3. Проверка на уровне модуля формы

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

Разберем по шагам реализацию через обработчик ПередЗаписьюНаСервере:


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

Важные технические нюансы при промышленной разработке

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

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