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