Ошибка «Данные были изменены или удалены другим пользователем» в 1С означает, что объект, который вы пытаетесь записать, уже успел записать кто-то другой: коллега, фоновое задание или обмен. Платформа сравнивает версию данных в форме и в базе и отменяет запись, чтобы не затереть чужие изменения. Избежать потери работы можно двумя приемами: занимать объект блокировкой при открытии формы и не менять «живые» объекты из обработок напрямую, а пропускать их через очередь.

Разберем, откуда берется конфликт, как устроены блокировки и какой код я ставлю в формы и обработки, где такие конфликты регулярно случаются.

Откуда берется конфликт

Типичная история: пользователь А открыл документ и полчаса его заполнял. Пользователь Б открыл тот же документ, поправил одно поле и записал. Пользователь А нажимает «Записать» — и получает ошибку. Работу приходится повторять.

Вторая частая причина — программные изменения: регламентное задание перепроводит документы, обмен загружает изменения, обработка массово правит реквизит. Пользователь в этот момент держит документ открытым и при записи получает тот же отказ.

Какие блокировки есть в платформе

МеханизмКак работаетКогда срабатывает
Оптимистическая блокировка (версия данных)При записи сверяется ВерсияДанных объектаВсегда при записи объекта
Объектная пессимистическая блокировкаЗаблокироватьДанныеДляРедактирования запрещает другим сеансам блокировать и редактировать объектВ форме при начале редактирования, либо явно из кода
Управляемая (транзакционная) блокировкаБлокировкаДанных внутри транзакцииДля согласованного чтения и записи, например остатков при проведении

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

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

Блокировка объекта при открытии формы

Если документ долго заполняется и конфликты бьют по людям, имеет смысл занимать его сразу при открытии. Второй пользователь увидит форму только для просмотра и сообщение о том, что документ занят.

1С
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

	ЗанятьОбъектДляРедактирования();

КонецПроцедуры

// Пытается заблокировать объект за этой формой. При неудаче переводит форму в режим просмотра.
&НаСервере
Процедура ЗанятьОбъектДляРедактирования()

	Если Объект.Ссылка.Пустая() Тогда
		Возврат;
	КонецЕсли;

	Попытка
		ЗаблокироватьДанныеДляРедактирования(Объект.Ссылка, , УникальныйИдентификатор);
	Исключение
		ТолькоПросмотр = Истина;
		ТекстСообщения = СтрШаблон(
			НСтр("ru = 'Документ сейчас редактирует другой пользователь, он открыт только для просмотра.
			|%1'"), КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
		ОбщегоНазначения.СообщитьПользователю(ТекстСообщения);
	КонецПопытки;

КонецПроцедуры

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

Не делайте разблокировку серверным вызовом из ПриЗакрытии, если параметр ЗавершениеРаботы равен Истина: при завершении работы приложения серверные вызовы из этого обработчика запрещены.

Компромиссы подхода

  • Пользователь, который просто открыл документ посмотреть, тоже его занимает. Для справочников и документов «на просмотр» это раздражает — применяйте прием там, где редактирование действительно долгое: заказы, сметы, сложные документы с табличными частями.
  • Ошибка блокировки содержит сведения о том, кем занят объект; покажите их пользователю, чтобы он мог договориться с коллегой.
  • Новый, еще не записанный объект блокировать нечего: проверка на пустую ссылку обязательна.

Программное изменение объектов: очередь вместо прямой записи

Обработки и регламентные задания должны уважать пользовательские блокировки. Надежная схема, которую я использую:

  1. Создать регистр сведений ОчередьОбработкиДокументов с измерением Документ и, при необходимости, ресурсами с параметрами обработки и датой постановки.
  2. Обработчик-источник не меняет документ, а записывает его в очередь.
  3. Регламентное задание читает очередь, пытается заблокировать каждый документ, при успехе обрабатывает, записывает и снимает блокировку. Занятые документы остаются в очереди до следующего запуска.
Данные были изменены или удалены другим пользователем: очередь обработки с блокировкой каждого документа
Данные были изменены или удалены другим пользователем: очередь обработки с блокировкой каждого документа
1С
// Обработчик регламентного задания: обрабатывает документы из очереди,
// пропуская те, что сейчас редактируют пользователи.
Процедура ОбработатьОчередьДокументов() Экспорт

	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(Метаданные.РегламентныеЗадания.ОбработкаОчередиДокументов);

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 500
	|	Очередь.Документ КАК Документ
	|ИЗ
	|	РегистрСведений.ОчередьОбработкиДокументов КАК Очередь
	|УПОРЯДОЧИТЬ ПО
	|	Очередь.ДатаПостановки";
	Выборка = Запрос.Выполнить().Выбрать();

	Обработанные = Новый Массив;

	Пока Выборка.Следующий() Цикл

		Попытка
			ЗаблокироватьДанныеДляРедактирования(Выборка.Документ);
		Исключение
			// Документ занят пользователем — вернемся к нему в следующий запуск.
			Продолжить;
		КонецПопытки;

		Попытка
			ДокументОбъект = Выборка.Документ.ПолучитьОбъект();
			ЗаполнитьДокументПоОчереди(ДокументОбъект);
			ДокументОбъект.Записать();
			Обработанные.Добавить(Выборка.Документ);
		Исключение
			ЗаписьЖурналаРегистрации(НСтр("ru = 'Очередь обработки документов'"),
				УровеньЖурналаРегистрации.Ошибка, , Выборка.Документ,
				ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		КонецПопытки;

		// Снимаем блокировку при любом исходе обработки.
		РазблокироватьДанныеДляРедактирования(Выборка.Документ);

	КонецЦикла;

	УдалитьИзОчереди(Обработанные);

КонецПроцедуры

Процедура УдалитьИзОчереди(Документы)

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

КонецПроцедуры

Детали, которые важны в продуктиве:

  • Порция. ПЕРВЫЕ 500 ограничивает время одного запуска, чтобы задание не висело часами.
  • Ошибка обработки не теряет документ. Он остается в очереди, а причина пишется в журнал регистрации. Для документов, которые падают постоянно, добавьте счетчик попыток в ресурсы регистра.
  • Блокировка снимается всегда, даже если запись упала. Иначе документ останется занятым до конца сеанса фонового задания.
  • Очищаемая очередь — обычный регистр сведений, его легко мониторить простым отчетом: сколько документов ждет и как давно.

Если процедура выполняется без регламентного задания (например, в обработке), уберите вызов ПриНачалеВыполненияРегламентногоЗадания — это метод БСП именно для обработчиков регламентных заданий.

Как разбирать конфликты в существующей базе

Когда пользователи жалуются на ошибку записи, я иду по такому порядку:

  1. Журнал регистрации по проблемному документу: кто и что записал между открытием и попыткой записи. Часто это не человек, а обмен или регламентное задание.
  2. Список регламентных заданий и подписок на события, которые пишут объекты этого типа.
  3. Обработчики форм, которые записывают связанные объекты (например, при записи заказа перезаписывается договор, открытый у другого пользователя).
  4. По итогам — очередь для программных изменений и блокировка при открытии для «тяжелых» документов.

Типичные ошибки

  • Блокировка без идентификатора формы в ПриСозданииНаСервере: она живет до конца сеанса, и документ остается занятым после закрытия формы.
  • Нет проверки пустой ссылки — ошибка при открытии нового документа.
  • В фоновом задании нет разблокировки в ветке ошибки.
  • Попытка решить проблему управляемыми блокировками — они не защищают форму пользователя.
  • Массовая обработка без очереди в рабочее время — десятки пользователей получают ошибку записи одновременно.

Если конфликты записи и ожидания на блокировках стали системными, это повод для аудита производительности: там же часто находятся и долгие транзакции. Точечные доработки форм и обработок делаю в рамках доработки 1С.

Итог

  • Ошибка «Данные были изменены или удалены другим пользователем» — срабатывание оптимистической блокировки по версии данных.
  • ЗаблокироватьДанныеДляРедактирования с идентификатором формы занимает документ на время работы с ним.
  • Программные изменения пропускайте через очередь с регламентным заданием и блокировкой каждого объекта.
  • Блокировку в фоновом коде снимайте при любом исходе, а занятые объекты оставляйте на следующий запуск.