Работа с базой 1С:Предприятие 7.7 через Automation (OLE) часто преподносит сюрпризы даже опытным разработчикам. Одной из самых распространенных задач является перегрузка данных, где необходимо создать элемент справочника (например, Контрагенты) и сразу же создать для него подчиненный элемент (например, Договоры). В этой статье мы подробно разберем, почему возникает ошибка «Не выбран элемент справочника-хозяина!» и как правильно организовать последовательность действий в коде.
Рассмотрим типичную ситуацию. Программист пытается создать контрагента, заполняет его реквизиты, записывает, а затем сразу переходит к созданию договора. Однако при попытке вызвать метод Записать() для договора система выдает исключительную ситуацию. Основная причина кроется в специфике работы платформы 7.7: для подчиненного справочника недостаточно просто присвоить ссылку на владельца в реквизит. Необходимо явно указать контексту объекта, с каким владельцем он будет работать, еще до момента создания новой записи или в процессе её инициализации.
Проанализируем фрагмент кода, с которым столкнулся автор темы. В первой редакции кода была попытка установить владельца напрямую:
ДогОЛЕ.Владелец = КонтрОЛЕ.ТекущийЭлемент();
ДогОЛЕ.Записать();
В среде 1С 7.7 такой подход часто не срабатывает через OLE, так как объект справочника требует вызова специального метода ИспользоватьВладельца(). Кроме того, часто встречается избыточное использование метода ТекущийЭлемент(). Рассмотрим основные моменты, которые требуют исправления:
КонтрагентОЛЕ.ОсновнойДоговор.ТекущийЭлемент() может привести к ошибке, если ОсновнойДоговор пуст. В 7.7 лучше проверять само значение или использовать Выбран().Новый() должен вызываться в правильном контексте относительно установленного владельца.Неопределено при работе с OLE-объектами 7.7 из внешней среды (например, из 1С 8.3) некорректно, так как OLE-объект всегда является объектом, даже если он не позиционирован на конкретной записи.Разберем по шагам, как должна выглядеть надежная процедура создания связки «Контрагент — Договор».
Шаг 1. Создание и запись владельца. Сначала мы должны создать объект Контрагенты, заполнить его данные и обязательно вызвать Записать(). Без физической записи в базу данных у объекта не появится ссылка (уникальный идентификатор), которую можно передать в качестве владельца договору.
Шаг 2. Позиционирование на созданном объекте. После записи важно убедиться, что КонтрОЛЕ.ТекущийЭлемент() возвращает валидную ссылку.
Шаг 3. Установка связи для подчиненного объекта. Перед вызовом метода Новый() для договора необходимо вызвать метод ИспользоватьВладельца().
Шаг 4. Создание договора и обратная связь. Если в конфигурации предусмотрен реквизит ОсновнойДоговор в справочнике Контрагенты, нам придется записать контрагента второй раз, чтобы зафиксировать в нем ссылку на только что созданный договор.
Посмотрим на пример кода, который решает поставленную задачу корректно:
// Создаем объекты справочников
КонтрОЛЕ = ОбъектV77.CreateObject("Справочник.Контрагенты");
ДогОЛЕ = ОбъектV77.CreateObject("Справочник.Договоры");
// Проверяем, существует ли контрагент (например, по ИНН)
Если КонтрОЛЕ.НайтиПоРеквизиту("ИНН", СокрЛП(Контрагент.ИНН), 1) = 0 Тогда
// Если не нашли, создаем нового
КонтрОЛЕ.Новый();
КонтрОЛЕ.Наименование = СокрЛП(Контрагент.Наименование);
КонтрОЛЕ.ИНН = СокрЛП(Контрагент.ИНН);
// Записываем, чтобы получить ссылку для владельца
КонтрОЛЕ.Записать();
КонецЕсли;
// Теперь работаем с договором
// Устанавливаем владельца ПЕРЕД созданием записи
ДогОЛЕ.ИспользоватьВладельца(КонтрОЛЕ.ТекущийЭлемент());
// Проверяем наличие договора (например, по наименованию у этого владельца)
Если ДогОЛЕ.НайтиПоНаименованию(СокрЛП(Договор.Наименование), 0, 1) = 0 Тогда
ДогОЛЕ.Новый();
ДогОЛЕ.Наименование = СокрЛП(Договор.Наименование);
// Если нужно установить валюту
Если ВалютаОЛЕ <> 0 Тогда
ДогОЛЕ.Валюта = ВалютаОЛЕ;
КонецЕсли;
ДогОЛЕ.Записать();
КонецЕсли;
// Устанавливаем созданный договор основным для контрагента
// Обратите внимание: работаем напрямую через объект справочника Контрагенты
КонтрОЛЕ.ОсновнойДоговор = ДогОЛЕ.ТекущийЭлемент();
КонтрОЛЕ.Записать();
Проанализируем несколько технических моментов, которые помогут избежать ошибок в будущем:
1. Метод ТекущийЭлемент()
В 1С 7.7 ТекущийЭлемент() возвращает ссылку на объект. Когда вы работаете внутри самой программы, платформа часто прощает отсутствие этого вызова. Однако при передаче данных через OLE-интерфейс вы передаете COM-объект. Чтобы 1С поняла, что вы хотите передать именно ссылку на конкретную запись в базу данных, использование ТекущийЭлемент() становится обязательным при присваивании значений ссылочным реквизитам.
2. ИспользоватьВладельца()
Этот метод не просто заполняет реквизит Владелец. Он накладывает фильтр на выборку справочника. Если вы не вызовете его, система при попытке записи метода Новый() не будет знать, к кому «прицепить» этот элемент, даже если вы попытаетесь заполнить атрибут Владелец вручную.
3. Проблема «Курицы и яйца»
Ситуация, когда Контрагенту нужен Договор, а Договору — Контрагент, решается только двойной записью владельца.
Не бойтесь лишнего вызова Записать(), в данном контексте это единственный надежный способ.
4. Обработка пустых значений
Вместо сравнения с Неопределено, которое характерно для 1С 8.х, при работе с 1С 7.7 рекомендуется использовать проверку через V77.EvalExpr("ПустоеЗначение(Значение)") или проверять Выбран(). Это позволит избежать ложных срабатываний кода в условиях, когда поиск элемента не дал результатов.
Мы выяснили причину ошибки при создании договора и рассмотрели правильную архитектуру кода. Главное правило при работе с подчиненными справочниками в 1С 7.7 через OLE: сначала установите владельца методом ИспользоватьВладельца(), и только потом создавайте и записывайте новый элемент. Соблюдение этой последовательности гарантирует стабильную работу ваших интеграционных механизмов.