При разработке отчетов или сервисных обработок в конфигурациях 1С (таких как 1С:Бухгалтерия предприятия, 1С:ERP или 1С:Управление торговлей) разработчики часто сталкиваются с ситуацией: визуально в карточке контрагента отображаются контактные лица и их контактная информация (телефоны, e-mail, адреса), однако простой запрос к базе данных или объектный отбор возвращает пустой результат. Рассмотрим подробно причину такого поведения платформы и разберем по шагам, как выявить и исправить эту проблему.
Проанализируем типичный сценарий. Разработчик пытается получить контактные данные или список контактных лиц конкретного контрагента. В конфигурациях на базе Библиотеки Стандартных Подсистем (БСП) справочник КонтактныеЛица является независимым, а связь с контрагентом или физическим лицом осуществляется через реквизит ОбъектВладелец составного типа (СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица, СправочникСсылка.Пользователи и др.).
Посмотрим на пример запроса, который на первый взгляд выглядит абсолютно корректным:
ВЫБРАТЬ
КонтактныеЛицаКонтактнаяИнформация.Представление КАК Представление
ИЗ
Справочник.КонтактныеЛица.КонтактнаяИнформация КАК КонтактныеЛицаКонтактнаяИнформация
ГДЕ
КонтактныеЛицаКонтактнаяИнформация.Ссылка.ОбъектВладелец = &Контрагент
Или аналогичный вариант программного отбора через объектную модель 1С:
Отбор = Новый Структура("ОбъектВладелец");
Отбор.ОбъектВладелец = НужныйКонтрагент;
Выборка = Справочники.КонтактныеЛица.Выбрать(,, Отбор);
_Ссылки = Новый Массив;
Пока Выборка.Следующий() Цикл
_Ссылки.Добавить(Выборка.Ссылка);
КонецЦикла;
В обоих случаях разработчик получает пустой результат. При этом в пользовательском интерфейсе в карточке контрагента контактное лицо присутствует, и данные адресов/телефонов заполнены.
Разберем, почему возникает расхождение между тем, что мы видим на форме, и тем, что получает запрос.
В платформе «1С:Предприятие 8» равенство ссылочных типы данных определяется не по визуальному представлению (наименованию, ИНН или коду), а по уникальному идентификатору записи (GUID / UUID). В интерфейсе два элемента справочника могут выглядеть абсолютно одинаково — например, ООО «Вектор». Однако с точки зрения СУБД это два совершенно разных объекта.
Причина описываемой проблемы заключается в **наличии дублей элементов в справочнике Контрагенты**:
&Контрагент или структуре отбора передается **Элемент Б** (дублирующий контрагент с таким же наименованием).ОбъектВладелец = &Контрагент возвращает ЛОЖЬ для всех записей.При этом в пользовательском интерфейсе оператор мог открыть карточку **Элемента А** и видеть контактные лица, а при проверке программного кода в качестве аргумента передавался **Элемент Б** (например, выбранный из другого документа или формы списка).
Рассмотрим по шагам алгоритм локализации этой ошибки.
Шаг 1. Выполним запрос без отбора по контрагенту.
Чтобы убедиться, к какому именно объекту привязаны контактные лица, выполним запрос без фильтрации по конкретной ссылке:
ВЫБРАТЬ
КонтактныеЛица.Ссылка КАК КонтактноеЛицо,
КонтактныеЛица.ОбъектВладелец КАК Владелец,
КонтактныеЛица.ОбъектВладелец.Код КАК КодВладельца,
КонтактныеЛица.ОбъектВладелец.ИНН КАК ИННВладельца
ИЗ
Справочник.КонтактныеЛица КАК КонтактныеЛица
ГДЕ
КонтактныеЛица.Наименование ПОДОБНО "%Иванов%"
Шаг 2. Сравним уникальные идентификаторы.
В отладчике 1С вычислим значение уникальных идентификаторов для контрагента из вашего параметра и владельца контактного лица:
// Значение 1: Идентификатор контрагента из параметра
ИдентификаторИзПараметра = ИскомыйКонтрагент.Ссылка.УникальныйИдентификатор();
// Значение 2: Идентификатор реального владельца контактного лица
ИдентификаторИзБазы = СсылкаНаКонтактноеЛицо.ОбъектВладелец.УникальныйИдентификатор();
Если эти строки с идентификаторами не совпадают, значит, в базе данных заведены два дублирующих контрагента.
Для исправления ситуации необходимо устранить дублирование элементов справочника `Контрагенты` и перевесить подчиненные объекты (включая контактные лица и документы) на один правильный элемент.
В типовых конфигурациях 1С на базе БСП встроен удобный инструмент очистки данных:
ОбъектВладелец справочника КонтактныеЛица), после чего пометит дубли на удаление.Если необходимо выполнить объединение дублей программно, задействуем механизм БСП — функцию ОбщегоНазначения.ЗаменитьСсылки():
// Правильный контрагент, который должен остаться в базе
Оригинал = Справочники.Контрагенты.НайтиПоКоду("00-000001");
// Дубль, от которого избавляемся
Дубликат = Справочники.Контрагенты.НайтиПоКоду("00-000002");
МассивДублей = Новый Массив;
МассивДублей.Добавить(Дубликат);
// Выполняем замену ссылок во всей базе данных
ОбщегоНазначения.ЗаменитьСсылки(Оригинал, МассивДублей);
// Помечаем дубликат на удаление
ОбъектДубль = Дубликат.ПолучитьОбъект();
ОбъектДубль.УстановитьПометкуУдаления(Истина);
При работе с реквизитами составного типа (такими как ОбъектВладелец) рекомендуется явно указывать ожидаемый тип данных в запросе с помощью функции ВЫРАЗИТЬ(). Это позволяет платформе генерировать более эффективный SQL-код и избегать лишних соединений с таблицами всех возможных типов:
ВЫБРАТЬ
КонтактныеЛица.Ссылка КАК КонтактноеЛицо,
КонтактныеЛица.Наименование КАК ФИО,
КонтактнаяИнформация.Представление КАК ТелефонИлиEmail
ИЗ
Справочник.КонтактныеЛица КАК КонтактныеЛица
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КонтактныеЛица.КонтактнаяИнформация КАК КонтактнаяИнформация
ПО КонтактнаяИнформация.Ссылка = КонтактныеЛица.Ссылка
ГДЕ
ВЫРАЗИТЬ(КонтактныеЛица.ОбъектВладелец КАК Справочник.Контрагенты) = &Контрагент
Использование ВЫРАЗИТЬ() гарантирует, что платформа 1С ограничит выборку объектами конкретного справочника и оптимизирует план выполнения запроса на СУБД.