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