Способ хранения данных расширения или дополнительной обработки

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

Разработка - Практика программирования

Обработка БСП Настройки Начинающим

Описание способа хранения данных расширения или дополнительной обработки в информационной базе с использованием функционала Библиотеки стандартных подсистем.

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

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

В своей практике я использую другой способ: в конфигурациях, использующих Библиотеку стандартных подсистем, есть встроенные объекты и процедуры для хранения каких либо данных настроек. Это два регистра сведений - БезопасноеХранилищеДанных и БезопасноеХранилищеДанныхОбластейДанных.

 
 Регистры сведений БезопасноеХранилищеДанных и БезопасноеХранилищеДанныхОбластейДанных

Эти регистры предназначены для хранения какой-либо конфиденциальной информации. В качестве измерения Владелец можно использовать ссылку на элемент Плана обмена, Справочника или использовать строку до 128 символов. Тип ресурса Данные - ХранилищеЗначения, в который обычно записывается данные типа Структура. Данные такого регистра просто не "вытащить" в пользовательском режиме универсальным отчетом или консолью запросов. Конечно, кроме паролей и токенов, можно хранить данные других типов. 

Чтобы не разбираться какой из регистров когда использовать, для работы с этими регистрами сведений имеется программный интерфейс - процедура и функция общего модуля ОбщегоНазначения:

Процедура ЗаписатьДанныеВБезопасноеХранилище(Владелец, Данные, Ключ = "Пароль") Экспорт
Функция ПрочитатьДанныеИзБезопасногоХранилища(Владелец, Ключи = "Пароль", ОбщиеДанные = Неопределено) Экспорт

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

Пример реализации

Рассмотрим пример сохранения каких-либо настроек Дополнительной обработки. Для этого создадим форму Настройки. В форме создадим команду ЗаписатьИЗакрыть и выведем ее в виде кнопки по умолчанию.

Форма будет иметь реквизиты для редактирования: ГруппаНоменклатуры и Контр агент, и два служебных: КлючДанных и ДополнительнаяОбработкаСсылка.

Поместим в модуль формы следующий код:

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

&НаСервере
Процедура ЗаписатьИЗакрытьНаСервере()
	Данные=Новый Структура;
	Для Каждого Элемент из Элементы Цикл
		Если ТипЗнч(Элемент)=Тип("ПолеФормы") Тогда
			Данные.Вставить(Элемент.Имя, ЭтаФорма[Элемент.Имя])
		КонецЕсли;
	КонецЦикла;
	ОбщегоНазначения.ЗаписатьДанныеВБезопасноеХранилище(ДополнительнаяОбработкаСсылка, Данные, КлючДанных); 
КонецПроцедуры

&НаКлиенте
Процедура ЗаписатьИЗакрыть(Команда)
	ЗаписатьИЗакрытьНаСервере();
	Закрыть();
КонецПроцедуры

В процедуре ПриСозданииНаСервере проверяются параметры, переданные в форму. Если параметры содержат свойство ДополнительнаяОбработкаСсылка, то это значит что форма открыта с использованием подсистемы ДополнительныеОтчетыИОбработки и в качестве владельца настроек будет использован элемент справочника ДополнительныеОтчетыИОбработки. В противном случае владельцем будет строка - полное имя обработки. После определения владельца считываем данные из регистра с использованием функции программного интерфейса, и, если сохранённые данные редставляют собой структуру, заполняем ими значения реквизитов формы. Теперь при создании формы в значения реквизитов будут установлены ранее сохранённые значения.

Обработку команды ЗаписатьИЗакрыть передадим на сервер. Там сформируем структуру из значений всех полей, которые выведены на форму. И эту структуру запишем в регистр, используя процедуру программного интерфейса Библиотеки стандартных подсистем.

Собственно, с формой настроек дополнительной обработки всё. Подобный механизм можно использовать и для хранения дополнительных данных справочника, если для него не включен механизм ДополнительныеРеквизитыИСведения, либо нет подходящего типа значения дополнительного реквизита. 

Дополнительная обработка

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

Функция СведенияОВнешнейОбработке() Экспорт	
	ПараметрыРегистрации=ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке();
	ПараметрыРегистрации.Вид=ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительнаяОбработка();
	ПараметрыРегистрации.Версия="1.0.0.1";
	ПараметрыРегистрации.Наименование="ДемоСохранениеНастроек";
	ПараметрыРегистрации.Информация="Описание способа хранения данных расширения или дополнительной обработки в информационной базе с использованием функционала Библиотеки стандартных подсистем.";
	
	Команда=ПараметрыРегистрации.Команды.Добавить();
	Команда.Идентификатор="Настройка";
	Команда.Представление="Настройка";
	Команда.Использование=ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода();
	
	Команда=ПараметрыРегистрации.Команды.Добавить();
	Команда.Идентификатор="Выполнить";
	Команда.Представление="Прочитать настройки";
	Команда.Использование=ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовКлиентскогоМетода();
	
	Возврат ПараметрыРегистрации;
КонецФункции

Дополнительная обработка будет выполнять две команды на клиенте: Настройка и Выполнить. Для обработки логики этих команд создадим еще одну форму нашей обработки - ОсновнаяФорма и сделаем ее формой обработки по умолчанию.

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

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


&НаКлиенте
Процедура ВыполнитьКоманду(Идентификатор) Экспорт
	Если Идентификатор="Настройка" Тогда
		ОткрытьФорму("ВнешняяОбработка."+ИмяОбработки+".Форма.Настройки", Новый Структура("ДополнительнаяОбработкаСсылка",ДополнительнаяОбработкаСсылка));
	ИначеЕсли Идентификатор="Выполнить" Тогда
		Сообщить(СохраненыеДанные())
	КонецЕсли; 	
КонецПроцедуры

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

В процедуре ПриСозданииНаСервере производится чтение переданного через параметры формы свойства ДополнительнаяОбработкаСсылка, которое указывает на элемент справочника ДополнительныеОтчетыИОбработки. Экспортная процедура ВыполнитьКоманду вызывается программным интерфейсом подсистемы ДополнительныеОтчетыИОбработк. В ней, в зависимости от переданного параметра - идентификатора команды, открывается либо форма настроек, либо производится чтение отображение и настроек. Поскольку в БСП права на чтение и запись описываемых регистров хранения настроек доступны только для роли ПолныеПрава, то перед чтением сохраненных данных в функции Сохранены еДанные установим привилегированный режим. В этом случае доступ к настройкам может получить пользователь, в независимости от установленных прав. Хочу обратить внимание, как в функции Сохранены еДанные реализовано соединение строк. В отличие от традиционного соединения строк в цикле вида Стр=Стр+Значение, строки сначала помещаются в массив, а затем с помощью функции СтрСоединить соединяются. Этот вариант рекомендован для сложения строк в цикле с большим числом итераций, так как дает ощутимый выигрыш в производительности при крупных многопользовательских системах.

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

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

Комментарии
Избранное Подписка Сортировка: Древо развёрнутое
Свернуть все
3. Sedaiko 382 01.06.20 13:15 Сейчас в теме
(1) Спасибо за комментарий. Для доп.обработок это правильный способ. Но я этот вариант перестал использовать с тех пор, как начал помещаять свои доп.обработки сразу в расширение.
4. dhurricane 01.06.20 13:20 Сейчас в теме
(3) Что тогда Вы используете в качестве владельца сохраняемых данных?
5. Sedaiko 382 01.06.20 13:21 Сейчас в теме
(4)
В качестве измерения Владелец можно использовать ссылку на элемент Плана обмена, Справочника или использовать строку до 128 символов
14. МимохожийОднако 130 07.06.20 08:20 Сейчас в теме
(3) Скинь пример для расширения. Например, создал регистр сведений в расширении и мне его надо сохранить на случай удаления расширения
15. Sedaiko 382 07.06.20 08:53 Сейчас в теме
(14) Если бэкапить регистр сведений - то проще его сериализовать в xml и залить в прикрепленный файл используя подсистему БСП РаботаСФайлами.
А пример расширений не скину - они у меня все под конкретную задачу
16. independ 1103 07.06.20 10:51 Сейчас в теме
(14)
НаборЗаписей = РегистрыСведений.Штрихкоды.СоздатьНаборЗаписей(); 
Строка=ЗначениеВСтрокуВнутр(НаборЗаписей.Прочитать().Выгрузить()); 
МимохожийОднако; +1 Ответить
2. Новиков 291 01.06.20 13:03 Сейчас в теме
Спасибо! Интересный пример с двумя формами :)
13. dsdred 1428 02.06.20 09:49 Сейчас в теме
6. VmvLer 01.06.20 13:36 Сейчас в теме
кто-нить понял, тут надувают грелку или жабу?
7. davdykin 24 01.06.20 17:56 Сейчас в теме
Спасибо, интересно, во-первых обработка с двумя формами, во-вторых кусочек БСП ))
8. json 2658 02.06.20 05:52 Сейчас в теме
Поставил плюс за доступное описание механизма безопасного хранения данных.

Интересно было бы узнать в каких именно случаях следует использовать данный механизм, а в каких не следует.

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

Скорее этот механизм есть смысл использовать, когда надо хранить пароли, токены для встроенных объектов или я ошибаюсь?
9. Sedaiko 382 02.06.20 06:40 Сейчас в теме
(8) Дополнительные обработки - это в качестве примера работы. На практике в Безопасном хранилище следует хранить токены и пароли, так как от доступен только под полными правами. Обычно я описанное хранилище использую для обработок встроенных в расширение. А помещаю их в расширение для защиты - доступен модуль менеджера, который можно запаролить.
10. PerlAmutor 104 02.06.20 06:43 Сейчас в теме
(8) Внешние печатные формы. У нас много подразделений со своими ответственными лицами (руководители, бухгалтеры и т.д.). При формировании печатных форм, в подвале документа, должны стоять соответствующие расшифровки подписей. Каждый документ из которого формируется печатная форма должен эту информацию как-то хранить. Для этого я делал отдельный регистр настроек печатных форм, где было 3 измерения: Уникальный идентификатор документа, Уникальный идентификатор обработки, Имя параметра и ресурс Значение.

Используя БезопасноеХранилищеДанных можно сделать подобное. В качестве измерения Владелец хранить хэш от комбинации
Уникальный идентификатор документа + уникальное имя внешней печатной формы. В Данные помещать структуру со всеми параметрами.

Для формирования ключей в модуле ОбщегоНазначения из БСП есть замечательная функция:

Функция СократитьСтрокуКонтрольнойСуммой(Строка, МаксимальнаяДлина) Экспорт
	ОбщегоНазначенияКлиентСервер.Проверить(МаксимальнаяДлина >= 32, НСтр("ru = 'Параметр МаксимальнаяДлина не может быть меньше 32';
																		|en = 'The МаксимальнаяДлина parameter cannot be less than 32'"),
		"ОбщегоНазначения.СократитьСтрокуКонтрольнойСуммой");
	
	Результат = Строка;
	Если СтрДлина(Строка) > МаксимальнаяДлина Тогда
		Результат = Лев(Строка, МаксимальнаяДлина - 32);
		ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.MD5);
		ХешированиеДанных.Добавить(Сред(Строка, МаксимальнаяДлина - 32 + 1));
		Результат = Результат + СтрЗаменить(ХешированиеДанных.ХешСумма, " ", "");
	КонецЕсли;
	Возврат Результат;
КонецФункции
Показать


Остается только "склеивать" уникальную комбинацию измерений в одну строку по переданным параметрам и записывать в качестве владельца. В качестве минуса можно отметить факт того, что нельзя запросом получить информацию о том, что в данный момент находится в регистре. Например получить данные только по одному измерению, только те строки, которые используются для одной конкретной обработки. Частично эту проблему можно решить перебором всех строк регистра получая Данные из ХранилищаЗначений и поиском в них какой-то ожидаемой структуры. Но для этого придется писать отдельную функцию и скорость её работы может оставлять желать лучшего.

Так что этот регистр может подойти не для всех случаев.
11. Sedaiko 382 02.06.20 06:47 Сейчас в теме
(10)
получить данные только по одному измерению, только те строки, которые используются для одной конкретной обработки
А чем ПОДОБНО "%"+ИмяОбработки+"%" не подходит?
12. PerlAmutor 104 02.06.20 06:52 Сейчас в теме
(11) Там будет MD5 хэш не превышающий 128 символов вместо "человеческих" строк.
17. Cyberhawk 118 09.07.20 23:46 Сейчас в теме
перед чтением сохраненных данных в функции СохраненыеДанные установим привилегированный режим. В этом случае доступ к настройкам может получить пользователь, в независимости от установленных прав
Только если обработка подключена в небезопасном режиме. Иначе установка привилегированного режима просто будет молча проигноирована.
Оставьте свое сообщение

См. также

Публикаций не найдено

Попробуйте расширить область поиска, проверьте поисковый запрос и повторите попытку.

Или закажите индивидуальную разработку вашего решения.

Создать заказ на разработку