Идея родилась из общения с крайне малограмотными клиентами, для которых в БП 3.0 добраться до справочника "Дополнительные отчёты и обработки" было нереально. Удалённого администрирования добиться не удалось. И тогда я подумал - а пусть внешка сама себя регистрирует. Уж проделать стандартное "Файл" - "Открыть" нынче всякий сможет.
И вот, стало быть, надо просто из "ПриОткрытии" основной формы отчёта/обработки что-то вызывать. Ниже приведено, что именно. Единственно, важно, чтобы в форме были параметр "ОбъектыНазначения" (произвольный) - ну, он-то для многих случаев необходим согласно требованиям БСП; и придуманный мной параметр "НезависимоеОткрытие" (булев, ключевой), он позволяет отличить, как вызвана внешка - через "Файл" - "Открыть" или уже штатно самой конфой. А если хотите, придумайте другие признаки, по которым в ПриОткрытии надо дёргать "ОбновлениеИзФайлаПомещениеНаКлиенте".
Проверялось для всех 36 случаев настроек регистрации внешки, на БСП 2.2.3.36 и 2.2.4.43; пока без прибамбасов вроде автоперезаполнения "Назначения" или расписания вызова команд (где оно возможно); но это тоже планирую сделать.
Особенно эта штука полезна, когда идут мелкие частые правки и надо отдавать все эти варианты клиенту на тест; например, печатные формы. Словом, вставьте вот это в код модуля формы и будет сразу легче.
#Область СобытияФормы
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если ТипЗнч(ЭтаФорма.Параметры.ОбъектыНазначения)<>Тип("Массив") Тогда
ЭтаФорма.Параметры.НезависимоеОткрытие=Истина;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Если ЭтаФорма.Параметры.НезависимоеОткрытие Тогда
ОбновлениеИзФайлаПомещениеНаКлиенте();
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#Область ОбслуживаниеАвторегистрацииПриОткрытииФормы
// Внимание! Все сообщения выдаются только из расчёта на русский язык, без использования НСтр!
&НаСервере
Процедура ОбновлениеИзФайлаМеханикаНаСервере(ПараметрыРегистрации)
//=============================================================================
// Определяемся с самим объектом
// Подключение и получение имени, под которым объект будет подключаться
Менеджер=?(ПараметрыРегистрации.ЭтоОтчет,ВнешниеОтчеты,ВнешниеОбработки);
// исходим из того, что у нас только тонкий или веб-клиент, получаем имя подключённой ВПФ
ИмяРегистрируемогоОбъекта=СокрЛП(Менеджер.Подключить(ПараметрыРегистрации.АдресДанныхОбработки,,Истина)); // в безопасном
// проверим, есть ли уже такой объект (по ИмяОбъекта и Вид); будем считать, что блок используется (т.к. только это даёт конфликт имён)
тз="ВЫБРАТЬ ПЕРВЫЕ 1
| ТаблицаСправочника.Ссылка КАК ВПФ
|ИЗ
| Справочник.ДополнительныеОтчетыИОбработки КАК ТаблицаСправочника
|ГДЕ
| ТаблицаСправочника.ИмяОбъекта = &УслИмяОбъекта
| И ТаблицаСправочника.Публикация = ЗНАЧЕНИЕ(Перечисление.ВариантыПубликацииДополнительныхОтчетовИОбработок.Используется)
| И ТаблицаСправочника.ПометкаУдаления = Ложь";
//
Если ПараметрыРегистрации.ЭтоОтчет Тогда
тз=тз+"
| И ТаблицаСправочника.Вид В (
| ЗНАЧЕНИЕ(Перечисление.ВидыДополнительныхОтчетовИОбработок.ДополнительныйОтчет),
| ЗНАЧЕНИЕ(Перечисление.ВидыДополнительныхОтчетовИОбработок.Отчет))";
Иначе
тз=тз+"
| И НЕ (ТаблицаСправочника.Вид В (
| ЗНАЧЕНИЕ(Перечисление.ВидыДополнительныхОтчетовИОбработок.ДополнительныйОтчет),
| ЗНАЧЕНИЕ(Перечисление.ВидыДополнительныхОтчетовИОбработок.Отчет)))";
КонецЕсли;
//
з=Новый Запрос(тз);
з.УстановитьПараметр("УслИмяОбъекта",ИмяРегистрируемогоОбъекта);
//
УстановитьПривилегированныйРежим(Истина);
трез=з.Выполнить().Выгрузить(ОбходРезультатаЗапроса.Прямой);
УстановитьПривилегированныйРежим(Ложь);
//
Если трез.Количество()=0 Тогда // создаём новый объект в корне
ОбъектСправочника=Справочники.ДополнительныеОтчетыИОбработки.СоздатьЭлемент();
// считаю правильным проставить сразу же
ОбъектСправочника.ИспользоватьДляФормыСписка=Истина;
ОбъектСправочника.ИспользоватьДляФормыОбъекта=Истина;
Иначе
ОбъектСправочника=трез[0].ВПФ.ПолучитьОбъект();
КонецЕсли;
//=============================================================================
// Обрабатываем объект справочника
КомандыСохраненные=ОбъектСправочника.Команды.Выгрузить(); // запомним команды
//
// запомним состояние реквизита Публикация и выключим временно, чтобы в регистрации обработки не делался поиск (мы уже его сделали)
ПубликацияСохраненная=ОбъектСправочника.Публикация;
ОбъектСправочника.Публикация=Перечисления.ВариантыПубликацииДополнительныхОтчетовИОбработок.Отключена;
// Разрешения используются для всех, кроме глобальных обработок и отчётов, т.е. кроме ВидДопОбработка и ВидДопОтчет
// Назначения используются для всех, кроме глобальных обработок и отчётов, т.е. кроме ВидДопОбработка и ВидДопОтчет
// Единожды указанные Назначения не изменяются, если сведения о них не были переданы (т.е. удалённое - остаётся).
// чтобы они перечитались, надо так: ОбъектСправочника.Назначение.Очистить(); // причём именно ДО вызова ЗарегистрироватьОбработку
// собственно выполним большинство штатных действий по считыванию рег.сведений и регистрации в системе
РезультатРегистрации=ДополнительныеОтчетыИОбработки.ЗарегистрироватьОбработку(ОбъектСправочника,ПараметрыРегистрации);
// закинем данные из РезультатРегистрации в ПараметрыРегистрации и дальше будем работать с ней
ОбщегоНазначенияКлиентСервер.ДополнитьСтруктуру(ПараметрыРегистрации,РезультатРегистрации,Истина);
Если не ПараметрыРегистрации.Успех Тогда
Если не ПустаяСтрока(ПараметрыРегистрации.КраткоеПредставлениеОшибки) Тогда
Сообщить("Ошибка регистрации внешнего блока: "+СокрЛП(ПараметрыРегистрации.КраткоеПредставлениеОшибки),СтатусСообщения.Важное);
Иначе
// считаем, что "занявших" имя не существует, и неуспех произошёл по другой причине:
// а) не хватило прав на подключения обработки, запускаемой в небезопасном режиме;
// б) не удалось сменить вид (обработка/отчёт итд) для уже имеющегося эл-та спр-ка;
// в) вид ВПФ, указанный в регистрационных данных, противоречит расширению файла.
Сообщить("Общая недиагностированная ошибка регистрации внешнего блока!",СтатусСообщения.ОченьВажное);
КонецЕсли;
Возврат;
КонецЕсли;
ОбъектСправочника.Публикация=ПубликацияСохраненная; // восстановим значение реквизита Публикация
// занимаемся таб.частью Команды
тз="ВЫБРАТЬ
| ДанныеРегистра.ИдентификаторКоманды,
| ДанныеРегистра.Пользователь
|ИЗ
| РегистрСведений.ПользовательскиеНастройкиДоступаКОбработкам КАК ДанныеРегистра
|ГДЕ
| ДанныеРегистра.ДополнительныйОтчетИлиОбработка = &УслСсылка
| И ДанныеРегистра.Доступно = Истина";
з=Новый Запрос(тз);
з.УстановитьПараметр("УслСсылка",ОбъектСправочника.Ссылка);
УстановитьПривилегированныйРежим(Истина);
БыстрыйДоступ=з.Выполнить().Выгрузить(ОбходРезультатаЗапроса.Прямой);
УстановитьПривилегированныйРежим(Ложь);
рАдресРазрешений=ПоместитьВоВременноеХранилище(ОбъектСправочника.Разрешения.Выгрузить(),ЭтотОбъект.УникальныйИдентификатор);
тКоманд=ОбъектСправочника.Команды.Выгрузить();
тКоманд.Сортировать("Представление");
// изменить вид единожды созданного элемента уже нельзя, таково положение БСП (см.перед записью объекта)
ВидДополнительнаяОбработка=Перечисления.ВидыДополнительныхОтчетовИОбработок.ДополнительнаяОбработка;
ВидДополнительныйОтчет=Перечисления.ВидыДополнительныхОтчетовИОбработок.ДополнительныйОтчет;
ВидОбъекта=ОбъектСправочника.Вид;
ПредставлениеПустогоРасписания=Строка(Новый РасписаниеРегламентногоЗадания);
// в таблице Команды нужны колонки, которых нет как реквизитов табчасти Команды, добавим
тКоманд.Колонки.Добавить("РегламентноеЗаданиеИспользование",Новый ОписаниеТипов("Булево"));
тКоманд.Колонки.Добавить("РегламентноеЗаданиеПредставление"); // строка0
тКоманд.Колонки.Добавить("РегламентноеЗаданиеРазрешено",Новый ОписаниеТипов("Булево"));
тКоманд.Колонки.Добавить("РегламентноеЗаданиеРасписание",Новый ОписаниеТипов("СписокЗначений")); // обычно 1 элемент типа "РасписаниеРегламентногоЗадания"
тКоманд.Колонки.Добавить("БыстрыйДоступПредставление"); // строка0
Для каждого строКоманд Из тКоманд Цикл
строКоманд.РегламентноеЗаданиеИспользование=Ложь; // по умолчанию
строКоманд.РегламентноеЗаданиеРазрешено=Ложь; // по умолчанию
//
Если ВидОбъекта=ВидДополнительнаяОбработка или ВидОбъекта=ВидДополнительныйОтчет Тогда
// сделаем представление строки в зависимости от количества пользователей, найденных по этой команде в регистре доступа
мНайденных=БыстрыйДоступ.НайтиСтроки(Новый Структура("ИдентификаторКоманды",строКоманд.Идентификатор));
Если мНайденных.Количество()=0 Тогда
строКоманд.БыстрыйДоступПредставление="Нет";
Иначе // схалявим - незачем красивости разводить
строКоманд.БыстрыйДоступПредставление="Пользователей: "+СокрЛП(мНайденных.Количество());
КонецЕсли;
КонецЕсли;
//
Если ВидОбъекта=ВидДополнительнаяОбработка
И (строКоманд.ВариантЗапуска=Перечисления.СпособыВызоваДополнительныхОбработок.ВызовСерверногоМетода
ИЛИ строКоманд.ВариантЗапуска=Перечисления.СпособыВызоваДополнительныхОбработок.СценарийВБезопасномРежиме)
Тогда
строКоманд.РегламентноеЗаданиеРазрешено=Истина;
//
РегламентноеЗаданиеGUID=строКоманд.РегламентноеЗаданиеGUID;
НайденнаяСтрока=КомандыСохраненные.Найти(строКоманд.Идентификатор,"Идентификатор");
Если НайденнаяСтрока<>Неопределено Тогда
РегламентноеЗаданиеGUID=НайденнаяСтрока.РегламентноеЗаданиеGUID;
КонецЕсли;
//
Если ЗначениеЗаполнено(РегламентноеЗаданиеGUID) Тогда // ID задания есть, разбираемся с самим заданием
РегламентноеЗадание=ДополнительныеОтчетыИОбработкиРегламентныеЗадания.НайтиЗадание(РегламентноеЗаданиеGUID);
Если РегламентноеЗадание<>Неопределено Тогда
ПараметрыЗадания=ДополнительныеОтчетыИОбработкиРегламентныеЗадания.ПолучитьПараметрыЗадания(РегламентноеЗадание);
// ставим параметры задания в строку команд
строКоманд.РегламентноеЗаданиеGUID=РегламентноеЗаданиеGUID;
строКоманд.РегламентноеЗаданиеПредставление=Строка(ПараметрыЗадания.Расписание);
строКоманд.РегламентноеЗаданиеИспользование=ПараметрыЗадания.Использование;
строКоманд.РегламентноеЗаданиеРасписание.Вставить(0,ПараметрыЗадания.Расписание);
// выключаем, если надо
Если строКоманд.РегламентноеЗаданиеПредставление=ПредставлениеПустогоРасписания Тогда
строКоманд.РегламентноеЗаданиеИспользование=Ложь;
КонецЕсли;
КонецЕсли;
КонецЕсли;
//
Если Не строКоманд.РегламентноеЗаданиеИспользование Тогда
строКоманд.РегламентноеЗаданиеПредставление="Расписание не задано";
КонецЕсли;
Иначе
// если это не глобальная доп.обработка
строКоманд.РегламентноеЗаданиеПредставление="Неприменимо для команд с вариантом запуска """+СокрЛП(строКоманд.ВариантЗапуска)+"""!";
КонецЕсли;
КонецЦикла; // по таблице команд
ОбъектСправочника.Команды.Загрузить(тКоманд);
// обратно включим использование
ОбъектСправочника.Публикация=Перечисления.ВариантыПубликацииДополнительныхОтчетовИОбработок.Используется;
Если ДополнительныеОтчетыИОбработки.ВозможнаЗагрузкаОбработкиИзФайла(ОбъектСправочника.Ссылка) Тогда
ддОбработки=ПолучитьИзВременногоХранилища(ПараметрыРегистрации.АдресДанныхОбработки);
ОбъектСправочника.ХранилищеОбработки=Новый ХранилищеЗначения(ддОбработки,Новый СжатиеДанных(9));
КонецЕсли;
Если ВидОбъекта=ВидДополнительнаяОбработка или ВидОбъекта=ВидДополнительныйОтчет Тогда
ОбъектСправочника.ДополнительныеСвойства.Вставить("АктуальныеКоманды",тКоманд);
Иначе
БыстрыйДоступ.Очистить();
КонецЕсли;
ОбъектСправочника.ДополнительныеСвойства.Вставить("БыстрыйДоступ",БыстрыйДоступ);
ОбъектСправочника.Разрешения.Загрузить(ПолучитьИзВременногоХранилища(рАдресРазрешений));
ОбъектСправочника.Ответственный=ПараметрыСеанса.ТекущийПользователь;
// собственно запишем
Попытка
ОбъектСправочника.Записать();
ПараметрыРегистрации.Вставить("СсылкаНаОбъект",ОбъектСправочника.Ссылка);
Исключение
Сообщить("Ошибка финальной записи регистрации: "+ОписаниеОшибки(),СтатусСообщения.ОченьВажное);
ПараметрыРегистрации.Вставить("Успех",Ложь);
КонецПопытки;
КонецПроцедуры
&НаСервере
Функция ОпределитьИмяИспользуемогоФайла()
рОбъект=РеквизитФормыВЗначение("Объект");
Возврат рОбъект.ИспользуемоеИмяФайла;
КонецФункции
&НаКлиенте
Процедура ОбновлениеИзФайлаПомещениеНаКлиенте()
рПутьИмяФайла=ОпределитьИмяИспользуемогоФайла();
// помещаем файл в хранилище на сервере
рДопПараметры=Новый Структура("Успешность",Истина); // предполагаем, что так
рОбработчик=Новый ОписаниеОповещения("ПомещениеФайлаНаСерверЗавершение",ЭтотОбъект,рДопПараметры);
//
Если ПодключитьРасширениеРаботыСФайлами() Тогда
мПомещенныхФайлов=Новый Массив;
Если не ПоместитьФайлы(,мПомещенныхФайлов,рПутьИмяФайла,Ложь,ЭтаФорма.УникальныйИдентификатор) Тогда
Сообщить("Ошибка при помещении файла внешнего отчёта/обработки на сервер!",СтатусСообщения.Важное);
рДопПараметры.Вставить("Успешность",Ложь);
Иначе
// мПомещенныхФайлов содержит элемент - служебку вида Имя (путь) и Хранение (GUID)
рДопПараметры.Вставить("Успешность",Истина);
КонецЕсли;
ВыполнитьОбработкуОповещения(рОбработчик,мПомещенныхФайлов);
Иначе
НачатьПомещениеФайла(рОбработчик,,рПутьИмяФайла,Ложь,ЭтаФорма.УникальныйИдентификатор);
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПомещениеФайлаНаСерверЗавершение(мПомещенныхФайлов,рПараметрыРегистрации) Экспорт
рОписаниеФайла=мПомещенныхФайлов.Получить(0);
//
мстро=СтроковыеФункцииКлиентСервер.РазложитьСтрокуВМассивПодстрок(рОписаниеФайла.Имя,"\");
рПараметрыРегистрации.Вставить("ИмяФайла",мстро.Получить(мстро.ВГраница()));
рПараметрыРегистрации.Вставить("АдресДанныхОбработки",рОписаниеФайла.Хранение);
//
// выясним тип, исходя из расширения (если бы не веб-клиент, можно было бы через объект "Файл")
рРасширение=ВРег(Прав(рПараметрыРегистрации.ИмяФайла,3));
Если рРасширение="ERF" Тогда
рПараметрыРегистрации.Вставить("ЭтоОтчет",Истина);
ИначеЕсли рРасширение="EPF" Тогда
рПараметрыРегистрации.Вставить("ЭтоОтчет",Ложь);
Иначе
Сообщить("Расширение файла не соответствует расширению внешнего отчёта (ERF) или обработки (EPF)!",СтатусСообщения.Важное);
рПараметрыРегистрации.Вставить("Успешность",Ложь);
Возврат;
КонецЕсли;
//
рПараметрыРегистрации.Вставить("ОтключатьПубликацию",Ложь);
рПараметрыРегистрации.Вставить("ОтключатьКонфликтующие",Ложь);
рПараметрыРегистрации.Вставить("Конфликтующие",Новый СписокЗначений);
// Подготовка к вызову сервера.
//ОбработчикРезультата = ПараметрыРегистрации.ОбработчикРезультата;
//ПараметрыРегистрации.Удалить("ОбработчикРезультата");
//============================================================================
// Вызов сервера.
ОбновлениеИзФайлаМеханикаНаСервере(рПараметрыРегистрации);
//============================================================================
Если рПараметрыРегистрации.Успех Тогда
Если рПараметрыРегистрации.Свойство("СсылкаНаОбъект") и ЗначениеЗаполнено(рПараметрыРегистрации.СсылкаНаОбъект) Тогда
гипСсылка=ПолучитьНавигационнуюСсылку(рПараметрыРегистрации.СсылкаНаОбъект);
ПоказатьОповещениеПользователя("Авторегистрация выполнена",гипСсылка,"Для перехода к регистрационной карточке щёлкните по ссылке.",гипСсылка);
ЭтаФорма.Закрыть();
//парф=Новый Структура("Ключ",рПараметрыРегистрации.СсылкаНаОбъект);
//ОткрытьФорму("Справочник.ДополнительныеОтчетыИОбработки.Форма.ФормаЭлемента",парф);
Иначе
ПоказатьОповещениеПользователя("При завершении авторегистрации внешнего блока произошла ошибка!");
КонецЕсли;
Иначе
ПоказатьОповещениеПользователя("Авторегистрация внешнего блока не удалась!");
КонецЕсли;
КонецПроцедуры
#КонецОбласти
Кто найдёт ошибки - пожалуйста, сообщайте, буду исправлять. Вообще, заранее оговорюсь, что развитие БСП в любой момент может сделать любую из вышеприведённых строк неактуальной, но, по идее, капитальных изменений идеологии быть уже не должно.
P.S. Если будет время, попробую разобрать каждый вариант подключения и использования внешки в отдельной статье, т.к. там есть весьма любопытные и нетривиальные заморочки.