При разработке интерфейсов на управляемых формах часто возникает задача отображения данных табличной части текущего документа непосредственно в форме списка. Это удобно для пользователей, так как позволяет просматривать содержимое документа, не открывая его. Однако разработчики сталкиваются с технической сложностью: если один пользователь (Пользователь_2) изменит документ, то у другого пользователя (Пользователь_1), у которого открыта форма списка, данные табличной части не обновятся автоматически. Даже нажатие клавиши F5 зачастую обновляет только данные самого динамического списка (шапку документа), но не связанные данные, выведенные в отдельные реквизиты формы.
В этой статье мы подробно разберем, почему возникает эта проблема, и рассмотрим несколько эффективных способов её решения, от простых обработчиков ожидания до оптимизированных механизмов отслеживания изменений.
Выясним причину, по которой стандартное обновление формы не дает нужного результата. Когда мы выводим на форму данные табличной части через обращение к реквизиту ссылки (например, Реквизит1.ТабличнаяЧасть1), платформа 1С использует механизм кэширования объектов на стороне клиента. При первом обращении к объекту по ссылке данные считываются из базы. При последующих обращениях или переключениях строк платформа может считать, что данные объекта у нее уже есть, и не обращается к серверу за актуальным состоянием, если сама ссылка не изменилась.
Обновление динамического списка через F5 или метод Обновить() заставляет платформу перечитать данные основного запроса списка, но не связанные с ним объектные данные, хранящиеся в памяти формы. Именно поэтому данные шапки в списке меняются, а табличная часть в отдельном элементе формы — нет.
Рассмотрим первый способ, который часто применяется для решения подобных задач. Суть его заключается в том, чтобы хранить данные табличной части не в прямой привязке к ссылке, а в отдельном реквизите формы типа ТаблицаЗначений. Мы будем принудительно перезаполнять эту таблицу данными с сервера.
Для этого нам понадобится процедура, работающая по таймеру. Разберем по шагам реализацию этого механизма.
Во-первых, создадим на форме реквизит ТЗ_ДанныеТЧ (тип ТаблицаЗначений) и выведем его на форму. Во-вторых, настроим обработчик ожидания.
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Подключаем обработчик ожидания, который будет срабатывать каждые 10 секунд
ПодключитьОбработчикОжидания("ОбновитьОтображениеТЧ", 10);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьОтображениеТЧ() Экспорт
// Вызываем серверную процедуру для получения актуальных данных
ОбновитьТЧНаСервере(Элементы.Список.ТекущаяСтрока);
КонецПроцедуры
&НаСервере
Процедура ОбновитьТЧНаСервере(СсылкаНаДокумент)
Если СсылкаНаДокумент = Неопределено Или СсылкаНаДокумент.Пустая() Тогда
ТЗ_ДанныеТЧ.Очистить();
Иначе
// Прямое считывание данных из БД минуя кэш через выгрузку ТЧ
// Важно: Ссылка.ТабличнаяЧасть.Выгрузить() всегда обращается к актуальным данным БД на сервере
ТЗ_ДанныеТЧ.Загрузить(СсылкаНаДокумент.ТабличнаяЧасть1.Выгрузить());
// Если требуется определенная сортировка (например, по номеру строки)
ТЗ_ДанныеТЧ.Сортировать("НомерСтроки Возр");
КонецЕсли;
КонецПроцедуры
Преимущества этого метода: данные всегда будут актуальными с задержкой не более заданного интервала таймера. Недостатки: постоянные вызовы сервера (даже если данные не менялись) могут создать лишнюю нагрузку на систему при большом количестве пользователей.
Проанализируем ситуацию: зачем нам обновлять данные каждые 10 секунд, если документ меняется раз в час? Мы можем оптимизировать процесс, добавив проверку факта изменения данных. Одним из эффективных способов является использование небольшого регистра сведений для хранения метки времени последнего изменения документов определенного типа.
Создадим непериодический регистр сведений ИсторияИзмененийДокументов с измерениями ИмяДокумента и ресурсом ДатаПоследнегоИзменения.
В модуле документа в процедуре ПередЗаписью или ПриЗаписи добавим код обновления метки:
Процедура ПриЗаписи(Отказ)
// Обновляем дату изменения для данного типа документа
ЗаписьМетки = РегистрыСведений.ИсторияИзмененийДокументов.СоздатьМенеджерЗаписи();
ЗаписьМетки.ИмяДокумента = "Документ1";
ЗаписьМетки.ДатаПоследнегоИзменения = ТекущаяДатаСеанса();
ЗаписьМетки.Записать();
КонецПроцедуры
Теперь на форме списка мы будем сравнивать дату последнего обновления формы с датой из регистра. Если в регистре дата больше — значит, кто-то в другом сеансе внес изменения, и пора обновить нашу таблицу.
&НаСервере
Процедура ПроверитьОбновлениеНаСервере()
// Получаем дату из регистра
ДатаИзРегистра = РегистрыСведений.ИсторияИзмененийДокументов.Получить(Новый Структура("ИмяДокумента", "Документ1")).ДатаПоследнегоИзменения;
// Сравниваем с датой последнего обновления в этом сеансе (реквизит формы)
Если ДатаИзРегистра > ДатаПоследнегоОбновленияФормы Тогда
// Только в этом случае перечитываем ТЧ и обновляем динамический список
Элементы.Список.Обновить();
ОбновитьТЧНаСервере(Элементы.Список.ТекущаяСтрока);
ДатаПоследнегоОбновленияФормы = ТекущаяДатаСеанса();
КонецЕсли;
КонецПроцедуры
Этот подход значительно снижает нагрузку на сервер, так как вместо тяжелой операции выгрузки данных табличной части мы выполняем легкий запрос к одной записи регистра.
Посмотрим на проблему с другой стороны. Платформа 1С обладает мощным инструментом — Динамическим списком. Вместо того чтобы использовать таблицу значений и программно её заполнять, мы можем создать второй динамический список для отображения табличной части.
Разберем алгоритм настройки:
СписокТЧ с типом ДинамическийСписок.Документ.Документ1.ТабличнаяЧасть1).Ссылка.ПриАктивизацииСтроки основного списка будем передавать ссылку текущего документа в параметры СписокТЧ.
&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
ТекущаяСсылка = Элемент.ТекущаяСтрока;
// Устанавливаем параметр для второго динамического списка
СписокТЧ.Параметры.УстановитьЗначениеПараметра("Ссылка", ТекущаяСсылка);
КонецПроцедуры
Почему это работает лучше? Динамические списки оптимизированы для работы с большими данными. При нажатии F5 пользователем оба списка (и основной, и список ТЧ) будут обновлены платформой автоматически, считав свежие данные напрямую из базы данных, игнорируя кэш объектов. Кроме того, в динамическом списке легко настроить порядок строк через свойство Порядок, решив проблему «случайной» сортировки.
Подводя итог, выделим важные моменты, которые помогут сделать форму стабильной и быстрой:
Элементы.Список.ТекущиеДанные.Ссылка.ТабличнаяЧасть1), так как это провоцирует использование устаревшего кэша клиента.НомерСтроки после метода Загрузить().Системы взаимодействия. Она позволяет серверу отправить сигнал (Push-уведомление) конкретным клиентам о том, что данные изменились, и форму нужно обновить немедленно.ПередЗакрытием формы, чтобы избежать попыток обращения к уже закрытой форме в памяти клиента.Использование предложенных методик позволит вам создать отзывчивый и актуальный интерфейс, который будет корректно отображать изменения данных, внесенные коллегами в параллельных сеансах работы.