Как обновить данные в открытой форме документа при изменении их из обработки

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

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

Анализ проблемы: почему данные не обновляются автоматически

Разберем типичную ситуацию. Программист открывает форму обработки, передает в неё ссылку на документ, на сервере выполняет метод ПолучитьОбъект(), меняет реквизиты и вызывает Записать(). Однако в открытой форме документа пользователь не видит изменений. Это происходит потому, что управляемая форма работает с собственной копией данных, которая находится в оперативной памяти (кэше) клиента. Изменения в базе данных (в таблицах SQL) никак не сигнализируют открытой форме о том, что её внутренние данные устарели. Более того, если пользователь нажмет "Записать" в самой форме документа после того, как обработка уже изменила объект в базе, возникнет ошибка "Объект изменен другим пользователем" (оптимистическая блокировка).

Решение 1: Работа с контекстом владельца формы (Рекомендуемый способ)

Самый эффективный и правильный способ обновить данные — это работать напрямую с контекстом формы, которая вызвала обработку. Для этого используется свойство ВладелецФормы. Рассмотрим алгоритм действий:

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

Проанализируем пример кода, который должен располагаться в форме обработки:


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

Важно помнить, что в этом случае метод Записать() вызывать не нужно. Пользователь сам решит, сохранить изменения или закрыть форму без сохранения. Это исключает риск потери данных, которые пользователь мог ввести в форму документа перед вызовом обработки.

Решение 2: Использование метода Прочитать()

Если логика обработки данных такова, что изменения все же вносятся напрямую в базу данных (в объект), нам нужно заставить форму владельца перечитать данные из базы. Для этого используется метод Прочитать().

Разберем ситуацию: обработка на сервере изменила документ и записала его. Чтобы форма увидела эти изменения, вызовем:


&НаКлиенте
Процедура ПослеЗаписиВБазу()
    Если ВладелецФормы <> Неопределено Тогда
        ВладелецФормы.Прочитать();
    КонецЕсли;
КонецПроцедуры

Важное предостережение: Метод Прочитать() полностью перезагружает данные формы из базы. Если пользователь начал заполнять какие-то поля в документе, но не сохранил их перед запуском обработки, все эти "черновые" изменения будут стерты данными из БД.

Решение 3: Сложные серверные расчеты с КопироватьДанныеФормы

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

Рассмотрим по шагам, как это реализовать:

  1. В форме обработки получаем структуру данных из ВладелецФормы.Объект.
  2. Передаем её на сервер.
  3. На сервере конвертируем данные формы в прикладной объект методом ДанныеФормыВЗначение.
  4. Выполняем расчеты.
  5. Конвертируем обратно методом ЗначениеВДанныеФормы.
  6. На клиенте обновляем форму владельца через КопироватьДанныеФормы.

Пример кода на сервере обработки:


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

На клиенте в обработке это будет выглядеть так:


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

Дополнительные инструменты: ОбновитьОтображениеДанных и Оповещения

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

Если же форма обработки и форма документа не связаны отношениями "Владелец", можно использовать механизм оповещений. Рассмотрим этот вариант:

В форме обработки:

Оповестить("ЗаполнениеЗавершено", Новый Структура("Результат", Истина), СсылкаНаДокумент);

В форме документа (в обработчике события ОбработкаОповещения):


&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
    Если ИмяСобытия = "ЗаполнениеЗавершено" И Источник = Объект.Ссылка Тогда
        ЭтотОбъект.Прочитать(); // Или другая логика обновления
    КонецЕсли;
КонецПроцедуры

Выводы и рекомендации

Подведем итоги нашего анализа. Для того чтобы данные в форме документа обновлялись корректно и без конфликтов:

Использование контекстного подхода не только решает проблему обновления интерфейса, но и делает работу пользователя более безопасной, предотвращая потерю несохраненных данных и конфликты версий объектов в многопользовательской среде.

← На главную