Владелец подчинённого справочника в 1С 8.3: получаем ссылку из кода, запроса и отладчика

· 9 мин

Содержание

К владельцу элемента справочника обращаются через стандартный реквизит Владелец: Договор.Владелец вернёт ссылку на контрагента, которому подчинён договор. Реквизит есть и у ссылки, и у объекта, и у поля в запросе. Сработает он только в серверном контексте и только у справочника, для которого в конфигураторе задан список владельцев.

Дальше — пять рабочих способов добраться до владельца справочника в 1С: от одной строчки кода до запроса с соединением и проверки в отладчике. Примеры на типовом сочетании «Контрагенты → Договоры контрагентов», которое есть в Бухгалтерии 3.0, УТ 11 и ERP. Всё написано под платформу 8.3 и управляемые формы.

Как устроено подчинение: настройка владельцев в конфигураторе

Подчинённость задаётся в метаданных, а не в коде. Пока у справочника нет владельцев, реквизита Владелец у него просто нет, и любое обращение к нему закончится ошибкой.

Где посмотреть настройку:

  1. Откройте конфигуратор и дерево конфигурации.
  2. Раскройте ветку Справочники и дважды щёлкните подчинённый справочник, например ДоговорыКонтрагентов.
  3. Перейдите на вкладку Владельцы.
  4. В списке «Владельцы справочника» перечислены справочники, которым подчинены элементы.

На той же вкладке есть свойство «Использование подчинения». Оно определяет, кто подчиняется владельцу:

  • 📄 Элементам — владелец есть только у элементов;
  • 📁 Группам — только у групп иерархического справочника;
  • 🗂 Группам и элементам — у тех и других.

Если владельцев в списке несколько, реквизит Владелец получает составной тип. Так устроены, например, банковские счета в типовых конфигурациях: счёт может принадлежать и контрагенту, и своей организации. Для кода это значит одно — перед обращением к полям владельца нужно проверить его тип.

Список владельцев можно прочитать и программно: Метаданные.Справочники.ДоговорыКонтрагентов.Владельцы. Это коллекция объектов метаданных, у неё есть метод Содержит().

МетаДоговоров = Метаданные.Справочники.ДоговорыКонтрагентов;
Если МетаДоговоров.Владельцы.Содержит(Метаданные.Справочники.Контрагенты) Тогда
	Сообщить("Договоры подчинены контрагентам");
КонецЕсли;

Такая проверка пригодится в универсальных обработках, которые работают с любым справочником и заранее не знают, есть ли у него владелец.

Свойство «Владелец» у ссылки на элемент справочника

Самый короткий способ — точка после ссылки. Платформа сама прочитает элемент из базы и вернёт значение реквизита.

&НаСервере
Функция КонтрагентПоДоговору(Договор)
	Если НЕ ЗначениеЗаполнено(Договор) Тогда
		Возврат Справочники.Контрагенты.ПустаяСсылка();
	КонецЕсли;
	Возврат Договор.Владелец;
КонецФункции

Результат — ссылка СправочникСсылка.Контрагенты. Через неё можно идти дальше по цепочке: Договор.Владелец.ИНН, Договор.Владелец.Наименование.

Что происходит «под капотом»:

  • 🔎 при первом обращении к реквизиту через ссылку платформа читает элемент целиком, а не одно поле;
  • ⏱ прочитанные данные кешируются на короткое время, повторное обращение в том же месте кода дешевле;
  • 🔁 каждая точка в цепочке Договор.Владелец.ОсновнойБанковскийСчет.Банк — ещё одно чтение из базы.

Для одного-двух элементов это нормально. В цикле по тысяче договоров такой код станет заметно медленным — там нужен запрос, о нём ниже.

Если в конфигурации есть БСП

В типовых конфигурациях на Библиотеке стандартных подсистем удобнее функция общего модуля:

Владелец = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Договор, "Владелец");

Она читает из базы только указанное поле, а не весь элемент. Для нескольких реквизитов сразу есть ЗначенияРеквизитовОбъекта(Ссылка, "Владелец, Наименование") — вернёт структуру. Обе функции серверные.

Через ПолучитьОбъект(): когда нужен объект, а не ссылка

Метод ПолучитьОбъект() у ссылки возвращает СправочникОбъект. У объекта тоже есть свойство Владелец, и его можно не только прочитать, но и изменить.

&НаСервере
Процедура ПеренестиДоговор(Договор, НовыйКонтрагент)
	ДоговорОбъект = Договор.ПолучитьОбъект();
	Если ДоговорОбъект = Неопределено Тогда
		Возврат; // элемент не найден в базе
	КонецЕсли;
	СтарыйВладелец = ДоговорОбъект.Владелец;
	ДоговорОбъект.Владелец = НовыйКонтрагент;
	ДоговорОбъект.Записать();
	Сообщить("Договор перенесён от " + СтарыйВладелец + " к " + НовыйКонтрагент);
КонецПроцедуры

Когда выбирать этот путь:

  • ✏️ нужно сменить владельца или поменять другие реквизиты вместе с ним;
  • 🧩 код уже работает с объектом: в модуле объекта, в обработчике ПередЗаписью, в форме элемента;
  • 🧪 нужно проверить заполнение владельца перед записью.

Внутри модуля объекта справочника к реквизиту обращаются без префикса: просто Владелец. В модуле формы элемента — через основной реквизит формы: Объект.Владелец. Это значение доступно и на клиенте, потому что лежит в данных формы.

Смена владельца у договора, по которому уже есть документы, ломает связку «контрагент документа — владелец договора». В типовых БП и УТ при проведении такие документы начнут ругаться на несоответствие договора контрагенту. Перед массовым переносом элементов сделайте резервную копию базы.

Новый подчинённый элемент: владельца указывают до записи

При создании элемента через менеджер владелец заполняется так же, как любой реквизит. Делать это нужно до вызова Записать():

&НаСервере
Функция СоздатьДоговор(Контрагент, Наименование)
	НовыйДоговор = Справочники.ДоговорыКонтрагентов.СоздатьЭлемент();
	НовыйДоговор.Владелец = Контрагент;
	НовыйДоговор.Наименование = Наименование;
	НовыйДоговор.Записать();
	Возврат НовыйДоговор.Ссылка;
КонецФункции

Если владельца забыть, элемент может записаться с пустым владельцем и «потеряется»: в списке договоров конкретного контрагента его не будет. В типовых конфигурациях у договоров есть свои проверки заполнения, но в самописных справочниках за этим следите сами — включите для стандартного реквизита Владелец проверку заполнения в конфигураторе.

Тот же порядок действует при копировании: метод Скопировать() переносит владельца из исходного элемента. Если копия нужна для другого контрагента, перезапишите Владелец до записи.

Если нужен только просмотр, ПолучитьОбъект() избыточен: он читает элемент со всеми табличными частями. Для чтения хватает ссылки или запроса.

Владелец справочника в запросе: разыменование и соединение

В запросе к владельцу обращаются как к обычному полю. Есть два варианта записи.

Разыменование через точку

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Договоры.Ссылка КАК Договор,
|	Договоры.Владелец КАК Контрагент,
|	Договоры.Владелец.ИНН КАК ИНН
|ИЗ
|	Справочник.ДоговорыКонтрагентов КАК Договоры
|ГДЕ
|	НЕ Договоры.ПометкаУдаления";

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	Сообщить(Строка(Выборка.Договор) + " — " + Выборка.Контрагент + ", ИНН " + Выборка.ИНН);
КонецЦикла;

Платформа сама построит соединение с таблицей контрагентов. Код короткий и читается легко.

Явное левое соединение

Запрос.Текст =
"ВЫБРАТЬ
|	Договоры.Ссылка КАК Договор,
|	Контрагенты.Ссылка КАК Контрагент,
|	Контрагенты.ИНН КАК ИНН
|ИЗ
|	Справочник.ДоговорыКонтрагентов КАК Договоры
|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
|		ПО Договоры.Владелец = Контрагенты.Ссылка";

Явное соединение полезно, когда из таблицы владельца нужно много полей или по ним стоит отбор. План запроса получается предсказуемым, а текст проще отлаживать.

Составной тип владельца

Если владельцев несколько, разыменование Владелец.ИНН заставит платформу соединиться со всеми таблицами-владельцами. На больших базах это тяжело. Сузьте тип явно:

|ВЫБРАТЬ
|	Счета.Ссылка,
|	ВЫРАЗИТЬ(Счета.Владелец КАК Справочник.Контрагенты).ИНН КАК ИНН
|ИЗ
|	Справочник.БанковскиеСчета КАК Счета
|ГДЕ
|	Счета.Владелец ССЫЛКА Справочник.Контрагенты

Оператор ССЫЛКА оставит только строки, где владелец — контрагент, а ВЫРАЗИТЬ уберёт лишние соединения.

Обратная задача: все элементы, подчинённые владельцу

Часто нужен не владелец, а наоборот: все договоры конкретного контрагента. Для этого есть три инструмента.

1. Запрос с отбором по владельцу — лучший вариант для списков:

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

2. Выборка менеджера справочника. У метода Выбрать() второй параметр — владелец:

Выборка = Справочники.ДоговорыКонтрагентов.Выбрать(, Контрагент);
Пока Выборка.Следующий() Цикл
	Сообщить(Выборка.Наименование);
КонецЦикла;

3. Поиск в пределах владельца. Методы НайтиПоНаименованию() и НайтиПоКоду() принимают владельца последним параметром:

Договор = Справочники.ДоговорыКонтрагентов.НайтиПоНаименованию("Основной договор", Истина, , Контрагент);

Без владельца поиск по наименованию вернёт первый попавшийся «Основной договор» любого контрагента. Это частая причина путаницы в обработках загрузки.

Отбор по владельцу в форме

Чтобы в поле «Договор» документа выбирались только договоры выбранного контрагента, в конфигураторе задайте полю свойство «Связи параметров выбора»: параметр Отбор.Владелец, реквизит — Объект.Контрагент. Кода не нужно.

Если список подчинённого справочника открывают из своего кода, отбор передают параметром формы:

ПараметрыФормы = Новый Структура("Отбор", Новый Структура("Владелец", Контрагент));
ОткрытьФорму("Справочник.ДоговорыКонтрагентов.ФормаСписка", ПараметрыФормы);

Как посмотреть владельца справочника в отладчике

Отладчик отвечает на вопрос «что сейчас лежит в реквизите» без правки кода.

  1. В конфигураторе поставьте точку останова (F9) на строке, где доступна ссылка или объект.
  2. Запустите отладку (F5) и выполните действие в режиме 1С:Предприятие.
  3. Когда выполнение остановится, выделите переменную и нажмите Shift+F9 — откроется окно «Выражение».
  4. Введите Договор.Владелец или Объект.Владелец и нажмите «Рассчитать».
  5. Чтобы узнать точный тип, введите ТипЗнч(Договор.Владелец).

Через то же окно удобно проверить цепочку целиком: Договор.Владелец.ИНН. Выражение можно добавить в табло, и оно будет пересчитываться на каждом шаге.

Когда ошибка возникает только у одного контрагента из сотни, помогает точка останова с условием. Щёлкните по уже установленной точке правой кнопкой, выберите изменение точки останова и впишите условие, например Выборка.Владелец.ИНН = "7701234567". Выполнение остановится только на нужной строке, а не на каждой итерации цикла.

Если остановка пришлась на клиентскую процедуру, разыменовать ссылку не получится — отладчик вычисляет выражение в том же контексте, что и код. Поставьте точку останова в серверном методе или смотрите Объект.Владелец из данных формы.

Какой способ обращения к владельцу выбрать

СпособКогда подходитМинусы
Ссылка.ВладелецОдин элемент, серверный кодЧитает весь элемент, медленно в цикле
ЗначениеРеквизитаОбъекта() (БСП)Типовая конфигурация, нужно одно-два поляЕсть только при наличии БСП
ПолучитьОбъект().ВладелецНужно изменить владельца и записатьСамый тяжёлый: читает табличные части
Запрос с ВладелецМного элементов, отчёты, отборыБольше кода
ВЫРАЗИТЬ + ССЫЛКАСоставной тип владельцаНужно знать нужный тип заранее
ОтладчикРазобраться, что в данныхНе для рабочего кода

Простое правило: один элемент — ссылка или БСП, коллекция — запрос, изменение — объект.

Пример из практики. Обработка выгружает 5 000 договоров с ИНН контрагентов. Вариант с Договор.Владелец.ИНН в цикле даёт до 5 000 отдельных чтений элементов из базы, вариант с запросом — одно обращение к базе. На файловой базе разница ощущается уже на сотнях строк, на серверной — по нагрузке на СУБД.

Типичные ошибки при работе с владельцем

Диагностика: выберите симптом

Ещё одна ловушка — пометка удаления. Если пометить на удаление владельца, платформа помечает и подчинённые ему элементы. Снятие пометки с контрагента не всегда очевидно для пользователя: договоры могут остаться помеченными, проверьте их отдельно.

Частые вопросы

Можно ли получить владельца владельца?
Да, цепочкой через точку: Элемент.Владелец.Владелец. Каждый уровень — отдельное чтение из базы, поэтому для коллекций лучше запрос.
Какой тип у пустого владельца?
Пустая ссылка справочника-владельца. Проверяйте её функцией ЗначениеЗаполнено(), а не сравнением с Неопределено.
Можно ли сделать владельцем документ или регистр?
Нет. Владельцами справочника в 1С 8.3 могут быть только другие справочники. Для связи с документом заводят обычный реквизит нужного типа.
Как узнать, подчинён ли справочник кому-нибудь, из кода?
Проверьте Метаданные.Справочники.ИмяСправочника.Владельцы.Количество() > 0. Если больше нуля — у справочника есть реквизит Владелец.
Влияет ли владелец на уникальность кода?
Да, если в свойстве «Серии кодов» выбран вариант «В пределах подчинения владельцу». Тогда у двух контрагентов могут быть договоры с одинаковым кодом.