Предположим, что нам поставлена задача создания дополнительного отчета. Сам алгоритм формирования отчета нестандартный и выполняется продолжительное время (например, мы получаем данные из внешних источников, анализируем результат и красиво выводим в макет схемы компоновки данных). Хорошим решением будет сделать отчет, алгоритм формирования которого выполняется в фоне и не блокирует работу пользователя. Также учтем, что стандартная форма отчета из БСП нам не подходит, и мы решаем сделать свою форму отчета.
Для решения задачи воспользуемся подсистемами «Дополнительные отчеты и обработки» и «Длительные операции». Во многом пример данной статьи будет основан на принципах запуска дополнительной обработки в фоне, которые я описывал в предыдущей публикации. Здесь также накладывается аналогичное ограничение – созданный дополнительный отчет необходимо разместить в справочнике «Дополнительные отчеты и обработки». Без этого решение не заработает.
Приступим к реализации. Первым делом открываем модуль объекта внешнего отчета и описываем алгоритм формирования отчета в обработчике «ПриКомпоновкеРезультата»:
#Область ОбработчикиСобытий
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Пауза();
ВнешниеИсточники = Новый Структура;
ВнешниеИсточники.Вставить("ТестовыеДанные", ТестовыеДанные());
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных,
КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеИсточники, ДанныеРасшифровки, Истина);
ДокументРезультат.Очистить();
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки, Истина);
Пауза();
КонецПроцедуры
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Процедура Пауза()
ФоновыйСеанс = ПолучитьТекущийСеансИнформационнойБазы().ПолучитьФоновоеЗадание();
Если ФоновыйСеанс <> Неопределено Тогда
ФоновыйСеанс.ОжидатьЗавершенияВыполнения(3);
КонецЕсли;
КонецПроцедуры
Функция ТестовыеДанные()
ТестовыеДанные = Новый ТаблицаЗначений;
ТестовыеДанные.Колонки.Добавить("Группа", ОбщегоНазначения.ОписаниеТипаСтрока(255));
ТестовыеДанные.Колонки.Добавить("Показатель", ОбщегоНазначения.ОписаниеТипаЧисло(10));
Для Сч = 1 По 5 Цикл
Группа = СтрШаблон("Группа_%1", Сч);
Для Индекс = 1 По 3 Цикл
Данные = ТестовыеДанные.Добавить();
Данные.Группа = Группа;
Данные.Показатель = Индекс;
КонецЦикла;
КонецЦикла;
Возврат ТестовыеДанные;
КонецФункции
#КонецОбласти
Алгоритм довольно простой – формируем внешний источник данных «ТестовыеДанные()» для СКД, имитируем задержку выполнения в процедуре «Пауза» и выводим результат в табличный документ.
Далее создадим макет основной схемы компоновки данных. Весь процесс довольно простой, поэтому подробно описывать не буду, детали можно посмотреть под спойлером.
Сам отчет у нас готов – можно даже проверить в режиме предприятия через «Файл-Открыть» и попробовать сформировать.
Теперь реализуем методы по регистрации дополнительного отчета в справочнике «Дополнительные отчеты и обработки». Добавим следующий код в модуле объекта отчета:
// СтандартныеПодсистемы.ДополнительныеОтчетыИОбработки
// см. ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("3.1.6.100");
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительныйОтчет();
ПараметрыРегистрации.Версия = "1.0.0.0";
ПараметрыРегистрации.БезопасныйРежим = Ложь;
Команда = ПараметрыРегистрации.Команды.Добавить();
Команда.Представление = НСтр("ru = 'Пример дополнительного отчета с фоновым формированием'");
Команда.Идентификатор = ИдентификаторКомандыОткрытияФормыОтчета();
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыОткрытиеФормы();
Команда.ПоказыватьОповещение = Ложь;
Команда = ПараметрыРегистрации.Команды.Добавить();
Команда.Представление = НСтр("ru = 'Фоновое формирование отчета'");
Команда.Идентификатор = ИдентификаторКомандыФоновогоФормирования();
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
Команда.ПоказыватьОповещение = Ложь;
Команда.Скрыть = Истина;
Возврат ПараметрыРегистрации;
КонецФункции
// см. ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода()
Процедура ВыполнитьКоманду(ИдентификаторКоманды, ПараметрыВыполнения) Экспорт
Если ИдентификаторКоманды = ИдентификаторКомандыФоновогоФормирования() Тогда
СформироватьДополнительныйОтчет(ПараметрыВыполнения);
КонецЕсли;
КонецПроцедуры
// Конец СтандартныеПодсистемы.ДополнительныеОтчетыИОбработки
Функция ИдентификаторКомандыОткрытияФормыОтчета()
Возврат Метаданные().Имя;
КонецФункции
Функция ИдентификаторКомандыФоновогоФормирования() Экспорт
Возврат СтрШаблон("%1_ФоновоеФормирование", Метаданные().Имя);
КонецФункции
Следует обратить внимание на следующие моменты:
1. Для дополнительного отчета отключаем безопасный режим, т.к. для формирования будет использоваться фоновый сеанс с созданием экземпляра дополнительного отчета:
ПараметрыРегистрации.БезопасныйРежим = Ложь;
2. Добавили две команды: первая команда для открытия формы отчета, а вторая будет исполняться в запущенном фоновом задании формирования отчета. Вторую команду мы будем запускать из формы отчета через фоновое задание:
…
// Открытие формы отчета
Команда.Идентификатор = ИдентификаторКомандыОткрытияФормыОтчета();
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыОткрытиеФормы();
…
// Фоновое формирование отчета
Команда.Идентификатор = ИдентификаторКомандыФоновогоФормирования();
Команда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипКомандыВызовСерверногоМетода();
3. Точка входа для фонового выполнения – это процедура «ВыполнитьКоманду». Здесь мы сравниваем входящий идентификатор и передаем управление в метод «СформироватьДополнительныйОтчет» где будем запускать формирование отчета:
Если ИдентификаторКоманды = ИдентификаторКомандыФоновогоФормирования() Тогда
СформироватьДополнительныйОтчет(ПараметрыВыполнения);
КонецЕсли;
Далее добавим в модуль объекта метод «СформироватьДополнительныйОтчет»:
// Параметры:
// ПараметрыВыполнения - Структура:
// * РезультатВыполнения
// * Контекст_АдресДанныхРасшифровки - Строка
// * Контекст_НастройкиКомпоновки - НастройкиКомпоновкиДанных
//
Процедура СформироватьДополнительныйОтчет(ПараметрыВыполнения)
КомпоновщикНастроек.ЗагрузитьНастройки(ПараметрыВыполнения.Контекст_НастройкиКомпоновки);
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
ДокументРезультат = Новый ТабличныйДокумент;
СкомпоноватьРезультат(ДокументРезультат, ДанныеРасшифровки);
ПоместитьВоВременноеХранилище(ДанныеРасшифровки, ПараметрыВыполнения.Контекст_АдресДанныхРасшифровки);
ПараметрыВыполнения.РезультатВыполнения.Вставить("ДокументРезультат", ДокументРезультат);
КонецПроцедуры
В этой процедуре мы формируем отчет и результат формирования помещаем в структуру, которую затем сможем получить после выполнения фонового формирования отчета. На вход процедуре поступает структура параметров, в которой:
- РезультатВыполнения – это структура, в которую мы можем поместить результат выполняемой команды (см. описание программного интерфейса ДополнительныеОтчетыИОбработки.ВыполнитьКоманду ()). Сюда мы поместим сформированный табличный документ с результатом отчета.
- Контекст_АдресДанныхРасшифровки – это наш параметр, переданный из клиентской формы в фоновое задание формирования отчета. Здесь содержится адрес временного хранилища, по которому нужно расположить данные расшифровки сформированного отчета.
- Контекст_НастройкиКомпоновки – также наш параметр. Здесь содержатся настройки отчета, которые установил пользователь. Полученные настройки нужно загрузить в компоновщик настроек отчета, чтобы результат соответствовал установленным настройкам пользователя.
Перед формированием отчета устанавливаем переданные настройки. Если этого не сделать, то отчет будет сформирован с настройками по умолчанию. (Пользователь мог изменить вариант отчета, а также задать параметры начало и конец периода. Если не установить настройки, то отчет сформируется в стандартном варианте и с пустыми датами начала и конца периода. Результат не будет соответствовать настройкам пользователя):
КомпоновщикНастроек.ЗагрузитьНастройки(ПараметрыВыполнения.Контекст_НастройкиКомпоновки);
Теперь можно сформировать отчет. Формируем отчет через вызов стандартного метода «СкомпоноватьРезультат» объекта «ВнешнийОтчет» (см. синтаксис-помощник: ВнешнийОтчет.СкомпоноватьРезультат):
ДанныеРасшифровки = Новый ДанныеРасшифровкиКомпоновкиДанных;
ДокументРезультат = Новый ТабличныйДокумент;
СкомпоноватьРезультат(ДокументРезультат, ДанныеРасшифровки);
Помещаем данные расшифровки во временное хранилище по переданному адресу (как получить и передать этот адрес будет рассмотрено далее). Если этого не сделать, то не будет возможности работы с расшифровкой отчета:
ПоместитьВоВременноеХранилище(ДанныеРасшифровки, ПараметрыВыполнения.Контекст_АдресДанныхРасшифровки);
Итоговый табличный документ помещаем в структуру «РезультатВыполнения». Именно эту структуру мы получим как результат фонового задания по формированию отчета:
ПараметрыВыполнения.РезультатВыполнения.Вставить("ДокументРезультат", ДокументРезультат);
Теперь приступим к созданию формы отчета. Добавим основную форму отчета:
Наша основная задача на текущем этапе – переопределить команду кнопки «Сформировать». Сейчас по нажатию этой кнопки отчет формируется в основном «потоке» пользователя и блокирует интерфейс (т.е. выполняется стандартная компоновка):
Самый очевидный вариант – это добавить свою кнопку «Сформировать» при нажатии на которую запускается формирование отчета в фоне. Именно так мы и поступим. А чтобы добавленная кнопка не вызывала подозрения и выглядела как стандартная, придется проявить чудеса смекалки и находчивости. Если интересно – подробности под спойлером.
Для формы в разделе «Параметры» добавляем два ключевых параметра:
- ДополнительнаяОбработкаСсылка (СправочникСсылка.ДополнительныеОтчетыИОбработки)
- ИдентификаторКоманды (Строка)
Теперь приступим к созданию процедуры запуска фонового формирования отчета. Для кнопки «Сформировать» создадим клиентский обработчик, пока оставим пустым:
#Область ОбработчикиКомандФормы
&НаКлиенте
Процедура Сформировать(Команда)
КонецПроцедуры
#КонецОбласти
Теперь подумаем, как запустить фоновое формирование отчета. В этом нам поможет программный интерфейс «ДлительныеОперации.ВыполнитьФункцию()». Данный метод выполняет запуск фонового выполнения переданной функции. Через указанный интерфейс мы запустим функцию «ДополнительныеОтчетыИОбработки.ВыполнитьКоманду()» с параметрами нашего отчета и идентификатором команды фонового формирования. Тем самым вызовем метод «ВыполнитьКоманду» модуля объекта нашего отчета в фоновом задании, а следовательно, сформируем отчет также в фоне.
Как мы помним, в процедуру формирования отчета нам также необходимо передать текущие установленные настройки отчета и адрес временного хранилища для данных расшифровки (напомню это параметры «Контекст_НастройкиКомпоновки» и «Контекст_АдресДанныхРасшифровки»).
Добавим в модуль формы функцию «ЗапуститьФоновоеФормированиеОтчета» реализующую описанный алгоритм:
&НаСервере
Функция ЗапуститьФоновоеФормированиеОтчета()
ПараметрыФоновогоЗадания = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
ПараметрыФоновогоЗадания.ОжидатьЗавершение = 0;
ПараметрыФоновогоЗадания.ЗапуститьВФоне = Истина;
ИдентификаторКомандыФоновогоВыполнения = РеквизитФормыВЗначение("Отчет").ИдентификаторКомандыФоновогоФормирования();
ДанныеРасшифровки = ПоместитьВоВременноеХранилище("", УникальныйИдентификатор);
ПараметрыФункции = Новый Структура;
ПараметрыФункции.Вставить("ДополнительнаяОбработкаСсылка", Параметры.ДополнительнаяОбработкаСсылка);
ПараметрыФункции.Вставить("ИдентификаторКоманды", ИдентификаторКомандыФоновогоВыполнения);
ПараметрыФункции.Вставить("Контекст_АдресДанныхРасшифровки", ДанныеРасшифровки);
ПараметрыФункции.Вставить("Контекст_НастройкиКомпоновки", Отчет.КомпоновщикНастроек.ПолучитьНастройки());
ИмяФункции = "ДополнительныеОтчетыИОбработки.ВыполнитьКоманду";
Возврат ДлительныеОперации.ВыполнитьФункцию(ПараметрыФоновогоЗадания, ИмяФункции, ПараметрыФункции);
КонецФункции
Для того чтобы просто запустить выполнение формирования отчета в фоне, не дожидаясь завершения, указываем следующие параметры для фонового задания:
ПараметрыФоновогоЗадания = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
ПараметрыФоновогоЗадания.ОжидатьЗавершение = 0;
ПараметрыФоновогоЗадания.ЗапуститьВФоне = Истина;
Для функции «ДополнительныеОтчетыИОбработки.ВыполнитьКоманду» обязательными являются параметры «ДополнительнаяОбработкаСсылка» и «ИдентификаторКоманды». Ссылка на обработку отчета хранится в одноименном ключевом параметре формы отчета, а идентификатор команды мы получим, вызвав соответствующий экспортный метод модуля объекта отчета:
ИдентификаторКомандыФоновогоВыполнения = РеквизитФормыВЗначение("Отчет").ИдентификаторКомандыФоновогоФормирования();
…
ПараметрыФункции = Новый Структура;
ПараметрыФункции.Вставить("ДополнительнаяОбработкаСсылка", Параметры.ДополнительнаяОбработкаСсылка);
ПараметрыФункции.Вставить("ИдентификаторКоманды", ИдентификаторКомандыФоновогоВыполнения);
Адрес временного хранилища, в который будут помещены данные расшифровки сформированного отчета присваиваем реквизиту формы «ДанныеРасшифровки» и передаем этот адрес в параметре «Контекст_АдресДанныхРасшифровки» (используется в процедуре «СформироватьДополнительныйОтчет()» модуля объекта)
ДанныеРасшифровки = ПоместитьВоВременноеХранилище("", УникальныйИдентификатор);
…
ПараметрыФункции.Вставить("Контекст_АдресДанныхРасшифровки", ДанныеРасшифровки);
Текущие настройки отчета передаем в параметре «Контекст_НастройкиКомпоновки» (используется в процедуре «СформироватьДополнительныйОтчет()» модуля объекта):
ПараметрыФункции.Вставить("Контекст_НастройкиКомпоновки", Отчет.КомпоновщикНастроек.ПолучитьНастройки());
И запускаем фоновое выполнение функции «ДополнительныеОтчетыИОбработки.ВыполнитьКоманду» с установленными параметрами:
ИмяФункции = "ДополнительныеОтчетыИОбработки.ВыполнитьКоманду";
Возврат ДлительныеОперации.ВыполнитьФункцию(ПараметрыФоновогоЗадания, ИмяФункции, ПараметрыФункции);
Теперь вернемся к клиентскому обработчику кнопки «Сформировать» и вставим в тело процедуры следующий код:
&НаКлиенте
Процедура Сформировать(Команда)
ОтображениеСостояния = Элементы.Результат.ОтображениеСостояния;
ОтображениеСостояния.Видимость = Истина;
ОтображениеСостояния.Текст = НСтр("ru = 'Отчет формируется...'");
ОтображениеСостояния.Картинка = БиблиотекаКартинок.ДлительнаяОперация48;
ОтображениеСостояния.ДополнительныйРежимОтображения = ДополнительныйРежимОтображения.Неактуальность;
Задание = ЗапуститьФоновоеФормированиеОтчета();
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтаФорма);
ПараметрыОжидания.ВыводитьОкноОжидания = Ложь;
ОповещениеОЗавершении = Новый ОписаниеОповещения("ПослеФормированияОтчета", ЭтаФорма);
ДлительныеОперацииКлиент.ОжидатьЗавершение(Задание, ОповещениеОЗавершении, ПараметрыОжидания);
КонецПроцедуры
Здесь мы устанавливаем отображение состояния для поля табличного документа, запускаем фоновое формирование отчета и подключаем отслеживание завершения выполнения.
Чтобы не отображать стандартное окно ожидания длительной операции, устанавливаем соответствующий параметр в значение «Ложь»:
ПараметрыОжидания.ВыводитьОкноОжидания = Ложь;
После завершения формирования отчета будет вызвана экспортная процедура модуля формы, указанная в описании оповещения о завершении:
ОповещениеОЗавершении = Новый ОписаниеОповещения("ПослеФормированияОтчета", ЭтаФорма);
Добавим в модуль формы реализацию экспортной процедуры «ПослеФормированияОтчета»:
&НаКлиенте
Процедура ПослеФормированияОтчета(Задание, ДополнительныеПараметры) Экспорт
Если Задание = Неопределено Тогда
Возврат;
КонецЕсли;
Если Задание.Статус = "Выполнено" Тогда
Результат = ПолучитьИзВременногоХранилища(Задание.АдресРезультата).ДокументРезультат;
ОтображениеСостояния = Элементы.Результат.ОтображениеСостояния;
ОтображениеСостояния.Видимость = Ложь;
ОтображениеСостояния.ДополнительныйРежимОтображения = ДополнительныйРежимОтображения.НеИспользовать;
ИначеЕсли Задание.Статус = "Ошибка" Тогда
ВызватьИсключение Задание.ПодробноеПредставлениеОшибки;
КонецЕсли;
КонецПроцедуры
Этот обработчик для нас важен тем, что здесь происходит получение сформированного табличного документа:
Результат = ПолучитьИзВременногоХранилища(Задание.АдресРезультата).ДокументРезультат;
Напомню, что табличный документ с результатом отчета мы помещали в структуру РезультатВыполнения в процедуре модуля объекта «СформироватьДополнительныйОтчет()». Именно этот табличный документ мы и получили в обработчике «ПослеФормированияОтчета».
Все готово. Можно добавлять отчет в справочник и проверять:
Отображение прогресса выполнения отчета.
Основную задачу мы выполнили. На текущий момент отчет формируется у нас в фоне и не блокирует интерфейс пользователя. Напоследок предлагаю добавить интересный функционал – отображение прогресса выполнения отчета. Для этого нам понадобится немного доработать обработчик команды кнопки «Сформировать» и добавить пару строк кода в событие «ПриКомпоновкеРезультата» в модуле объекта.
Начнем с обработчика события «ПриКомпоновкеРезультата» модуля объекта. Для передачи информации о прогрессе выполнения воспользуемся функцией:
ДлительныеОперации.СообщитьПрогресс()
После каждого вызова добавим паузу:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ДлительныеОперации.СообщитьПрогресс(0, "Начало формирования отчета");
Пауза();
ВнешниеИсточники = Новый Структура;
ВнешниеИсточники.Вставить("ТестовыеДанные", ТестовыеДанные());
ДлительныеОперации.СообщитьПрогресс(20, "Получили внешние источники");
Пауза();
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных,
КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеИсточники, ДанныеРасшифровки, Истина);
ДлительныеОперации.СообщитьПрогресс(80, "Инициализировали процессор компоновки");
Пауза();
ДокументРезультат.Очистить();
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки, Истина);
ДлительныеОперации.СообщитьПрогресс(99, "Почти все готово");
Пауза();
КонецПроцедуры
А в клиентском обработчике кнопки «Сформировать» укажем описание оповещения процедуры, в которой будем обрабатывать сообщения прогресса. Это оповещение необходимо передать в параметре ожидания «ОповещениеОПрогрессеВыполнения»:
ПараметрыОжидания.ОповещениеОПрогрессеВыполнения = Новый ОписаниеОповещения("ПослеПолученияПрогресса", ЭтаФорма);
&НаКлиенте
Процедура Сформировать(Команда)
ОтображениеСостояния = Элементы.Результат.ОтображениеСостояния;
ОтображениеСостояния.Видимость = Истина;
ОтображениеСостояния.Текст = НСтр("ru = 'Отчет формируется...'");
ОтображениеСостояния.Картинка = БиблиотекаКартинок.ДлительнаяОперация48;
ОтображениеСостояния.ДополнительныйРежимОтображения = ДополнительныйРежимОтображения.Неактуальность;
Задание = ЗапуститьФоновоеФормированиеОтчета();
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтаФорма);
ПараметрыОжидания.ВыводитьОкноОжидания = Ложь;
ПараметрыОжидания.ОповещениеОПрогрессеВыполнения = Новый ОписаниеОповещения("ПослеПолученияПрогресса", ЭтаФорма);
ОповещениеОЗавершении = Новый ОписаниеОповещения("ПослеФормированияОтчета", ЭтаФорма);
ДлительныеОперацииКлиент.ОжидатьЗавершение(Задание, ОповещениеОЗавершении, ПараметрыОжидания);
КонецПроцедуры
Добавим в модуль формы реализацию процедуры «ПослеПолученияПрогресса»:
&НаКлиенте
Процедура ПослеПолученияПрогресса(ДанныеПрогресса, ДополнительныеПараметры) Экспорт
Если ДанныеПрогресса.Прогресс = Неопределено Тогда
Возврат;
КонецЕсли;
Процент = ДанныеПрогресса.Прогресс.Процент;
Текст = ДанныеПрогресса.Прогресс.Текст;
ОтображениеСостояния = Элементы.Результат.ОтображениеСостояния;
ОтображениеСостояния.Текст = СтрШаблон("Выполнено %1%%. %2", Процент, Текст);
КонецПроцедуры
Теперь можно обновлять отчет в справочнике и проверять выполнение:
Пример отчета прикреплен к данной публикации.
Тестировалось на:
- БСП 3.1.6.100
- Управление торговлей (Демо) 11.5.12.256
Спасибо за внимание!
Проверено на следующих конфигурациях и релизах:
- 1С:Библиотека стандартных подсистем, редакция 3.1, релизы 3.1.6.100