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