Как обновить управляемую форму при программном изменении и записи документа в 1С?

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

Разработка в среде 1С:Предприятие 8 на базе управляемого приложения накладывает строгие правила на работу с контекстом формы и серверными вызовами. Одна из самых распространенных ошибок начинающих программистов — появление предупреждения «Данные были изменены или удалены другим пользователем» при попытке повторно записать документ или продолжить работу с формой после ее программного сохранения. В этой статье мы подробно разберем, почему возникает эта ошибка, как устроен механизм версионирования в 1С, и рассмотрим несколько надежных способов синхронизации данных между базой данных и открытой формой.

Почему возникает рассинхронизация данных (Механизм оптимистической блокировки)

Для того чтобы понять природу проблемы, давайте проанализируем, как платформа 1С осуществляет контроль ссылочной целостности и предотвращает перезапись данных. В платформе реализован механизм так называемой оптимистической блокировки. Каждая таблица СУБД для объектных сущностей (документов, справочников) содержит скрытое системное поле ВерсияДанных. Этот реквизит представляет собой уникальную метку (хэш-код или метку времени), которая изменяется автоматически при каждой транзакции записи объекта в базу данных.

Когда пользователь открывает форму документа, клиентское приложение кэширует текущие данные объекта, включая его текущую ВерсиюДанных. Дальнейшие события развиваются по следующему сценарию:

  1. Пользователь совершает какое-либо действие (например, нажимает кнопку печати), которое запускает процедуру программной перезаписи объекта на сервере.
  2. На сервере разработчик преобразует данные формы в объект через вызов ДанныеФормыВЗначение или напрямую получает объект из базы данных по ссылке, вносит изменения и вызывает метод Записать().
  3. Платформа успешно записывает объект в СУБД и генерирует для него новую версию данных.
  4. При этом на клиенте в открытой форме контекст по-прежнему содержит старую версию данных, так как форма «не знает» о том, что объект на сервере перезаписался с новыми параметрами.
  5. Когда пользователь пытается нажать на стандартную кнопку «Записать» на форме или выполняет другое серверное действие, требующее синхронизации, платформа сравнивает версию данных формы с версией данных в СУБД. Обнаружив расхождение, система немедленно блокирует транзакцию и выбрасывает исключение: «Данные изменены другим пользователем».

Давайте выясним, как правильно обойти это поведение платформы и какие паттерны проектирования рекомендует использовать фирма 1С.

Решение 1. Использование стандартного метода записи формы на клиенте

В большинстве случаев усложнение кода и написание собственных процедур записи на сервере избыточно. Если перед выводом документа на печать вам необходимо просто зафиксировать набранные пользователем данные (например, проставленные в табличной части галочки), самым логичным и безопасным решением будет вызов стандартного метода управляемой формы Записать() прямо на клиенте.

Давайте посмотрим на пример реализации такого подхода:


&НаКлиенте
Процедура Подключаемый_ВыполнитьКомандуПечати(Команда)
    
    // Пытаемся записать форму стандартными средствами платформы
    ПараметрыЗаписи = Новый Структура;
    Если ЭтаФорма.Записать(ПараметрыЗаписи) Тогда
        // Если запись прошла успешно, данные в БД и на форме синхронизированы
        УправлениеПечатьюКлиент.ВыполнитьПодключаемуюКомандуПечати(Команда, ЭтаФорма, Объект);
    Иначе
        Сообщить("Не удалось записать документ. Проверьте заполнение реквизитов!");
    КонецЕсли;
    
КонецПроцедуры

Преимущества данного метода:

Решение 2. Классический паттерн синхронизации контекста на сервере

Если специфика вашей бизнес-логики требует обязательного программного изменения реквизитов непосредственно в коде сервера перед записью, мы должны использовать специализированные методы взаимного преобразования данных формы и прикладных объектов.

Стандарты разработки 1С предписывают использовать связку методов управляемой формы РеквизитФормыВЗначение() и ЗначениеВРеквизитФормы(). Рассмотрим подробнее правильный алгоритм работы на сервере:


&НаСервере
Функция ЗаписатьДокументНаСервере()
    
    Попытка
        // 1. Преобразуем данные формы (реквизит "Объект") в полноценный прикладной объект ДокументОбъект
        ОбъектДокумент = РеквизитФормыВЗначение("Объект");
        
        // 2. Вносим необходимые изменения в прикладной объект
        // Например, программно проставляем флаг печати в шапке или строках таблицы
        ОбъектДокумент.ФлагДокументРаспечатан = Истина;
        
        // 3. Записываем объект в базу данных. В этот момент в СУБД обновляется версия данных
        ОбъектДокумент.Записать();
        
        // 4. КРИТИЧЕСКИ ВАЖНЫЙ ШАГ: загружаем измененный объект обратно в реквизит формы.
        // Этот метод не только обновит отображение данных на экране пользователя,
        // но и передаст актуальную "ВерсиюДанных" из СУБД в контекст управляемой формы!
        ЗначениеВРеквизитФормы(ОбъектДокумент, "Объект");
        
        Возврат Истина;
    Исключение
        ЗаписьЖурналаРегистрации("Ошибка записи документа при печати", 
            УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат Ложь;
    КонецПопытки;
    
КонецФункции

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

Решение 3. Использование метода Прочитать() и его скрытые опасности

Еще одним способом быстрого обновления данных в форме является вызов метода Прочитать() расширения управляемой формы. Этот метод принудительно обращается к базе данных, перечитывает текущее состояние объекта по ссылке и обновляет реквизиты формы.

На клиенте вызов выглядит следующим образом:


&НаКлиенте
Процедура Подключаемый_ВыполнитьКомандуПечати(Команда)
    
    Если ЗаписатьДокументНаСервере() Тогда
        УправлениеПечатьюКлиент.ВыполнитьПодключаемуюКомандуПечати(Команда, ЭтаФорма, Объект);
        
        // Перечитываем данные формы напрямую из БД
        ЭтаФорма.Прочитать();
    Иначе
        Сообщить("Ошибка при сохранении данных перед печатью!");
    ЕслиКонец;
    
КонецПроцедуры

Важное предостережение: Метод Прочитать() работает «жестко». Если перед нажатием кнопки печати пользователь изменил какие-либо текстовые поля (например, написал комментарий к документу), но не сохранил документ, вызов метода Прочитать() безвозвратно сотрет все несохраненные изменения пользователя, заменив их версией из базы данных. Используйте данный метод только в тех случаях, когда вы абсолютно уверены, что на форме нет несохраненного ввода, либо если форма открыта исключительно в режиме просмотра.

Решение 4. Архитектурная альтернатива (Реквизиты формы без записи в БД)

Давайте проанализируем саму задачу автора темы. Ему требуется отмечать галочками определенные строки табличной части непосредственно в момент печати. Здесь возникает важный архитектурный вопрос: а нужно ли вообще сохранять эти галочки в базе данных на постоянной основе?

Если эти отметки носят сугубо оперативный характер (нужны только в момент формирования печатной формы и их история не важна для бизнеса), то хранить их в реквизитах табличной части документа в СУБД — грубая архитектурная ошибка. Избыточные транзакции записи приводят к износу оборудования, росту объема базы данных и блокировкам таблиц.

Правильное проектирование в таких случаях:

  1. Создайте колонку (реквизит) типа Булево непосредственно в самом Реквизите Формы (внутри дерева реквизитов формы), добавив ее в подчинение табличной части формы, но не добавляя в метаданные самого документа в конфигураторе.
  2. Пользователь спокойно проставляет галочки на клиенте. Эти данные живут исключительно в оперативной памяти его сеанса.
  3. При нажатии кнопки печати алгоритм считывает состояние этих галочек прямо из данных формы и передает массив отмеченных строк в процедуру печати.
  4. При этом документ вообще не требуется записывать! Нет записи — нет транзакций, нет конфликта версий данных и нет ошибок блокировок у других пользователей.

Резюме

Мы рассмотрели ключевые аспекты работы с версионированием данных в управляемых формах 1С. При проектировании механизмов программного изменения документов всегда оценивайте необходимость сохранения данных в БД. Если сохранение неизбежно, отдавайте приоритет стандартному клиентскому методу ЭтаФорма.Записать(). Если логика требует серверных манипуляций, используйте безопасный контекстный паттерн РеквизитФормыВЗначение - ЗначениеВРеквизитФормы.

← На главную