Как правильно программно создать элемент подчиненного справочника и указать его владельца в 1С

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет Промышленность, строительство и АПК
← На главную

При разработке сложных обработок по копированию данных или автоматическому созданию структур в 1С (особенно в таких конфигурациях, как 1С:ERP или 1С:Комплексная автоматизация), программисты часто сталкиваются с проблемой назначения владельца для подчиненных справочников. Типичный сценарий: мы копируем основную сущность (например, РесурснаяСпецификация) и одновременно хотим создать для неё «дочерние» записи (например, ЭтапыПроизводства).

В этой статье мы подробно разберем, почему возникает ошибка при попытке записи подчиненного элемента, как работает механизм ссылок в 1С и как правильно организовать код, чтобы все связи в базе данных сохранялись корректно.

Разбор проблемы: почему ссылка оказывается пустой

Рассмотрим ситуацию, с которой столкнулся автор темы. При программном копировании спецификации используется метод Скопировать(). Рассмотрим фрагмент кода:


СпецНовая = Выборка.Ссылка.Скопировать();
// ... изменение реквизитов новой спецификации ...
НовыйЭлемент = Справочники.ЭтапыПроизводства.СоздатьЭлемент();
НовыйЭлемент.Владелец = СпецНовая.Ссылка; 
НовыйЭлемент.Записать();

Проанализируем этот участок. Метод Скопировать() создает в оперативной памяти новый объект. Этот объект еще не существует в физических таблицах базы данных. Следовательно, свойство СпецНовая.Ссылка для этого объекта является «пустой ссылкой» соответствующего типа. Когда мы пытаемся присвоить эту пустую ссылку в поле Владелец подчиненного справочника и вызвать Записать(), система выдает ошибку, так как поле Владелец фактически остается незаполненным или заполненным некорректно.

Решение №1: Предварительная запись владельца

Самый простой и надежный способ — зафиксировать объект-владелец в базе данных перед тем, как создавать подчиненные элементы. Как только мы вызываем Записать() для спецификации, СУБД выделяет ей уникальный идентификатор (GUID), и свойство Ссылка перестает быть пустым.

Выясним, как должен выглядеть правильный алгоритм:

  1. Копируем объект-владелец (спецификацию) с помощью Скопировать().
  2. Заполняем необходимые реквизиты (наименование, номенклатуру, характеристики).
  3. Обязательно вызываем СпецНовая.Записать().
  4. Создаем подчиненный элемент (этап производства).
  5. Присваиваем НовыйЭлемент.Владелец = СпецНовая.Ссылка.
  6. Записываем подчиненный элемент.

Рассмотрим пример исправленного кода:


СпецНовая = Выборка.Ссылка.Скопировать();
// Устанавливаем новые значения
СпецНовая.Наименование = "Копия " + Выборка.Ссылка.Наименование;
// Записываем владельца, чтобы получить реальную ссылку
СпецНовая.Записать(); 

// Теперь приступаем к этапам
НовыйЭлемент = Справочники.ЭтапыПроизводства.СоздатьЭлемент();
НовыйЭлемент.Наименование = "Этап 1";
НовыйЭлемент.Владелец = СпецНовая.Ссылка; // Теперь здесь не пустая ссылка!

// В ERP часто есть доп. реквизит, дублирующий владельца
НовыйЭлемент.Спецификация = СпецНовая.Ссылка; 

НовыйЭлемент.Записать();

Решение №2: Использование метода УстановитьСсылкуНового()

Иногда логика приложения требует, чтобы вся структура (спецификация и её этапы) записывалась «одним махом» внутри транзакции, и мы не хотим записывать спецификацию заранее, пока не подготовим все данные. В 1С предусмотрен механизм предварительного получения ссылки.

Мы можем использовать метод УстановитьСсылкуНового(). Это позволяет нам вручную или автоматически сгенерировать ссылку для объекта еще до того, как он попадет в базу данных. Посмотрим на пример:


СпецНовая = Справочники.РесурсныеСпецификации.СоздатьЭлемент();
// Генерируем новую уникальную ссылку
НоваяСсылка = Справочники.РесурсныеСпецификации.ПолучитьСсылку(Новый УникальныйИдентификатор());
СпецНовая.УстановитьСсылкуНового(НоваяСсылка);

// Теперь мы можем использовать НоваяСсылка как владельца, 
// хотя сама СпецНовая еще не записана!
НовыйЭтап = Справочники.ЭтапыПроизводства.СоздатьЭлемент();
НовыйЭтап.Владелец = НоваяСсылка;
НовыйЭтап.Записать();

// В конце записываем саму спецификацию
СпецНовая.Записать();

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

Особенности работы в 1С:ERP и Комплексной автоматизации

Проанализируем ошибку, упомянутую в сообщении №10: «Значение поля "Входит в спецификацию" не заполнено». В прикладных решениях на базе ERP справочник ЭтапыПроизводства имеет сложную структуру. Кроме стандартного системного поля Владелец, разработчики фирмы «1С» часто добавляют дополнительные реквизиты, которые обязательны для заполнения.

В справочнике этапов таким реквизитом обычно является Спецификация или ВходитВСпецификацию. При программном создании элемента проверьте следующее:

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

Копирование подчиненных структур в цикле

Когда мы копируем спецификацию, нам обычно нужно перенести не один новый этап, а все те этапы, которые были у исходного документа. Для этого проанализируем ситуацию и составим цикл обхода исходных данных.

Посмотрим на пример реализации полного копирования этапов:


// Предположим, ИсточникСпец - это ссылка на оригинал
СпецНовая = ИсточникСпец.Скопировать();
СпецНовая.Записать();

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

Пока ВыборкаЭтапов.Следующий() Цикл
    // Копируем каждый этап
    НовыйЭтапОбъект = ВыборкаЭтапов.Ссылка.Скопировать();
    // Подменяем владельца на новую спецификацию
    НовыйЭтапОбъект.Владелец = СпецНовая.Ссылка;
    // Не забываем про специфический реквизит ERP
    НовыйЭтапОбъект.Спецификация = СпецНовая.Ссылка;
    
    НовыйЭтапОбъект.Записать();
КонецЦикла;

Совет: При массовом копировании реквизитов этапа (длительность, подразделение, статья затрат) лучше использовать метод ЗаполнитьЗначенияСвойств(НовыйЭтапОбъект, ВыборкаЭтапов.Ссылка.ПолучитьОбъект()). Это гарантирует, что вы не забудете перенести важные настройки бизнес-логики, которые могут быть добавлены в новых релизах конфигурации.

Заключение и рекомендации

Подведем итог нашему разбору. Для успешного указания владельца подчиненного справочника необходимо соблюдать правила объектной модели 1С:

  1. Объект != Ссылка. Ссылка появляется только после физической записи объекта в базу (или принудительной установки через УстановитьСсылкуНового).
  2. Соблюдайте последовательность. Сначала создаем «родителя», записываем его, и только потом создаем «детей», передавая им полученную ссылку родителя.
  3. Проверяйте состав реквизитов. В типовых решениях (ERP/УТ/КА) поле Владелец — не единственное, что нужно заполнить для установления связи. Всегда проверяйте наличие реквизитов типа СправочникСсылка, указывающих на основной объект.
  4. Транзакционность. Если вы выполняете копирование в цикле, оберните весь процесс в конструкцию НачатьТранзакцию() ... ЗафиксироватьТранзакцию(). Это предотвратит появление «сиротских» записей в базе данных, если в середине процесса произойдет ошибка.

Применяя эти подходы, вы сможете создавать надежные механизмы автоматизации, которые корректно работают с иерархическими данными и не вызывают ошибок целостности базы данных.

← На главную