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