Продолжаем знакомиться с правилами доработки типовых конфигураций, соблюдение которых позволяет значительно облегчить последующее обновление и поддержку измененных конфигураций.
Первую часть читайте здесь. В этой статье будет рассказано о некоторых приемах и методах, которые, по моему мнению, обязательно должны присутствовать в редактируемой конфигурации вне зависимости от масштаба проекта.
0. Оглавление
- Использование хранилища конфигураций
- Самоидентификация тестовых баз
- Обработка инициализации
- Справочник предопределённых значений
- Просмотр временных таблиц в отладчике
- Версии внешних отчётов и обработок
1. Использование хранилища конфигураций
Разработка обязательно должна вестись с использованием хранилища конфигурации. В некоторых случаях хранилищ конфигураций может быть несколько. Например, в схеме ниже используется 2 независимых хранилища:
- Для разработочных и одной тестовой базы.
- Для рабочей базы и специальной базы — сборки.
В этом случае задача с момента начала разработки до переноса в продуктивную базу проходит следующие этапы:
- Разработка ведется в одной из разработочных баз
- После выполнения, задача помещается в разработочное хранилище конфигурации
- Далее, через хранилище задача попадает в тестовую базу (Тестовая 1)
- Если по задаче есть замечания, разработчик исправляет их в своей разработочной базе, а затем через хранилище снова передает изменения на тест в тестовую базу.
- Когда все этапы тестирования пройдены, разработчик сохраняет файл конфигурации из тестовой базы и посредством сравнения / объединения конфигураций переносит свою задачу в специальную базу — сборку.
- В сборке происходит дополнительный тест задачи (после переноса).
- Затем, все затронутые объекты помещаются в хранилище рабочей базы и сборки.
- В назначенное время все задачи помещенные в хранилище переносятся в рабочую базу.
На совсем небольших проектах возможно подключение всех баз к одному общему хранилищу конфигурации:
2. Самоидентификация тестовых баз
В проекте часто имеется несколько тестовых и разработочных баз. Они периодически обновляются выгрузками из рабочей базы. В связи с этим, для снижения риска порчи данных и повышения удобства работы, полезен следующий функционал. Информационная база хранит реквизиты рабочей базы (имя базы, имя сервера или полный путь подключения) — например, в константе.
В результате каждая копия может определить, является она рабочей базой или нет.
Если база — не рабочая, то:
- При начале работы системы в заголовок главного окна нужно автоматически добавить информацию о том, что данная база является тестовой.
- Не должен выполняться код обработчиков некоторых регламентных заданий. Например, проверка на рабочую базу должна осуществляться для всех регламентных заданий, выполняющий периодический обмен с внешними системами. При необходимости отладки таких заданий константу можно изменить на реквизиты данной тестовой базы (после изменения настроек обмена).
Функцию проверку можно добавить, например, в процедуру «ПриНачалеРаботыСистемы()» модуля управляемого приложения.
Процедура ПриНачалеРаботыСистемы() // СтандартныеПодсистемы СтандартныеПодсистемыКлиент.ПриНачалеРаботыСистемы(); // Конец СтандартныеПодсистемы //++ VION 08.09.2016 Общие объекты АБ_ОбщегоНазначенияКлиент.УстановитьЗаголовокИнформационнойБазы(); //-- VION 08.09.2016 КонецПроцедуры
Где процедура «УстановитьЗаголовокИнформационнойБазы()» имеет следующий вид:
//************************************************** Процедура УстановитьЗаголовокИнформационнойБазы() Экспорт Если АБ_ОбщегоНазначенияСервер.ЭтоРабочаяБаза() Тогда Возврат; КонецЕсли; ИмяИнформационнойБазы = АБ_ОбщегоНазначенияСервер.ПолучитьИмяИнформационнойБазы(); УстановитьКраткийЗаголовокПриложения("(! "+ИмяИнформационнойБазы+" !)"); КонецПроцедуры
Также в этом примере используются следующие серверные процедуры:
//************************************************** Функция ЭтоРабочаяБаза() Экспорт Если Врег(ПолучитьИмяИнформационнойБазы() )= Врег(СокрЛП(Константы.АБ_ИмяРабочейБазы.Получить())) Тогда Возврат Истина; Иначе Возврат Ложь; КонецЕсли; Конецфункции //************************************************** Функция ПолучитьИмяИнформационнойБазы() Экспорт СтрокаПодключения = СтрокаСоединенияИнформационнойБазы(); НомерСимвола = СтрНайти(СтрокаПодключения,"File="); Если НомерСимвола > 0 Тогда //Это Файловая база СтрокаСправа = Сред(СтрокаПодключения,НомерСимвола+6); Возврат Сред(СтрокаСправа,1,СтрДлина(СтрокаСправа)-2); КонецЕсли; НомерСимвола = СтрНайти(СтрокаПодключения,"Ref="); Если НомерСимвола > 0 Тогда //Это серверная база СтрокаСправа = Сред(СтрокаПодключения,НомерСимвола+5); Возврат Сред(СтрокаСправа,1,СтрДлина(СтрокаСправа)-2); КонецЕсли; Возврат ""; Конецфункции
Результат:
Обработчик регламентного задания, связанного с обменом данными, должен иметь соответствующую проверку:
//************************************************** Процедура ВыполнитьОбменДанными() Экспорт Если НЕ АБ_ОбщегоНазначенияСервер.ЭтоРабочаяБаза() Тогда Возврат; КонецЕсли; //... Код регламентной процедуры Конецфункции
3. Обработка инициализации
Некоторые модификации требуют действий, которые нельзя выполнить в конфигураторе. Например, заполнение реквизитов предопределённых элементов справочника (помимо кода и наименования), обязательное первоначальное заполнение каких-либо значений. Все подобные действия по разным задачам проекта следует выполнять в одном месте — в обработке инициализации.
Для данной обработки должны соблюдаться следующие правила:
- Обработка должна быть доступна только пользователям с полными правами.
- Отдельное вынесение в интерфейс данной обработки не требуется.
- Обработка должна иметь одну форму с кратким пояснительным текстом и кнопкой вызова процедуры выполнения пакета инициализационных действий.
- Данная процедура должна быть доступна для программного вызова извне.
- Процедура выполнения пакета должна последовательно вызвать подпроцедуры инициализации, написанные разными разработчиками в рамках своих задач. В каждой из них должна быть собственная (независимая) обработка исключительных ситуаций, с выдачей сообщений об ошибках.
- Ошибка, возникшая в одной в подпроцедуре, не должна влиять на остальную инициализацию.
- Обработку предполагается запускать многократно. Каждое действие должно корректно отрабатывать как при первом запуске обработки, так и при повторных запусках. Во втором случае не должно быть каких-либо побочных эффектов или потери введённых пользователем данных.
В конфигурацию также должен быть добавлен механизм автоматического обнаружения изменений в обработке инициализации и предложения пользователю с полными правами выполнить инициализацию при первой же возможности.
Один из вариантов реализации такого механизма:
- В конфигурации предусматривается константа, хранящая версию последней выполненной обработки инициализации.
- В обработке предусматривается экспортная переменная, хранящая версию (целое число). Версия устанавливается при создании обработки-объекта, значение версии прописано в коде модуля. При любой модификации обработки разработчик должен увеличить данную версию на единицу.
- При начале работы системы, если текущий пользователь имеет полные права и версии обработки в конфигурации и в константе отличаются, выполняется обработка инициализации.
В данном примере, добавлена константа «АБ_ВерсияВыполненнойОбработкиИнициализации»
В модуле управляемого приложения в процедуру «ПриНачалеРаботыСистемы()» добавлен вызов серверной процедуры:
Процедура ПриНачалеРаботыСистемы() // СтандартныеПодсистемы СтандартныеПодсистемыКлиент.ПриНачалеРаботыСистемы(); // Конец СтандартныеПодсистемы //++ VION 08.09.2016 Общие объекты АБ_ОбщегоНазначенияКлиент.УстановитьЗаголовокИнформационнойБазы(); АБ_ОбщегоНазначенияСервер.ВыполнитьОбработкуИнициализации(); //-- VION 08.09.2016 КонецПроцедуры
Которая проверяет наличие прав и вызывает обработку инициализации:
//************************************************** Процедура ВыполнитьОбработкуИнициализации() Экспорт Если РольДоступна("ПолныеПрава") Тогда Обработки.АБ_ОбработкаИнициализации.Создать().ВыполнитьОбработку(); КонецЕсли; КонецПроцедуры
Код модуля обработки инициализации
////////////////////////////////////////////////////////// // ОБРАБОТКА ИНИЦИАЛИЗАЦИИ // -------------------------- // Обработку предполагается запускать многократно. // Каждое действие должно корректно отрабатывать как при первом запуске обработки, так и при повторных запусках. // Во втором случае не должно быть каких-либо побочных эффектов или потери введённых пользователем данных. // В некоторых случаях (если того требует задача) возможно выявление и исправление настроек, уже сделанных, // но некорректно изменённых пользователем с момента предыдущей инициализации. // -------------------------- // Используемые объекты конфигурации: // 1. Константа.ВерсияВыполненнойОбработкиИнициализации - Тип: Число (5,0) // 2. Вызов обработки при старте системы: Обработки.ОбработкаИнициализации.Создать().ВыполнитьОбработку(); // -------------------------- // Алгоритм для разработчика: // 1. В процедуре ПолучитьТекущуюВерсиюОбработки() увеличиваете весрию на 1, напрмер было 3, вы ставите 4. // 2. В модуль обработки добавляете процедуру ОбработкаИнициализации_Весия_4(Отказ). // 3. Если в процессе обработки возникли ошибки, необходимо установить переменную Отказ в значение Истина. // 4. Необходимый код располагаете в данной процедуре. //******************************************************** Функция ПолучитьТекущуюВерсиюОбработки() Экспорт // ВНИМАНИЕ! // Код не комментируем, просто увеличиваем текущую версию обработки // Тип версии - Число (5,0) Возврат 3; КонецФункции //******************************************************** Функция ПолучитьПрошлуюВерсиюОбработки() Экспорт Возврат Константы.АБ_ВерсияВыполненнойОбработкиИнициализации.Получить(); КонецФункции //******************************************************** Функция УстановитьВерсиюОбработки(ТекущаяВерсия) Экспорт //Запишем текущую версию в константу Попытка Константы.АБ_ВерсияВыполненнойОбработкиИнициализации.Установить(ТекущаяВерсия); Возврат Истина; Исключение Сообщить("Обработка инициализации: " + ОписаниеОшибки()); Возврат Ложь; КонецПопытки; КонецФункции //******************************************************** Функция ВыполнитьОбработку(ВсеОбработчики = Ложь) Экспорт //Обработку инициализации может выполнить только пользователь с полными правами Если Не РольДоступна("ПолныеПрава") Тогда Сообщить("Обработка инициализации: Недостаточно прав для выполнения обработки!"); Возврат Ложь; КонецЕсли; Если ВсеОбработчики Тогда ПрошлаяВерсия = 0; Иначе ПрошлаяВерсия = ПолучитьПрошлуюВерсиюОбработки(); КонецЕсли; ТекущаяВерсия = ПолучитьТекущуюВерсиюОбработки(); //Обработка не нужна Если ТекущаяВерсия <= ПрошлаяВерсия Тогда Возврат Истина; КонецЕсли; //В цикле выполняем все необходимые обработки Отказ = Ложь; Для Сч = ПрошлаяВерсия+1 По ТекущаяВерсия Цикл Выполнить("ОбработкаИнициализации_Весия_"+Сч+"(Отказ);"); КонецЦикла; Если Отказ Тогда Сообщить("Обработка инициализации: Ошибка выполнения обоработки!"); Возврат Ложь; КонецЕсли; //Запишем текущую версию в константу Если УстановитьВерсиюОбработки(ТекущаяВерсия) Тогда Сообщить("Обработка инициализации: Переход на версию " + ТекущаяВерсия + " выполнен успешно!"); Возврат Истина; Иначе Возврат Ложь; КонецЕсли; КонецФункции //******************************************************** Процедура ОбработкаИнициализации_Весия_1(Отказ) //Действия обработчика Сообщить("Обработка инициализации, выполнен переход на версию 1"); КонецПроцедуры
Форма обработки:
Скачать обработку можно, например, здесь.
4. Справочник предопределённых значений
При доработке конфигураций часто возникает необходимость обращаться из кода с существующим элементам справочников, не являющихся предопределёнными. Т. к. поиск по коду, наименованию или реквизиту не является надёжным решением, можно добавить в конфигурацию справочник «Предопределённые значения» с единственным реквизитом «Значение» типа «СправочникСсылка».
Далее по каждой возникшей задаче в этот справочник добавляется предопределенный элемент.
Реквизит «Значение» данного элемента заполняется обработкой инициализации или вручную.
Обращение к такому значению осуществляется подобным кодом:
КонтрагентАгроимпульс = Справочники.АБ_ПредопределенныеЗначения.Контрагент_Агроимпульс.Значение;
Права на справочник предопределенных значений должны обеспечивать как доступность его содержимого, так и возможность настройки значений только пользователями с нужными ролями.
5. Просмотр временных таблиц в отладчике
При отладке сложных запросов с временными таблицами нужна возможность просматривать содержимое этих таблиц, которую отладчик не предоставляет.
Для просмотра временных таблиц запроса в отладчике удобно использовать специальную функцию. Данная функция парсит текст запроса, вычленяет все временные таблицы, вычисляет их и результат складывает в структуру.
Для удобства использования удобно поместить процедуру в один из глобальных модулей конфигурации.
Функция предназначена для использования в окне «Вычислить выражение» отладчика — возвращает значение, удобное для просмотра в этом окне. Функция максимально автоматизирована и может работать с входными данными разных типов. Назначение аргументов функции понятно из их имён.
Текст процедур можно посмотреть — здесь.
6. Версии внешних отчётов и обработок
При использовании внешних отчетов и обработок в поле «Комментарий» следует указывать строку, отображающую условную версию объекта. Например, начиная с «v1.0» и далее увеличивая старший или младший номер (в зависимости от объёма изменений) при каждом цикле доработки объекта.
Данный комментарий доступен из кода, и при открытии формы его следует добавлять к заголовку окна:
#Область СобытияФормы //////////////////////////////////////////////////////////// // ОБРАБОТЧИКИ СОБЫТИЙ ФОРМЫ //********************************************************** &НаСервере Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка) //Заголовок формы ЭтаФорма.АвтоЗаголовок = Ложь; ЭтаФорма.Заголовок = ЗаголовокОтчета(); КонецПроцедуры //********************************************************** &НаСервере Функция ЗаголовокОтчета() ТекОбъект = РеквизитФормыВЗначение("Объект"); Комментарий = ТекОбъект.Метаданные().Комментарий; Возврат ТекОбъект.Метаданные().Синоним + ?(ЗначениеЗаполнено(Комментарий)," (" + Комментарий + ")",""); КонецФункции #КонецОбласти
Тогда версия обработки или отчета всегда будет видна в заголовке формы.
В качестве префиксации объектов метаданных и реквизитов все таки лучше использовать символы в нижнем регистре. Очень отвлекает внимание и мешает чтению кода префиксы вида НАШАСУПЕРКОМПАНИЯ_ДатаНачалаПримененияФункционала. Особенно если доработку ведет франчайзи.
Применение префиксов в нижнем регистре позволяет решать все те же задачи, но при этом благодаря применяемой в 1С нотации сразу перейти глазами к осмысленной части имени переменной.
Посмотрите например как сделано в «Инструментах разработчика» ир_ДатаНачалаДейстия. Гораздо удобнее читать код с ас_ДобавленныйРеквизит, чем АС_ДобавленныйРеквизит.
Конечно это дело вкуса, но думаю многие с этим согласятся.
Дополнительно стоит отметить что префиксация с заглавных букв часто применяется для придания префиксу логического смысла. «ФСС_НашДокумент», «ККТ_Настройки», конечно это не в принятой в 1С нотации , но встречается часто. И префикс в верхнем регистре сразу настраивает на поиск логического смысла в этом префиксе. Префиксы в нижнем регистре являются стандартным способом выделить внешний по отношении к конфигурации добавленный объект и не сбивают с толку.
В общем советую попробовать ))