Как в 1С реализовать выбор элемента справочника только один раз?

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

При разработке прикладных решений на платформе «1С:Предприятие 8.3» часто возникает задача обеспечения уникальности связей между объектами. Классический пример — конфигурация для учебного заведения, где необходимо распределить темы дипломных проектов между студентами так, чтобы одна и та же тема не могла быть выбрана дважды. В этой статье мы подробно разберем, как спроектировать такую систему, используя лучшие практики разработки, механизмы платформы 1С и современные подходы к UX/UI.

Способ 1. Использование Регистра сведений (Профессиональный архитектурный подход)

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

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

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

Проанализируем важный архитектурный нюанс — выбор режима записи регистра сведений. Регистр может быть независимым или подчиненным регистратору. Мы крайне рекомендуем использовать подчиненный режим:

Рассмотрим пример кода в обработчике проведения документа ЗакреплениеТемы:


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

Способ 2. Улучшение UX (Исключение занятых тем из списка выбора)

Вызывать системную ошибку при попытке записать дубликат — надежный, но не самый дружелюбный к пользователю подход. Намного лучше сделать так, чтобы методист или студент при заполнении документа просто не видел уже занятые темы в списке выбора.

Разберем по шагам, как исключить уже занятые темы из выпадающего списка автоподбора с помощью обработчика ОбработкаПолученияДанныхВыбора в модуле менеджера справочника ТемыРабот:

  1. Откройте модуль менеджера справочника ТемыРабот.
  2. Создайте шаблон процедуры ОбработкаПолученияДанныхВыбора.
  3. Напишите запрос, который выбирает все темы из справочника, за исключением тех, которые уже присутствуют в регистре сведений ЗанятыеТемыДипломов.

Посмотрим на пример реализации данного обработчика:


Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка)
	
	// Отключаем стандартную генерацию списка выбора платформой
	СтандартнаяОбработка = Ложь;
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	Темы.Ссылка КАК Ссылка,
		|	Темы.Наименование КАК Представление
		|ИЗ
		|	Справочник.ТемыРабот КАК Темы
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЗанятыеТемыДипломов КАК ЗанятыеТемы
		|		ПО Темы.Ссылка = ЗанятыеТемы.Тема
		|ГДЕ
		|	ЗанятыеТемы.Тема ИСЛУЛЛ
		|	И НЕ Темы.ПометкаУдаления";
	
	ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
	
	СписокЗначенийВыбора = Новый СписокЗначений;
	Для Каждого Строка Из ТаблицаРезультата Цикл
		СписокЗначенийВыбора.Добавить(Строка.Ссылка, Строка.Представление);
	КонецЦикла;
	
	ДанныеВыбора = СписокЗначенийВыбора;
	
КонецПроцедуры

Благодаря этому коду при попытке ввести название темы в поле ввода или при нажатии на кнопку выпадающего списка пользователь увидит исключительно свободные темы дипломных работ.

Способ 3. Предотвращение «Гонки данных» (Транзакционные блокировки)

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

Для предотвращения этой проблемы в транзакции проведения документа необходимо использовать механизм БлокировкаДанных. Разберем подробнее, как принудительно заблокировать запись конкретной темы в регистре перед осуществлением проверки ее занятости:


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

Способ 4. Простой вариант для учебных задач (Реквизит «Занята»)

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

Реализуем данный метод по следующему алгоритму:

  1. В справочнике ТемыРабот создайте реквизит Занята с типом данных Булево.
  2. В свойствах поля формы вашего документа, где выбирается тема, настройте параметры выбора. Установите фиксированный отбор: Отбор.Занята(Ложь).
  3. В модуле проведения документа программно изменяйте значение этого реквизита.

Проанализируем пример кода для изменения статуса темы при проведении и отмене проведения документа:


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

Процедура ОбработкаУдаленияПроведения(Отказ)
	
	// Снимаем признак занятости темы при отмене проведения
	ОбъектТема = ТемаРаботы.ПолучитьОбъект();
	ОбъектТема.Занята = Ложь;
	ОбъектТема.Записать();
	
КонецПроцедуры

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

Способ 5. Использование Регистра накопления для групповых проектов

Иногда логика процесса усложняется: например, над одной сложной комплексной темой дипломного проекта могут работать два или три студента одновременно. В таком случае жесткий контроль уникальности регистра сведений не позволит закрепить тему повторно.

Выходом из этой ситуации является использование Регистра накопления остатков (например, под названием ДоступныеМестаПоТемам):

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

← На главную