новое событие
Информационный поток
Задания вакансии материалы разработки сообщения форума

Конфигурирование методом «структурного невмешательства».

  • Добавить свою публикацию
  • для этого требуется регистрация

В 2005 году руководством было решено прекратить поддержку самописного программного обеспечения и перейти на 1С версии 8.0 «Управление производственным предприятием». При этом было принято «политическое решение» минимизировать изменения в стандартной конфигурации, чтобы обеспечить дальнейшую совместимость с последующими обновлениями, выпускаемыми компанией 1С. А так как наша фирма довольно крупная (на тот момент мы были одними из первых в России, кто приобретал SQL версию на 100 лицензий), то было необходимо довольно большое число доработок и настроек конфигурации. Так и родилась концепция «неизменяемой конфигурации». При этом базовый функционал остается практически без изменений и обновление конфигурации занимает на порядок меньше времени. Конечно, везде есть плюсы /минусы, и данная концепция может вызывать споры и использоваться далеко не везде. Проблема в том, что зачастую программисты не до конца понимают все тонкости бизнес-процессов на фирме. И нет людей, которые «видят» всю систему целиком и понимают как та или иная модификация отражается на функционировании этой программной системы. Начальники подразделений тянут одеяло на себя и вносят изменения, необходимые на их взгляд. При этом учетная система расплывается, теряет стройность, обрастает «подпорами». А когда конфигурацию «изменять нельзя» появляется некая постоянная точка опоры. И бывает проще перестроить бизнес-процесс, чтобы можно было обойтись стандартными средствами конфигурации. В общем, в нашем случае концепция показала свою дееспособность на практике. И даже если ею не пользоваться в полном объеме, некоторые решения всё равно могут найти применение. Итак, о сути концепции.

Начнем с того, что как не старайся – изменения вносить все равно придется. Изменения в модули форм/объектов вносятся с помощью специализированной конфигурации «Автоапдейтер». Работе в ней посвящена отдельная статья. Реквизиты объектов, как и сами объекты, можно только добавлять - ни в коем случае не изменять/удалять/переименовывать. Причем каждое добавление тщательно продумывать – зачастую его можно тем или иным образом избежать.

Необходимо создать как минимум 2 общих модуля: клиент/серверный и модуль с полными правами. Кроме того необходимо хранить огромное множество разнообразных по формату настроек. Для этого были созданы две универсальные структуры : План видов характеристик «Категории метанастроек» и регистр сведений «Метанастройки».Формат их следующий :

рис.1 Категории метанастроек

  • ИД – строковый идентификатор настройки. Уникален. Например «ДополнительноеМеню».
  • Объект – любая ссылка
  • Показатель – Строка, число, дата, булево
  • Хранилище – хранилище значения

рис.2 Регистр сведений "Метанастройки"

  • Категория настройки - Ссылка на элемент плана видов характеристик «Категории метанастроек», описанный выше
  • Ведущий объект – Любая ссылка
  • Объект – Любая ссылка
  • Значение настройки – Произвольный составной тип

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

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

 

// Параметры, используемые в функции...
// ИДНастройки - строковый идентификатор искомой настройки. Уникален.
// НаименованиеНастройки - Если задано, то настройка будет создана с этим наим. в случае отсутствия. Иначе вернется Неопределено 
// ИДРодителей - строковые идентификаторы родителей,разделенные символом "/" Например "Общие/Настройки/Ценообразование"
// Необязательный параметр. Нужен в том случае, если необходимо создавать настройку при отсутствии
// НаименованияРодителей - наименования родителей,разделенные "/", если не заданы будет использован ИДРодителя
// СписокТиповНастройки - Список типов настройки. Форматы
// 1.ОписаниеТипов - конкретное описание типов
// 2.Массив строк - описаний типов
// 3.Список типов через запятую. Например "Строка,СправочникСсылка.Контрагенты,Дата"
// 4.Любое значение - тип будет взят с него.
Функция ПолучитьКатегориюМетанастройки(ИДНастройки,НаименованиеНастройки = Неопределено, 
									Знач ИДРодителей = Неопределено,
									Знач НаименованияРодителей = Неопределено, 
									СписокТиповНастройки = Неопределено ) Экспорт
	
	Настройка = ПланыВидовХарактеристик.КатегорииМетанастроек.НайтиПоРеквизиту("ИД",ИДНастройки);
	Если Настройка.Пустая() И НаименованиеНастройки <> Неопределено Тогда
		Попытка	
			НачатьТранзакцию();
			
			//Ищем родителей по ИД
			ИДРодителей = СтрЗаменить(ИДРодителей,"/",Символы.ПС);
			Если НаименованияРодителей <> Неопределено Тогда
				НаименованияРодителей = СтрЗаменить(НаименованияРодителей,"/",Символы.ПС);
			КонецЕсли;
			
			Родитель = ПланыВидовХарактеристик.КатегорииМетанастроек.ПустаяСсылка();
			Для К = 1 По СтрЧислоСтрок(ИДРодителей)  Цикл
				ТекИД = СтрПолучитьСтроку(ИДРодителей,К);
				ПредРодитель = Родитель;
				Родитель = ПланыВидовХарактеристик.КатегорииМетанастроек.НайтиПоРеквизиту("ИД",ТекИД,Родитель);
				Если Родитель.Пустая() Тогда
					// создаем, если нету
					РодительОбъект = ПланыВидовХарактеристик.КатегорииМетанастроек.СоздатьГруппу();
					РодительОбъект.ИД = ТекИД;
					РодительОбъект.Наименование = ?(НаименованияРодителей <> Неопределено,СтрПолучитьСтроку(НаименованияРодителей,К),ТекИД);
					РодительОбъект.Родитель = ПредРодитель;
					РодительОбъект.Записать();
					Родитель = РодительОбъект.Ссылка;
				КонецЕсли;
			КонецЦикла;
			
			//Создаем настройку
			НастройкаОбъект =  ПланыВидовХарактеристик.КатегорииМетанастроек.СоздатьЭлемент();
			НастройкаОбъект.ИД = ИДНастройки;
			НастройкаОбъект.Родитель = Родитель;
			НастройкаОбъект.Наименование = НаименованиеНастройки;
			
			//Определяем ее тип
			Если СписокТиповНастройки <> Неопределено Тогда
				
				Если ТипЗнч(СписокТиповНастройки) = Тип("ОписаниеТипов") Тогда
					//передано описание типов
					ОписаниеТипов = СписокТиповНастройки;
					
				ИначеЕсли ТипЗнч(СписокТиповНастройки) = Тип("Массив") Тогда
					//Массив строк - названий типов
					мТипов = Новый Массив();
					Для К=0 По СписокТиповНастройки.Количество()-1 Цикл
						мТипов.Добавить(Тип(СписокТиповНастройки[К]));	
					КонецЦикла;	
					ОписаниеТипов = Новый ОписаниеТипов(мТипов);
					
				ИначеЕсли ТипЗнч(СписокТиповНастройки) = Тип("Строка") Тогда
					//Список типов через запятую
					СписокТиповНастройки = СтрЗаменить(СписокТиповНастройки,",",Символы.ПС);
					мТипов = Новый Массив();
					Для К = 1 По СтрЧислоСтрок(СписокТиповНастройки)  Цикл
						мТипов.Добавить(Тип(СтрПолучитьСтроку(СписокТиповНастройки,К)));	
					КонецЦикла;
					ОписаниеТипов = Новый ОписаниеТипов(мТипов);
				Иначе
					//Попытаемся определить тип по значению
					мТипов = Новый Массив();
					мТипов.Добавить(ТипЗнч(СписокТиповНастройки));
					ОписаниеТипов = Новый ОписаниеТипов(мТипов);
				КонецЕсли;
			КонецЕсли;
			
			НастройкаОбъект.ТипЗначения = ОписаниеТипов;
			НастройкаОбъект.Записать();
			ЗафиксироватьТранзакцию();
			Возврат НастройкаОбъект.Ссылка;
		Исключение
			Сообщить("Ошибка создания метанастройки: "+ОписаниеОшибки());
			ОтменитьТранзакцию();
			Возврат Неопределено;
		КонецПопытки;	
	ИначеЕсли НЕ Настройка.Пустая() Тогда
		Возврат Настройка;
	Иначе
		Возврат Неопределено;
	КонецЕсли;
КонецФункции // ПолучитьКатегориюМетанастроек
Функция ПолучитьРодителяМетанастройки(Знач ИДРодителей = Неопределено, 
									  Знач НаименованияРодителей = Неопределено) Экспорт
	ИДРодителей = СтрЗаменить(ИДРодителей,"/",Символы.ПС);
	ИДПоследнего = СтрПолучитьСтроку(ИДРодителей,СтрЧислоСтрок(ИДРодителей));
	Родитель = ПланыВидовХарактеристик.КатегорииМетанастроек.НайтиПоРеквизиту("ИД",ИДПоследнего);
	Если Родитель.Пустая() И НаименованияРодителей <> Неопределено Тогда
		
		НаименованияРодителей = СтрЗаменить(НаименованияРодителей,"/",Символы.ПС);
		Родитель = ПланыВидовХарактеристик.КатегорииМетанастроек.ПустаяСсылка();
		Попытка
			НачатьТранзакцию();
			Предок = ПланыВидовХарактеристик.КатегорииМетанастроек.ПустаяСсылка();
			Для К = 1 По СтрЧислоСтрок(ИДРодителей)  Цикл
				ТекИД = СтрПолучитьСтроку(ИДРодителей,К);
				Родитель = ПланыВидовХарактеристик.КатегорииМетанастроек.НайтиПоРеквизиту("ИД",ТекИД,Предок);
				Если Родитель.Пустая() Тогда
					// создаем, если нету
					РодительОбъект = ПланыВидовХарактеристик.КатегорииМетанастроек.СоздатьГруппу();
					РодительОбъект.ИД = ТекИД;
					РодительОбъект.Наименование = ?(НаименованияРодителей <> Неопределено,СтрПолучитьСтроку(НаименованияРодителей,К),ТекИД);
					РодительОбъект.Родитель = Предок;
					РодительОбъект.Записать();
					Родитель = РодительОбъект.Ссылка;
					Предок = Родитель;
				Иначе
					Предок = Родитель;
				КонецЕсли;
			КонецЦикла;
			ЗафиксироватьТранзакцию();
			Возврат Родитель;
		Исключение
			Сообщить("Ошибка создания метанастройки: "+ОписаниеОшибки());
			ОтменитьТранзакцию();
			Возврат Неопределено;
		КонецПопытки;	
	ИначеЕсли ИДРодителей = Неопределено Тогда
		Возврат Неопределено;
	ИначеЕсли Не Родитель.ЭтоГруппа Тогда
		Возврат Неопределено;
	Иначе	
		Возврат Родитель;
	КонецЕсли;
	
КонецФункции // ПолучитьРодителяМетанастройки 

 

 

 

Функции получают/создают если нету нужные категории и группы метанастроек.

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

рис.3 Дополнительное меню

Список отображаемых элементов для каждого пользователя/группы пользователей можно сделать различным, отображая только нужные пункты. Назначение горячих клавиш, добавление и удаление пунктов меню производятся обработкой «Редактор дополнительного меню». В самом меню для полных прав действуют клавиши ctrl + F12 – обновить текущую обработку из файла, что позволяет оперативно вносить изменения.

Для интеграции дополнительного меню в общий модуль следует внести следующие

 

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

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

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

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

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

В конфигурации в общий для всех пользователей интерфейс нужно добавить новый пункт меню "Дополнительное меню" с горячей клавишей F12 и привязать к нему помещенную ранее в общий модуль одноименную процедуру "ДополнительноеМеню()".

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

 
0
Читайте также
Настройка 1С
Установка любого продукта 1С требует дальнейшей настройки под особенности фирмы. В дальнейшем, настройка производится при выявлении ошибок или изменениях в работе предприятия.
Продажа товара собственным юр. лицам - зачем это надо и как это сделать в 1С:Управление Торговлей 8
Необходимость продажи товара между собственными юридическими лицами
Пример перевода конфигурации на управляемый интерфейс
Когда количество изменений на предприятии превышает критический уровень в сторону необновляемых конфигураций - 1С предлагает создание управляемого приложения
Разработки
Нумератор документов 7.7
Как установить нумерацию документов? Можно использовать во всех конфигурациях.
Универсальный журнал документов
Разработка универсальный журнал документов
Интеграция 1С 8 "Управление торговлей" и VirtueMert
Интеграция 1С Предприятие 8 УТ10.3
Еще от автора
≡ к списку статей