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