Расшифровка отчета на СКД — это то, что происходит при двойном щелчке по ячейке готового отчета: платформа показывает меню действий (открыть значение, расшифровать по другому полю, отфильтровать или упорядочить по значению ячейки) и строит по выбранному пункту новый отчет. Пока этого хватает, код не нужен. Но пользователи регулярно просят другого: открыть документ прямо из суммы, добавить в меню свой пункт или перейти из итоговой цифры в соседний отчет с отбором по товару. Ниже разобрано, как устроена стандартная обработка расшифровки СКД и как перехватить ее в управляемой форме: код формы целиком, разбор ключевых строк и ошибки, которые встречаются чаще всего.
При выводе результата процессор компоновки кладет в ячейку табличного документа не значения полей, а только идентификатор — объект типа ИдентификаторРасшифровкиКомпоновкиДанных. Сами значения хранятся отдельно, в объекте ДанныеРасшифровкиКомпоновкиДанных, который заполняется при формировании отчета. Если с устройством схемы вы пока на «вы», начните со статьи о схеме компоновки данных.
Данные расшифровки — это набор связанных элементов двух типов:
ЭлементРасшифровкиКомпоновкиДанныхГруппировка — выведенная группировка отчета;ЭлементРасшифровкиКомпоновкиДанныхПоля — значения полей: для группировки это поля, по которым она сгруппирована, для ячейки — поля, попавшие в ее расшифровку.Элемент ячейки ссылается на родителя — элемент своей группировки, тот в свою очередь на вышестоящую группировку и так далее. У ячейки на пересечении строк и колонок кросс-таблицы родителей два. Поэтому, чтобы узнать все, что стоит за числом (товар, склад, месяц), нужно подняться по родителям до самого верха. Как группировки вкладываются друг в друга, подробно разобрано в статье про группировки СКД.
В управляемой форме данные расшифровки живут на сервере. В форме, которую создает конструктор, табличный документ выводится в реквизит Результат, а реквизит ДанныеРасшифровки (тип Строка) хранит адрес данных во временном хранилище. При двойном щелчке по ячейке у элемента Результат срабатывает событие ОбработкаРасшифровки. Если обработчика нет или он оставил СтандартнаяОбработка равным Истина, платформа сама показывает меню и строит расшифровку объектом ОбработкаРасшифровкиКомпоновкиДанных.
Отсюда план: достать по идентификатору значения полей на сервере, решить, нужна ли для этой ячейки своя логика, и если нужна — показать меню самостоятельно, дописав в него свои пункты.
Пример рассчитан на отчет с основной схемой компоновки и управляемой формой, созданной конструктором; обычный это отчет или внешний — неважно. В схеме есть поля Номенклатура и Регистратор. Если в ячейке есть документ или товар, двойной щелчок показывает стандартное меню плюс пункты «Открыть документ» и «Движения по товару». В остальных ячейках все работает как раньше. Платформа 8.3, тонкий и веб-клиент, без модальных вызовов. Полный модуль формы — в поле «Код» ниже, здесь он разобран по частям.
Функция работает на сервере без контекста формы: туда уходят адрес данных, идентификатор ячейки и список нужных полей, данные формы не передаются. Рекурсия поднимается по родителям и собирает в соответствие «путь к полю → значение» только перечисленные поля. У ячейки широкого отчета их бывают десятки, и гонять все на клиент при каждом двойном щелчке незачем.
&НаСервереБезКонтекста
Функция ЗначенияПолейРасшифровки(АдресДанныхРасшифровки, Расшифровка, ИменаПолей)
ЗначенияПолей = Новый Соответствие;
Данные = ПолучитьИзВременногоХранилища(АдресДанныхРасшифровки);
Если ТипЗнч(Данные) = Тип("ДанныеРасшифровкиКомпоновкиДанных") Тогда
ДобавитьЗначенияПолей(Данные.Элементы.Получить(Расшифровка), ЗначенияПолей, ИменаПолей);
КонецЕсли;
Возврат ЗначенияПолей;
КонецФункции
&НаСервереБезКонтекста
Процедура ДобавитьЗначенияПолей(ЭлементРасшифровки, ЗначенияПолей, ИменаПолей)
Если ЭлементРасшифровки = Неопределено Тогда
Возврат;
КонецЕсли;
Если ТипЗнч(ЭлементРасшифровки) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
Для Каждого ЗначениеПоля Из ЭлементРасшифровки.ПолучитьПоля() Цикл
// Берем только нужные поля. Значение ближе к ячейке
// важнее значения из группировки выше.
Если ИменаПолей.Найти(ЗначениеПоля.Поле) <> Неопределено
И ЗначенияПолей.Получить(ЗначениеПоля.Поле) = Неопределено Тогда
ЗначенияПолей.Вставить(ЗначениеПоля.Поле, ЗначениеПоля.Значение);
КонецЕсли;
КонецЦикла;
КонецЕсли;
// У ячейки кросс-таблицы два родителя: группировка строк и колонок.
Для Каждого Родитель Из ЭлементРасшифровки.ПолучитьРодителей() Цикл
ДобавитьЗначенияПолей(Родитель, ЗначенияПолей, ИменаПолей);
КонецЦикла;
КонецПроцедуры
Обработчик назначайте из палитры свойств элемента Результат (событие «Обработка расшифровки»): конструктор подставит сигнатуру, которую ждет ваша версия платформы. В актуальных релизах 8.3 у процедуры четыре параметра.
&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка, ДополнительныеПараметры)
Если ПустаяСтрока(ДанныеРасшифровки)
Или ТипЗнч(Расшифровка) <> Тип("ИдентификаторРасшифровкиКомпоновкиДанных") Тогда
Возврат;
КонецЕсли;
ЗначенияПолей = ЗначенияПолейРасшифровки(ДанныеРасшифровки, Расшифровка,
СтрРазделить("Регистратор,Номенклатура", ","));
ДокументЯчейки = ЗначенияПолей.Получить("Регистратор");
ТоварЯчейки = ЗначенияПолей.Получить("Номенклатура");
ДополнительныеПункты = Новый СписокЗначений;
Если ЗначениеЗаполнено(ДокументЯчейки) Тогда
ДополнительныеПункты.Добавить("ОткрытьДокумент", НСтр("ru = 'Открыть документ'"));
КонецЕсли;
Если ЗначениеЗаполнено(ТоварЯчейки) Тогда
ДополнительныеПункты.Добавить("ДвиженияТовара", НСтр("ru = 'Движения по товару'"));
КонецЕсли;
Если ДополнительныеПункты.Количество() = 0 Тогда
Возврат; // в ячейке нет нужных полей - работает стандартная расшифровка
КонецЕсли;
СтандартнаяОбработка = Ложь;
КонтекстВыбора = Новый Структура;
КонтекстВыбора.Вставить("Расшифровка", Расшифровка);
КонтекстВыбора.Вставить("ДокументЯчейки", ДокументЯчейки);
КонтекстВыбора.Вставить("ТоварЯчейки", ТоварЯчейки);
Оповещение = Новый ОписаниеОповещения("ВыборДействияРасшифровкиЗавершение", ЭтотОбъект, КонтекстВыбора);
ОбработкаРасшифровки = Новый ОбработкаРасшифровкиКомпоновкиДанных(ДанныеРасшифровки,
Новый ИсточникДоступныхНастроекКомпоновкиДанных(Отчет));
ОбработкаРасшифровки.ПоказатьВыборДействия(Оповещение, Расшифровка, , ДополнительныеПункты);
КонецПроцедуры
&НаКлиенте
Процедура ВыборДействияРасшифровкиЗавершение(ВыполненноеДействие, ПараметрВыполненногоДействия, КонтекстВыбора) Экспорт
Если ВыполненноеДействие = "ОткрытьДокумент" Тогда
ПоказатьЗначение(, КонтекстВыбора.ДокументЯчейки);
ИначеЕсли ВыполненноеДействие = "ДвиженияТовара" Тогда
ОткрытьОтчетПоТовару(КонтекстВыбора.ТоварЯчейки);
ИначеЕсли ВыполненноеДействие = ДействиеОбработкиРасшифровкиКомпоновкиДанных.ОткрытьЗначение Тогда
ПоказатьЗначение(, ПараметрВыполненногоДействия);
ИначеЕсли ТипЗнч(ВыполненноеДействие) = Тип("ДействиеОбработкиРасшифровкиКомпоновкиДанных")
И ВыполненноеДействие <> ДействиеОбработкиРасшифровкиКомпоновкиДанных.Нет Тогда
// Стандартный пункт (Расшифровать, Отфильтровать и т. п.):
// платформа уже подготовила настройки, открываем с ними
// новое окно этого же отчета.
ОписаниеРасшифровки = Новый ОписаниеОбработкиРасшифровкиКомпоновкиДанных(
ДанныеРасшифровки, КонтекстВыбора.Расшифровка, ПараметрВыполненногоДействия);
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Расшифровка", ОписаниеРасшифровки);
ПараметрыФормы.Вставить("СформироватьПриОткрытии", Истина);
ОткрытьФорму(ИмяФормы, ПараметрыФормы, ЭтотОбъект, Новый УникальныйИдентификатор);
КонецЕсли;
// Действие "Нет" или Неопределено - пользователь закрыл меню.
КонецПроцедуры
&НаКлиенте
Процедура ОткрытьОтчетПоТовару(Товар)
ПараметрыФормы = Новый Структура;
ПараметрыФормы.Вставить("Отбор", Новый Структура("Номенклатура", Товар));
ПараметрыФормы.Вставить("СформироватьПриОткрытии", Истина);
ОткрытьФорму("Отчет.ДвиженияТоваров.Форма", ПараметрыФормы, ЭтотОбъект, Новый УникальныйИдентификатор);
КонецПроцедуры
Отчет.ДвиженияТоваров — условное имя, подставьте свой отчет. Для внешнего отчета имя формы начинается с ВнешнийОтчет., и сам отчет к этому моменту должен быть подключен в сеансе.
Расшифровка. В ячейку табличного документа можно положить расшифровку любого типа, от СКД приходит ИдентификаторРасшифровкиКомпоновкиДанных. Остальное пропускаем без изменений.СтрРазделить("Регистратор,Номенклатура", ",") — пути к полям, которые нужны обработчику. Функция есть с версии 8.3.6, в более старых релизах соберите массив через Добавить.СтандартнаяОбработка = Ложь только там, где меню показываем сами. Ячейки без нужных полей (например, строка общего итога) уходят в стандартную расшифровку, и привычное поведение не ломается.Новый ИсточникДоступныхНастроекКомпоновкиДанных(Отчет). Обработке расшифровки нужны схема и доступные поля, в форме отчета их источник — основной реквизит Отчет.ПоказатьВыборДействия выводит то же меню, что и платформа, плюс пункты из списка ДополнительныеПункты. Значение элемента списка вернется в оповещение параметром ВыполненноеДействие, представление станет текстом пункта. Третий параметр (доступные действия) пропущен, поэтому стандартные пункты доступны все. Если передать массив значений ДействиеОбработкиРасшифровкиКомпоновкиДанных, меню можно урезать.ПараметрВыполненногоДействия приходят готовые настройки расшифровки. Их вместе с адресом данных и идентификатором упаковываем в ОписаниеОбработкиРасшифровкиКомпоновкиДанных и открываем эту же форму (свойство ИмяФормы) с параметром Расшифровка.Новый УникальныйИдентификатор в ОткрытьФорму — ключ уникальности: каждая расшифровка открывается в своем окне и не подменяет уже открытое.Отбор — структура «имя поля → значение». Форма второго отчета установит отбор, если поле с таким именем есть в его схеме.У поля табличного документа есть второе событие — ОбработкаДополнительнойРасшифровки. Оно срабатывает, когда расшифровку вызывают не двойным щелчком, а командой контекстного меню ячейки. Параметры у него те же, поэтому удобно вынести тело обработчика в отдельную процедуру и вызывать ее из обоих событий.
В конфигурациях на БСП (УТ 11, КА 2.0, ERP) большинство отчетов не имеет своей формы: они открываются в общей форме подсистемы «Варианты отчетов», и обработчик в форму отчета добавить некуда. В БП 3.0 основные бухгалтерские отчеты (оборотно-сальдовая ведомость, анализ и карточка счета) устроены иначе: у них собственные формы со своей обработкой расшифровки, поэтому обработчик добавляют в форму конкретного отчета через расширение. Для отчетов в общей форме путей два. Первый — дать отчету собственную форму; для внешнего отчета это обычно самый короткий путь, и код выше подходит без изменений. Второй — перехватить событие в клиентском переопределяемом модуле БСП ОтчетыКлиентПереопределяемый (процедура ОбработкаРасшифровки) через расширение, обязательно с проверкой, какой отчет открыт, чтобы не задеть остальные. Реквизиты в общей форме называются иначе, поэтому адрес данных расшифровки берите из переданной в процедуру формы отчета, а сигнатуру и имена сверяйте со своей редакцией БСП.
ДанныеРасшифровкиКомпоновкиДанных и его элементы — серверные объекты, в тонком и веб-клиенте их нет. Все обращения к Элементы, ПолучитьПоля() и ПолучитьРодителей() выносите в серверную функцию, а на клиент возвращайте простые значения и ссылки.ДанныеРасшифровки пустой. Так бывает, когда отчет формируется своей командой, а не стандартной «Сформировать», и данные расшифровки при компоновке не запрошены. Ячейки выводятся без расшифровки, обработчик выходит на первой проверке. Данные нужно получить и положить во временное хранилище формы, а при повторном формировании записать по тому же адресу, чтобы старые копии не висели в хранилище до закрытия формы:
&НаСервере
Процедура СформироватьНаСервере()
ОтчетОбъект = РеквизитФормыВЗначение("Отчет");
ДанныеРасшифровкиОбъект = Новый ДанныеРасшифровкиКомпоновкиДанных;
Результат.Очистить();
ОтчетОбъект.СкомпоноватьРезультат(Результат, ДанныеРасшифровкиОбъект);
// Повторное формирование перезаписывает данные по старому адресу.
Если ЭтоАдресВременногоХранилища(ДанныеРасшифровки) Тогда
ПоместитьВоВременноеХранилище(ДанныеРасшифровкиОбъект, ДанныеРасшифровки);
Иначе
ДанныеРасшифровки = ПоместитьВоВременноеХранилище(ДанныеРасшифровкиОбъект, УникальныйИдентификатор);
КонецЕсли;
КонецПроцедуры
СтандартнаяОбработка = Ложь ставят в процедуре-оповещении. Это поздно: платформа читает параметр сразу после выхода из обработчика события и уже показала свое меню. Ставьте его в самом обработчике, до вызова ПоказатьВыборДействия.Экспорт. После выбора пункта платформа не найдет процедуру и выдаст ошибку «Метод объекта не обнаружен (ВыборДействияРасшифровкиЗавершение)».ПолучитьПоля() приходит путь к данным, например Номенклатура или Номенклатура.Родитель, а не заголовок колонки «Товар». Кроме того, поле попадает в расшифровку, только если оно участвует в группировке или детальных записях отчета: уберете группировку по регистратору — пропадет и пункт «Открыть документ».Добавьте в структуру отчета детальные записи или группировку по полю документа (в регистрах это обычно Регистратор). По двойному щелчку на ячейке с документом в стандартном меню расшифровки будет пункт открытия значения. Код нужен, когда документ надо открыть из ячейки с суммой или из строки группировки, где самого документа не видно. Как добавить поле в структуру, описано в статье о настройке отчета.
Да. В обработчике ОбработкаРасшифровки установите СтандартнаяОбработка = Ложь и больше ничего не делайте. Если запретить нужно только часть ячеек, проверяйте их поля функцией из шага 1 и для остальных выходите раньше.
В тонком и веб-клиенте — да: модальных вызовов нет, меню показывается асинхронно. В обычных формах толстого клиента событие поля табличного документа называется так же, но данные расшифровки там доступны на клиенте напрямую, а меню обычно показывают синхронным методом ВыбратьДействие, так что код придется переписать.
Параметр Отбор задает только отборы. Для периода добавьте во второй отчет параметр формы Период типа СтандартныйПериод, а в первом перед ОткрытьФорму положите его в структуру: ПараметрыФормы.Вставить("Период", Новый СтандартныйПериод(НачалоМесяца(ТекущаяДата()), КонецМесяца(ТекущаяДата()))). Применять значение в ПриСозданииНаСервере бесполезно: это событие выполняется до загрузки варианта отчета, и вариант перезапишет настройки компоновщика. Подходящие места — обработчики событий формы ПриЗагрузкеВариантаНаСервере и ПриЗагрузкеПользовательскихНастроекНаСервере, их назначают в палитре свойств формы. Если параметр вынесен в пользовательские настройки, менять его нужно и там, иначе сохраненное значение пользователя перекроет ваше. Код формы второго отчета, в схеме которого есть параметр Период:
&НаСервере
Процедура ПриЗагрузкеВариантаНаСервере(Настройки)
ПрименитьПереданныйПериод();
КонецПроцедуры
&НаСервере
Процедура ПриЗагрузкеПользовательскихНастроекНаСервере(Настройки)
ПрименитьПереданныйПериод();
КонецПроцедуры
&НаСервере
Процедура ПрименитьПереданныйПериод()
Если ТипЗнч(Параметры.Период) <> Тип("СтандартныйПериод") Тогда
Возврат; // отчет открыт без периода, например из меню
КонецЕсли;
Компоновщик = Отчет.КомпоновщикНастроек;
ПараметрНастроек = Компоновщик.Настройки.ПараметрыДанных.НайтиЗначениеПараметра(
Новый ПараметрКомпоновкиДанных("Период"));
Если ПараметрНастроек = Неопределено Тогда
Возврат; // в схеме нет параметра Период
КонецЕсли;
ПараметрНастроек.Значение = Параметры.Период;
ПараметрНастроек.Использование = Истина;
// Параметр в пользовательских настройках: значение берется оттуда.
Если ЗначениеЗаполнено(ПараметрНастроек.ИдентификаторПользовательскойНастройки) Тогда
ПользовательскийПараметр = Компоновщик.ПользовательскиеНастройки.Элементы.Найти(
ПараметрНастроек.ИдентификаторПользовательскойНастройки);
Если ПользовательскийПараметр <> Неопределено Тогда
ПользовательскийПараметр.Значение = Параметры.Период;
ПользовательскийПараметр.Использование = Истина;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Если отчет типовой и открывается в общей форме БСП или расшифровка должна вести в несколько связанных отчетов, быстрее отдать задачу программисту: такие доработки делают расширением, не снимая конфигурацию с поддержки. Опишите, из какой ячейки и куда нужно переходить, на странице размещения задачи — оценка бесплатная, ответ в течение рабочего дня. Подробнее об этом направлении — в услуге доработки отчетов и обработок 1С.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.