Разберем распространенную задачу, с которой часто сталкиваются разработчики при проектировании отчетов на базе Системы компоновки данных (СКД). Представим ситуацию: в отчете выводится текстовая строка, содержащая тип, номер и дату документа (например, полученная из содержания проводки при сторнировании операций). При двойном клике по этой ячейке необходимо открыть соответствующий документ в базе.
Автор темы на форуме пришел к решению использовать конструкцию Элемент.ТекущаяОбласть.Текст на клиенте. Выясним причину, почему в профессиональной разработке этот подход считается нерекомендуемым (антипаттерном). Чтение экранного текста крайне нестабильно: если пользователь изменит ширину колонки, применит условное оформление, изменит региональные настройки дат или чисел, парсинг текста ячейки гарантированно сломается.
Рассмотрим подробнее четыре элегантных, надежных и гибких способа решения этой задачи, основанных на лучших практиках работы с СКД.
Вместо чтения текстового представления ячейки на клиенте, правильнее передать идентификатор расшифровки на сервер и получить исходное (сырое) значение поля, которое пришло из запроса СКД до всякого форматирования.
Разберем по шагам реализацию этого механизма. На клиенте мы перехватываем стандартную обработку расшифровки, отменяем ее и вызываем серверную функцию:
&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка, ДополнительныеПараметры)
СтандартнаяОбработка = Ложь; // Отменяем стандартное контекстное меню 1С
// Передаем идентификатор расшифровки и адрес временного хранилища на сервер
СсылкаНаДокумент = ПолучитьДокументПоРасшифровке(Расшифровка, ДанныеРасшифровки);
Если ЗначениеЗаполнено(СсылкаНаДокумент) Тогда
// Открываем форму найденного документа
ПоказатьЗначение(, СсылкаНаДокумент);
Иначе
ПоказатьПредупреждение(, "Не удалось найти документ-источник!");
КонецЕсли;
КонецПроцедуры
Теперь проанализируем ситуацию на сервере. Нам необходимо извлечь коллекцию данных расшифровки из временного хранилища формы, найти нужный элемент по его индексу и прочитать исходное значение поля:
&НаСервереБезКонтекста
Функция ПолучитьДокументПоРасшифровке(Знач ИдентификаторРасшифровки, Знач АдресДанных)
// Данные расшифровки СКД хранятся во временном хранилище формы
Данные = ПолучитьИзВременногоХранилища(АдресДанных);
Элемент = Данные.Элементы[ИдентификаторРасшифровки];
Если ТипЗнч(Элемент) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
// Получаем значение поля (это будет исходная строка из запроса, а не текст из ячейки)
СтрокаДокумента = Элемент.ПолучитьПоля()[0].Значение;
// Здесь мы выполняем поиск документа в базе по строке
Возврат НайтиДокументПоСтроке(СтрокаДокумента);
КонецЕсли;
Возврат Неопределено;
КонецФункции
Система компоновки данных позволяет разделить то, что видит пользователь на экране, и то, что записывается в расшифровку ячейки. Посмотрим на пример настройки на вкладке «Наборы данных» в схеме СКД для нашего поля:
"Реализация № " + Номер + " от " + Формат(Дата, "ДФ=dd.MM.yyyy"))."РеализацияТоваровУслуг|" + СокрЛП(Номер) + "|" + Формат(Дата, "ДФ=yyyyMMddHHmmss")) или передайте готовую ссылку на документ, если она доступна в запросе.При клике на ячейку платформа считает скрытое техническое значение из ДанныеРасшифровки (как показано в Способе 1). Разобрать строго форматированную строку через функцию СтрРазделить гораздо проще и надежнее, чем пытаться анализировать произвольный пользовательский текст.
Чтобы полностью отказаться от парсинга строк, мы можем передать в расшифровку полноценный объект Структура с готовыми свойствами (Тип, Номер, Дата). Для этого используется макет поля СКД.
Выполним следующие шаги:
ПараметрРасшифровкиДокумента).Новый Структура("ТипДокумента, Номер, Дата", ИмяТаблицыДокумента, НомерДокумента, ДатаДокумента)В обработчике расшифровки на сервере мы сразу получим готовую структуру параметров без какого-либо анализа строк:
// На сервере без контекста
Поля = Данные.Элементы[ИдентификаторРасшифровки].ПолучитьПоля();
СтруктураПоиска = Поля[0].Значение; // Здесь будет Структура с готовыми свойствами!
// Ищем документ по реквизитам
ДокСсылка = Документы[СтруктураПоиска.ТипДокумента].НайтиПоНомеру(
СтруктураПоиска.Номер,
СтруктураПоиска.Дата
);
Иногда тип, номер и дата документа выводятся в отчете в отдельных колонках (или группировках), но нам нужно собрать их воедино при клике на конкретную ячейку ресурса. В таких сценариях мы можем рекурсивно подняться по родительским элементам расшифровки строки.
Рассмотрим пример кода, реализующий обход дерева расшифровки с помощью метода ПолучитьРодителей():
&НаСервереБезКонтекста
Функция СобратьДанныеСтроки(Знач ИдентификаторРасшифровки, Знач АдресДанных)
Данные = ПолучитьИзВременногоХранилища(АдресДанных);
Элемент = Данные.Элементы[ИдентификаторРасшифровки];
ПараметрыПоиска = Новый Структура("ТипДокумента, Номер, Дата");
// Рекурсивно поднимаемся по родителям элемента расшифровки
ТекущийЭлемент = Элемент;
Пока ТекущийЭлемент <> Неопределено Цикл
Если ТипЗнч(ТекущийЭлемент) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
Для Каждого Поле Из ТекущийЭлемент.ПолучитьПоля() Цикл
Если ПараметрыПоиска.Свойство(Поле.Поле) Тогда
ПараметрыПоиска[Поле.Поле] = Поле.Значение;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Родители = ТекущийЭлемент.ПолучитьРодителей();
ТекущийЭлемент = ?(Родители.Количество() > 0, Родители[0], Неопределено);
КонецЦикла;
Возврат ПараметрыПоиска;
КонецФункции
Этот способ незаменим, когда в самой кликабельной ячейке нет всех данных для поиска, но они гарантированно присутствуют в строке отчета в качестве группировок или соседних реквизитов.