Как при создании нового документа из формы вернуть и сохранить ссылку в исходном документе?

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

В практике разработки на платформе 1С:Предприятие 8.3 (управляемые формы) часто возникает задача: из формы одного документа (назовем его "Документ-основание") программно создать другой документ и тут же записать ссылку на него в реквизит первого. На первый взгляд задача кажется тривиальной, но начинающие разработчики часто сталкиваются с тем, что ссылка в форме появляется, но не сохраняется в базе данных при закрытии. Рассмотрим подробно, как правильно реализовать эту логику, разберем типичные ошибки и оптимальные методы работы с контекстом формы.

Анализ исходной задачи и типичные ошибки

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


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

Проанализируем этот фрагмент. На стороне сервера документ создается и записывается успешно. Ссылка возвращается в ЭтаФорма.Объект.ДокументСсылка. Однако, если пользователь просто закроет форму "Документа1", изменения не попадут в базу данных. Почему это происходит? Потому что изменение Объект.ДокументСсылка происходит в памяти формы, но сам Документ1 в базе еще не обновлен.

Решение 1: Установка признака модифицированности

Чтобы система "поняла", что данные в форме изменились и их нужно сохранить, необходимо использовать свойство Модифицированность. Рассмотрим, как изменится клиентская процедура:


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

Важный момент: установка Модифицированность = Истина приведет к тому, что при попытке закрыть форму 1С задаст пользователю вопрос: "Данные были изменены. Сохранить изменения?". Это защищает от потери ссылки, но не гарантирует запись, если пользователь нажмет "Нет".

Решение 2: Принудительная запись основного документа

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


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

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

Решение 3: Оптимизация через бесконтекстный вызов

В приведенных выше примерах используется &НаСервере (контекстный вызов). Это означает, что на сервер передается вся форма целиком, что может замедлять работу при слабом интернет-соединении. Разберем, как сделать это профессионально через &НаСервереБезКонтекста. В этом случае мы передаем только необходимые данные.


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

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

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

Работа с модальностью и интерактивным открытием

Иногда нужно не просто создать документ в "фоне", но и дать пользователю возможность проверить его или дозаполнить перед сохранением. В этом случае лучше использовать метод ОткрытьФорму с параметром ЗначениеЗаполнения.

  1. В Документе2 в модуле объекта прописываем процедуру ОбработкаЗаполнения.
  2. В Документе1 вызываем открытие формы.

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

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

Проблема транзакционности и "сиротских" записей

Проанализируем ситуацию с точки зрения целостности данных. Если мы записываем Документ2 на сервере, но пользователь отменяет сохранение Документа1, у нас в базе появляется "сиротский" документ — Документ2, на который никто не ссылается из первичных документов.

Рекомендация: Для предотвращения таких ситуаций можно использовать механизм Оповещений. Когда Документ2 записывается (даже если он открыт в отдельном окне), он может посылать сигнал через Оповестить(). Форма Документа1 поймает это событие в обработчике ОбработкаОповещения и обновит ссылку. Это обеспечит актуальность данных без жесткой привязки к моменту создания.

Выводы

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

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

← На главную