Каталог решений - Регистр сведений как дополнение объекта

Регистр сведений как дополнение объекта

Регистр сведений как дополнение объекта

В наличии

Подключаем регистры сведений к формам связанных с ними объектов, быстро и надежно.

Категория:

Описание

Лень – двигатель прогресса. Эту фразу любила повторять моя школьная преподаватель математики, и по совместительству директор школы, когда приводила примеры решения одной и той же задачи разными путями, один из которых выглядел много проще и короче остальных. Работа программиста в этот принцип вписывается как нельзя лучше. По большому счету, мы стремимся, чтобы система учета работала как часы, потому что лень заниматься поддержкой пользователей. Ведь гораздо удобнее, когда пользователь в поддержке не нуждается и не отвлекает лишний раз от более серьезных занятий, чем разбирательства с ошибкой в данных. Мы стремимся делать наш код удобочитаемым, потому что лень в него вчитываться, пытаясь понять, что же все-таки там происходит. В конечном итоге от лени мы стараемся дробить свой код на процедуры и функции так, чтобы его можно было использовать в последующих разработках уже не заморачиваясь отладкой куска, который всегда отлично работал и продолжает работать. И именно от лени многие из нас создают разного рода универсализмы.

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

Без регистров сведений сегодня сложно представить  какую-либо конфигурацию. Эта довольно несложная для понимания группа метаданных одновременно представляет собой мощное средство для решения возникающих у 1С-ника задач. Реквизиты компаний имеют свойство иногда меняться? – смотрим в сторону Регистров сведений.  Хотим учитывать подходящую к оборудованию номенклатуру запчастей? Снова к регистрам сведений. И не предлагайте табличные части, ведь менеджер, который закупает запчасти, тоже захочет оценить куда они подходят, когда сообразит, что ему это весьма полезно. Вот тогда с табличной частью-то и намучаемся.

Есть и менее очевидные вещи – если пересадить в 1С фанатичных любителей Excel, независимо от того добровольно  для них это было или нет, очень быстро появляются те, кому не нравится для редактирования документов открывать их в отдельной форме. Помучавшись с чтением / записью этих документов при активизации строки скоро начинаешь понимать – а ведь проще работая с такими господами ничего кроме номера, даты и пары реквизитов,  которые в здравом уме после его создания никто не соберется править, в самом документе не иметь (о документе говорим нестандартном, конечно же) а всё, что господа там дозаполняют по 20 раз на день  вынести все в тот же регистр сведений. Ну а если господа выдумали некую группу показателей, которую даже теоретически должны видеть только три человека, в то время как все остальные – половина пользователей, сами Боги велели вынести эти показатели в отдельную табличку, и естественно это будет регистр сведений.
Еще одно из решений, связанных с использованием регистров сведений, к которому пришел я (и вполне допускаю, что и еще много кто), причем задолго до появления таких технологий, как расширения, это вынесение в них нетиповой информации вместо добавления реквизитов или табличных частей  к основным объектам.  В свое время даже делал публикацию публикацию на эту тему.

Скрывать нечего, в моем случае наиболее богатым на всяческие дополняющие таблички оказался справочник номенклатуры. Помимо уже имеющихся из коробки УПП, касающихся его регистров сведений, добавлены еще несколько своих. Информация, хранимая в них, поделена на логически законченные блоки: один регистр хранит параметры, которые имеют значение при планировании закупок запчастей и материалов для ремонта оборудования, другой – сведения о размерах упаковок продукции предприятия, третий – необходимые для интеграции с головными предприятием параметры ну и т.д. У каждой подобной таблицы свой круг пользователей, которых интересует их участок, но соседний для них что-то слишком заумное и «лишнее». Естественно, что «лишнее» нужно прятать, а еще лучше вообще ограничить к нему доступ. Группировка данных в регистры сведений «по тематике»  тут оказывается очень кстати. Возьмем, к примеру, такие:

 

 

Положим, со структурой данных определились, насоздавали регистров в конфигурации, но что делать с интерфейсом? Ведь пользователя, который в восторге от того, что для занесения данных нужно пробежаться по нескольким таблицам найти очень сложно. Пользователю удобно, когда все в одном окне – открыл, скажем, все ту же номенклатуру и увидел все, что о ней интересно знать, а что и как там хранится в недрах базы ну совершенно не интересно. Решение есть, и оно весьма простое. В зависимости от того имитируем для пользователя набор реквизитов объекта или его табличную часть, на форму объекта добавляем реквизиты типа «РегистрСведенийМенеджерЗаписей» или «РегистрСведенийНаборЗаписей» размещаем на форме Элементы как захочется, в событии формы «ПриСозданииНаСервере» обеспечиваем их прочтение, в событии «ПослеЗаписиНаСервере» — запись.

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

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

&НаСервере
Процедура ПрочитатьРазмеры()

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

		РазмерыОбъектов.Объект = Номенклатура;
		
	КонецЕсли;	
	
КонецПроцедуры	

&НаСервере
Процедура ЗаписатьРазмеры()

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

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

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

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

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

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

	НаборЗаписейУФ = УпрФорма[ИмяРеквизитаУФ];

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

Процедура ПрочитатьМенеджерЗаписиРС(УпрФорма, ИмяРегистра, СтруктураОтбора, ИмяРеквизитаУФ = Неопределено) Экспорт

	Если ИмяРеквизитаУФ = Неопределено Тогда
		ИмяРеквизитаУФ = ИмяРегистра;	
	КонецЕсли;	

	МенеджерЗаписиУФ = УпрФорма[ИмяРеквизитаУФ];
	
	Запрос = ПодготовитьЗапросНаЧтениеРегистраСведений(ИмяРегистра, СтруктураОтбора);
	Выборка = Запрос.Выполнить().Выбрать();

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

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

	Для Каждого Зн Из ЗначенияЗаполнения Цикл
		Если Не ЗначениеЗаполнено(МенеджерЗаписи[Зн.Ключ]) Тогда
			МенеджерЗаписи[Зн.Ключ] = Зн.Значение;	
		КонецЕсли;
	КонецЦикла;	
	
	Писать = Ложь;
	Для Каждого Поле Из ПроверитьЗаполнение Цикл
		Если ЗначениеЗаполнено(МенеджерЗаписи[Поле.Ключ]) Тогда
			Писать = Истина;
			Прервать;	
		КонецЕсли;	
	КонецЦикла;	
	
	Если Писать Тогда
		МенеджерЗаписи.Записать();
	Иначе
		МенеджерЗаписи.Удалить();
	КонецЕсли;
	
КонецПроцедуры

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

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

КонецФункции

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

	Если МетаданныеРС.ПериодичностьРегистраСведений <> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический Тогда
		НеключевыеРеквизиты.Вставить("Период");
	КонецЕсли;
	Возврат НеключевыеРеквизиты;
	
КонецФункции	

 

    Вызывается это так:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)

	СтруктураОтбора = Новый Структура("Номенклатура", Номенклатура);
	inf_РаботаСДиалогамиСервер.ПрочитатьНаборЗаписейРС(ЭтаФорма, "вичи_ПрименимостьНоменклатуры",
		СтруктураОтбора, "Применимость");
	
	СтруктураОтбора2 = Новый Структура("Объект", Номенклатура);
	inf_РаботаСДиалогамиСервер.ПрочитатьМенеджерЗаписиРС(ЭтаФорма, "inf_РазмерыОбъектов",
			СтруктураОтбора2, "РазмерыОбъектов");
КонецПроцедуры

&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи) 

	СтруктураОтбора = Новый Структура("Номенклатура", Объект.Ссылка);
	ЗначенияЗаполнения = Новый Структура("Назначение",
			ПредопределенноеЗначение("Перечисление.inf_НазначенияИспользованияНоменклатуры.ТехническоеОбслуживание"));
		
	inf_РаботаСДиалогамиСервер.ЗаписатьНаборЗаписейРС(ЭтаФорма,  "inf_ПрименимостьНоменклатуры", СтруктураОтбора,
				"Применимость", ЗначенияЗаполнения);

	СтруктураОтбора2 = Новый Структура("Объект", Номенклатура);	
	inf_РаботаСДиалогамиСервер.ЗаписатьМенеджерЗаписейРС(ЭтаФорма, "inf_РазмерыОбъектов",
			СтруктураОтбора2, "РазмерыОбъектов");

КонецПроцедуры

 
Основных процедур получилось четыре: ПрочитатьНаборЗаписейРС, ЗаписатьНаборЗаписейРС, ПрочитатьМенеджерЗаписиРС, ЗаписатьМенеджерЗаписейРС с почти идентичными параметрами:

  • УпрФорма – форма, к которой выполняем подключение, обязательный параметр
  • ИмяРегистра – Имя регистра сведений в дереве метаданных, обязательный параметр
  • СтруктураОтбора – ключ структуры аналогичен имени измерения регистра, по которому требуется отфильтровать записи, значение – собственно значение, по которому фильтруем, обязательный параметр
  • ИмяРеквизитаУФ – Имя реквизита на переданной в первом параметре форме, в который / из которого нужно выполнить чтение / запись, необязательный параметр, если не задан, имя реквизита будет считаться идентичным имени регистра (второй параметр)
  • ЗначенияЗаполнения – есть только в процедурах записи, позволяет автоматически проставить значения ресурсов и реквизитов регистра, которыми пользователь не управляет, но они требуются нам для корректной работы системы.

Теперь получается много удобнее и быстрее. Больше не нужно расписывать очередной регистр, интегрируя его на форму, пару строк и все, что нужно прочитано и в нужный момент записано. Справедливости ради, должен заметить, что покрывая 99% подобных задач, всеобщей панацеей это решение все-таки не является. Исключением будут те редкие случаи, когда в редактировании одного и того же объекта одновременно участвуют несколько пользователей. В таком режиме остается место для ситуации, когда один из пользователей открывает объект и долго-нудно его поправляет. В это время второй открывает, быстро вносит свою лепту и сохраняет. Когда же первый заканчивает, он записывает свою версию, в том числе затерев то, что сделал его коллега. Вероятность такой ситуации всегда нужно учитывать. Если в нашей номенклатуре она нулевая, то в гипотетической системе управления задачами она вполне реальна. На те случаи выход тоже есть и заключается в использовании динамических списков с отборами, но это уже отдельная история со своей спецификой и своими подводными камнями.

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