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

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

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

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

Выясняем причину: почему классический код не работает на управляемых формах?

В обычных формах (1С 8.1 и «обычный» режим 8.2) код внешней обработки работал напрямую с объектом документа, который был непосредственно связан с экранной формой. Любые изменения в табличной части мгновенно отображались на экране.

В управляемом приложении (1С 8.2 и 8.3) архитектура построена иначе. На клиенте мы работаем не с самим прикладным объектом базы данных, а с его программной проекцией — типом ДанныеФормыСтруктура (доступным через свойство формы Объект). Когда мы вызываем команду внешней обработки заполнения, происходит следующее:

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

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

Давайте разберем три эффективных способа решения этой задачи.

Способ 1. Принудительное копирование данных формы (Самый простой вариант)

Если ваша внешняя обработка вызывается интерактивно и у вас есть доступ к свойству ВладелецФормы, наиболее простым решением будет использование платформенного метода КопироватьДанныеФормы. Этот метод выполняет глубокое копирование измененной структуры данных обратно в контекст формы-владельца.

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


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

&НаСервереБезКонтекста
Процедура ПолучитьОстаткиНаСервере(ДокОб)
    
    // Запрос для получения актуальных остатков номенклатуры на складе
    Запрос = Новый Запрос(
    "ВЫБРАТЬ
    |    ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
    |    ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Количество
    |ИЗ
    |    РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатков, Склад = &Склад) КАК ТоварыНаСкладахОстатки");
    
    Запрос.УстановитьПараметр("ДатаОстатков", Новый Граница(ДокОб.Дата, ВидГраницы.Включая));
    Запрос.УстановитьПараметр("Склад", ДокОб.Склад);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        СтрокаДок = ДокОб.Товары.Добавить();
        СтрокаДок.Номенклатура = Выборка.Номенклатура;
        СтрокаДок.Количество = Выборка.Количество;
    КонецЦикла; 
    
КонецПроцедуры

Использование метода КопироватьДанныеФормы позволяет мгновенно синхронизировать интерфейс открытого документа с измененной во внешней обработке коллекцией данных.

Способ 2. Использование штатного механизма БСП и методов преобразования данных

Если ваша конфигурация построена на базе Библиотеки Стандартных Подсистем (БСП) — к таким относятся Управление торговлей, ERP, Комплексная автоматизация, УНФ и другие, — то правильным архитектурным решением будет использование штатного механизма БСП.

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

Разберем по шагам, как написать код в модуле объекта внешней обработки:


// Процедура вызывается механизмом БСП при выполнении команды заполнения формы
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначения, ПараметрыВыполнения) Экспорт
    
    // Получаем доступ к форме документа из параметров БСП
    ФормаДокумента = ПараметрыВыполнения.ЭтаФорма;
    
    // Преобразуем ДанныеФормы структуры в реальный прикладной объект ДокументОбъект для удобной работы
    ДокументОбъект = ДанныеФормыВЗначение(ФормаДокумента.Объект, Тип("ДокументОбъект.РеализацияТоваровУслуг"));
    
    // Очищаем табличную часть
    ДокументОбъект.Товары.Очистить();
    
    // Выполняем запрос к базе данных
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ТоварыНаСкладахОстатки.Номенклатура,
        |    ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Количество
        |ИЗ
        |    РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК ТоварыНаСкладахОстатки";
        
    Запрос.УстановитьПараметр("Дата", ДокументОбъект.Дата);
    Запрос.УстановитьПараметр("Склад", ДокументОбъект.Склад);
    
    РезультатЗапроса = Запрос.Выполнить();
    Если Не РезультатЗапроса.Пустой() Тогда
        ДокументОбъект.Товары.Загрузить(РезультатЗапроса.Выгрузить());
    КонецЕсли;
    
    // КЛЮЧЕВОЙ МОМЕНТ: Преобразуем измененный прикладной объект обратно в данные управляемой формы
    ЗначениеВДанныеФормы(ДокументОбъект, ФормаДокумента.Объект);
    
    // Помечаем форму как измененную (модифицированную)
    ФормаДокумента.Модифицированность = Истина;
    
КонецПроцедуры

Преимущество этого метода заключается в том, что использование функций ДанныеФормыВЗначение и ЗначениеВДанныеФормы гарантирует корректное прохождение всех платформенных проверок и автозаполнение связанных реквизитов формы. Это наиболее стабильный и «красивый» с точки зрения стандартов разработки 1С метод.

Способ 3. Оптимизированный перенос данных через Временное хранилище

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

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


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

&НаСервереБезКонтекста
Функция ПолучитьТаблицуОстатковНаСервере(Склад, ДатаОстатков)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |    ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
        |    ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Количество
        |ИЗ
        |    РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК ТоварыНаСкладахОстатки";
        
    Запрос.УстановитьПараметр("Дата", Новый Граница(ДатаОстатков, ВидГраницы.Включая));
    Запрос.УстановитьПараметр("Склад", Склад);
    
    ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
    
    // Помещаем таблицу во временное хранилище и возвращаем ее адрес
    Возврат ПоместитьВоВременноеХранилище(ТаблицаРезультата);
    
КонецФункции

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

Подводим итоги

Мы проанализировали три различных подхода к решению проблемы обновления управляемой формы при заполнении табличной части из внешней обработки. Каждый из методов имеет свои сценарии применения:

Выбирайте наиболее подходящий под ваши задачи инструмент, пишите производительный код и делайте интерфейсы ваших решений на платформе 1С удобными для пользователей!

← На главную