Как обновить табличную часть документа на форме внешней обработки после его изменения?

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

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

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

Почему данные на форме «зависают»?

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

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


Решение 1. Использование глобального метода ОповеститьОбИзменении()

Это самый быстрый и простой способ, который идеально подходит в случаях, когда табличная часть на форме отображается через стандартные механизмы динамических списков или связана с реквизитами формы типа ДокументСсылка.

Метод ОповеститьОбИзменении() является клиентским. Он посылает системе сигнал о том, что конкретный объект базы данных (переданный в качестве параметра) был изменен. Платформа улавливает это событие, сбрасывает локальный кэш этого объекта и автоматически обновляет все открытые формы, в которых отображается данный объект.

Рассмотрим пример реализации этого подхода по шагам:


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

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

Преимущества метода: Требует написания всего одной строки кода на клиенте. Отлично работает для связанных стандартных списков и форм самого документа.

Ограничения: Если табличная часть на форме вашей внешней обработки — это несвязанная произвольная Таблица Значений, которую вы заполняли вручную при выборе документа (например, через запрос), то метод ОповеститьОбИзменении() сам по себе ее не обновит. Для таких случаев потребуются другие подходы.


Решение 2. Использование связки РеквизитФормыВЗначение и ЗначениеВРеквизитФормы

Если вы проектируете внешнюю обработку так, что её основным реквизитом (или одним из реквизитов формы) является сам прикладной объект типа ДокументОбъект.ИмяДокумента, и именно его табличная часть размещена на форме обработки, то лучшим решением будет использование стандартного механизма сериализации управляемых форм.

Давайте разберем этот процесс по шагам на сервере:

  1. Конвертируем данные формы (реквизит) в реальный прикладной объект базы данных с помощью функции РеквизитФормыВЗначение(). Это позволит нам получить полноценный доступ ко всем свойствам, табличным частям и методам документа.
  2. Проводим все необходимые манипуляции с табличной частью объекта в коде.
  3. Записываем объект в базу данных (если это требуется бизнес-логикой).
  4. Конвертируем измененный объект обратно в реквизит формы с помощью процедуры ЗначениеВРеквизитФормы(). В этот момент платформа автоматически синхронизирует и перерисует все элементы интерфейса на форме.

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


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

Этот метод считается наиболее надежным и «чистым» с точки зрения стандартов разработки фирмы «1С», так как он полностью управляет контекстом формы и исключает рассинхронизацию отображаемых данных.


Решение 3. Ручное обновление кастомной Таблицы Значений запросом

Довольно часто разработчики не выводят весь объект на форму внешней обработки, а используют для отображения обычную Таблицу Значений (реквизит формы с типом ТаблицаЗначений), данные в которую загружаются при помощи SQL-запроса при выборе документа.

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


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

&НаСервере
Процедура ВыполнитьОбновлениеДанныхНаСервере()
    
    // 1. Изменяем данные в базе данных
    ДокументОбъект = Объект.ДокументСсылка.ПолучитьОбъект();
    Для Каждого СтрокаТЧ Из ДокументОбъект.Товары Цикл
        СтрокаТЧ.НужныйПризнак = Истина;
    КонецЦикла;
    ДокументОбъект.Записать();
    
    // 2. Считываем измененные данные запросом для обновления формы
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ДокументТовары.НомерСтроки КАК НомерСтроки,
        |   ДокументТовары.Номенклатура КАК Номенклатура,
        |   ДокументТовары.Количество КАК Количество,
        |   ДокументТовары.НужныйПризнак КАК НужныйПризнак
        |ИЗ
        |   Документ.РеализацияТоваровУслуг.Товары КАК ДокументТовары
        |ГДЕ
        |   ДокументТовары.Ссылка = &Ссылка";
        
    Запрос.УстановитьПараметр("Ссылка", Объект.ДокументСсылка);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    // 3. Очищаем старые данные в таблице на форме
    // "ТаблицаТоваровНаФорме" - это имя вашего реквизита формы типа ТаблицаЗначений
    ЭтаФорма.ТаблицаТоваровНаФорме.Очистить();
    
    // 4. Загружаем новые актуальные данные из результата запроса
    ЭтаФорма.ТаблицаТоваровНаФорме.Загрузить(РезультатЗапроса.Выгрузить());
    
КонецПроцедуры

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


Решение 4. Взаимодействие через механизмы оповещения (для независимых форм)

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

Для реализации такой связки мы можем применить механизм оповещений управляемых форм 1С:

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

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


Резюме и рекомендации

Мы детально изучили проблему обновления табличных частей на управляемых формах 1С при работе с внешними обработками. Подводя итог, выделим ключевые правила выбора метода решения:

← На главную