В практике разработки на платформе 1С:Предприятие 8.3 (управляемые формы) часто возникает задача: из формы одного документа (назовем его "Документ-основание") программно создать другой документ и тут же записать ссылку на него в реквизит первого. На первый взгляд задача кажется тривиальной, но начинающие разработчики часто сталкиваются с тем, что ссылка в форме появляется, но не сохраняется в базе данных при закрытии. Рассмотрим подробно, как правильно реализовать эту логику, разберем типичные ошибки и оптимальные методы работы с контекстом формы.
Представим ситуацию: у нас есть Документ1, в форме которого расположена кнопка "Выписать документ". При нажатии должен создаться Документ2, заполниться данными из первого документа, а ссылка на него должна вернуться в Документ1 и сохраниться в реквизите ДокументСсылка. Рассмотрим базовый код, который часто используют разработчики на начальном этапе:
&НаСервере
Функция ВыписатьДокументНаСервере()
// Создаем новый объект документа программно
НовыйДокумент = Документы.Расчет.СоздатьДокумент();
НовыйДокумент.Дата = ТекущаяДата();
НовыйДокумент.Организация = ЭтаФорма.Объект.Организация;
НовыйДокумент.Ответственный = Пользователи.ТекущийПользователь();
// Устанавливаем связь
НовыйДокумент.ДокументОснование = ЭтаФорма.Объект.Ссылка;
// Записываем новый документ в базу данных
НовыйДокумент.Записать();
// Присваиваем ссылку реквизиту текущей формы
ЭтаФорма.Объект.ДокументСсылка = НовыйДокумент.Ссылка;
Возврат НовыйДокумент.Ссылка;
КонецФункции
Проанализируем этот фрагмент. На стороне сервера документ создается и записывается успешно. Ссылка возвращается в ЭтаФорма.Объект.ДокументСсылка. Однако, если пользователь просто закроет форму "Документа1", изменения не попадут в базу данных. Почему это происходит? Потому что изменение Объект.ДокументСсылка происходит в памяти формы, но сам Документ1 в базе еще не обновлен.
Чтобы система "поняла", что данные в форме изменились и их нужно сохранить, необходимо использовать свойство Модифицированность. Рассмотрим, как изменится клиентская процедура:
&НаКлиенте
Процедура ВыписатьДокумент(Команда)
// Вызываем серверную функцию для создания документа
СсылкаНаДокумент = ВыписатьДокументНаСервере();
// Открываем созданный документ для пользователя
ПоказатьЗначение(, СсылкаНаДокумент);
// Устанавливаем флаг, что форма изменена
ЭтаФорма.Модифицированность = Истина;
// Обновляем интерфейс (если требуется)
УправлениеФормой(ЭтаФорма);
КонецПроцедуры
Важный момент: установка Модифицированность = Истина приведет к тому, что при попытке закрыть форму 1С задаст пользователю вопрос: "Данные были изменены. Сохранить изменения?". Это защищает от потери ссылки, но не гарантирует запись, если пользователь нажмет "Нет".
Если бизнес-логика требует, чтобы связь зафиксировалась в базе данных немедленно и без лишних вопросов пользователю, следует программно инициировать запись формы. Для этого в клиентском коде мы вызываем метод Записать().
&НаКлиенте
Процедура ВыписатьДокумент(Команда)
СсылкаНаДокумент = ВыписатьДокументНаСервере();
// Если ссылка получена успешно, записываем текущую форму
Если Не СсылкаНаДокумент.Пустая() Тогда
ПараметрыЗаписи = Новый Структура;
ЭтаФорма.Записать(ПараметрыЗаписи);
КонецЕсли;
ПоказатьЗначение(, СсылкаНаДокумент);
КонецПроцедуры
Этот подход гарантирует, что Документ1 будет сохранен в базе данных вместе с новой ссылкой сразу после выполнения серверной функции.
В приведенных выше примерах используется &НаСервере (контекстный вызов). Это означает, что на сервер передается вся форма целиком, что может замедлять работу при слабом интернет-соединении. Разберем, как сделать это профессионально через &НаСервереБезКонтекста. В этом случае мы передаем только необходимые данные.
&НаКлиенте
Процедура ВыписатьДокумент(Команда)
// Подготавливаем данные для создания
ДанныеДляЗаполнения = Новый Структура;
ДанныеДляЗаполнения.Вставить("Организация", Объект.Организация);
ДанныеДляЗаполнения.Вставить("Основание", Объект.Ссылка);
// Вызываем эффективный серверный метод
Результат = СоздатьДокументРасчетНаСервере(ДанныеДляЗаполнения);
// Обновляем реквизит формы на клиенте
Объект.ДокументСсылка = Результат;
Модифицированность = Истина;
ПоказатьЗначение(, Результат);
КонецПроцедуры
&НаСервереБезКонтекста
Функция СоздатьДокументРасчетНаСервере(Данные)
НовыйОбъект = Документы.Расчет.СоздатьДокумент();
ЗаполнитьЗначенияСвойств(НовыйОбъект, Данные);
НовыйОбъект.Дата = ТекущаяДата();
НовыйОбъект.ДокументОснование = Данные.Основание;
НовыйОбъект.Записать();
Возврат НовыйОбъект.Ссылка;
КонецФункции
Рассмотрим преимущества этого метода: мы используем функцию ЗаполнитьЗначенияСвойств, что значительно сокращает код и делает его более универсальным. Если в будущем в обоих документах появятся новые одинаковые реквизиты, они заполнятся автоматически.
Иногда нужно не просто создать документ в "фоне", но и дать пользователю возможность проверить его или дозаполнить перед сохранением. В этом случае лучше использовать метод ОткрытьФорму с параметром ЗначениеЗаполнения.
ОбработкаЗаполнения.
&НаКлиенте
Процедура ВыписатьИнтерактивно(Команда)
ПараметрыЗаполнения = Новый Структура("Основание", Объект.Ссылка);
ПараметрыФормы = Новый Структура("ЗначениеЗаполнения", ПараметрыЗаполнения);
// Открываем форму нового документа
// После того как пользователь нажмет "Записать", нам нужно получить ссылку назад
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗакрытияФормыСоздания", ЭтотОбъект);
ОткрытьФорму("Документ.Расчет.ФормаОбъекта", ПараметрыФормы, ЭтаФорма, , , , ОписаниеОповещения);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗакрытияФормыСоздания(Результат, ДополнительныеПараметры) Экспорт
// Если документ был создан и ссылка передана обратно
Если ТипЗнч(Результат) = Тип("ДокументСсылка.Расчет") Тогда
Объект.ДокументСсылка = Результат;
Модифицированность = Истина;
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию с точки зрения целостности данных. Если мы записываем Документ2 на сервере, но пользователь отменяет сохранение Документа1, у нас в базе появляется "сиротский" документ — Документ2, на который никто не ссылается из первичных документов.
Рекомендация: Для предотвращения таких ситуаций можно использовать механизм Оповещений. Когда Документ2 записывается (даже если он открыт в отдельном окне), он может посылать сигнал через Оповестить(). Форма Документа1 поймает это событие в обработчике ОбработкаОповещения и обновит ссылку. Это обеспечит актуальность данных без жесткой привязки к моменту создания.
Для успешного решения задачи связки документов необходимо:
Объектом (данными в базе) и ЭтаФорма.Объект (данными в памяти формы).Модифицированность = Истина, если данные формы меняются программно, чтобы система предложила их сохранить.ЗаполнитьЗначенияСвойств для минимизации ошибок при копировании реквизитов.&НаСервереБезКонтекста для оптимизации производительности приложения.ДокументСсылка в метаданных строго соответствует типу создаваемого документа (или является составным, включающим этот тип).Рассмотренные методы позволяют строить надежные цепочки документов, обеспечивая корректную работу пользователей и сохранность связей в базе данных 1С.