При работе в конфигурациях на базе обычных форм (например, 1С:Управление производственным предприятием 1.3) разработчики часто сталкиваются с ситуацией, когда после программного проведения документа или манипуляций с его движениями открытая форма документа неожиданно получает флаг ЭтаФорма.Модифицированность = Истина. Даже явный вызов ЭтаФорма.Модифицированность = Ложь или методы перечитывания данных не всегда снимают этот признак.
Рассмотрим подробно, почему возникает эта проблема, проанализируем распространенные ошибки при работе с транзакциями и сформируем правильный подход к обновлению состояния формы.
Проанализируем механику работы платформы 1С. Признак модифицированности формы связывается не только с прямым изменением полей пользователем, но и с изменением служебного реквизита ВерсияДанных, который присутствует у каждого ссылочного объекта базы данных.
Разберем основные причины расхождения версий:
Ссылка.ПолучитьОбъект(), в памяти создается второй экземпляр документа. Любая запись этого экземпляра меняет запись в БД и обновляет ВерсияДанных. Открытая же форма продолжает удерживать прежний экземпляр ЭтотОбъект со старым значением версии. При последующем обращении платформа фиксирует рассинхронизацию и взводит признак модифицированности.ЭлементыФормы.ПолеВвода.Значение = ...) или пересчет в событиях формы автоматически взводит модифицированность.Рассмотрим конкретную проблему из практики разработчиков. Часто используется паттерн "виртуального проведения": документ проводится внутри транзакции, считываются полученные движения, после чего транзакция откатывается назад, чтобы не менять сам документ.
Посмотрим на пример кода, в котором содержалась критическая ошибка:
НачатьТранзакцию();
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
ПроведениеУдалось = Истина;
Исключение
Сообщить(ОписаниеОшибки());
ОтменитьТранзакцию();
КонецПопытки;
ПроведениеУдалось = Ложь; // Ошибка! Сброс флага перед проверкой
Если ПроведениеУдалось Тогда
ДокументОбъект.Движения["Закупки"].Прочитать();
ТЗЗакупки = ДокументОбъект.Движения["Закупки"].Выгрузить();
ОтменитьТранзакцию(); // Этот блок никогда не выполнялся!
// Запись наборов записей напрямую...
КонецЕсли;
Выясним причину, почему в данном коде форма становилась модифицированной. Из-за строки ПроведениеУдалось = Ложь;, случайно поставленной перед условием Если ПроведениеУдалось Тогда, блок с отменой транзакции ОтменитьТранзакцию() не вызывался. В результате транзакция не отменялась корректно, проведение фиксировалось в базе данных, из-за чего менялся реквизит ВерсияДанных, а открытая форма документа становилась модифицированной.
Исправим структуру транзакции, чтобы при отмене проведения данные документа не сохранялись в базе и версия объекта не менялась:
ПроведениеУдалось = Ложь;
НачатьТранзакцию();
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
ПроведениеУдалось = Истина;
Исключение
Сообщить(ОписаниеОшибки());
ОтменитьТранзакцию();
КонецПопытки;
Если ПроведениеУдалось Тогда
// Считываем движения документа, пока транзакция не завершена
ДокументОбъект.Движения["Закупки"].Прочитать();
ТЗЗакупки = ДокументОбъект.Движения["Закупки"].Выгрузить();
ДокументОбъект.Движения["Затраты"].Прочитать();
ТЗЗатраты = ДокументОбъект.Движения["Затраты"].Выгрузить();
// Обязательно отменяем транзакцию, чтобы сбросить проведение
ОтменитьТранзакцию();
// Записываем сформированные движения напрямую в регистры
НаборЗаписей = РегистрыНакопления["Закупки"].СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ДокументОбъект.Ссылка);
НаборЗаписей.Загрузить(ТЗЗакупки);
НаборЗаписей.Записать();
НаборЗаписей = РегистрыНакопления["Затраты"].СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Регистратор.Установить(ДокументОбъект.Ссылка);
НаборЗаписей.Загрузить(ТЗЗатраты);
НаборЗаписей.Записать();
КонецЕсли;
Для предотвращения нежелательной модифицированности обычных форм рекомендуем придерживаться следующих правил:
ФормаИсточника.ЭтотОбъект. Если манипуляции совершаются с тем же экземпляром объекта, платформа корректно синхронизирует состояние.ЭтотОбъект.Прочитать() и затем явного сброса ЭтаФорма.Модифицированность = Ложь.ОповеститьОбИзменении(СсылкаНаДокумент), чтобы связанные списки и формы своевременно обновили данные.