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

Публикация № 1088885

Программирование - Ошибки в отраслевых решениях

БСП Дополнительные реквизиты

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

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С".

30

См. также

Специальные предложения

Комментарии
Избранное Подписка Сортировка: Древо
1. riposte 241 04.07.19 15:44 Сейчас в теме
Может ТиУ исправило бы ситуацию? Пробовали?
Просмотрел сейчас две конфигурации на наличие функции ПриПолученииПредопределенныхНаборовСвойств - ничего нет. Обе используют БСП разной степени свежести. Скорее всего это является следствием или чей-то доработки, или спецификой конкретно этой конфигурации.
3. Enigma 34 04.07.19 16:31 Сейчас в теме
(1) , (2) - БСП 3.0.2.300. Конфигурация практически типовая (УТ), БСП определенно никто не трогал. По поводу сломается - благодарю за информацию, надо будет аккуратно проверить все при обновлении. В любом случае, предопределенные элементы добавлены в расширение, обновить "забыв" про них не получится.

Но вообще - кто-нибудь может объяснить, зачем 1С-ники пихают УИДы в код конфигурации?
4. riposte 241 04.07.19 16:42 Сейчас в теме
(3)
Сложно сказать. Скорее всего, попало в обновление по запарке.
Тут как бы никто не спорит, что это чьи-то левые гуиды прилетели и таких объектов в базе не будет.
В чем был сакральный смысл добавлять их так, учитывая, что это все предопределенные элементы - останется тайной.
Иногда тоже уже на износе пишешь какой-то код и откровенно засыпаешь. Потом просыпаешься, читаешь что написал и волосы дыбом встают.
2. SlavaKron 04.07.19 16:23 Сейчас в теме
Проверил в БСП 3.0.3.17.
Процедура ПриПолученииПредопределенныхНаборовСвойств есть в том виде, как у автора.
А вот предопределенные элементы:
Справочники.НаборыДополнительныхРеквизитовИСведений.Справочник_ВнешниеПользователи
Справочники.НаборыДополнительныхРеквизитовИСведений.Справочник_Пользователи.УникальныйИдентификатор
отсутствуют. Есть УдалитьСправочник_ВнешниеПользователи и УдалитьСправочник_Пользователи.
Автор, уточните какая версия БСП у вас, потому что ваше исправление поломается на версии 3.0.3.
5. Erne100 244 05.07.19 07:56 Сейчас в теме
Но недавно, создавая клиенту (УТ 11.4.9.73) нового пользователя
Опечатка с не вышедшим релизом отвлекала меня от смысла статьи... Может Вы имели в виду релиз 11.4.8.73? ...содержание +
7. Enigma 34 05.07.19 09:46 Сейчас в теме
(5) Действительно опечатка. Поправлю.
6. dhurricane 05.07.19 08:45 Сейчас в теме
Есть подозрение, что некорректно была обновлена конфигурация УТ. Да, действительно в свежих версиях БСП разраотчики перешли от "предопределенных в объекте" к "предопределенным в коде". Чтобы этот переход был корректным и не задублировал уже существующие элементы справочников, был реализован механизм подмены старых предопределенных на новые во время выполнения обработчиков обновления. Сопоставляются они по имени предопределенного. При этом очень важно, чтобы имена предопределенных в конфигурации получили префикс "Удалить". Судя по приведенному Вами коду, этого не случилось, у Вас имена предопределенных старые: "Справочник_ВнешниеПользователи" и "Справочник_Пользователи".

Это к вопросу, как могло все поломаться.

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

Но все это мои догадки, к сожалению ничем не подкрепленные.
serg_gres; CSiER; A_Max; acanta; +4 Ответить
12. qwinter 596 05.07.19 18:17 Сейчас в теме
(6)
Что касается вопроса, зачем разработчики БСП все это затеяли, то к моему сожалению, никакой информацией я не располагаю.
Это уже не секрет. 1С своим разработчикам платит за количество кода, вот некоторые редиски и плодят такой код ради кода. Меня на днях вывело из себя появление в ЗУПе в регистре "Данные для подбора сотрудников" второго измерения "Наименование" (ранее было одно "Сотрудник") которое используется в одном единственном месте, в списке сотрудников где по нему идет соединение с наименованием сотрудника. В итоге пользователи получили тормоза и исчезнувших из списка сотрудников, а программисты лишний геморрой.
8. Dmitryiv 120 05.07.19 10:17 Сейчас в теме
О сколько нам открытий чудных
Готовят просвещенья дух
И Опыт, сын ошибок трудных,
И Гений, парадоксов друг,
И Случай, бог изобретатель...

Пушкин А.С., 1829
9. SlavaKron 05.07.19 11:41 Сейчас в теме
Покопался я в демо УТ 11.4.8.73. В общем зря вы удалили этот элемент. Он нужен был разработчикам и поэтому они поставили заглушку на его удаление. Да, в типовой должно быть 2 элемента справочника НаборыДополнительныхРеквизитовИСведений "Пользователи": один предопределённый, другой – нет. У непредопределённого УИД – "2bf06771-775a-406a-a5dc-45a10e98914f". И все доп. реквизиты работают именно с непредопределённым. Добавление доп. реквизитов в предопределенный набор "Пользователи" не имеет результата. Стало быть, у разработчиков на него были планы. Скорее всего, предопределенные будут удалены в следующих релизах.
Прикрепленные файлы:
IntellXeon; user774630; +2 Ответить
10. Enigma 34 05.07.19 11:50 Сейчас в теме
(9) Я потом понял, что возможно у меня были неправильно назначены наборы реквизитов (как это получилось - другой вопрос, обновление было штатным). Правда, я припоминаю, что вроде как я пытался это исправить, но это не дало результатов.

В любом случае, "предопределять" объект в коде и потом смущать администраторов считаю дурным тоном. Пока что у меня все работает, причем ИМО более правильно. Эту УТ обновляем не очень часто, поэтому вопрос откладывается.
11. jONES1979 05.07.19 16:42 Сейчас в теме
Cпасибо за пост!
Расследования - это самые интересные темы!
13. ellavs 598 14.07.19 21:48 Сейчас в теме
Как раз прикручиваю в конфигурации механизм допреквизитов.
В ИТС по БСП (использую версию 3.0.3) так и написано:
7. В процедуре ПриПолученииПредопределенныхНаборовСвойств общего модуля УправлениеСвойствамиПереопределяемый описать предопределенную группу с именем Справочник_<ИмяОбъекта>, если объект – справочник; Документ_<ИмяОбъекта>, если объект – документ, и т. д. Например, для справочника Номенклатура создана предопределенная группа Справочник_Номенклатура:

Набор = Наборы.Строки.Добавить();
Набор.Имя = "Справочник_Номенклатура";
Набор.ЭтоГруппа = Истина;
Набор.Идентификатор = Новый УникальныйИдентификатор("c7cd91d8-6f8a-4d10-82bf-c6fba8475a98");


В демо базе БСП они используют этот же код.

В поисках того, какой же идентификатор мне нужно написать для своего документа, попалась эта публикация.
Решение, к сожалению, не подошло.
Так и не понятно, какой всё-таки идентификатор нужно использовать? Любой (т.е. придумать GUID самому) или какой-то конкретный?
Прикрепленные файлы:
14. ellavs 598 15.07.19 13:27 Сейчас в теме
(13) упс, не тот скрин приложила (в этом ошибка, которую я потом исправила, но всё равно не работало).
В итоге еще и баг БСП нашла, который некорректно обрабатывал добавляемый предопределенный элемент в справочник Наборов, если это группа (т.е. задано Набор.ЭтоГруппа = Истина;), которая используется для случая, описанного в ИТС как "Особые случаи настройки наборов свойств. Настройка объектов с разными наборами свойств у разных групп объектов". Настроила всё в точности по инструкции, но элемент записывался в справочник наборов упорно не как группа, в итоге при попытке добавить дочерний элемент (из спец.справочника на базе которого формировались наборы реквизитов) выходила ошибка на строке в модуле БСП "УправлениеСвойствами" "НаборСвойствОбъект.Записать(); по причине: Значение поля "Входит в группу" должно быть группой".

Для исправления ищем Общий модуль БСП УправлениеСвойствамиСлужебный, процедура СоздатьНаборСвойств:
Заменяем строку:
Если СвойстваНабора.ДочерниеНаборы = Неопределено Или СвойстваНабора.ДочерниеНаборы.Количество() = 0 Тогда
НА
Если (СвойстваНабора.ДочерниеНаборы = Неопределено Или СвойстваНабора.ДочерниеНаборы.Количество() = 0) И Не СвойстваНабора.ЭтоГруппа Тогда

День убила на поиск этой ошибки.
15. dhurricane 15.07.19 13:43 Сейчас в теме
(13)
придумать GUID самому
Да, самому. Я обычно пользуюсь для этих целей различными онлайн-генераторами.
Оставьте свое сообщение