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