Каталог решений - "Прелести" БСП. Часть 1 — Дополнительные реквизиты / сведения

"Прелести" БСП. Часть 1 — Дополнительные реквизиты / сведения

"Прелести" БСП. Часть 1 — Дополнительные реквизиты / сведения

В наличии

История небольшого расследования — сломались дополнительные реквизиты, или как НЕ НАДО использовать УИД в программном коде.

Категория:

Описание

TLDR: Нашел в типовой интересный код. По-моему это грубая ошибка и нарушение стандартов. Подробности ниже.

Процедура ПриПолученииПредопределенныхНаборовСвойств(Наборы) Экспорт
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_ВнешниеПользователи";
	Набор.Идентификатор = Новый УникальныйИдентификатор("d9c30d48-a72a-498a-9faa-c078bf652776");
	Набор.Используется  = ПолучитьФункциональнуюОпцию("ИспользоватьВнешнихПользователей");
	
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_Пользователи";
	Набор.Идентификатор = Новый УникальныйИдентификатор("2bf06771-775a-406a-a5dc-45a10e98914f");
КонецПроцедуры

 

На своих внедрениях я активно использую дополнительные реквизиты (и реже сведения). На мой взгляд, этот механизм — одна из наиболее классных фишек БСП. В моей персональной библиотеке 1С (готовится к публикации на этом сайте) есть отдельный модуль для удобной работы с дополнительными реквизитами и сведениями.

 

Но недавно, создавая клиенту (УТ 11.4.8.73) нового пользователя, я заметил, что вкладка "Дополнительные реквизиты" даже не появляется, хотя у списка пользователей их должно быть 5. Зайдя в существующих пользователей, я обнаружил, что у них вкладка присутствует, но там находятся только уже заполненные допреквизиты, причем они зачеркнуты и в режиме "Только чтение". Обычно подобные эффекты возникают, если дополнительный реквизит помечен на удаление, но в данном случае все было на месте.

 

Поскольку "пользовательский" анализ не помогал, пришло время открывать Конфигуратор. Достаточно общеизвестно, что дополнительные реквизиты инициализируются в процедуре "ПриСозданииНаСервере" модулей форм соответствующих объектов метаданных. В данной версии за это отвечает следующий код:

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

Позволю себе прокомментировать не вполне очевидную конструкцию:

МодульУправлениеСвойствами = ОбщегоНазначения.ОбщийМодуль("УправлениеСвойствами");
МодульУправлениеСвойствами.ПриСозданииНаСервере(ЭтотОбъект, ДополнительныеПараметры);

На первый взгляд может показаться, что разработчики 1С перемудрили и стоило написать намного проще:

УправлениеСвойствами.ПриСозданииНаСервере(ЭтотОбъект, ДополнительныеПараметры);

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

 

В самой процедуре меня поджидал сюрприз:

Если Контекст.ОтложеннаяИнициализация Тогда

Судя по полному коду (не буду приводить его в статье, БСП доступно всем, можете взглянуть самостоятельно) подсистема дополнительных реквизитов поддерживает вариант работы, когда поля для реквизитов создаются в тот момент, когда пользователь открывает соответствующую вкладку (а не в момент открытия формы). И этот механизм использовался в справочнике "Пользователи". Сперва я подумал, что в механизме есть какая-то ошибка, но его отключение (с помощью переопределения процедуры в расширении) не исправило ошибку.

 

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

ВЫБРАТЬ
	НаборыСвойств.Набор КАК Набор,
	НаборыСвойств.ПорядокНабора КАК ПорядокНабора
ПОМЕСТИТЬ НаборыСвойств
ИЗ
	&НаборыСвойств КАК НаборыСвойств
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
	НаборыСвойств.Набор КАК Набор,
	НаборыСвойств.ПорядокНабора КАК ПорядокНабора,
	СвойстваНаборов.Свойство КАК Свойство,
	СвойстваНаборов.ПометкаУдаления КАК ПометкаУдаления,
	СвойстваНаборов.НомерСтроки КАК ПорядокСвойства
ПОМЕСТИТЬ СвойстваНаборов
ИЗ
	НаборыСвойств КАК НаборыСвойств
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.НаборыДополнительныхРеквизитовИСведений.ДополнительныеРеквизиты КАК СвойстваНаборов
		ПО (СвойстваНаборов.Ссылка = НаборыСвойств.Набор)
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ПланВидовХарактеристик.ДополнительныеРеквизитыИСведения КАК Свойства
		ПО (СвойстваНаборов.Свойство = Свойства.Ссылка)
ГДЕ
	НЕ СвойстваНаборов.ПометкаУдаления
	И НЕ Свойства.ПометкаУдаления
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
	Свойства.Ссылка КАК Свойство
ПОМЕСТИТЬ ЗаполненныеСвойства
ИЗ
	ПланВидовХарактеристик.ДополнительныеРеквизитыИСведения КАК Свойства
ГДЕ
	Свойства.Ссылка В(&Свойства)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	СвойстваНаборов.Набор КАК Набор,
	СвойстваНаборов.ПорядокНабора КАК ПорядокНабора,
	СвойстваНаборов.Свойство КАК Свойство,
	СвойстваНаборов.ПорядокСвойства КАК ПорядокСвойства,
	СвойстваНаборов.ПометкаУдаления КАК Удалено
ПОМЕСТИТЬ ВсеСвойства
ИЗ
	СвойстваНаборов КАК СвойстваНаборов

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	ЗНАЧЕНИЕ(Справочник.НаборыДополнительныхРеквизитовИСведений.ПустаяСсылка),
	0,
	ЗаполненныеСвойства.Свойство,
	0,
	ИСТИНА
ИЗ
	ЗаполненныеСвойства КАК ЗаполненныеСвойства
		ЛЕВОЕ СОЕДИНЕНИЕ СвойстваНаборов КАК СвойстваНаборов
		ПО ЗаполненныеСвойства.Свойство = СвойстваНаборов.Свойство
ГДЕ
	СвойстваНаборов.Свойство ЕСТЬ NULL
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
	ВсеСвойства.Набор КАК Набор,
	ВсеСвойства.Свойство КАК Свойство,
	ДополнительныеРеквизитыИСведения.ВладелецДополнительныхЗначений КАК ВладелецДополнительныхЗначений,
	ДополнительныеРеквизитыИСведения.ЗаполнятьОбязательно КАК ЗаполнятьОбязательно,
	ВЫБОР
		КОГДА &ЭтоОсновнойЯзык
			ТОГДА ДополнительныеРеквизитыИСведения.Заголовок
		ИНАЧЕ ВЫРАЗИТЬ(ЕСТЬNULL(СвойстваПредставления.Заголовок, ДополнительныеРеквизитыИСведения.Заголовок) КАК СТРОКА(150))
	КОНЕЦ КАК Наименование,
	ДополнительныеРеквизитыИСведения.ТипЗначения КАК ТипЗначения,
	ДополнительныеРеквизитыИСведения.ФорматСвойства КАК ФорматСвойства,
	ДополнительныеРеквизитыИСведения.МногострочноеПолеВвода КАК МногострочноеПолеВвода,
	ВсеСвойства.Удалено КАК Удалено,
	ДополнительныеРеквизитыИСведения.Доступен КАК Доступен,
	ДополнительныеРеквизитыИСведения.Виден КАК Виден,
	ВЫБОР
		КОГДА &ЭтоОсновнойЯзык
			ТОГДА ДополнительныеРеквизитыИСведения.Подсказка
		ИНАЧЕ ВЫРАЗИТЬ(ЕСТЬNULL(СвойстваПредставления.Подсказка, ДополнительныеРеквизитыИСведения.Подсказка) КАК СТРОКА(150))
	КОНЕЦ КАК Подсказка,
	ДополнительныеРеквизитыИСведения.ВыводитьВВидеГиперссылки КАК ВыводитьВВидеГиперссылки,
	ДополнительныеРеквизитыИСведения.ЗависимостиДополнительныхРеквизитов.(
		ЗависимоеСвойство КАК ЗависимоеСвойство,
		Реквизит КАК Реквизит,
		Условие КАК Условие,
		Значение КАК Значение
	) КАК ЗависимостиДополнительныхРеквизитов
ИЗ
	ВсеСвойства КАК ВсеСвойства
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ПланВидовХарактеристик.ДополнительныеРеквизитыИСведения КАК ДополнительныеРеквизитыИСведения
		ПО ВсеСвойства.Свойство = ДополнительныеРеквизитыИСведения.Ссылка
		ЛЕВОЕ СОЕДИНЕНИЕ ПланВидовХарактеристик.ДополнительныеРеквизитыИСведения.Представления КАК СвойстваПредставления
		ПО (СвойстваПредставления.Ссылка = ДополнительныеРеквизитыИСведения.Ссылка)
			И (СвойстваПредставления.КодЯзыка = &КодЯзыка)

УПОРЯДОЧИТЬ ПО
	Удалено,
	ВсеСвойства.ПорядокНабора,
	ВсеСвойства.ПорядокСвойства

Этот запрос делает две вещи — получает актуальные дополнительные реквизиты согласно данным в наборах и объединяет их с дополнительными реквизитами, заполненными в текущем объекте. Но у меня временная таблица "СвойстваНаборов" оказалась пустой. Запрос был перенесен в консоль, и на этапе настройки параметров выяснилось интересное — в справочнике "Наборы свойств дополнительных реквизитов и сведений" было два элемента с именем "Пользователи" — один предопределенный, а другой обычный. Причем в консоли предопределенный элемент давал нужные результаты, а обычный — пустую таблицу.

 

Создалось впечатление, что каким-то образом появился дубль, и система находит не тот элемент. Недолго думая, я решил попробовать удалить НЕ предопределенный элемент "Пользователи". Но удалить не получилось (ошибка, как выяснилось, была не только с пользователями):

Попытка удаления 

Я склонен воспринимать это сообщение как баг системы. Я четко вижу, что удаляю НЕ ПРЕДОПРЕДЕЛЕННЫЙ элемент, но система пытается убедить меня в обратном. У неё не получилось — я отловил место, в котором возникает это сообщение, и переопределил его в расширении на что-то пустое. После этого "предопределенный элемент" удалился.

 

Но после этого неожиданно перестала открываться форма справочника "Пользователи". Это меня, мягко говоря, удивило. Удивление выросло, когда я прошелся отладчиком и обнаружил, что в ходе процедуры открытия система получает "битую ссылку" на удаленный элемент справочника. Напоминаю, что элемент был удален "Удалением помеченных объектов", ссылок на него в системе не было. Я копнул чуть глубже, и обнаружил замечательную процедуру:

Процедура ПриПолученииПредопределенныхНаборовСвойств(Наборы) Экспорт
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_ВнешниеПользователи";
	Набор.Идентификатор = Новый УникальныйИдентификатор("d9c30d48-a72a-498a-9faa-c078bf652776");
	Набор.Используется  = ПолучитьФункциональнуюОпцию("ИспользоватьВнешнихПользователей");
	
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_Пользователи";
	Набор.Идентификатор = Новый УникальныйИдентификатор("2bf06771-775a-406a-a5dc-45a10e98914f");
КонецПроцедуры

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

1. Есть механизм предопределенных элементов, который, казалось бы, ПРЕДНАЗНАЧЕН для того чтобы не делать таких вещей.

2. Ломается обработка удаления — она не видит этой "неявной ссылки" на объект.

3. Ломается визуальный интерфейс предопределенных элементов.

Так или иначе, процедура была отрефакторена к чему-то, больше похожему на разумный программный код:

&Вместо("ПриПолученииПредопределенныхНаборовСвойств")
Процедура А1_ПриПолученииПредопределенныхНаборовСвойств(Наборы)
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_ВнешниеПользователи";
	//Набор.Идентификатор = Новый УникальныйИдентификатор("d9c30d48-a72a-498a-9faa-c078bf652776");
	Набор.Идентификатор = Справочники.НаборыДополнительныхРеквизитовИСведений.Справочник_ВнешниеПользователи.УникальныйИдентификатор();
	Набор.Используется  = ПолучитьФункциональнуюОпцию("ИспользоватьВнешнихПользователей");
	
	Набор = Наборы.Строки.Добавить();
	Набор.Имя = "Справочник_Пользователи";
	//Набор.Идентификатор = Новый УникальныйИдентификатор("2bf06771-775a-406a-a5dc-45a10e98914f");
	Набор.Идентификатор = Справочники.НаборыДополнительныхРеквизитовИСведений.Справочник_Пользователи.УникальныйИдентификатор();
КонецПроцедуры

На этом история заканчивается — после исправления формы открываются и дополнительные реквизиты показываются. Ждем следующих интересных решений от фирмы "1С".

has been added to your cart:
Оформление заказа