При разработке отчетов «руками» (без использования системы компоновки данных — СКД) программисты часто сталкиваются с ситуацией, когда расшифровка в сформированном табличном документе ведет себя странно. Вы открываете отчет, кликаете на любую строку, но вместо данных конкретной строки 1С предлагает вам значения из самой последней выведенной секции. Рассмотрим подробнее, почему так происходит, разберем механизм работы ссылочных типов данных и выясним, как правильно передавать СписокЗначений в параметры расшифровки.
Представим стандартную задачу: у нас есть запрос, результат которого выгружен в ТаблицаЗначений. Мы обходим эту таблицу в цикле, заполняем параметры области макета и выводим их в ТабличныйДокумент. Нам нужно, чтобы при клике на ячейку «Договор» открывался не один документ, а список связанных договоров. Для этого в параметр расшифровки мы передаем объект СписокЗначений.
Типичная ошибка начинающего разработчика выглядит так:
&НаСервере
Процедура СформироватьОтчетНаСервере()
// Создаем один объект списка ДО цикла
СписокДогов = Новый СписокЗначений;
Для Каждого Стр Из ТаблицаТЗ Цикл
// Очищаем старый список и наполняем его новыми данными
СписокДогов.Очистить();
СписокДогов.Добавить(Стр.Договор);
// Устанавливаем ссылку на этот объект в параметр области
ОбластьСтрока.Параметры.РасшифровкаКарточкаСубконто = СписокДогов;
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
КонецПроцедуры
Проанализируем этот код. На первый взгляд всё логично: создали список, очистили, добавили текущий договор, вывели строку. Но на практике во всех строках отчета расшифровка будет содержать только договор из последней строки ТаблицаТЗ. Почему?
В 1С объекты коллекций (такие как СписокЗначений, Структура, Массив) являются ссылочными типами данных. Когда вы пишете ОбластьСтрока.Параметры.Расшифровка = СписокДогов, вы не копируете содержимое списка в ячейку. Вы записываете в «память» табличного документа адрес (ссылку) на конкретный объект в оперативной памяти.
В нашем «неправильном» примере адрес объекта СписокДогов не меняется на протяжении всего цикла. Мы просто меняем содержимое по этому адресу. В итоге табличный документ содержит тысячи строк, и в каждой строке в поле расшифровки записан один и тот же адрес. Когда цикл завершается, по этому адресу в памяти лежит то, что мы добавили в последней итерации. В момент передачи отчета на клиент 1С считывает данные по этой ссылке и «замораживает» их для пользователя.
Чтобы каждая строка отчета имела свою уникальную расшифровку, нам необходимо на каждой итерации цикла создавать новый объект. В этом случае для каждой строки в табличном документе будет сохранен уникальный адрес в памяти.
Рассмотрим исправленный алгоритм:
&НаСервере
Процедура СформироватьОтчетНаСервере()
Для Каждого Стр Из ТаблицаТЗ Цикл
// Создаем НОВЫЙ объект на каждом шаге цикла
СписокДогов = Новый СписокЗначений;
СписокДогов.Добавить(Стр.Договор);
// Теперь здесь хранится ссылка на уникальный объект для этой строки
ОбластьСтрока.Параметры.РасшифровкаКарточкаСубконто = СписокДогов;
ТабДок.Вывести(ОбластьСтрока);
КонецЦикла;
КонецПроцедуры
В данном варианте СписокДогов каждый раз указывает на новый участок памяти. Предыдущие объекты не удаляются сразу, так как на них теперь «ссылается» табличный документ. После завершения формирования отчета и его передачи на клиентскую сторону, 1С корректно сериализует каждый отдельный список.
После того как мы корректно заполнили данные на сервере, нам нужно правильно обработать нажатие на ячейку. Для этого используется событие ОбработкаРасшифровки табличного поля на форме. Проанализируем пример кода для обработки:
&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка, ДополнительныеПараметры)
// Отключаем стандартное поведение (открытие формы значения)
СтандартнаяОбработка = Ложь;
// Проверяем, какой тип данных пришел в расшифровку
Если ТипЗнч(Расшифровка) = Тип("СписокЗначений") Тогда
// Если в списке всего один элемент, можем сразу его открыть
Если Расшифровка.Количество() = 1 Тогда
ПоказатьЗначение(, Расшифровка[0].Значение);
Иначе
// Если элементов много, предлагаем пользователю выбор
Расшифровка.ПоказатьВыборЭлемента(Новый ОписаниеОповещения("ПослеВыбораДоговора", ЭтотОбъект));
КонецЕсли;
ИначеЕсли ТипЗнч(Расшифровка) = Тип("СправочникСсылка.ДоговорыКонтрагентов") Тогда
ПоказатьЗначение(, Расшифровка);
КонецЕсли;
КонецПроцедуры
Использование метода Новый СписокЗначений в цикле по 10 000 строк может быть ресурсозатратным. Посмотрим на способы оптимизации, которые используют профессиональные разработчики:
Структура. Она работает быстрее, чем СписокЗначений, и позволяет обращаться к полям по имени ключа.Соответствие, где ключом будет ссылка на договор, а значением — уже созданный ранее СписокЗначений. Это позволит сэкономить память, не создавая дублирующие объекты.СправочникСсылка). Обрабатывайте СписокЗначений только там, где это действительно необходимо.Важный нюанс: Помните, что при передаче табличного документа с сервера на клиент происходит сериализация данных расшифровки. Большое количество сложных коллекций (списков со списками или вложенных структур) может существенно замедлить открытие отчета у пользователя. Старайтесь передавать в расшифровку только необходимый минимум данных.
Подведем итог: главная причина «одинаковых» расшифровок — это работа с одним и тем же экземпляром объекта. Всегда используйте конструктор Новый внутри цикла вывода строк, если хотите получить уникальное содержимое расшифровки для каждой ячейки.