Как установить блокировку на данные регистра при открытии формы обработки

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

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

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

Проблема управляемых и транзакционных блокировок

Прежде всего, проанализируем, почему нельзя просто использовать объект БлокировкаДанных. Управляемые блокировки в 1С работают исключительно внутри транзакции. Транзакция же на сервере 1С живет только в рамках одного серверного вызова. Как только процедура ПриСозданииНаСервере завершилась и управление вернулось на клиента (пользователю открылась форма), транзакция фиксируется, и все блокировки снимаются.

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

Решение 1: Использование метода ЗаблокироватьДанныеДляРедактирования

Этот метод позволяет установить «мягкую» блокировку, которая привязывается к идентификатору формы. Если другой пользователь попытается вызвать этот же метод для тех же данных, система выдаст исключение.

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


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

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


&НаСервере
Процедура ЗаблокироватьРегистраторыНаСервере(МассивРегистраторов)
    Для Каждого СсылкаНаДокумент Из МассивРегистраторов Цикл
        Попытка
            ЗаблокироватьДанныеДляРедактирования(СсылкаНаДокумент, , ЭтаФорма.УникальныйИдентификатор);
        Исключение
            Сообщить("Документ " + СсылкаНаДокумент + " уже редактируется другим пользователем!");
        КонецПопытки;
    КонецЦикла;
КонецПроцедуры

Решение 2: Создание системы семафоров через служебный регистр

Если блокировка отдельных документов или ключей записей вам не подходит (например, нужно заблокировать весь регистр по определенному разрезу или организации), наиболее надежным способом будет создание собственного механизма «семафоров».

Для этого выполним следующие шаги:

  1. Создадим служебный регистр сведений СлужебныеБлокировкиОбъектов с измерениями ИмяОбъекта, КлючБлокировки и ресурсами Пользователь, ИдентификаторСеанса, ДатаУстановки.
  2. При открытии обработки будем записывать в этот регистр информацию о том, что данные заняты.
  3. В модуле набора записей целевого регистра (в обработчике ПередЗаписью) добавим проверку: если в служебном регистре есть активная запись о блокировке, вызываем Отказ = Истина.

Пример кода при открытии формы:


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

Важный нюанс: Обязательно реализуйте механизм очистки «брошенных» блокировок. Если сеанс пользователя аварийно завершится, запись в регистре останется. При проверке блокировки всегда проверяйте, существует ли еще сеанс с указанным номером (используя ПолучитьСеансыИнформационнойБазы()).

Решение 3: Оптимистическая блокировка (Пост-контроль)

Рассмотрим ситуацию, когда блокировка на длительное время нежелательна из-за большого количества пользователей. В этом случае мы можем использовать стратегию «Оптимистической блокировки»:

  1. При заполнении таблицы в обработке мы запоминаем текущее состояние данных (например, сумму по регистратору или момент времени последней записи).
  2. Пользователь редактирует данные в форме сколько угодно долго.
  3. В момент нажатия кнопки «Выполнить изменения» мы открываем короткую транзакцию, снова считываем данные из регистра и сравниваем их с тем, что было при открытии.
  4. Если данные изменились — отменяем операцию и просим пользователя обновить форму.

Этот подход считается наиболее «цивилизованным» в высоконагруженных системах, так как он минимизирует время ожидания для всех участников процесса.

Резюме

Выбор метода зависит от строгости ваших требований:

← На главную