Как автоматически добавить ссылку на один создаваемый документ в другой документ в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

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

Выясняем причину: почему Ссылка нового документа пустая?

Давайте проанализируем исходную ситуацию. При программном создании нового документа с помощью метода СоздатьДокумент() в оперативной памяти сервера выделяется область под новый объект типа ДокументОбъект. На этом этапе документ существует исключительно в памяти (в транзитивном состоянии) и еще не записан в базу данных (СУБД).

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

ТекДанные.СсылкаИзд = НовДок.Ссылка;

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

Давайте рассмотрим три основных способа решения этой задачи: от простого к профессиональному.

Способ 1. Принудительная запись нового документа (Простой подход)

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

Рассмотрим, как изменить серверную функцию создания документа, чтобы этот механизм заработал:


&НаСервере 
Функция СоздатьДокументНаСервере(ДанныеФормы, Идентификатор)
    // 1. Создаем объект нового документа в памяти
    НовДок = Документы.КонкретноеИзделиеВЗаказе.СоздатьДокумент();
    НовДок.Дата = ТекущаяДата();
    НовДок.НомерЗаказа = Объект.Номер;
    
    // 2. Заполняем табличную часть нового документа данными из строки родителя
    ТекДанные = Объект.Заказ.НайтиПоИдентификатору(Идентификатор);
    НовДок.КодИзделияВЗаказе = ТекДанные.КодИзделия;
    НовДок.НаименованиеИзделия = ТекДанные.НаименованиеИзделия;   
    
    РазИзд = НовДок.ОсновноеИзделие.Добавить();  
    РазИзд.Размер = ТекДанные.Размер;
    
    // 3. Записываем новый документ в базу данных для генерации Ссылки
    НовДок.Записать(РежимЗаписиДокумента.Запись);
    
    // 4. Теперь Ссылка заполнена, записываем ее в табличную часть родителя
    ТекДанные.СсылкаИзд = НовДок.Ссылка;
    
    // 5. Передаем данные обратно в форму для интерактивного открытия
    ЗначениеВДанныеФормы(НовДок, ДанныеФормы);
КонецФункции

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

Способ 2. Использование метода УстановитьСсылкуНового() (Оптимальный программный подход)

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

Этот метод позволяет разработчику самостоятельно сгенерировать уникальный идентификатор (GUID) на сервере, назначить его новому незаписанному документу и использовать эту ссылку для связывания объектов. Если документ в итоге будет записан пользователем, он сохранится именно с этим GUID. Если пользователь закроет форму без сохранения, транзакция записи не произойдет, и в базе данных не останется лишних записей.

Разберем по шагам реализацию этого механизма в коде:


&НаСервере 
Функция СоздатьДокументНаСервере(ДанныеФормы, Идентификатор)
    // 1. Создаем объект нового документа в памяти
    НовДок = Документы.КонкретноеИзделиеВЗаказе.СоздатьДокумент();
    НовДок.Дата = ТекущаяДата();
    НовДок.НомерЗаказа = Объект.Номер;
    
    // 2. Генерируем новую уникальную ссылку без записи в базу данных
    НоваяСсылка = Документы.КонкретноеИзделиеВЗаказе.ПолучитьСсылку(Новый УникальныйИдентификатор);
    
    // 3. Назначаем эту ссылку нашему новому документу
    НовДок.УстановитьСсылкуНового(НоваяСсылка);
    
    // 4. Заполняем данные документа
    ТекДанные = Объект.Заказ.НайтиПоИдентификатору(Идентификатор);
    НовДок.КодИзделияВЗаказе = ТекДанные.КодИзделия;
    НовДок.НаименованиеИзделия = ТекДанные.НаименованиеИзделия;   
    
    РазИзд = НовДок.ОсновноеИзделие.Добавить();  
    РазИзд.Размер = ТекДанные.Размер;
    
    // 5. Записываем еще не созданную в БД ссылку в строку родителя
    ТекДанные.СсылкаИзд = НоваяСсылка;
    
    // 6. Передаем подготовленный объект в форму
    ЗначениеВДанныеФормы(НовДок, ДанныеФормы);
КонецФункции

При таком подходе база данных не засоряется незаполненными объектами. Ссылка уже существует в строке табличной части документа-родителя, но сам подчиненный документ физически запишется в базу данных только тогда, когда пользователь нажмет кнопку «Записать и закрыть» в его форме.

Способ 3. Асинхронный событийный подход на клиенте (Современный стандарт UX)

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

Схема работы выглядит следующим образом:

  1. Из формы родительского документа открывается форма создания нового изделия. В параметры формы передаются нужные значения заполнения.
  2. Пользователь интерактивно заполняет форму нового документа и нажимает «Записать».
  3. В форме нового документа отрабатывает событие ПослеЗаписиНаСервере или ПослеЗаписи, которое посылает оповещение всей системе.
  4. Форма-родитель перехватывает это оповещение и автоматически подставляет готовую ссылку в нужную строку табличной части.

Реализуем этот механизм. Код в форме родительского документа («Индивидуальный заказ»):


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

&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    // Перехватываем событие успешной записи документа
    Если ИмяСобытия = "ЗаписанДокументИзделия" Тогда
        // Ищем строку по переданному ранее идентификатору
        СтрокаТЧ = Объект.Заказ.НайтиПоИдентификатору(Параметр.ИдентификаторСтроки);
        Если СтрокаТЧ <> Неопределено Тогда
            СтрокаТЧ.СсылкаИзд = Параметр.Ссылка;
            Модифицированность = Истина;
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Теперь напишем код в модуле формы подчиненного документа («Конкретное изделие в заказе»):


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Сохраняем идентификатор строки родителя во временном хранилище формы
    Если Параметры.Свойство("ЗначенияЗаполнения") Тогда
        Параметры.ЗначенияЗаполнения.Свойство("ИдентификаторСтрокиИсточника", ИдентификаторСтрокиИсточника);
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    // Если запись прошла успешно, посылаем оповещение владельцу
    ПараметрыОповещения = Новый Структура;
    ПараметрыОповещения.Вставить("Ссылка", Объект.Ссылка);
    ПараметрыОповещения.Вставить("ИдентификаторСтроки", ИдентификаторСтрокиИсточника);
    
    Оповестить("ЗаписанДокументИзделия", ПараметрыОповещения, ЭтаФорма);
КонецПроцедуры

Для реализации этого метода не забудьте создать реквизит формы ИдентификаторСтрокиИсточника (тип: Число) на форме подчиненного документа для временного хранения связи.

Архитектурный совет: Изменение направления связи

Проанализируем проектирование базы данных. Хранение ссылки на подчиненные документы в табличной части родительского документа («Индивидуальный заказ» хранит ссылки на «Конкретные изделия») часто считается ошибкой проектирования (антипаттерном) в среде 1С по следующим причинам:

  1. При каждом добавлении или изменении подчиненного изделия нам приходится перезаписывать и заново проводить родительский документ («Индивидуальный заказ»). Это вызывает блокировки таблиц базы данных и усложняет многопользовательскую работу.
  2. Нарушается логика последовательности проведения документов.

Рекомендованное решение: Направьте связь в обратную сторону. В подчиненном документе («Конкретное изделие в заказе») создайте реквизит шапки ДокументОснование с типом ссылки на родительский документ («Индивидуальный заказ»).

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

← На главную