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