Каждый разработчик или опытный пользователь 1С рано или поздно сталкивается с системным сообщением: «Операция не может быть выполнена из-за несоответствия версии или отсутствия записи в базе данных (возможно, запись была изменена или удалена)!». Эта ошибка может возникать как в типовых конфигурациях (ЗУП, УТ, ERP), так и в самописных решениях. Рассмотрим подробнее, почему это происходит и как мы можем исправить ситуацию.
Прежде чем переходить к коду, разберем теорию. Платформа «1С:Предприятие» использует механизм так называемой оптимистической блокировки. Это означает, что система позволяет нескольким пользователям одновременно открывать один и тот же объект для редактирования. Однако, чтобы один пользователь случайно не затер изменения другого, платформа хранит номер версии объекта.
Проанализируем ситуацию по шагам:
В этот момент платформа блокирует запись и выдает ошибку о несоответствии версии. Это защитный механизм, предотвращающий потерю данных.
Если мы программно модифицируем объект и уверены, что нам нужно актуализировать данные перед записью, следует использовать метод Прочитать(). Рассмотрим пример, когда ошибка возникает в бизнес-процессах или задачах.
Выясним причину: если объект был изменен фоновым процессом или другим обработчиком в этой же транзакции, текущий набор данных в памяти устаревает — поможет инструмент пошаговой отладки кода в режиме Предприятия. Посмотрим на код, который помогает решить эту проблему:
Процедура ЗавершениеПриЗавершении(ТочкаМаршрутаБизнесПроцесса, Отказ)
// Актуализируем данные объекта из базы данных
ЭтотОбъект.Прочитать();
Если ДатаЗавершения = Дата(1,1,1) Тогда
ДатаЗавершения = ТекущаяДатаСеанса;
Записать();
КонецЕсли;
КонецПроцедуры
Метод Прочитать() сбрасывает все изменения, внесенные в объект в памяти, и загружает текущее состояние из БД. Используйте его с осторожностью, чтобы не потерять введенные пользователем данные.
Рассмотрим ситуацию, когда мы перебираем объекты в цикле и выполняем их запись — пригодится консоль инструментов разработчика и управления заданиями. Распространенная ошибка — считывать объект до начала транзакции, внутри которой происходят изменения связанных сущностей. Проанализируем пример кода, вызывающий ошибку:
Пока Выборка.Следующий() Цикл
Задача = Выборка.Задача.ПолучитьОбъект(); // Объект получен ВНЕ транзакции
ЗаполнитьЗначенияСвойств(Задача, Выборка);
НачатьТранзакцию();
// Здесь код, который может косвенно изменить задачу
Задача.Выполнена = Истина;
Задача.Записать(); // Здесь возникнет ошибка версии
ЗафиксироватьТранзакцию();
КонецЦикла;
Разберем, как сделать правильно. Для стабильной работы получение объекта должно происходить внутри транзакции. Это обеспечит корректную работу блокировок и актуальность версии:
Пока Выборка.Следующий() Цикл
НачатьТранзакцию();
// Получаем объект уже внутри транзакции
Задача = Выборка.Задача.ПолучитьОбъект();
ЗаполнитьЗначенияСвойств(Задача, Выборка);
Задача.Выполнена = Истина;
Задача.Записать();
ЗафиксироватьТранзакцию();
КонецЦикла;
Иногда ошибка возникает из-за невнимательности при работе с коллекциями. Если мы пытаемся получить объект не из ссылки, а напрямую из объекта выборки в некоторых контекстах, платформа может некорректно идентифицировать версию. Посмотрим на пример:
// ОШИБОЧНЫЙ ВАРИАНТ
Для Каждого ВыбЭлем ИЗ СписНом Цикл
// Если в СписНом попал элемент типа СправочникВыборка, будет ошибка
Номенклатура = ВыбЭлем.Значение.ПолучитьОбъект();
Номенклатура.Записать();
КонецЦикла;
// ПРАВИЛЬНЫЙ ВАРИАНТ
Пока Выборка.Следующий() Цикл
// Явно добавляем только ССЫЛКУ
СписНом.Добавить(Выборка.Ссылка);
КонецЦикла;
Всегда проверяйте, что метод ПолучитьОбъект() вызывается у ссылки (СправочникСсылка, ДокументСсылка), а не у других типов данных.
Если код кажется верным, но ошибка продолжает возникать (особенно при создании новых документов), причина может крыться в окружении 1С (рассмотрите альтернативную конфигурацию для внешнего управления сеансами).
%AppData%\Local\1C\1Cv8.Repeatable Read возможны ложные срабатывания данной ошибки из-за специфики снимков данных СУБД.Если ошибка возникла в интерактивном режиме (в форме документа), самым простым решением будет нажать кнопку «Перечитать» (обычно находится в меню «Еще» или выглядит как значок обновления). Это принудительно обновит данные в форме, после чего можно будет внести правки и успешно записать документ. В крайнем случае, если перечитать не удается, придется закрыть форму без сохранения и открыть её заново.
Подведем итог: ошибка несоответствия версии — это не баг, а фича платформы для защиты целостности данных. Для её устранения нам необходимо либо обеспечить актуальность данных через Прочитать(), либо скорректировать логику работы с транзакциями и блокировками.