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