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