1. Вывод кастомной кнопки на форму
2. Добавление новой команды на примере http-сервиса
3. Удобная отладка дополнительных обработок
Итак, в основном дополнительные обработки используют тогда, когда нельзя/не хочется включать возможность изменения конфигурации. Но помимо этого, с помощью библиотеки можно добавлять кнопки на форму, создавать регламентные задания, или просто вызывать выполнение команды обработки из кода, что также полезно и в конфигурациях "снятых с замка".
Но и тут есть ограничения. Например, для вывода кнопки на форму мы можем использовать такие виды обработок: ЗаполнениеОбъекта
, Отчет
, ПечатнаяФорма
и СозданиеСвязанныхОбъектов.
Но не можем вывести кастомную кнопку, которая бы говорила пользователю о своем назначении.
Ниже я покажу свой вариант обхода таких ограничений. Да, все примеры ниже можно решить как доработкой самой конфигурации, так и подключением расширения для разработки нужного функционала. Это просто еще один способ разработки, который ни на что не претендует. Все это родилось при исследовании возможностей библиотеки.
Все изменения в библиотеке дополнительных обработок будем делать в расширении.
Версия платформы, на которой ставили эксперименты - 8.3.22.2143.
Версия БСП - 3.1.7.422
Режим совместимости - 8.3.21
Вывод кастомной кнопки на форму
Задача. Реализовать алгоритм отправки табелей из ЗУП на почту ответственным лицам. Надо в форму списка и в форму документа вывести кнопку, на которую будет нажимать пользователь, вследствие чего печатные формы табеля отправятся ответственным лицам для сверки/редактирования.
Мы уже знаем из официальной документации, что вывести кнопку на форму с произвольным алгоритмом мы не можем. А значит, надо создать свой кастомный вид обработки. Для этого выносим в расширение перечисление ВидыДополнительныхОтчетовИОбработок и добавляем туда новое значение ОтправкаОбъектов. Далее выносим в расширение модуль ДополнительныеОтчетыИОбработкиКлиентСервер и добавляем туда функцию
Функция ВидОбработкиОтправкаОбъектов() Экспорт
Возврат "ОтправкаОбъектов";
КонецФункции
В сведениях о дополнительной обработке вид обработки и вид команды указываются строковыми значениями. Поэтому нужно написать несколько подобных функций для корректной работы механизмов библиотеки. Выносим в расширение модуль ДополнительныеОтчетыИОбработки и переопределяем метод ПолучитьВидОбработкиПоСтроковомуПредставлениюВида
&Вместо("ПолучитьВидОбработкиПоСтроковомуПредставлениюВида")
Функция РДО_ПолучитьВидОбработкиПоСтроковомуПредставлениюВида(СтроковоеПредставление)
Если СтроковоеПредставление = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиОтправкаОбъектов() Тогда
Возврат Перечисления.ВидыДополнительныхОтчетовИОбработок.ОтправкаОбъектов;
Иначе
Возврат ПродолжитьВызов(СтроковоеПредставление);
КонецЕсли;
КонецФункции
Тут мы как бы используем аннотацию &Вместо, которую не очень любят. Но в тоже время мы никак не ломаем типовой механизм, мы вызываем типовой метод, если вид обработки не Отправка объектов.
Хорошо, новый вид обработки мы добавили. НО! Эта обработка будет назначаемой. То есть мы в сведениях будем указывать объекты, в которых будет фигурировать наша кнопка. За это отвечает РС НазначениеДополнительныхОбработок.
В ресурсах этого РС указывается, какие обработки, с каким видом, к какому объекту подключены. Для того, чтобы подключить нашу кнопку к объекту, мы также должны добавить сюда ресурс ИспользоватьОтправкуОбъектов. НО! Делать мы этого, конечно, не будем. Не потому что это плохо, а потому, что в расширениях мы не можем менять состав измерений, ресурсов и реквизитов регистров. Для регистрации нашей обработки мы создадим в расширении свой РС
Измерения можно просто скопировать с типового, а ресурс создаем сами с типом Булево. Теперь заглянем в ММ типового регистра и увидим там метод обновления записей. То есть типовой механизм собирает данные из подключаемой обработки и передает их в этот метод. Нам нужно повторить эти методы в своем регистре и немного их подкорректировать, ведь состав ресурсов у нас совсем другой
Много скопированного кода
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
#Область СлужебныеПроцедурыИФункции
// Собирает данные справочника по ссылкам объектов метаданных и обновляет по ним данные регистра.
//
Процедура ОбновитьДанныеПоСсылкамОбъектовМетаданных(СсылкиОбъектовМетаданных) Экспорт
Запрос = НовыйЗапросОбновленияДанныхРегистра(СсылкиОбъектовМетаданных);
ВыборкаСсылок = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаСсылок.Следующий() Цикл
ВыборкаЗаписей = ВыборкаСсылок.Выбрать();
Пока ВыборкаЗаписей.Следующий() Цикл
МенеджерЗаписи = СоздатьМенеджерЗаписи();
ЗаполнитьЗначенияСвойств(МенеджерЗаписи, ВыборкаЗаписей);
МенеджерЗаписи.Записать(Истина);
КонецЦикла;
// Регистрация использующихся ссылок для последующей очистки регистра от неиспользуемых.
СсылкиОбъектовМетаданных.Удалить(СсылкиОбъектовМетаданных.Найти(ВыборкаСсылок.ОбъектНазначения));
КонецЦикла;
// Очистка регистра по неиспользуемым ссылкам.
Для Каждого ОбъектНазначения Из СсылкиОбъектовМетаданных Цикл
НаборЗаписей = СоздатьНаборЗаписей();
НаборЗаписей.Отбор.ОбъектНазначения.Установить(ОбъектНазначения);
НаборЗаписей.Записать(Истина);
КонецЦикла;
КонецПроцедуры
// Возвращает текст запроса, который используется для обновления данных регистра.
//
Функция НовыйЗапросОбновленияДанныхРегистра(СсылкиОбъектовМетаданных)
Запрос = Новый Запрос;
ТекстЗапроса =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ДополнительныеОтчетыИОбработкиНазначение.ОбъектНазначения КАК ОбъектНазначения,
| ВЫБОР
| КОГДА ДополнительныеОтчетыИОбработкиНазначение.Ссылка.Вид = &ВидОтправкаОбъектов
| ТОГДА ИСТИНА
| ИНАЧЕ ЛОЖЬ
| КОНЕЦ КАК ИспользоватьОтправкуОбъектов,
| ДополнительныеОтчетыИОбработкиНазначение.Ссылка.ИспользоватьДляФормыОбъекта,
| ДополнительныеОтчетыИОбработкиНазначение.Ссылка.ИспользоватьДляФормыСписка
|ПОМЕСТИТЬ втПервичныеДанные
|ИЗ
| Справочник.ДополнительныеОтчетыИОбработки.Назначение КАК ДополнительныеОтчетыИОбработкиНазначение
|ГДЕ
| ДополнительныеОтчетыИОбработкиНазначение.ОбъектНазначения В(&СсылкиОбъектовМетаданных)
| И ДополнительныеОтчетыИОбработкиНазначение.Ссылка.Публикация <> &ПубликацияНеРавно
| И ДополнительныеОтчетыИОбработкиНазначение.Ссылка.ПометкаУдаления = ЛОЖЬ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ФормыОбъектов.ОбъектНазначения,
| ФормыОбъектов.ИспользоватьОтправкуОбъектов,
| &ТипФормыОбъекта КАК ТипФормы
|ПОМЕСТИТЬ втРезультат
|ИЗ
| втПервичныеДанные КАК ФормыОбъектов
|ГДЕ
| ФормыОбъектов.ИспользоватьДляФормыОбъекта = ИСТИНА
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ОтключенныеФормыОбъектов.ОбъектНазначения,
| ЛОЖЬ,
| &ТипФормыОбъекта
|ИЗ
| втПервичныеДанные КАК ОтключенныеФормыОбъектов
|ГДЕ
| ОтключенныеФормыОбъектов.ИспользоватьДляФормыОбъекта = ЛОЖЬ
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ФормыСписков.ОбъектНазначения,
| ФормыСписков.ИспользоватьОтправкуОбъектов,
| &ТипФормыСписка
|ИЗ
| втПервичныеДанные КАК ФормыСписков
|ГДЕ
| ФормыСписков.ИспользоватьДляФормыСписка = ИСТИНА
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
| ОтключенныеФормыСписков.ОбъектНазначения,
| ЛОЖЬ,
| &ТипФормыСписка
|ИЗ
| втПервичныеДанные КАК ОтключенныеФормыСписков
|ГДЕ
| ОтключенныеФормыСписков.ИспользоватьДляФормыСписка = ЛОЖЬ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| табРезультат.ОбъектНазначения КАК ОбъектНазначения,
| табРезультат.ТипФормы,
| МАКСИМУМ(табРезультат.ИспользоватьОтправкуОбъектов) КАК ИспользоватьОтправкуОбъектов
|ИЗ
| втРезультат КАК табРезультат
|
|СГРУППИРОВАТЬ ПО
| табРезультат.ОбъектНазначения,
| табРезультат.ТипФормы
|ИТОГИ ПО
| ОбъектНазначения";
Если СсылкиОбъектовМетаданных = Неопределено Тогда
ТекстЗапроса = СтрЗаменить(
ТекстЗапроса,
"ДополнительныеОтчетыИОбработкиНазначение.ОбъектНазначения В(&СсылкиОбъектовМетаданных)
| И ",
"");
Иначе
Запрос.УстановитьПараметр("СсылкиОбъектовМетаданных", СсылкиОбъектовМетаданных);
КонецЕсли;
Запрос.УстановитьПараметр("ПубликацияНеРавно", Перечисления.ВариантыПубликацииДополнительныхОтчетовИОбработок.Отключена);
Запрос.УстановитьПараметр("ВидОтправкаОбъектов", Перечисления.ВидыДополнительныхОтчетовИОбработок.ОтправкаОбъектов);
Запрос.УстановитьПараметр("ТипФормыСписка", ДополнительныеОтчетыИОбработкиКлиентСервер.ТипФормыСписка());
Запрос.УстановитьПараметр("ТипФормыОбъекта", ДополнительныеОтчетыИОбработкиКлиентСервер.ТипФормыОбъекта());
Запрос.Текст = ТекстЗапроса;
Возврат Запрос;
КонецФункции
#КонецОбласти
#КонецЕсли
Отлично! Мы имитируем поведение типовой, осталось только вызвать наш метод. Метод типового вызывается в справочнике ДополнительныеОтчетыИОбработки в методе ПриЗаписиНазначаемойОбработки. Вынесем этот метод в расширение
&После("ПриЗаписиНазначаемойОбработки")
Процедура РДО_ПриЗаписиНазначаемойОбработки(Отказ)
Если Отказ ИЛИ НЕ ДополнительныеСвойства.Свойство("СсылкиОбъектовМетаданных") Тогда
Возврат;
КонецЕсли;
РегистрыСведений.РДО_НазначениеДополнительныхОбработок.ОбновитьДанныеПоСсылкамОбъектовМетаданных(ДополнительныеСвойства.СсылкиОбъектовМетаданных);
КонецПроцедуры
И вроде все бы ничего. НО! В типовой метод заполнения регистра, и в наш метод заполнения регистра мы передаем одну и ту же коллекцию с назначенными объектами для регистрации. И весь юмор в том, что если у вас есть обработка, например, печатная форма для табеля, и нашу кастомную обработку мы тоже делаем для табеля, то наша обработка не зарегистрируется. А все потому, что в методе ОбновитьДанныеПоСсылкамОбъектовМетаданных регистра есть такая строка
// Регистрация использующихся ссылок для последующей очистки регистра от неиспользуемых.
СсылкиОбъектовМетаданных.Удалить(СсылкиОбъектовМетаданных.Найти(ВыборкаСсылок.ОбъектНазначения));
То есть при регистрации печатной формы из коллекции пропадет ссылка на табель, и мы не сможем его зарегистрировать у себя. А значит, нам надо предварительно скопировать коллекцию объектов и передавать в типовой регистр копию. Для этого переопределим в расширении типовой метод ОбновитьДанныеПоСсылкамОбъектовМетаданных типового регистра НазначениеДополнительныхОбработок
&Вместо("ОбновитьДанныеПоСсылкамОбъектовМетаданных")
Процедура РДО_ОбновитьДанныеПоСсылкамОбъектовМетаданных(СсылкиОбъектовМетаданных)
ОбъектыМетаданных = ОбщегоНазначения.СкопироватьРекурсивно(СсылкиОбъектовМетаданных);
ПродолжитьВызов(ОбъектыМетаданных);
КонецПроцедуры
Тем самым мы не меняем изначальную коллекцию ссылок и передаем ее в первозданном виде в наш метод заполнения регистра. Это никак не поломает типовой механизм, очистка регистров от неиспользуемых будет работать в штатном режиме.
Теперь нам нужно описать вызов нашей команды. Для этого переопределим метод ВыполнитьКомандуВнешнегоОбъекта модуля ДополнительныеОтчетыИОбработки
&Вместо("ВыполнитьКомандуВнешнегоОбъекта")
Функция РДО_ВыполнитьКомандуВнешнегоОбъекта(ВнешнийОбъект, ИдентификаторКоманды, ПараметрыКоманды, АдресРезультата)
РезультатИзменен = Ложь;
Результат = ПродолжитьВызов(ВнешнийОбъект, ИдентификаторКоманды, ПараметрыКоманды, АдресРезультата);
СведенияОВнешнемОбъекте = ВнешнийОбъект.СведенияОВнешнейОбработке(); // см. СведенияОВнешнейОбработке
ВидОбработки = ПолучитьВидОбработкиПоСтроковомуПредставлениюВида(СведенияОВнешнемОбъекте.Вид);
ПередаватьПараметры = (
СведенияОВнешнемОбъекте.Свойство("ВерсияБСП")
И ОбщегоНазначенияКлиентСервер.СравнитьВерсии(СведенияОВнешнемОбъекте.ВерсияБСП, "1.2.1.4") >= 0);
РезультатВыполнения = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(ПараметрыКоманды, "РезультатВыполнения");
Если ТипЗнч(РезультатВыполнения) <> Тип("Структура") Тогда
ПараметрыКоманды.Вставить("РезультатВыполнения", Новый Структура());
КонецЕсли;
Если ВидОбработки = Перечисления.ВидыДополнительныхОтчетовИОбработок.ОтправкаОбъектов Тогда
РезультатИзменен = Истина;
ОбъектыНазначения = Неопределено;
ПараметрыКоманды.Свойство("ОбъектыНазначения", ОбъектыНазначения);
ВыполнитьНазначаемуюКомандуДополнительногоОтчетаИлиОбработки(
ВнешнийОбъект,
ИдентификаторКоманды,
?(ПередаватьПараметры, ПараметрыКоманды, Неопределено),
ОбъектыНазначения
);
Если РезультатИзменен Тогда
Если ТипЗнч(АдресРезультата) = Тип("Строка") И ЭтоАдресВременногоХранилища(АдресРезультата) Тогда
ПоместитьВоВременноеХранилище(ПараметрыКоманды.РезультатВыполнения, АдресРезультата);
КонецЕсли;
Результат = ПараметрыКоманды.РезультатВыполнения;
КонецЕсли;
Возврат Результат;
КонецФункции
Тут мы сначала выполняем типовой код, потом сверяем вид обработки с нашим доработанным видом. Если это наша обработка, делаем вызов обработки, возводим флаг РезультатИзменен в истину для того, чтобы в последующем вернуть правильный результат обратно.
Теперь нам нужно создать саму кнопку на форме. Это делается через подключаемые команды, но само описание команды описывается в модуле ДополнительныеОтчетыИОбработки в методе ПриОпределенииКомандПодключенныхКОбъекту. Добавим его в расширение с аннотацией &После
&После("ПриОпределенииКомандПодключенныхКОбъекту")
Процедура РДО_ПриОпределенииКомандПодключенныхКОбъекту(НастройкиФормы, Источники, ПодключенныеОтчетыИОбработки, Команды)
Если Не ПравоДоступа("Чтение", Метаданные.РегистрыСведений.НазначениеДополнительныхОбработок) Тогда
Возврат;
КонецЕсли;
Если НастройкиФормы.ЭтоФормаОбъекта Тогда
ТипФормы = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипФормыОбъекта();
Иначе
ТипФормы = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипФормыСписка();
КонецЕсли;
УстанавливатьПараметрыФО = (Метаданные.ОбщиеКоманды.Найти("СозданиеСвязанныхОбъектов") <> Неопределено);
Если УстанавливатьПараметрыФО Тогда
НастройкиФормы.ФункциональныеОпции.Вставить("ДополнительныеОтчетыИОбработкиОбъектНазначения", Справочники.ИдентификаторыОбъектовМетаданных.ПустаяСсылка());
НастройкиФормы.ФункциональныеОпции.Вставить("ДополнительныеОтчетыИОбработкиТипФормы", ТипФормы);
КонецЕсли;
Если Не ПолучитьФункциональнуюОпцию("ИспользоватьДополнительныеОтчетыИОбработки") Тогда
Возврат;
КонецЕсли;
ИдентификаторыОбъектов = Новый Массив;
ИсточникиКоманд = Новый Соответствие;
Для Каждого Источник Из Источники.Строки Цикл
Для Каждого ДокументРегистратор Из Источник.Строки Цикл
ИдентификаторыОбъектов.Добавить(ДокументРегистратор.СсылкаМетаданных);
ИсточникиКоманд.Вставить(ДокументРегистратор.СсылкаМетаданных, ДокументРегистратор);
КонецЦикла;
ИдентификаторыОбъектов.Добавить(Источник.СсылкаМетаданных);
ИсточникиКоманд.Вставить(Источник.СсылкаМетаданных, Источник);
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Назначение.ОбъектНазначения КАК ОбъектНазначения,
| Назначение.ИспользоватьОтправкуОбъектов КАК ИспользоватьОтправкуОбъектов
|ИЗ
| РегистрСведений.РДО_НазначениеДополнительныхОбработок КАК Назначение
|ГДЕ
| Назначение.ОбъектНазначения В(&ИОМы)
| И Назначение.ТипФормы = &ТипФормы";
Запрос.УстановитьПараметр("ИОМы", ИдентификаторыОбъектов);
Если ТипФормы = Неопределено Тогда
Запрос.Текст = СтрЗаменить(Запрос.Текст, "И Назначение.ТипФормы = &ТипФормы", "");
Иначе
Запрос.УстановитьПараметр("ТипФормы", ТипФормы);
КонецЕсли;
ТипыОтправкаОбъектов = Новый Массив;
ТаблицаРегистра = Запрос.Выполнить().Выгрузить();
Для Каждого СтрокаТаблицы Из ТаблицаРегистра Цикл
Если СтрокаТаблицы.ИспользоватьОтправкуОбъектов Тогда
ПодключаемыеКоманды.ДополнитьМассивТипов(ТипыОтправкаОбъектов, Источник.ТипСсылкиДанных);
КонецЕсли;
КонецЦикла;
Если ТипыОтправкаОбъектов.Количество() > 0 Тогда
Команда = Команды.Добавить();
Команда.Вид = "КоманднаяПанель";
Команда.Представление = НСтр("ru = 'Отправка объектов...'");
Команда.Картинка = БиблиотекаКартинок.Отправить;
Команда.Порядок = 50;
Команда.Обработчик = "ДополнительныеОтчетыИОбработкиКлиент.ОткрытьСписокКоманд";
Команда.РежимЗаписи = "Записывать";
Команда.МножественныйВыбор = Истина;
Команда.ТипПараметра = Новый ОписаниеТипов(ТипыОтправкаОбъектов);
Команда.ДополнительныеПараметры = ДополнительныеПараметрыКоманды(ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиОтправкаОбъектов(), Ложь);
КонецЕсли;
КонецПроцедуры
И тут же переопределим код метода ПодключенныеОбъектыМетаданных
&ИзменениеИКонтроль("ПодключенныеОбъектыМетаданных")
Функция РДО_ПодключенныеОбъектыМетаданных(Вид)
Результат = Новый ТаблицаЗначений;
Результат.Колонки.Добавить("Метаданные");
Результат.Колонки.Добавить("ПолноеИмя", Новый ОписаниеТипов("Строка"));
Результат.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.ИдентификаторыОбъектовМетаданных, СправочникСсылка.ИдентификаторыОбъектовРасширений"));
Результат.Колонки.Добавить("Вид", Новый ОписаниеТипов("Строка"));
Результат.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка"));
Результат.Колонки.Добавить("ПолноеПредставление", Новый ОписаниеТипов("Строка"));
Результат.Индексы.Добавить("Ссылка");
Результат.Индексы.Добавить("Вид");
Результат.Индексы.Добавить("ПолноеИмя");
МассивТиповИлиМетаданных = Новый Массив;
Если Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ЗаполнениеОбъекта
Или Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.Отчет
#Вставка
Или Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ОтправкаОбъектов
#КонецВставки
Или Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.СозданиеСвязанныхОбъектов Тогда
МассивТиповИлиМетаданных = Метаданные.ОпределяемыеТипы.ОбъектСДополнительнымиКомандами.Тип.Типы();
ИначеЕсли Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ШаблонСообщения Тогда
Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ШаблоныСообщений") Тогда
МодульШаблоныСообщенийСлужебный = ОбщегоНазначения.ОбщийМодуль("ШаблоныСообщенийСлужебный");
МассивТиповИлиМетаданных = МодульШаблоныСообщенийСлужебный.ИсточникиШаблоновСообщений()
Иначе
Возврат Результат;
КонецЕсли;
ИначеЕсли Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ПечатнаяФорма Тогда
Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Печать") Тогда
МодульУправлениеПечатью = ОбщегоНазначения.ОбщийМодуль("УправлениеПечатью");
МассивТиповИлиМетаданных = МодульУправлениеПечатью.ИсточникиКомандПечати()
Иначе
Возврат Результат;
КонецЕсли;
ИначеЕсли Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ДополнительнаяОбработка Тогда
МассивТиповИлиМетаданных = РазделыДополнительныхОбработок();
ИначеЕсли Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ДополнительныйОтчет Тогда
МассивТиповИлиМетаданных = РазделыДополнительныхОтчетов();
Иначе
Возврат Неопределено;
КонецЕсли;
Для Каждого ТипИлиМетаданные Из МассивТиповИлиМетаданных Цикл
Если ТипЗнч(ТипИлиМетаданные) = Тип("Тип") Тогда
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипИлиМетаданные);
Если ОбъектМетаданных = Неопределено Тогда
Продолжить;
КонецЕсли;
Иначе
ОбъектМетаданных = ТипИлиМетаданные;
КонецЕсли;
СтрокаТаблицы = Результат.Добавить();
СтрокаТаблицы.Метаданные = ОбъектМетаданных;
Если ОбъектМетаданных = ДополнительныеОтчетыИОбработкиКлиентСервер.ИмяНачальнойСтраницы() Тогда
СтрокаТаблицы.ПолноеИмя = ДополнительныеОтчетыИОбработкиКлиентСервер.ИмяНачальнойСтраницы();
СтрокаТаблицы.Ссылка = Справочники.ИдентификаторыОбъектовМетаданных.ПустаяСсылка();
СтрокаТаблицы.Вид = "Подсистема";
СтрокаТаблицы.Представление = СтандартныеПодсистемыСервер.ПредставлениеНачальнойСтраницы();
Иначе
СтрокаТаблицы.ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
СтрокаТаблицы.Ссылка = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ОбъектМетаданных);
СтрокаТаблицы.Вид = Лев(СтрокаТаблицы.ПолноеИмя, СтрНайти(СтрокаТаблицы.ПолноеИмя, ".") - 1);
СтрокаТаблицы.Представление = ОбъектМетаданных.Представление();
КонецЕсли;
СтрокаТаблицы.ПолноеПредставление = СтрокаТаблицы.Представление + " (" + СтрокаТаблицы.Вид + ")";
КонецЦикла;
Возврат Результат;
КонецФункции
Это не кнопка для выполнения команды из нашей обработки, это кнопка для открытия окна, в котором мы сможем вызвать команду из нашей обработки. Осталось только добавить в это окно наш тип вызова ОтправкаОбъектов. Для этого нам надо вынести в расширение перечисление СпособыВызоваДополнительныхОбработок из типовой конфигурации . В это перечисление добавить новое значение ОтправкаОбъектов.
И последним штрихом вынести в расширение общую форму ДополнительныеОтчетыИОбработки и написать чуть-чуть кода.
Во-первых, сделать вызов после события ПриСозданииНаСервере.
&НаСервере
Процедура РДО_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
Если ЗначениеЗаполнено(Параметры.ИмяРаздела)
И Параметры.ИмяРаздела <> ДополнительныеОтчетыИОбработкиКлиентСервер.ИмяНачальнойСтраницы() Тогда
СсылкаРаздела = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(Метаданные.Подсистемы.Найти(Параметры.ИмяРаздела));
КонецЕсли;
ВидОбработок = ДополнительныеОтчетыИОбработки.ПолучитьВидОбработкиПоСтроковомуПредставлениюВида(Параметры.Вид);
Если ВидОбработок = Перечисления.ВидыДополнительныхОтчетовИОбработок.ОтправкаОбъектов Тогда
ЭтоНазначаемыеОбработки = Истина;
Заголовок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'Отправка объектов (%1)'"),
ДополнительныеОтчетыИОбработки.ПредставлениеРаздела(СсылкаРаздела));
КонецЕсли;
Если ЗначениеЗаполнено(Параметры.РежимОткрытияОкна) Тогда
РежимОткрытияОкна = Параметры.РежимОткрытияОкна;
КонецЕсли;
Если Не ПустаяСтрока(Параметры.Заголовок) Тогда
Заголовок = Параметры.Заголовок;
КонецЕсли;
Если ЭтоНазначаемыеОбработки Тогда
Элементы.НастроитьСписок.Видимость = Ложь;
ОбъектыНазначения.ЗагрузитьЗначения(Параметры.ОбъектыНазначения.ВыгрузитьЗначения());
Если ОбъектыНазначения.Количество() = 0 Тогда
Отказ = Истина;
Возврат;
КонецЕсли;
ИнформацияОВладельце = ДополнительныеОтчетыИОбработкиПовтИсп.ПараметрыФормыНазначаемогоОбъекта(Параметры.ИмяФормы);
МетаданныеРодителя = Метаданные.НайтиПоТипу(ТипЗнч(ОбъектыНазначения[0].Значение));
Если МетаданныеРодителя = Неопределено Тогда
СсылкаРодителя = ИнформацияОВладельце.СсылкаРодителя;
Иначе
СсылкаРодителя = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(МетаданныеРодителя);
КонецЕсли;
Если ТипЗнч(ИнформацияОВладельце) = Тип("ФиксированнаяСтруктура") Тогда
ЭтоФормаОбъекта = ИнформацияОВладельце.ЭтоФормаОбъекта;
Иначе
ЭтоФормаОбъекта = Ложь;
КонецЕсли;
КонецЕсли;
ЗаполнитьТаблицуОбработок();
КонецПроцедуры
Во-вторых, в этой же форме переопределим метод ЗаполнитьТаблицуОбработок и добавим в него нашу команду.
&НаСервере
&ИзменениеИКонтроль("ЗаполнитьТаблицуОбработок")
Процедура РДО_ЗаполнитьТаблицуОбработок()
ТипыКоманд = Новый Массив;
ТипыКоманд.Добавить(Перечисления.СпособыВызоваДополнительныхОбработок.ВызовКлиентскогоМетода);
ТипыКоманд.Добавить(Перечисления.СпособыВызоваДополнительныхОбработок.ВызовСерверногоМетода);
ТипыКоманд.Добавить(Перечисления.СпособыВызоваДополнительныхОбработок.ОткрытиеФормы);
ТипыКоманд.Добавить(Перечисления.СпособыВызоваДополнительныхОбработок.СценарийВБезопасномРежиме);
#Вставка
ТипыКоманд.Добавить(Перечисления.СпособыВызоваДополнительныхОбработок.ОтправкаОбъектов);
#КонецВставки
Запрос = ДополнительныеОтчетыИОбработки.НовыйЗапросПоДоступнымКомандам(ВидОбработок, ?(ЭтоГлобальныеОбработки, СсылкаРаздела, СсылкаРодителя), ЭтоФормаОбъекта, ТипыКоманд);
ТаблицаРезультат = Запрос.Выполнить().Выгрузить();
ТаблицаКоманд.Загрузить(ТаблицаРезультат);
КонецПроцедуры
Ну и наконец, опишем вызов нашего метода.
И еще совсем немного кода
&НаКлиенте
&После("ВыполнитьОбработкуПоПараметрам")
Процедура РДО_ВыполнитьОбработкуПоПараметрам()
ДанныеОбработки = Элементы.ТаблицаКоманд.ТекущиеДанные;
Если ДанныеОбработки = Неопределено Тогда
Возврат;
КонецЕсли;
ВыполняемаяКоманда = Новый Структура(
"Ссылка, Представление,
|Идентификатор, ВариантЗапуска, ПоказыватьОповещение,
|Модификатор, ОбъектыНазначения, ЭтоОтчет, Вид");
ЗаполнитьЗначенияСвойств(ВыполняемаяКоманда, ДанныеОбработки);
Если НЕ ЭтоГлобальныеОбработки Тогда
ВыполняемаяКоманда.ОбъектыНазначения = ОбъектыНазначения.ВыгрузитьЗначения();
КонецЕсли;
ВыполняемаяКоманда.ЭтоОтчет = ЭтоОтчеты;
ВыполняемаяКоманда.Вид = ВидОбработок;
Если ДанныеОбработки.ВариантЗапуска = ПредопределенноеЗначение("Перечисление.СпособыВызоваДополнительныхОбработок.ОтправкаОбъектов") Тогда
// Изменение элементов формы
Элементы.ПоясняющаяДекорация.Заголовок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
НСтр("ru = 'Выполняется команда ""%1""...'"),
ДанныеОбработки.Представление);
Элементы.Страницы.ТекущаяСтраница = Элементы.СтраницаВыполненияОбработки;
Элементы.НастроитьСписок.Видимость = Ложь;
Элементы.ВыполнитьОбработку.Видимость = Ложь;
// Вызов сервера только после перехода формы в консистентное состояние.
ПодключитьОбработчикОжидания("ВыполнитьСерверныйМетодОбработки", 0.1, Истина);
КонецЕсли;
КонецПроцедуры
И вот тут стоит обратить внимание на последний вызов в этом методе.
// Вызов сервера только после перехода формы в консистентное состояние.
ПодключитьОбработчикОжидания("ВыполнитьСерверныйМетодОбработки", 0.1, Истина);
Это типовой метод для выполнения команды обработки на сервере. Вот тут вы можете написать свой вызов, если того потребует ситуация. Как именно вызывать команды своей обработки, вы можете посмотреть в этом же методе в типовой форме.
Все, создаем обработку.
Код дополнительной обработки
Функция СведенияОВнешнейОбработке() Экспорт
Сведения = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке();
Сведения.ВерсияБСП = "3.1.7.422";
Сведения.Вид = "ОтправкаОбъектов";
Сведения.Назначение = СтрРазделить("Документ.ТабельУчетаРабочегоВремени", ",");
Сведения.Версия = "1.0";
Сведения.БезопасныйРежим = Ложь;
Сведения.Информация = НСтр("ru = 'Функционал для отправки данных из документа на почту'");
Команда = Сведения.Команды.Добавить();
Команда.Представление = "Отправить на почту";
Команда.Использование = "ОтправкаОбъектов";
Команда.Идентификатор = "Отправить";
Возврат Сведения;
КонецФункции
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыКоманды, НазначенныеОбъекты) Экспорт
Если ИдентификаторКоманды = "Отправить" Тогда
// Невероятно полезный алгоритм для отправки сообщений
КонецЕсли;
КонецПроцедуры
Загружаем ее в базу, открываем документ Табель.
Усё готово. Таким образом можно сделать сколько угодно различных типов команд для формы.
Добавление новой команды на примере http-сервиса
Задача. Сделать обработчик входящих http-запросов через дополнительные обработки.
По аналогии с предыдущим примером, нам надо добавить пару строковых констант в модуль ДополнительныеОтчетыИОбработкиКлиентСервер, который мы уже вынесли в расширение ранее.
Функция ВидОбработкиHTTPСервис() Экспорт
Возврат "HTTPСервис";
КонецФункции
Функция ТипКомандыВызовHTTPСервиса() Экспорт
Возврат "ВызовHTTPСервиса";
КонецФункции
И добавить в перечисление ВидыДополнительныхОтчетовИОбработок значение HTTPСервис, а в перечисление СпособыВызоваДополнительныхОбработок значение ВызовHTTPСервиса.
Потом переопределить функцию ПолучитьВидОбработкиПоСтроковомуПредставлениюВида для возврата вида обработки по строковому значению в модуле ДополнительныеОтчетыИОбработки
&Вместо("ПолучитьВидОбработкиПоСтроковомуПредставлениюВида")
Функция РДО_ПолучитьВидОбработкиПоСтроковомуПредставлениюВида(СтроковоеПредставление)
Если СтроковоеПредставление = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиHTTPСервис() Тогда
Возврат Перечисления.ВидыДополнительныхОтчетовИОбработок.HTTPСервис;
Иначе
Возврат ПродолжитьВызов(СтроковоеПредставление);
КонецЕсли;
КонецФункции
Теперь оформим вызов команды нашей обработки в модуле ДополнительныеОтчетыИОбработки переопределим метод ВыполнитьКомандуВнешнегоОбъекта.
&Вместо("ВыполнитьКомандуВнешнегоОбъекта")
Функция РДО_ВыполнитьКомандуВнешнегоОбъекта(ВнешнийОбъект, ИдентификаторКоманды, ПараметрыКоманды, АдресРезультата)
РезультатИзменен = Ложь;
Результат = ПродолжитьВызов(ВнешнийОбъект, ИдентификаторКоманды, ПараметрыКоманды, АдресРезультата);
СведенияОВнешнемОбъекте = ВнешнийОбъект.СведенияОВнешнейОбработке(); // см. СведенияОВнешнейОбработке
ВидОбработки = ПолучитьВидОбработкиПоСтроковомуПредставлениюВида(СведенияОВнешнемОбъекте.Вид);
ПередаватьПараметры = (
СведенияОВнешнемОбъекте.Свойство("ВерсияБСП")
И ОбщегоНазначенияКлиентСервер.СравнитьВерсии(СведенияОВнешнемОбъекте.ВерсияБСП, "1.2.1.4") >= 0);
РезультатВыполнения = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(ПараметрыКоманды, "РезультатВыполнения");
Если ТипЗнч(РезультатВыполнения) <> Тип("Структура") Тогда
ПараметрыКоманды.Вставить("РезультатВыполнения", Новый Структура());
КонецЕсли;
Если ВидОбработки = Перечисления.ВидыДополнительныхОтчетовИОбработок.HTTPСервис Тогда
РезультатИзменен = Истина;
ВыполнитьКомандуHTTPСервиса(
ВнешнийОбъект,
ИдентификаторКоманды,
ПараметрыКоманды
);
КонецЕсли;
Если РезультатИзменен Тогда
Если ТипЗнч(АдресРезультата) = Тип("Строка") И ЭтоАдресВременногоХранилища(АдресРезультата) Тогда
ПоместитьВоВременноеХранилище(ПараметрыКоманды.РезультатВыполнения, АдресРезультата);
КонецЕсли;
Результат = ПараметрыКоманды.РезультатВыполнения;
КонецЕсли;
Возврат Результат;
КонецФункции
Процедура ВыполнитьКомандуHTTPСервиса(ВнешнийОбъект, Знач ИдентификаторКоманды, ПараметрыКоманды)
ВнешнийОбъект.ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыКоманды);
КонецПроцедуры
Соответственно, если мы делаем несколько кастомных команд, тут их можно проверять через ИначеЕсли. Например, если добавить в расширение, и этот пример, и пример с кнопкой формы.
Потом я посмотрел на поля, которые мы можем указать в команде:
ТаблицаКоманд = Новый ТаблицаЗначений;
ТаблицаКоманд.Колонки.Добавить("Представление", РеквизитыТабличнойЧасти.Представление.Тип);
ТаблицаКоманд.Колонки.Добавить("Идентификатор", РеквизитыТабличнойЧасти.Идентификатор.Тип);
ТаблицаКоманд.Колонки.Добавить("Использование", Новый ОписаниеТипов("Строка"));
ТаблицаКоманд.Колонки.Добавить("ПоказыватьОповещение", РеквизитыТабличнойЧасти.ПоказыватьОповещение.Тип);
ТаблицаКоманд.Колонки.Добавить("Модификатор", РеквизитыТабличнойЧасти.Модификатор.Тип);
ТаблицаКоманд.Колонки.Добавить("Скрыть", РеквизитыТабличнойЧасти.Скрыть.Тип);
ТаблицаКоманд.Колонки.Добавить("ЗаменяемыеКоманды", РеквизитыТабличнойЧасти.ЗаменяемыеКоманды.Тип);
И понял, что их не хватает для меня. Я хочу передавать в обработчик метод http-запроса, чтобы я мог в обработке для сервиса написать обработчик, и GET запроса, и POST запроса. Для этого в модуле ДополнительныеОтчетыИОбработки переопределяем метод СведенияОВнешнейОбработке.
&ИзменениеИКонтроль("СведенияОВнешнейОбработке")
Функция РДО_СведенияОВнешнейОбработке(ВерсияБСП)
ПараметрыРегистрации = Новый Структура;
ПараметрыРегистрации.Вставить("Вид", "");
ПараметрыРегистрации.Вставить("Версия", "0.0");
ПараметрыРегистрации.Вставить("Назначение", Новый Массив);
ПараметрыРегистрации.Вставить("Наименование", Неопределено);
ПараметрыРегистрации.Вставить("БезопасныйРежим", Истина);
ПараметрыРегистрации.Вставить("Информация", Неопределено);
ПараметрыРегистрации.Вставить("ВерсияБСП", ВерсияБСП);
ПараметрыРегистрации.Вставить("ОпределитьНастройкиФормы", Ложь);
РеквизитыТабличнойЧасти = Метаданные.Справочники.ДополнительныеОтчетыИОбработки.ТабличныеЧасти.Команды.Реквизиты;
ТаблицаКоманд = Новый ТаблицаЗначений;
ТаблицаКоманд.Колонки.Добавить("Представление", РеквизитыТабличнойЧасти.Представление.Тип);
ТаблицаКоманд.Колонки.Добавить("Идентификатор", РеквизитыТабличнойЧасти.Идентификатор.Тип);
ТаблицаКоманд.Колонки.Добавить("Использование", Новый ОписаниеТипов("Строка"));
ТаблицаКоманд.Колонки.Добавить("ПоказыватьОповещение", РеквизитыТабличнойЧасти.ПоказыватьОповещение.Тип);
ТаблицаКоманд.Колонки.Добавить("Модификатор", РеквизитыТабличнойЧасти.Модификатор.Тип);
ТаблицаКоманд.Колонки.Добавить("Скрыть", РеквизитыТабличнойЧасти.Скрыть.Тип);
ТаблицаКоманд.Колонки.Добавить("ЗаменяемыеКоманды", РеквизитыТабличнойЧасти.ЗаменяемыеКоманды.Тип);
#Вставка
ТаблицаКоманд.Колонки.Добавить("Метод", РеквизитыТабличнойЧасти.Метод.Тип);
#КонецВставки
ПараметрыРегистрации.Вставить("Команды", ТаблицаКоманд);
ПараметрыРегистрации.Вставить("Разрешения", Новый Массив);
Возврат ПараметрыРегистрации;
КонецФункции
И добавляем сюда поле метод. Так же нам надо вынести в расширение справочник ДополнительныеОтчетыИОбработки и добавить наше поле в реквизит табличной части с типом строка(20).
И все, доработки закончены. Осталось создать обработку, сам сервис и проверить работу
Создадим такой сервис с корневым URL: test_service и шаблоном /*
Опишем обработку нашего сервиса.
Функция УниверсальныйМетод(HTTPЗапрос)
Массив = СтрРазделить(HTTPЗапрос.ОтносительныйURL, "/");
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ДополнительныеОтчетыИОбработкиКоманды.Идентификатор КАК Идентификатор,
| ДополнительныеОтчетыИОбработкиКоманды.Метод КАК Метод,
| ДополнительныеОтчетыИОбработкиКоманды.Ссылка КАК Ссылка
|ИЗ
| Справочник.ДополнительныеОтчетыИОбработки.Команды КАК ДополнительныеОтчетыИОбработкиКоманды
|ГДЕ ИСТИНА
| И ДополнительныеОтчетыИОбработкиКоманды.Идентификатор = &Идентификатор
| И ДополнительныеОтчетыИОбработкиКоманды.Метод = &Метод
| И ДополнительныеОтчетыИОбработкиКоманды.ВариантЗапуска = ЗНАЧЕНИЕ(Перечисление.СпособыВызоваДополнительныхОбработок.ВызовHTTPСервиса)
|";
Запрос.Параметры.Вставить("Идентификатор", Массив[1]);
Запрос.Параметры.Вставить("Метод", HTTPЗапрос.HTTPМетод);
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Возврат Новый HTTPСервисОтвет(404);
КонецЕсли;
Выборка = Результат.Выбрать();
Выборка.Следующий();
ПараметрыКоманды = Новый Структура;
ПараметрыКоманды.Вставить("HTTPЗапрос", HTTPЗапрос);
ПараметрыКоманды.Вставить("Метод", Выборка.Метод);
ПараметрыКоманды.Вставить("ДополнительнаяОбработкаСсылка", Выборка.Ссылка);
ПараметрыКоманды.Вставить("ИдентификаторКоманды", Выборка.Идентификатор);
Возврат ДополнительныеОтчетыИОбработки.ВыполнитьКоманду(ПараметрыКоманды).HTTPОтвет;
КонецФункции
Тут мы смотрим на первую часть пути относительного URL, на метод http-запроса, создаем запрос и пытаемся найти по этим данным обработчик. Если обработчик не найден, кидаем ответ 404, если же обработчик мы нашли, отправляем далее вызов в дополнительную обработку.
Пришла пора написать саму обработку.
Функция СведенияОВнешнейОбработке() Экспорт
Сведения = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке();
Сведения.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиHTTPСервис();
Команда = Сведения.Команды.Добавить();
Команда.Представление = "Пинг";
Команда.Идентификатор = "Ping";
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовHTTPСервиса();
Команда.Метод = "GET";
Команда = Сведения.Команды.Добавить();
Команда.Представление = "Привет Мир";
Команда.Идентификатор = "HelloWorld";
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовHTTPСервиса();
Команда.Метод = "GET";
Команда = Сведения.Команды.Добавить();
Команда.Представление = "Привет Мир";
Команда.Идентификатор = "HelloWorld";
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовHTTPСервиса();
Команда.Метод = "POST";
Возврат Сведения;
КонецФункции
Процедура ВыполнитьКоманду(Идентификтаор, ПараметрыВыполнения) Экспорт
Если Идентификтаор = "HelloWorld" Тогда
ПриветМир(ПараметрыВыполнения);
ИначеЕсли Идентификтаор = "Ping" Тогда
Пинг(ПараметрыВыполнения);
КонецЕсли;
КонецПроцедуры
Процедура Пинг(ПараметрыВыполнения)
HTTPОтвет = Новый HTTPСервисОтвет(200);
HTTPОтвет.УстановитьТелоИзСтроки(ТекущаяУниверсальнаяДатаВМиллисекундах());
ПараметрыВыполнения.РезультатВыполнения.Вставить("HTTPОтвет", HTTPОтвет);
КонецПроцедуры
Процедура ПриветМир(ПараметрыВыполнения)
HTTPОтвет = Новый HTTPСервисОтвет(200);
Шаблон = "Привет, %1!";
Запрос = ПараметрыВыполнения.HTTPЗапрос;
Если ПараметрыВыполнения.Метод = "GET" Тогда
Тело = СтрШаблон(Шаблон, "Человек");
ИначеЕсли ПараметрыВыполнения.Метод = "POST" Тогда
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТелоЗапроса);
XDTOОбъект = ФабрикаXDTO.ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Тело = СтрШаблон(Шаблон, XDTOОбъект.name);
Иначе
HTTPОтвет = Новый HTTPСервисОтвет(404);
Тело = "Нет обработчика запрашиваемого метода";
КонецЕсли;
HTTPОтвет.УстановитьТелоИзСтроки(Тело);
ПараметрыВыполнения.РезультатВыполнения.Вставить("HTTPОтвет", HTTPОтвет);
КонецПроцедуры
Подгружаем обработку в базу, публикуем сервис, делаем запросы.
http://domen/basename/hs/test_service/ping
http://domen/basename/hs/test_service/helloworld
или делаем POST запрос к helloworld, передавая в тело запроса JSON.
{"name": "Какое-то имя"}
Если вы дочитали до этого момента и у вас не бомбит, не подгорает, вы не поставили минус и не написали в комментах что-то типа "Аффтар выпей йаду", тогда я восхищаюсь вашей здоровой психикой и говорю Спасибо!
На закуску
Удобная отладка дополнительных обработок
Уже давно не в новинку, что можно переопределить метод ПодключитьВнешнююОбработку модуля ДополнительныеОтчетыИОбработки и радоваться подключению обработки из заранее заготовленного файла. Меня в этом методе смущает только одно. Я должен создать папку, положить туда файлик с обработкой, где-то что-то настроить в базе, чтобы отладить обработку, исправить ее и потом опять же руками загрузить эту обработку в базу. Это совсем не для такого ленивого человека, как я. Потому, вот мой рецепт для ленивых.
Переопределяем метод.
&ИзменениеИКонтроль("ПодключитьВнешнююОбработку")
Функция РДО_ПодключитьВнешнююОбработку(Ссылка)
СтандартнаяОбработка = Истина;
Результат = Неопределено;
ИнтеграцияПодсистемБСП.ПриПодключенииВнешнейОбработки(Ссылка, СтандартнаяОбработка, Результат);
Если Не СтандартнаяОбработка Тогда
Возврат Результат;
КонецЕсли;
// Проверка корректности переданных параметров.
Если ТипЗнч(Ссылка) <> Тип("СправочникСсылка.ДополнительныеОтчетыИОбработки")
Или Ссылка = Справочники.ДополнительныеОтчетыИОбработки.ПустаяСсылка() Тогда
Возврат Неопределено;
КонецЕсли;
// Подключение
#Если ТолстыйКлиентОбычноеПриложение Тогда
ИмяОбработки = ПолучитьИмяВременногоФайла();
ХранилищеОбработки = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "ХранилищеОбработки");
ДвоичныеДанные = ХранилищеОбработки.Получить();
ДвоичныеДанные.Записать(ИмяОбработки);
Возврат ИмяОбработки;
#КонецЕсли
Вид = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Вид");
Если Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.Отчет
Или Вид = Перечисления.ВидыДополнительныхОтчетовИОбработок.ДополнительныйОтчет Тогда
Менеджер = ВнешниеОтчеты;
Иначе
Менеджер = ВнешниеОбработки;
КонецЕсли;
ПараметрыЗапуска = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Ссылка, "БезопасныйРежим, ХранилищеОбработки");
АдресВоВременномХранилище = ПоместитьВоВременноеХранилище(ПараметрыЗапуска.ХранилищеОбработки.Получить());
Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ПрофилиБезопасности") Тогда
МодульРаботаВБезопасномРежиме = ОбщегоНазначения.ОбщийМодуль("РаботаВБезопасномРежиме");
ИспользуютсяПрофилиБезопасности = МодульРаботаВБезопасномРежиме.ИспользуютсяПрофилиБезопасности();
Иначе
ИспользуютсяПрофилиБезопасности = Ложь;
КонецЕсли;
Если ИспользуютсяПрофилиБезопасности Тогда
МодульРаботаВБезопасномРежимеСлужебный = ОбщегоНазначения.ОбщийМодуль("РаботаВБезопасномРежимеСлужебный");
БезопасныйРежим = МодульРаботаВБезопасномРежимеСлужебный.РежимПодключенияВнешнегоМодуля(Ссылка);
Если БезопасныйРежим = Неопределено Тогда
БезопасныйРежим = Истина;
КонецЕсли;
Иначе
БезопасныйРежим = ПолучитьФункциональнуюОпцию("СтандартныеПодсистемыВМоделиСервиса") Или ПараметрыЗапуска.БезопасныйРежим;
Если БезопасныйРежим Тогда
ЗапросРазрешений = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1
| ДополнительныеОтчетыИОбработкиРазрешения.НомерСтроки,
| ДополнительныеОтчетыИОбработкиРазрешения.ВидРазрешения
|ИЗ
| Справочник.ДополнительныеОтчетыИОбработки.Разрешения КАК ДополнительныеОтчетыИОбработкиРазрешения
|ГДЕ
| ДополнительныеОтчетыИОбработкиРазрешения.Ссылка = &Ссылка");
ЗапросРазрешений.УстановитьПараметр("Ссылка", Ссылка);
ЕстьРазрешений = Не ЗапросРазрешений.Выполнить().Пустой();
РежимСовместимости = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "РежимСовместимостиРазрешений");
Если РежимСовместимости = Перечисления.РежимыСовместимостиРазрешенийДополнительныхОтчетовИОбработок.Версия_2_2_2
И ЕстьРазрешений Тогда
БезопасныйРежим = Ложь;
КонецЕсли;
КонецЕсли;
КонецЕсли;
ЗаписатьПримечание(Ссылка,
СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Подключение, %1 = ""%2"".'"), "БезопасныйРежим", БезопасныйРежим));
#Вставка
Если Ссылка.Публикация = Перечисления.ВариантыПубликацииДополнительныхОтчетовИОбработок.РежимОтладки Тогда
Попытка
ОбработкаОбъект = Менеджер.Создать(Ссылка.ПутьДоФайла + "\" + Ссылка.ИмяФайла, Ложь, ОбщегоНазначения.ОписаниеЗащитыБезПредупреждений());
Исключение
Представление = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ВызватьИсключение Представление;
КонецПопытки;
ИмяОбработки = ОбработкаОбъект.Метаданные().Имя;
Иначе
#КонецВставки
ИмяОбработки = Менеджер.Подключить(АдресВоВременномХранилище, , БезопасныйРежим,
ОбщегоНазначения.ОписаниеЗащитыБезПредупреждений());
#Вставка
КонецЕсли;
#КонецВставки
Возврат ИмяОбработки;
КонецФункции
Как вы можете видеть в коде, тут используется реквизит ПутьДоФайла справочника ДополнительныеОтчетыИОбработки. Этого реквизита в типовом справочнике нет, поэтому добавляем его с типом Строка с неограниченной длиной.
Теперь подумаем о логике. В форме объекта справочника мы точно видим разные режимы работы, и там точно есть отладка. Вот давайте к этой отладке и привяжемся. Для начала, добавим метод с аннотацией после в МО справочника ДополнительныеОтчетыИОбработки.
&После("ОбработкаПроверкиЗаполнения")
Процедура РДО_ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
Если Публикация = Перечисления.ВариантыПубликацииДополнительныхОтчетовИОбработок.РежимОтладки Тогда
ПроверяемыеРеквизиты.Добавить("ПутьДоФайла");
КонецЕсли;
КонецПроцедуры
Это чтобы совершенно случайно не искать файл в пустоте.
Потом идем на форму, находим элемент формы Публикация и назначаем ему обработчик события.
&НаКлиенте
Асинх Процедура РДО_ПубликацияПриИзмененииПосле(Элемент)
Если Объект.Публикация = ПредопределенноеЗначение("Перечисление.ВариантыПубликацииДополнительныхОтчетовИОбработок.РежимОтладки") Тогда
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
Диалог.Заголовок = "Укажите каталог для сохранения файла отладки";
Диалог.МножественныйВыбор = Ложь;
Ответ = Ждать Диалог.ВыбратьАсинх();
Если Ответ <> Неопределено Тогда
Объект.ПутьДоФайла = Ответ[0];
Адрес = ДополнительныеОтчетыИОбработкиВызовСервера.ПоместитьВХранилище(Объект.Ссылка, УникальныйИдентификатор);
ПараметрыСохранения = ФайловаяСистемаКлиент.ПараметрыСохраненияФайла();
ПараметрыСохранения.Интерактивно = Ложь;
ФайловаяСистемаКлиент.СохранитьФайл(Неопределено, Адрес, Объект.ПутьДоФайла + "\" + Объект.ИмяФайла, ПараметрыСохранения);
КонецЕсли;
Иначе
ТекстВопроса = "Обновить обработку из файла отладки?";
Ответ = Ждать ВопросАсинх(ТекстВопроса, РежимДиалогаВопрос.ДаНетОтмена, 20, КодВозвратаДиалога.Да, "Внимание!", КодВозвратаДиалога.Да);
Если Ответ = КодВозвратаДиалога.Да Тогда
Объект.ИмяФайла = Объект.ПутьДоФайла + "\" + Объект.ИмяФайла;
ПараметрыРегистрации = Новый Структура;
ПараметрыРегистрации.Вставить("Успех", Ложь);
ПараметрыРегистрации.Вставить("АдресДанныхОбработки", АдресДанныхОбработки);
Обработчик = Новый ОписаниеОповещения("ОбновитьИзФайлаПослеВыбораФайла", ЭтотОбъект, ПараметрыРегистрации);
ПараметрыЗагрузки = ФайловаяСистемаКлиент.ПараметрыЗагрузкиФайла();
ПараметрыЗагрузки.Интерактивно = Ложь;
ПараметрыЗагрузки.ИдентификаторФормы = УникальныйИдентификатор;
ФайловаяСистемаКлиент.ЗагрузитьФайл(Обработчик, ПараметрыЗагрузки, Объект.ИмяФайла);
КонецЕсли;
Объект.ПутьДоФайла = "";
КонецЕсли;
КонецПроцедуры
При установке режима отладки система сама попросит вас указать каталог, куда надо выгрузить обработку. Запишет путь в реквизит справочника, и будет по мере вызова подключать файл обработки. Вы можете открыть этот файл в конфигураторе и ставить там точки остановы, можете редактировать код. После того, как закончите изменения, вы меняете режим на Используется, и система предложит вам автомагически обновить обработку в базе из файла. Все работает само, нам остается только наслаждаться жизнью.
НО! Автор разрабатывает разработки на базе в режиме к-с. У автора сервер приложений находится на удаленной машине. И автор осознал, что тут не все так просто. Если ваш сервер находится на другой машине, значит вам на сервере надо создать расшаренную папку, дать в эту папку доступ себе и пользователю, от чьего имени запущен сервис 1С, и хранить обработки для отладки строго там. Тогда все будет работать как должно.
Вам совершенно не обязательно проходить тот же путь разработки, что я указал тут. Достаточно просто скачать из гитхаба расширение по этой ссылке.
Вот теперь точно все. Всем спасибо за внимание.