Почему при заполнении расшифровки Табличного Документа во всех строках отображается значение последней строки?

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

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

Анализируем ситуацию: работа с объектами в цикле

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

Типичная ошибка начинающего разработчика выглядит так:


&НаСервере
Процедура СформироватьОтчетНаСервере()
    // Создаем один объект списка ДО цикла
    СписокДогов = Новый СписокЗначений; 
    
    Для Каждого Стр Из ТаблицаТЗ Цикл
        // Очищаем старый список и наполняем его новыми данными
        СписокДогов.Очистить();
        СписокДогов.Добавить(Стр.Договор);
        
        // Устанавливаем ссылку на этот объект в параметр области
        ОбластьСтрока.Параметры.РасшифровкаКарточкаСубконто = СписокДогов;
        ТабДок.Вывести(ОбластьСтрока);
    КонецЦикла;
КонецПроцедуры

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

Разница между ссылкой и значением

В 1С объекты коллекций (такие как СписокЗначений, Структура, Массив) являются ссылочными типами данных. Когда вы пишете ОбластьСтрока.Параметры.Расшифровка = СписокДогов, вы не копируете содержимое списка в ячейку. Вы записываете в «память» табличного документа адрес (ссылку) на конкретный объект в оперативной памяти.

В нашем «неправильном» примере адрес объекта СписокДогов не меняется на протяжении всего цикла. Мы просто меняем содержимое по этому адресу. В итоге табличный документ содержит тысячи строк, и в каждой строке в поле расшифровки записан один и тот же адрес. Когда цикл завершается, по этому адресу в памяти лежит то, что мы добавили в последней итерации. В момент передачи отчета на клиент 1С считывает данные по этой ссылке и «замораживает» их для пользователя.

Правильное решение: создание нового экземпляра

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

Рассмотрим исправленный алгоритм:


&НаСервере
Процедура СформироватьОтчетНаСервере()
    Для Каждого Стр Из ТаблицаТЗ Цикл
        // Создаем НОВЫЙ объект на каждом шаге цикла
        СписокДогов = Новый СписокЗначений; 
        СписокДогов.Добавить(Стр.Договор);
        
        // Теперь здесь хранится ссылка на уникальный объект для этой строки
        ОбластьСтрока.Параметры.РасшифровкаКарточкаСубконто = СписокДогов;
        ТабДок.Вывести(ОбластьСтрока);
    КонецЦикла;
КонецПроцедуры

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

Обработка расшифровки на клиенте

После того как мы корректно заполнили данные на сервере, нам нужно правильно обработать нажатие на ячейку. Для этого используется событие ОбработкаРасшифровки табличного поля на форме. Проанализируем пример кода для обработки:


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

Оптимизация производительности

Использование метода Новый СписокЗначений в цикле по 10 000 строк может быть ресурсозатратным. Посмотрим на способы оптимизации, которые используют профессиональные разработчики:

  1. Использование Структур: Если вам нужно передать несколько разных параметров (например, договор, период и склад), лучше использовать Структура. Она работает быстрее, чем СписокЗначений, и позволяет обращаться к полям по имени ключа.
  2. Кэширование объектов: Если значения в строках часто повторяются (например, один договор для группы из 100 строк), можно создать Соответствие, где ключом будет ссылка на договор, а значением — уже созданный ранее СписокЗначений. Это позволит сэкономить память, не создавая дублирующие объекты.
  3. Отказ от коллекций: Если в 90% случаев в расшифровке передается только одно значение, передавайте саму ссылку (СправочникСсылка). Обрабатывайте СписокЗначений только там, где это действительно необходимо.

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

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

← На главную