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