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