Чтобы добавить поле на форму отчета БСП, не нужно делать отчету собственную форму. В модуле отчета в ОпределитьНастройкиФормы включается событие ПриСозданииНаСервере, и в нем программно создаются реквизит и элемент на общей форме ФормаОтчета. Обработчики событий элемента (ПриИзменении, Регулирование и другие) размещаются в расширении общей формы как универсальные процедуры, а значение поля передается в компоновку через настройки компоновщика.
Разберем это на практической задаче: дать пользователю выбрать месяц отчета так, как в типовых документах, — поле «Месяц» с кнопками регулирования «влево-вправо».
Почему не своя форма и не правка общей формы
У подсистемы «Варианты отчетов» одна общая форма на все отчеты. В ней панель быстрых настроек, работа с вариантами, рассылки, формирование в фоне. Варианты решения задачи:
| Подход | Плюсы | Минусы |
|---|---|---|
| Собственная форма отчета | Полный контроль | Теряется часть функциональности БСП, приходится поддерживать копию |
Правка общей формы ФормаОтчета в конфигурации | Быстро | Конфликты при каждом обновлении типовой |
| Программное добавление элемента + расширение формы | Обновляется безболезненно, механизм переиспользуется | Нужно немного инфраструктурного кода |
На проектах я выбираю третий вариант: элемент создается из модуля конкретного отчета, а общая форма получает только набор универсальных обработчиков в расширении.
Шаг 1. Включаем событие ПриСозданииНаСервере в модуле отчета
В модуле объекта отчета, подключенного к подсистеме «Варианты отчетов», есть экспортная процедура ОпределитьНастройкиФормы. В ней можно подписаться на события общей формы.
#Область ПрограммныйИнтерфейс
// Настройки общей формы отчета подсистемы «Варианты отчетов».
Процедура ОпределитьНастройкиФормы(Форма, КлючВарианта, Настройки) Экспорт
Настройки.События.ПриСозданииНаСервере = Истина;
КонецПроцедуры
// Вызывается из общей формы отчета при создании на сервере.
Процедура ПриСозданииНаСервере(Форма, Отказ, СтандартнаяОбработка) Экспорт
ДополнительныеПоляОтчетов.ДобавитьПолеМесяца(Форма, "ПериодОтчета", НСтр("ru = 'Месяц'"));
КонецПроцедуры
#КонецОбластиОтчет сам решает, нужно ли ему поле. Другие отчеты общей формы не затрагиваются.
Шаг 2. Создаем реквизит и элемент программно
Логику создания выносим в серверный общий модуль ДополнительныеПоляОтчетов, чтобы любой отчет мог добавить поле месяца одной строкой.
// Добавляет на общую форму отчета поле выбора месяца.
//
// Параметры:
// Форма - ФормаКлиентскогоПриложения - общая форма отчета.
// ИмяСвойства - Строка - имя, под которым значение будет доступно при компоновке.
// Заголовок - Строка - заголовок поля.
//
Процедура ДобавитьПолеМесяца(Форма, ИмяСвойства, Заголовок) Экспорт
ИмяРеквизита = ДополнительныеПоляОтчетовКлиентСервер.ИмяРеквизитаМесяца(ИмяСвойства);
НовыеРеквизиты = Новый Массив;
НовыеРеквизиты.Добавить(Новый РеквизитФормы(ИмяРеквизита,
Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата)), , Заголовок));
Форма.ИзменитьРеквизиты(НовыеРеквизиты);
Поле = Форма.Элементы.Добавить(ИмяРеквизита, Тип("ПолеФормы"), Форма.Элементы.ПанельБыстрыхНастроек);
Поле.Вид = ВидПоляФормы.ПолеВвода;
Поле.ПутьКДанным = ИмяРеквизита;
Поле.Формат = "ДФ='MMMM yyyy'";
Поле.ФорматРедактирования = "ДФ=MM.yyyy";
Поле.КнопкаРегулирования = Истина;
Поле.Ширина = 14;
Поле.УстановитьДействие("ПриИзменении", "Подключаемый_ПолеМесяцаПриИзменении");
Поле.УстановитьДействие("Регулирование", "Подключаемый_ПолеМесяцаРегулирование");
// Значение по умолчанию и место, откуда его прочитает компоновка.
ДополнительныеСвойства = Форма.Отчет.КомпоновщикНастроек.ФиксированныеНастройки.ДополнительныеСвойства;
Значение = Неопределено;
Если Не ДополнительныеСвойства.Свойство(ИмяСвойства, Значение) Или Не ЗначениеЗаполнено(Значение) Тогда
Значение = НачалоМесяца(ТекущаяДатаСеанса());
ДополнительныеСвойства.Вставить(ИмяСвойства, Значение);
КонецЕсли;
Форма[ИмяРеквизита] = Значение;
КонецПроцедурыИ маленький клиент-серверный модуль, чтобы правило именования было в одном месте:
// Модуль ДополнительныеПоляОтчетовКлиентСервер.
Функция ИмяРеквизитаМесяца(ИмяСвойства) Экспорт
Возврат "ДопМесяц_" + ИмяСвойства;
КонецФункции
Функция ИмяСвойстваПоРеквизиту(ИмяРеквизита) Экспорт
Возврат СтрЗаменить(ИмяРеквизита, "ДопМесяц_", "");
КонецФункцииИмя группы ПанельБыстрыхНастроек в разных версиях БСП может отличаться: перед использованием откройте общую форму в конфигураторе и проверьте, в какую группу логичнее поместить поле.

Шаг 3. Универсальные обработчики в расширении общей формы
УстановитьДействие лишь связывает событие с именем процедуры. Сама процедура должна существовать в модуле формы, иначе при создании формы будет ошибка. Поэтому создаем расширение, заимствуем в него общую форму ФормаОтчета и добавляем в модуль формы расширения универсальные обработчики. Они ничего не знают о конкретном отчете и только передают управление в клиентский общий модуль.
&НаКлиенте
Процедура Подключаемый_ПолеМесяцаПриИзменении(Элемент)
ДополнительныеПоляОтчетовКлиент.ПолеМесяцаПриИзменении(ЭтотОбъект, Элемент);
КонецПроцедуры
&НаКлиенте
Процедура Подключаемый_ПолеМесяцаРегулирование(Элемент, Направление, СтандартнаяОбработка)
ДополнительныеПоляОтчетовКлиент.ПолеМесяцаРегулирование(ЭтотОбъект, Элемент, Направление, СтандартнаяОбработка);
КонецПроцедурыКлиентский модуль ДополнительныеПоляОтчетовКлиент:
// Приводит введенную дату к началу месяца и сохраняет ее для компоновки.
Процедура ПолеМесяцаПриИзменении(Форма, Элемент) Экспорт
Значение = НачалоМесяца(Форма[Элемент.Имя]);
УстановитьЗначениеМесяца(Форма, Элемент.Имя, Значение);
КонецПроцедуры
// Листает месяцы кнопками регулирования.
Процедура ПолеМесяцаРегулирование(Форма, Элемент, Направление, СтандартнаяОбработка) Экспорт
СтандартнаяОбработка = Ложь;
ТекущееЗначение = Форма[Элемент.Имя];
Если Не ЗначениеЗаполнено(ТекущееЗначение) Тогда
ТекущееЗначение = ОбщегоНазначенияКлиент.ДатаСеанса();
КонецЕсли;
Значение = ДобавитьМесяц(НачалоМесяца(ТекущееЗначение), Направление);
УстановитьЗначениеМесяца(Форма, Элемент.Имя, Значение);
КонецПроцедуры
Процедура УстановитьЗначениеМесяца(Форма, ИмяРеквизита, Значение)
Форма[ИмяРеквизита] = Значение;
ИмяСвойства = ДополнительныеПоляОтчетовКлиентСервер.ИмяСвойстваПоРеквизиту(ИмяРеквизита);
Форма.Отчет.КомпоновщикНастроек.ФиксированныеНастройки.ДополнительныеСвойства.Вставить(ИмяСвойства, Значение);
КонецПроцедурыДля нового вида поля (например, выбора организации с особым отбором) добавляется пара процедур в расширение и в клиентский модуль. Отчеты, которые этим полем пользуются, остаются чистыми.
А если хочется полной универсальности
Можно сделать одну процедуру на каждое событие (ПриИзменении, НачалоВыбора, АвтоПодбор и т. д.), которая вызывает сервер, а модуль отчета возвращает строку кода для Выполнить. Такой вариант гибче, но я его не рекомендую:
- код в строке не проверяется синтаксическим контролем и плохо отлаживается;
- каждое событие, включая
АвтоПодборпри вводе текста, дает серверный вызов; - стандарты разработки не одобряют
Выполнитьдля прикладной логики.
Явные процедуры в общем модуле надежнее, а писать их приходится не так уж много.
Шаг 4. Читаем значение при формировании отчета
Динамически созданный реквизит формы в объект отчета при формировании не попадает. Поэтому значение мы и кладем в дополнительные свойства фиксированных настроек: они путешествуют вместе с компоновщиком. В модуле отчета:
Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
МесяцОтчета = Неопределено;
ДополнительныеСвойства = КомпоновщикНастроек.ФиксированныеНастройки.ДополнительныеСвойства;
Если Не ДополнительныеСвойства.Свойство("ПериодОтчета", МесяцОтчета) Или Не ЗначениеЗаполнено(МесяцОтчета) Тогда
МесяцОтчета = НачалоМесяца(ТекущаяДатаСеанса());
КонецЕсли;
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("НачалоПериода", НачалоМесяца(МесяцОтчета));
Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("КонецПериода", КонецМесяца(МесяцОтчета));
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
ДокументРезультат.Очистить();
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
КонецПроцедурыПараметры НачалоПериода и КонецПериода в схеме лучше не включать в пользовательские настройки: иначе в форме окажутся два способа задать период, и пользователь запутается, какой из них главный.
Вариант для ЗУП и других конфигураций на БЗК
В конфигурациях на библиотеке зарплатно-кадровых решений есть готовый механизм ввода месяца, как в документах: строковое поле и процедуры модуля ЗарплатаКадрыКлиент с префиксом ВводМесяца для событий начала выбора, регулирования, автоподбора и окончания ввода текста. Если вы работаете в такой конфигурации, удобнее вызывать их из клиентского модуля вместо собственного кода: поле будет выглядеть и вести себя точно как в типовых документах. Сигнатуры процедур сверяйте с вашей версией библиотеки.
Типичные ошибки
- Ошибка при открытии формы «процедура не найдена». Имя в
УстановитьДействиене совпадает с процедурой в расширении формы или расширение не применено. - Значение не доходит до отчета. Читается реквизит формы, которого нет в объекте отчета. Используйте свойства настроек компоновщика.
- Поле пропадает после смены варианта. Общая форма пересоздает часть элементов при переключении вариантов. Проверяйте поведение и при необходимости повторно вызывайте добавление поля; значение по умолчанию в
ПриКомпоновкеРезультатадолжно браться и при отсутствии свойства. - Конфликт с обновлением. Изменения внесены прямо в общую форму конфигурации. Переносите их в расширение.
- Регулирование листает дни, а не месяцы. Не выставлено
СтандартнаяОбработка = Ложьв обработчике регулирования.
Доработку отчетов на БСП и перенос изменений типовой в расширения я делаю в рамках услуг отчеты на СКД и расширения 1С.
Итог
- Добавить поле на форму отчета БСП можно без собственной формы: через
ОпределитьНастройкиФормыиПриСозданииНаСервере. - Реквизит и элемент создаются программно, обработчики событий живут в расширении общей формы
ФормаОтчета. - Обработчики делаем универсальными и отправляем в клиентский общий модуль, без
Выполнить. - Значение передается в компоновку через дополнительные свойства настроек и читается в
ПриКомпоновкеРезультата.




