Как заполнить табличную часть документа из дополнительной формы в 1С 8.3 Управляемые формы

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

При разработке интерфейсов на платформе 1С:Предприятие 8.3 (Управляемые формы) часто возникает задача реализации вспомогательных форм. Это могут быть формы подбора номенклатуры, калькуляторы услуг, мастера импорта данных или различные параметрические помощники. Главный вопрос, с которым сталкиваются разработчики при проектировании такого функционала: как корректно передать заполненную табличную часть из дополнительной формы назад в основную форму документа?

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

Почему запись объекта напрямую в базу данных — это плохая практика?

Начинающие разработчики часто пытаются решить задачу «в лоб», как это предложено в сообщении 1 обсуждения форума:

  1. Передать ссылку на документ в дополнительную форму.
  2. В дополнительной форме получить объект документа на сервере через метод ПолучитьОбъект().
  3. Заполнить его табличную часть новыми строками.
  4. Записать измененный объект в базу данных с помощью метода Записать().
  5. Обновить основную форму, перечитав данные из базы.

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

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

---

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

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

Поскольку тип данных ТаблицаЗначений является мутабельным и существует только на сервере, напрямую передать его через клиентскую процедуру Закрыть() нельзя. Мы обойдем это ограничение, преобразовав строки нашей таблицы в массив простых структур на клиенте.

Шаг 1. Открытие дополнительной формы из основной формы документа

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


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

Шаг 2. Подготовка и передача данных при закрытии дополнительной формы

В дополнительной форме (например, при нажатии на кнопку «ОК» или «Перенести в документ») мы обходим заполненную табличную часть, упаковываем ее в массив структур и передаем в метод Закрыть():


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

Шаг 3. Прием данных в процедуре оповещения основной формы

В модуле основной формы должна быть объявлена экспортная процедура, имя которой мы указали при создании ОписаниеОповещения:


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

---

Способ 2. Использование Временного хранилища (Для больших таблиц данных)

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

Шаг 1. Перенос данных во временное хранилище в модуле дополнительной формы

При нажатии кнопки «Перенести» мы вызываем серверный контекст дополнительной формы, выгружаем ТЧ в полноценную ТаблицаЗначений, помещаем ее во временное хранилище и возвращаем на клиент только строковый адрес-идентификатор:


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

&НаКлиенте
Процедура ПеренестиИЗакрытьЧерезХранилище(Команда)
    
    АдресДанных = ПоместитьДанныеВХранилищеНаСервере();
    
    // Закрываем форму, возвращая только строковый адрес во временном хранилище
    Закрыть(АдресДанных);
    
КонецПроцедуры

Шаг 2. Загрузка данных из временного хранилища в модуле основной формы

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


&НаКлиенте
Процедура ПослеЗакрытияДопФормыХранилище(РезультатЗакрытия, ДополнительныеПараметры) Экспорт
    
    Если ЗначениеЗаполнено(РезультатЗакрытия) Тогда
        ЗагрузитьДанныеИзХранилищаНаСервере(РезультатЗакрытия);
    КонецЕсли;
    
КонецПроцедуры

&НаСервере
Процедура ЗагрузитьДанныеИзХранилищаНаСервере(АдресХранилища)
    
    // Извлекаем таблицу значений из оперативной памяти сервера приложений
    ТаблицаИзХранилища = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    Если ТипЗнч(ТаблицаИзХранилища) = Тип("ТаблицаЗначений") Тогда
        
        // Быстро загружаем данные в табличную часть документа
        // Метод Загрузить() очищает старые данные. 
        // Если нужно добавить к существующим — используйте обход в цикле.
        Объект.Товары.Загрузить(ТаблицаИзХранилища);
        
    КонецЕсли;
    
КонецПроцедуры

---

Способ 3. Прямой вызов экспортного метода Владельца формы

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

Шаг 1. Открытие формы с указанием владельца


&НаКлиенте
Процедура ОткрытьФормуЧерезВладельца(Команда)
    
    // Третий параметр метода ОткрытьФорму — это Владелец. Передаем "ЭтаФорма".
    ОткрытьФорму("Документ.РеализацияТоваровУслуг.Форма.ДополнительнаяФорма", , ЭтаФорма);
    
КонецПроцедуры

Шаг 2. Объявление экспортного метода в основной форме

В модуле основной формы создаем клиентскую процедуру с ключевым словом Экспорт:


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

Шаг 3. Вызов метода владельца из дополнительной формы

По кнопке переноса данных в дополнительной форме мы вызываем этот метод и закрываем окно:


&НаКлиенте
Процедура ПеренестиКВладельцу(Команда)
    
    МассивДанных = ПодготовитьДанныеДляПереноса(); // возвращает массив структур
    
    // Проверяем доступность владельца и вызываем его экспортную процедуру
    Если ВладелецФормы <> Неопределено Тогда
        ВладелецФормы.ДобавитьСтрокиИзДополнительнойФормы(МассивДанных);
    КонецЕсли;
    
    Закрыть();
    
КонецПроцедуры

---

Способ 4. Стандартный механизм интерактивного выбора (ОповеститьОВыборе)

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

В дополнительной форме достаточно вызвать один метод:


&НаКлиенте
Процедура ВыполнитьВыбор(Команда)
    
    МассивДанных = ПодготовитьДанныеДляПереноса();
    
    // Оповещаем форму-владельца о совершенном выборе
    ОповеститьОВыборе(МассивДанных);
    
КонецПроцедуры

В основной форме при этом не нужно писать никаких обработчиков оповещений при открытии. Достаточно определить стандартное событие формы ОбработкаВыбора:


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

---

Решение проблемы обновления интерфейса (Ответ на Сообщение 7)

Автор исходной темы на форуме в сообщении 7 столкнулся с проблемой: данные в табличную часть добавлены кодом, но на экране пользователя они не появились. Выясним причину этой проблемы и рассмотрим способы её решения.

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

Если этого не произошло, скорее всего, были допущены следующие ошибки:

  1. Данные были записаны напрямую в базу данных на сервере, минуя контекст формы (в этом случае форма просто "не знает", что в базе данных что-то поменялось). Решение — использовать описанные выше Способы 1–4 вместо прямой записи объекта.
  2. Данные были добавлены в ТЧ на сервере в контексте формы, но интерфейс по какой-то причине не перерисовал изменения (это редкое поведение, иногда встречающееся при сложных условных оформлениях или использовании связанных динамических списков).

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

ЭтаФорма.ОбновитьОтображениеДанных();

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

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

← На главную