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