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