Как исправить ошибку «Данные были изменены или удалены другим пользователем» в 1С

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

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

Разбор ситуации: почему возникает конфликт версий

Рассмотрим ситуацию, описанную пользователем. Разработчик пытается реализовать следующую логику: при удалении строки из табличной части справочника в управляемой форме необходимо программно удалить соответствующие записи в регистре сведений и изменить реквизит самого справочника. Проблема возникает в момент интерактивного нажатия кнопки «Записать» на форме.

Для начала проанализируем фрагмент кода, который вызывает проблему:


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

Разберем по шагам, что происходит в системе в этот момент:

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

Механизм оптимистической блокировки

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

Проблема в коде пользователя заключается в использовании метода &НаСервереБезКонтекста вместе с методом Записать() для того же объекта, который в данный момент редактируется в форме. Форма не «знает», что данные в базе уже изменились, и продолжает считать свою версию актуальной.

Решение №1: Работа с контекстом формы

Самый простой способ избежать ошибки в управляемых формах — работать напрямую с данными формы, не вызывая преждевременную запись объекта в базу данных. Вместо того чтобы создавать отдельный экземпляр объекта через ПолучитьОбъект(), мы изменим реквизиты прямо в контексте формы.

Рассмотрим, как изменить логику. Переведем выполнение на сервер с сохранением контекста:


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

    // Вместо записи всего объекта справочника, меняем данные в реквизите формы "Объект"
    Объект.Родитель = Справочники.ЭФО.ПустаяСсылка; 
    // Теперь, когда пользователь нажмет "Записать" в форме, 
    // платформа сама запишет и измененного родителя, и удаленные строки ТЧ.
КонецПроцедуры

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

Решение №2: Синхронизация данных через ЗначениеВРеквизитФормы

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

Проанализируем пример кода с синхронизацией:


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

Решение №3: Перенос логики в модуль объекта (Best Practice)

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

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

Рассмотрим правильную реализацию через событие ПередЗаписью в модуле справочника:


// В модуле объекта справочника
Процедура ПередЗаписью(Отказ)
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    // Здесь можно реализовать проверку измененных строк ТЧ
    // и очистку связанного регистра сведений.
    // Это гарантирует, что данные в регистре всегда будут актуальны
    // независимо от того, откуда пришла команда на запись.
КонецПроцедуры

Советы по предотвращению ошибки

Чтобы больше не сталкиваться с проблемой изменения данных «другим пользователем», рекомендуем придерживаться следующих правил:

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

← На главную