Почему обычная форма документа становится модифицированной при программном проведении и как это исправить

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При работе в конфигурациях на базе обычных форм (например, 1С:Управление производственным предприятием 1.3) разработчики часто сталкиваются с ситуацией, когда после программного проведения документа или манипуляций с его движениями открытая форма документа неожиданно получает флаг ЭтаФорма.Модифицированность = Истина. Даже явный вызов ЭтаФорма.Модифицированность = Ложь или методы перечитывания данных не всегда снимают этот признак.

Рассмотрим подробно, почему возникает эта проблема, проанализируем распространенные ошибки при работе с транзакциями и сформируем правильный подход к обновлению состояния формы.

В чем причина взведения флага модифицированности?

Проанализируем механику работы платформы 1С. Признак модифицированности формы связывается не только с прямым изменением полей пользователем, но и с изменением служебного реквизита ВерсияДанных, который присутствует у каждого ссылочного объекта базы данных.

Разберем основные причины расхождения версий:

  1. Разделение экземпляров объекта в памяти: Если внешняя обработка получает документ по ссылке через метод Ссылка.ПолучитьОбъект(), в памяти создается второй экземпляр документа. Любая запись этого экземпляра меняет запись в БД и обновляет ВерсияДанных. Открытая же форма продолжает удерживать прежний экземпляр ЭтотОбъект со старым значением версии. При последующем обращении платформа фиксирует рассинхронизацию и взводит признак модифицированности.
  2. Завершенные транзакции записи: Если в процессе программной работы документ был фактически записан или проведен (транзакция фиксирована), платформа меняет версию объекта в базе данных.
  3. Программное изменение элементов формы: В обычных формах прямая запись в свойства элементов управления (например, ЭлементыФормы.ПолеВвода.Значение = ...) или пересчет в событиях формы автоматически взводит модифицированность.

Анализ ошибки в коде транзакции

Рассмотрим конкретную проблему из практики разработчиков. Часто используется паттерн "виртуального проведения": документ проводится внутри транзакции, считываются полученные движения, после чего транзакция откатывается назад, чтобы не менять сам документ.

Посмотрим на пример кода, в котором содержалась критическая ошибка:


НачатьТранзакцию();
Попытка
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
    ПроведениеУдалось = Истина;
Исключение
    Сообщить(ОписаниеОшибки());
    ОтменитьТранзакцию();
КонецПопытки;

ПроведениеУдалось = Ложь; // Ошибка! Сброс флага перед проверкой

Если ПроведениеУдалось Тогда
    ДокументОбъект.Движения["Закупки"].Прочитать();
    ТЗЗакупки = ДокументОбъект.Движения["Закупки"].Выгрузить();
    
    ОтменитьТранзакцию(); // Этот блок никогда не выполнялся!
    
    // Запись наборов записей напрямую...
КонецЕсли;

Выясним причину, почему в данном коде форма становилась модифицированной. Из-за строки ПроведениеУдалось = Ложь;, случайно поставленной перед условием Если ПроведениеУдалось Тогда, блок с отменой транзакции ОтменитьТранзакцию() не вызывался. В результате транзакция не отменялась корректно, проведение фиксировалось в базе данных, из-за чего менялся реквизит ВерсияДанных, а открытая форма документа становилась модифицированной.

Как правильно исправить код транзакции

Исправим структуру транзакции, чтобы при отмене проведения данные документа не сохранялись в базе и версия объекта не менялась:


ПроведениеУдалось = Ложь;

НачатьТранзакцию();
Попытка
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
    ПроведениеУдалось = Истина;
Исключение
    Сообщить(ОписаниеОшибки());
    ОтменитьТранзакцию();
КонецПопытки;

Если ПроведениеУдалось Тогда
    // Считываем движения документа, пока транзакция не завершена
    ДокументОбъект.Движения["Закупки"].Прочитать();
    ТЗЗакупки = ДокументОбъект.Движения["Закупки"].Выгрузить();
    
    ДокументОбъект.Движения["Затраты"].Прочитать();
    ТЗЗатраты = ДокументОбъект.Движения["Затраты"].Выгрузить();
    
    // Обязательно отменяем транзакцию, чтобы сбросить проведение
    ОтменитьТранзакцию();
    
    // Записываем сформированные движения напрямую в регистры
    НаборЗаписей = РегистрыНакопления["Закупки"].СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Регистратор.Установить(ДокументОбъект.Ссылка);
    НаборЗаписей.Загрузить(ТЗЗакупки);
    НаборЗаписей.Записать();
    
    НаборЗаписей = РегистрыНакопления["Затраты"].СоздатьНаборЗаписей();
    НаборЗаписей.Отбор.Регистратор.Установить(ДокументОбъект.Ссылка);
    НаборЗаписей.Загрузить(ТЗЗатраты);
    НаборЗаписей.Записать();
КонецЕсли;

Рекомендации по работе с модифицированностью формы

Для предотвращения нежелательной модифицированности обычных форм рекомендуем придерживаться следующих правил:

  1. Передавайте в обработку контекст формы: Если внешняя обработка вызывается из открытой формы, передавайте в нее объект формы ФормаИсточника.ЭтотОбъект. Если манипуляции совершаются с тем же экземпляром объекта, платформа корректно синхронизирует состояние.
  2. Синхронизируйте данные после внешней записи: Если объект был записан сторонним кодом в базе данных, обновляйте экземпляр формы с помощью вызова ЭтотОбъект.Прочитать() и затем явного сброса ЭтаФорма.Модифицированность = Ложь.
  3. Используйте оповещения: При изменении документа вне формы вызывайте глобальную процедуру ОповеститьОбИзменении(СсылкаНаДокумент), чтобы связанные списки и формы своевременно обновили данные.
  4. Выносите логику расчетов из проведения: Вместо проведения с последующей отменой транзакции предпочтительнее выносить алгоритм формирования движений в экспортную функцию модуля объекта. Это позволит получать таблицы движений без выполнения реальной записи и рисков рассинхронизации версий.
← На главную