Как в 1С при расшифровке отчета СКД получить данные из ячейки и открыть документ по строке?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разберем распространенную задачу, с которой часто сталкиваются разработчики при проектировании отчетов на базе Системы компоновки данных (СКД). Представим ситуацию: в отчете выводится текстовая строка, содержащая тип, номер и дату документа (например, полученная из содержания проводки при сторнировании операций). При двойном клике по этой ячейке необходимо открыть соответствующий документ в базе.

Автор темы на форуме пришел к решению использовать конструкцию Элемент.ТекущаяОбласть.Текст на клиенте. Выясним причину, почему в профессиональной разработке этот подход считается нерекомендуемым (антипаттерном). Чтение экранного текста крайне нестабильно: если пользователь изменит ширину колонки, применит условное оформление, изменит региональные настройки дат или чисел, парсинг текста ячейки гарантированно сломается.

Рассмотрим подробнее четыре элегантных, надежных и гибких способа решения этой задачи, основанных на лучших практиках работы с СКД.

Способ 1. Получение исходного значения через «ДанныеРасшифровки» на сервере

Вместо чтения текстового представления ячейки на клиенте, правильнее передать идентификатор расшифровки на сервер и получить исходное (сырое) значение поля, которое пришло из запроса СКД до всякого форматирования.

Разберем по шагам реализацию этого механизма. На клиенте мы перехватываем стандартную обработку расшифровки, отменяем ее и вызываем серверную функцию:


&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка, ДополнительныеПараметры)
    
    СтандартнаяОбработка = Ложь; // Отменяем стандартное контекстное меню 1С
    
    // Передаем идентификатор расшифровки и адрес временного хранилища на сервер
    СсылкаНаДокумент = ПолучитьДокументПоРасшифровке(Расшифровка, ДанныеРасшифровки);
    
    Если ЗначениеЗаполнено(СсылкаНаДокумент) Тогда
        // Открываем форму найденного документа
        ПоказатьЗначение(, СсылкаНаДокумент);
    Иначе
        ПоказатьПредупреждение(, "Не удалось найти документ-источник!");
    КонецЕсли;
    
КонецПроцедуры

Теперь проанализируем ситуацию на сервере. Нам необходимо извлечь коллекцию данных расшифровки из временного хранилища формы, найти нужный элемент по его индексу и прочитать исходное значение поля:


&НаСервереБезКонтекста
Функция ПолучитьДокументПоРасшифровке(Знач ИдентификаторРасшифровки, Знач АдресДанных)
    
    // Данные расшифровки СКД хранятся во временном хранилище формы
    Данные = ПолучитьИзВременногоХранилища(АдресДанных); 
    Элемент = Данные.Элементы[ИдентификаторРасшифровки];
    
    Если ТипЗнч(Элемент) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
        // Получаем значение поля (это будет исходная строка из запроса, а не текст из ячейки)
        СтрокаДокумента = Элемент.ПолучитьПоля()[0].Значение;
        
        // Здесь мы выполняем поиск документа в базе по строке
        Возврат НайтиДокументПоСтроке(СтрокаДокумента);
    КонецЕсли;
    
    Возврат Неопределено;
    
КонецФункции

Способ 2. Разделение представления и значения расшифровки в СКД

Система компоновки данных позволяет разделить то, что видит пользователь на экране, и то, что записывается в расшифровку ячейки. Посмотрим на пример настройки на вкладке «Наборы данных» в схеме СКД для нашего поля:

  1. Выражение представления: укажите понятный пользователю текст (например: "Реализация № " + Номер + " от " + Формат(Дата, "ДФ=dd.MM.yyyy")).
  2. Выражение расшифровки: укажите техническую строку в строгом формате, которую легко распарсить (например, через разделители: "РеализацияТоваровУслуг|" + СокрЛП(Номер) + "|" + Формат(Дата, "ДФ=yyyyMMddHHmmss")) или передайте готовую ссылку на документ, если она доступна в запросе.

При клике на ячейку платформа считает скрытое техническое значение из ДанныеРасшифровки (как показано в Способе 1). Разобрать строго форматированную строку через функцию СтрРазделить гораздо проще и надежнее, чем пытаться анализировать произвольный пользовательский текст.

Способ 3. Передача структуры через макет поля СКД (идеальный вариант)

Чтобы полностью отказаться от парсинга строк, мы можем передать в расшифровку полноценный объект Структура с готовыми свойствами (Тип, Номер, Дата). Для этого используется макет поля СКД.

Выполним следующие шаги:

  1. В схеме СКД перейдите на вкладку «Макеты» и добавьте макет поля для вашей колонки.
  2. В параметрах расшифровки ячейки макета укажите новый параметр (например, ПараметрРасшифровкиДокумента).
  3. В выражении этого параметра соберите структуру:
    Новый Структура("ТипДокумента, Номер, Дата", ИмяТаблицыДокумента, НомерДокумента, ДатаДокумента)

В обработчике расшифровки на сервере мы сразу получим готовую структуру параметров без какого-либо анализа строк:


// На сервере без контекста
Поля = Данные.Элементы[ИдентификаторРасшифровки].ПолучитьПоля();
СтруктураПоиска = Поля[0].Значение; // Здесь будет Структура с готовыми свойствами!

// Ищем документ по реквизитам
ДокСсылка = Документы[СтруктураПоиска.ТипДокумента].НайтиПоНомеру(
    СтруктураПоиска.Номер, 
    СтруктураПоиска.Дата
);

Способ 4. Получение данных из соседних полей или группировок строки

Иногда тип, номер и дата документа выводятся в отчете в отдельных колонках (или группировках), но нам нужно собрать их воедино при клике на конкретную ячейку ресурса. В таких сценариях мы можем рекурсивно подняться по родительским элементам расшифровки строки.

Рассмотрим пример кода, реализующий обход дерева расшифровки с помощью метода ПолучитьРодителей():


&НаСервереБезКонтекста
Функция СобратьДанныеСтроки(Знач ИдентификаторРасшифровки, Знач АдресДанных)
    
    Данные = ПолучитьИзВременногоХранилища(АдресДанных);
    Элемент = Данные.Элементы[ИдентификаторРасшифровки];
    
    ПараметрыПоиска = Новый Структура("ТипДокумента, Номер, Дата");
    
    // Рекурсивно поднимаемся по родителям элемента расшифровки
    ТекущийЭлемент = Элемент;
    Пока ТекущийЭлемент <> Неопределено Цикл
        
        Если ТипЗнч(ТекущийЭлемент) = Тип("ЭлементРасшифровкиКомпоновкиДанныхПоля") Тогда
            Для Каждого Поле Из ТекущийЭлемент.ПолучитьПоля() Цикл
                Если ПараметрыПоиска.Свойство(Поле.Поле) Тогда
                    ПараметрыПоиска[Поле.Поле] = Поле.Значение;
                КонецЕсли;
            КонецЦикла;
        КонецЕсли;
        
        Родители = ТекущийЭлемент.ПолучитьРодителей();
        ТекущийЭлемент = ?(Родители.Количество() > 0, Родители[0], Неопределено);
        
    КонецЦикла;
    
    Возврат ПараметрыПоиска;
    
КонецФункции

Этот способ незаменим, когда в самой кликабельной ячейке нет всех данных для поиска, но они гарантированно присутствуют в строке отчета в качестве группировок или соседних реквизитов.

← На главную