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