При работе в системе 1С:Предприятие 8.3 часто возникает необходимость предотвратить одновременное редактирование одного и того же документа разными пользователями. Это критично для поддержания целостности данных и избегания ситуаций, когда изменения одного пользователя могут быть перезаписаны или потеряны из-за действий другого. Рассмотрим, как правильно реализовать строгую блокировку документов, чтобы только один сотрудник мог заниматься его редактированием в каждый момент времени, а остальные пользователи получали соответствующее уведомление.
Исторически платформа 1С всегда стремилась обеспечивать целостность данных. До определенной версии платформы (до 8.3.15) поведение по умолчанию было пессимистичным: если один пользователь открывал документ на редактирование, для других он автоматически блокировался, и они не могли начать его редактирование. Однако, начиная с версии 8.3.15, в поведении объектных блокировок произошли изменения. Теперь платформа позволяет пользователю "форсировать" редактирование, даже если документ уже открыт и изменяется другим. Это означает, что при попытке второго пользователя открыть и начать редактировать тот же документ, система может предложить "Начать редактирование документа?", и если он согласится, изменения первого пользователя могут быть потеряны при его попытке записи. Это создает серьезные проблемы, требующие программного вмешательства для восстановления желаемого строгого поведения.
Прежде чем перейти к решению, важно различать основные типы блокировок, которые существуют в 1С:
Для программной реализации строгой блокировки в 1С 8.3 используются следующие методы:
ДокументОбъект.Заблокировать()Этот метод предназначен для блокировки объектов ссылочного типа (например, документов) в контексте сервера. Если объект уже заблокирован, вызов этого метода вызовет исключение.
Особенности использования:
Заблокировать() доступен только в серверном контексте, толстом клиенте или внешнем соединении. Попытка вызвать его на клиенте (например, в обработчике события формы, выполняющемся на клиенте) приведет к ошибке "Метод объекта не обнаружен".ДокументОбъект.Разблокировать(), чтобы снять блокировку. Если этого не сделать, блокировка может остаться "висеть" до завершения сеанса пользователя, что потенциально может приводить к проблемам с доступом.
// В модуле формы документа, в серверном обработчике (например, ПриСозданииНаСервере или перед записью)
&НаСервере
Процеду БлокироватьДокументНаСервере()
// Получаем объект документа из формы
ОбъектДокумента = РеквизитФормыВЗначение("Объект");
Попытка
Если Не ОбъектДокумента.Заблокирован() Тогда
ОбъектДокумента.Заблокировать();
КонецЕсли;
Исключение
// Обработка ошибки, если документ уже заблокирован
ОписаниеОшибки = ОписаниеОшибки();
Сообщить("Документ уже заблокирован другим пользователем: " + ОписаниеОшибки);
// Можно закрыть форму или перевести ее в режим только для чтения
КонецПопытки;
КонецПроцеду
Несмотря на свою прямолинейность, метод ДокументОбъект.Заблокировать() может быть менее удобен в управляемых формах, особенно в сценариях, когда время жизни блокировки должно быть строго привязано к времени жизни открытой формы. При некорректном завершении сеанса или закрытии формы блокировка может не сняться, что потребует ручного вмешательства администратора.
ЗаблокироватьДанныеДляРедактирования()Этот метод, доступный в глобальном контексте, предлагает более гибкий и надежный механизм для блокировки данных, особенно при работе с управляемыми формами. Он позволяет блокировать не только объекты ссылочного типа, но и записи регистров сведений, а также управлять блокировкой с учетом версии данных.
Преимущества ЗаблокироватьДанныеДляРедактирования():
ИдентификаторФормы, блокировка будет установлена на все время жизни этой формы.РазблокироватьДанныеДляРедактирования() с указанием того же идентификатора формы.Эта функциональность значительно упрощает управление блокировками и снижает риск "зависших" блокировок по сравнению с ДокументОбъект.Заблокировать().
Реализация блокировки в модуле формы документа с использованием ЗаблокироватьДанныеДляРедактирования():
Наиболее подходящим местом для установки блокировки является обработчик события формы, который выполняется на сервере при ее создании. Это гарантирует, что блокировка будет установлена до того, как пользователь сможет начать взаимодействовать с данными документа.
Добавим следующий код в обработчик события формы документа ПриСозданииНаСервере:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Определяем ссылку на текущий документ.
// Если документ новый (не записан), то блокировать его бесполезно,
// так как он еще не существует в базе данных и не имеет уникальной ссылки.
Если Объект.ЭтоНовый() Тогда
Возврат;
КонецЕсли;
// Попытка установить блокировку на данные документа
Попытка
// Использование глобального метода ЗаблокироватьДанныеДляРедактирования()
// Ссылка: Ссылка на блокируемый объект (в нашем случае - документ)
// ,: Опускаем второй параметр, если не нужно блокировать записи регистра сведений
// ЭтаФорма.УникальныйИдентификатор: Идентификатор формы, к которой привязывается блокировка
// Это обеспечит автоматическое снятие блокировки при закрытии формы.
ЗаблокироватьДанныеДляРедактирования(Объект.Ссылка, , ЭтаФорма.УникальныйИдентификатор);
// Если блокировка успешно установлена, можем продолжить работу
// или выдать сообщение, что документ теперь заблокирован для других.
// Сообщить("Документ успешно заблокирован для редактирования этим сеансом.");
Исключение
// Если произошла ошибка (например, документ уже заблокирован другим пользователем)
ОписаниеОшибкиТекст = ОписаниеОшибки();
// Формируем сообщение для пользователя
СообщениеПользователю = Новый СообщениеПользователю;
СообщениеПользователю.Текст = "Не удалось открыть документ для редактирования!" + Символы.ПС;
// Попытаемся извлечь информацию о том, кто заблокировал документ
Если СтрНайти(ОписаниеОшибкиТекст, "Объект уже заблокирован:") > 0 Тогда
// Извлекаем часть текста с информацией о блокирующем пользователе/сеансе
ПозицияНачалаИнфо = СтрНайти(ОписаниеОшибкиТекст, "Объект уже заблокирован:");
СтрокаСИнфоОБлокировке = Сред(ОписаниеОшибкиТекст, ПозицияНачалаИнфо);
// Можно дополнительно распарсить СтрокаСИнфоОБлокировке для более детального вывода
// Например, выделить имя пользователя, компьютер, сеанс.
// Для упрощения, покажем всю строку как есть.
СообщениеПользователю.Текст = СообщениеПользователю.Текст + СтрокаСИнфоОБлокировке;
СообщениеПользователю.Текст = СтрЗаменить(СообщениеПользователю.Текст, "Объект уже заблокирован: ", "Документ заблокирован ");
Иначе
СообщениеПользователю.Текст = СообщениеПользователю.Текст + "Причина: " + ОписаниеОшибкиТекст;
КонецЕсли;
СообщениеПользователю.Сообщить();
// Устанавливаем Отказ = Истина, чтобы предотвратить открытие формы для редактирования.
// Можно также перевести форму в режим только для просмотра.
Отказ = Истина;
// Если хотим открыть в режиме просмотра, то:
// ЭтаФорма.ТолькоПросмотр = Истина;
// Сообщить("Документ открыт в режиме только для просмотра.");
// Отказ = Ложь; // чтобы форма открылась
КонецПопытки;
КонецПроцеду
Пояснение к коду:
ЗаблокироватьДанныеДляРедактирования() вызывается с передачей ссылки на текущий документ (Объект.Ссылка) и ЭтаФорма.УникальныйИдентификатор. Это ключевой момент, так как он связывает блокировку со временем жизни открытой формы.Попытка...Исключение необходим, потому что если документ уже заблокирован другим пользователем, вызов ЗаблокироватьДанныеДляРедактирования() вызовет исключение.Исключение мы перехватываем ошибку, получаем ее описание и формируем понятное сообщение для пользователя, указывая, кто и когда заблокировал документ.Отказ = Истина в обработчике ПриСозданииНаСервере предотвращает открытие формы документа. Вы также можете вместо этого установить ЭтаФорма.ТолькоПросмотр = Истина;, чтобы документ открывался в режиме чтения, но без возможности редактирования.Благодаря использованию ЭтаФорма.УникальныйИдентификатор в методе ЗаблокироватьДанныеДляРедактирования(), блокировка будет снята автоматически при закрытии формы. Это сильно упрощает логику, так как вам не нужно вручную вызывать РазблокироватьДанныеДляРедактирования() в обработчике ПриЗакрытииНаСервере, что минимизирует риски "зависших" блокировок.
Однако, если вы хотите иметь явный контроль или предусмотреть нестандартные сценарии, вы можете добавить вызов РазблокироватьДанныеДляРедактирования(Объект.Ссылка, , ЭтаФорма.УникальныйИдентификатор); в обработчике ПриЗакрытииНаСервере, но в большинстве случаев это будет излишним.
&НаСервере
Процедура ПриЗакрытииНаСервере()
// Если блокировка была успешно установлена с привязкой к форме,
// она будет снята автоматически при закрытии формы.
// Явный вызов РазблокироватьДанныеДляРедактирования() здесь, как правило, не требуется.
// Однако, если вы хотите явно снять блокировку или если блокировка была установлена
// без привязки к форме (что не рекомендуется для такого сценария), можно добавить:
// Если Не Объект.ЭтоНовый() Тогда
// РазблокироватьДанныеДляРедактирования(Объект.Ссылка, , ЭтаФорма.УникальныйИдентификатор);
// КонецЕсли;
КонецПроцеду
Несмотря на автоматическое снятие блокировок, привязанных к форме, в редких случаях (например, при аварийном завершении работы сервера 1С) блокировки могут "зависнуть". Для их снятия существуют следующие способы:
1Cv8.cdn из каталога информационной базы (предварительно сделав резервную копию).Рассмотренный механизм обеспечивает строгую блокировку для всех пользователей, имеющих право на редактирование документа. Если вам требуется более сложная логика (например, разрешение редактирования только автору документа, или определенным ролям), то эту логику нужно будет добавить поверх блокировки. Например, в том же ПриСозданииНаСервере, до попытки установки блокировки, вы можете проверить права текущего пользователя или соответствие его идентификатора автору документа и, при необходимости, сразу установить ЭтаФорма.ТолькоПросмотр = Истина;.
Следует отметить, что подход со строгой блокировкой "один пользователь в один момент времени" не всегда подходит для сценариев, где действительно требуется совместная работа над документом (например, над его табличными частями). Для такого "совместного редактирования" обычно используют другие механизмы:
В большинстве случаев, когда требуется предотвратить одновременное изменение всего документа, использование ЗаблокироватьДанныеДляРедактирования() с привязкой к форме является наиболее эффективным, надежным и простым решением, которое позволяет избежать потери данных и повысить удобство работы пользователей, информируя их о занятости документа.