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