Переход с обычных форм на управляемые в платформе 1С:Предприятие 8 принес разработчикам множество преимуществ, но вместе с тем и новые архитектурные вызовы. Одна из самых распространенных проблем, с которой сталкиваются программисты при написании внешних обработок заполнения табличных частей (ТЧ), — это отсутствие визуального обновления данных на форме документа после работы алгоритма заполнения. Вы программируете заполнение ТЧ, данные в объекте меняются, но пользователь на экране видит пустую таблицу.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем внутренние механизмы платформы 1С и рассмотрим три надежных и проверенных способа обновления управляемой формы без необходимости принудительной записи документа в базу данных.
В обычных формах (1С 8.1 и «обычный» режим 8.2) код внешней обработки работал напрямую с объектом документа, который был непосредственно связан с экранной формой. Любые изменения в табличной части мгновенно отображались на экране.
В управляемом приложении (1С 8.2 и 8.3) архитектура построена иначе. На клиенте мы работаем не с самим прикладным объектом базы данных, а с его программной проекцией — типом ДанныеФормыСтруктура (доступным через свойство формы Объект). Когда мы вызываем команду внешней обработки заполнения, происходит следующее:
ДокОб.Методы ВладелецФормы.ОбновитьОтображениеДанных(), ВладелецФормы.Элементы.Товары.Обновить() или ВладелецФормы.Прочитать() в данном случае не принесут результата. Метод Прочитать() пытается перечитать данные из базы данных, но так как наш документ еще не записан, в базе данных новых строк попросту нет, и форма останется пустой.
Давайте разберем три эффективных способа решения этой задачи.
Если ваша внешняя обработка вызывается интерактивно и у вас есть доступ к свойству ВладелецФормы, наиболее простым решением будет использование платформенного метода КопироватьДанныеФормы. Этот метод выполняет глубокое копирование измененной структуры данных обратно в контекст формы-владельца.
Рассмотрим, как должен выглядеть код в модуле управляемой формы вашей внешней обработки:
&НаКлиенте
Процедура ВыполнитьКоманду(Команда, ОбъектыНазначенияМассив) Экспорт
// Получаем локальную копию данных формы документа
ДокОб = ВладелецФормы.Объект;
Если Вопрос("Заполнить табличную часть остатками товаров?", РежимДиалогаВопрос.ДаНет) = КодВозвратаДиалога.Да Тогда
// Очищаем текущие товары на клиенте
ДокОб.Товары.Очистить();
// Вызываем серверный метод для заполнения ТЧ остатками
ПолучитьОстаткиНаСервере(ДокОб);
// КЛЮЧЕВОЙ МОМЕНТ: Копируем измененную структуру обратно в форму-владелец
КопироватьДанныеФормы(ДокОб, ВладелецФормы.Объект);
// Устанавливаем признак модифицированности формы, чтобы пользователь мог сохранить изменения
ВладелецФормы.Модифицированность = Истина;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПолучитьОстаткиНаСервере(ДокОб)
// Запрос для получения актуальных остатков номенклатуры на складе
Запрос = Новый Запрос(
"ВЫБРАТЬ
| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
| ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатков, Склад = &Склад) КАК ТоварыНаСкладахОстатки");
Запрос.УстановитьПараметр("ДатаОстатков", Новый Граница(ДокОб.Дата, ВидГраницы.Включая));
Запрос.УстановитьПараметр("Склад", ДокОб.Склад);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СтрокаДок = ДокОб.Товары.Добавить();
СтрокаДок.Номенклатура = Выборка.Номенклатура;
СтрокаДок.Количество = Выборка.Количество;
КонецЦикла;
КонецПроцедуры
Использование метода КопироватьДанныеФормы позволяет мгновенно синхронизировать интерфейс открытого документа с измененной во внешней обработке коллекцией данных.
Если ваша конфигурация построена на базе Библиотеки Стандартных Подсистем (БСП) — к таким относятся Управление торговлей, ERP, Комплексная автоматизация, УНФ и другие, — то правильным архитектурным решением будет использование штатного механизма БСП.
Для этого при регистрации внешней обработки в модуле объекта в функции СведенияОВнешнейОбработке() необходимо указать тип запуска команды как ЗаполнениеФормы. В этом случае БСП сама передаст контекст открытой формы в параметры выполнения, и нам не потребуется использовать свойство ВладелецФормы напрямую.
Разберем по шагам, как написать код в модуле объекта внешней обработки:
// Процедура вызывается механизмом БСП при выполнении команды заполнения формы
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ОбъектыНазначения, ПараметрыВыполнения) Экспорт
// Получаем доступ к форме документа из параметров БСП
ФормаДокумента = ПараметрыВыполнения.ЭтаФорма;
// Преобразуем ДанныеФормы структуры в реальный прикладной объект ДокументОбъект для удобной работы
ДокументОбъект = ДанныеФормыВЗначение(ФормаДокумента.Объект, Тип("ДокументОбъект.РеализацияТоваровУслуг"));
// Очищаем табличную часть
ДокументОбъект.Товары.Очистить();
// Выполняем запрос к базе данных
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладахОстатки.Номенклатура,
| ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК ТоварыНаСкладахОстатки";
Запрос.УстановитьПараметр("Дата", ДокументОбъект.Дата);
Запрос.УстановитьПараметр("Склад", ДокументОбъект.Склад);
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
ДокументОбъект.Товары.Загрузить(РезультатЗапроса.Выгрузить());
КонецЕсли;
// КЛЮЧЕВОЙ МОМЕНТ: Преобразуем измененный прикладной объект обратно в данные управляемой формы
ЗначениеВДанныеФормы(ДокументОбъект, ФормаДокумента.Объект);
// Помечаем форму как измененную (модифицированную)
ФормаДокумента.Модифицированность = Истина;
КонецПроцедуры
Преимущество этого метода заключается в том, что использование функций ДанныеФормыВЗначение и ЗначениеВДанныеФормы гарантирует корректное прохождение всех платформенных проверок и автозаполнение связанных реквизитов формы. Это наиболее стабильный и «красивый» с точки зрения стандартов разработки 1С метод.
Если табличная часть документа содержит тысячи строк, передача всего контекста формы (объекта ДанныеФормыСтруктура) на сервер и обратно может замедлить работу интерфейса. В целях оптимизации производительности рекомендуется разделять получение данных и визуальное заполнение формы.
Идея метода заключается в следующем: мы запрашиваем данные на сервере без контекста формы, помещаем полученную таблицу во временное хранилище, а затем на клиенте быстро заполняем табличную часть формы напрямую, обращаясь к элементам интерфейса. Посмотрим на практический пример реализации:
&НаКлиенте
Процедура ВыполнитьКоманду(Команда, ОбъектыНазначенияМассив) Экспорт
ТекущийСклад = ВладелецФормы.Объект.Склад;
ТекущаяДата = ВладелецФормы.Объект.Дата;
Если Не ЗначениеЗаполнено(ТекущийСклад) Тогда
ПоказатьПредупреждение(, "Для заполнения остатков необходимо выбрать склад в документе!");
Возврат;
КонецЕсли;
// Получаем адрес во временном хранилище, где лежит готовая таблица с остатками
АдресДанных = ПолучитьТаблицуОстатковНаСервере(ТекущийСклад, ТекущаяДата);
Если АдресДанных <> Неопределено Тогда
// Получаем таблицу из хранилища на клиенте
ТаблицаОстатков = ПолучитьИзВременногоХранилища(АдресДанных);
// Напрямую работаем с табличной частью формы на клиенте
ТоварыФормы = ВладелецФормы.Объект.Товары;
ТоварыФормы.Очистить();
Для Каждого СтрокаОстатка Из ТаблицаОстатков Цикл
НоваяСтрока = ТоварыФормы.Добавить();
НоваяСтрока.Номенклатура = СтрокаОстатка.Номенклатура;
НоваяСтрока.Количество = СтрокаОстатка.Количество;
КонецЦикла;
ВладелецФормы.Модифицированность = Истина;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьТаблицуОстатковНаСервере(Склад, ДатаОстатков)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
| ТоварыНаСкладахОстатки.ВНаличииОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК ТоварыНаСкладахОстатки";
Запрос.УстановитьПараметр("Дата", Новый Граница(ДатаОстатков, ВидГраницы.Включая));
Запрос.УстановитьПараметр("Склад", Склад);
ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
// Помещаем таблицу во временное хранилище и возвращаем ее адрес
Возврат ПоместитьВоВременноеХранилище(ТаблицаРезультата);
КонецФункции
Этот способ отлично подходит для высоконагруженных систем, так как минимизирует объем трафика между клиентом и сервером: на сервер уходят только параметры отбора, а обратно возвращается легкая плоская таблица данных без накладных расходов на сериализацию всей формы документа.
Мы проанализировали три различных подхода к решению проблемы обновления управляемой формы при заполнении табличной части из внешней обработки. Каждый из методов имеет свои сценарии применения:
Выбирайте наиболее подходящий под ваши задачи инструмент, пишите производительный код и делайте интерфейсы ваших решений на платформе 1С удобными для пользователей!