Как динамически создавать колонки в таблице значений 1С и обращаться к ним в цикле

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

Почему возникают ошибки при динамическом создании колонок

Проанализируем две типичные ошибки, которые допускаются при добавлении динамических колонок в объект ТаблицаЗначений:

  1. Передача ссылки на объект вместо строкового идентификатора. Метод ТабЗнач.Колонки.Добавить() в качестве первого параметра ожидает строку, которая должна отвечать правилам именования переменных в 1С (идентификатор). Если передать туда ссылку на элемент справочника (например, СтрТаблицаТипЦен.ТипЦен), платформа либо преобразует ее в строковое представление с пробелами и спецсимволами, либо выдаст исключение. При последующем обращении СтрокаТаб[СтрТаблицаТипЦен.ТипЦен] обращение по ссылке как по индексу невозможно.
  2. Использование некорректных символов или цифры в начале имени. Если для имени колонки взять код справочника (Код), который часто начинается с нулей (например, "000000001") или содержит дефисы, возникнет системная ошибка «Неверное имя колонки», так как имя колонки не может начинаться с цифры.
  3. Несоответствие типов данных. Если колонка типизирована как СправочникСсылка.ТипыЦенНоменклатуры, а при заполнении строки в нее записывается форматированная строка с ценой и символом валюты, возникнет ошибка или значение не запишется.

Способ 1. Использование объекта Соответствие для связки данных и колонок

Рассмотрим наиболее надежный и гибкий подход. Чтобы избежать проблем с недопустимыми символами в наименованиях или кодах справочников, создадим уникальные программные имена колонок с префиксом, а связь между ссылкой и именем колонки сохраним в объекте Соответствие.

Разберем по шагам программный код:


// 1. Создаем таблицу значений и соответствие для хранения имен колонок
ТабЗнач = Новый ТаблицаЗначений;
ТабЗнач.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТабЗнач.Колонки.Добавить("НаименованиеТовар", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));

// Соответствие: Ключ - СсылкаНаТипЦен, Значение - ИмяКолонкиВТаблице
СоответствиеКолонокЦен = Новый Соответствие;
СоответствиеКолонокЕдИзм = Новый Соответствие;

ИндексКолонки = 0;
Для Каждого СтрТипЦен Из ТаблицаТипЦен Цикл
    ИндексКолонки = ИндексКолонки + 1;
    
    // Формируем гарантированно валидный идентификатор колонки
    ИмяКолонкиЦены = "ЦенаТип_" + Формат(ИндексКолонки, "ЧГ=0");
    ИмяКолонкиЕд   = "ЕдИзмТип_" + Формат(ИндексКолонки, "ЧГ=0");
    
    // Добавляем колонки с указанием понятного заголовка
    ТабЗнач.Колонки.Добавить(ИмяКолонкиЦены, Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)), СокрЛП(СтрТипЦен.ТипЦен.Наименование));
    ТабЗнач.Колонки.Добавить(ИмяКолонкиЕд, Новый ОписаниеТипов("СправочникСсылка.КлассификаторЕдиницИзмерения"), "Ед. изм.");
    
    // Сохраняем связку
    СоответствиеКолонокЦен.Вставить(СтрТипЦен.ТипЦен, ИмяКолонкиЦены);
    СоответствиеКолонокЕдИзм.Вставить(СтрТипЦен.ТипЦен, ИмяКолонкиЕд);
КонецЦикла;

// 2. Заполнение таблицы данными
Пока ВыборкаТЗ.Следующий() Цикл
    СтрокаТаб = ТабЗнач.Добавить();
    СтрокаТаб.Номенклатура = ВыборкаТЗ.Номенклатура;
    СтрокаТаб.НаименованиеТовар = ВыборкаТЗ.Номенклатура.Наименование;
    
    // Обходим типы цен и обращаемся к динамическим колонкам по имени из соответствия
    Для Каждого СтрТипЦен Из ТаблицаТипЦен Цикл
        ИмяКолонкиЦены = СоответствиеКолонокЦен[СтрТипЦен.ТипЦен];
        ИмяКолонкиЕд   = СоответствиеКолонокЕдИзм[СтрТипЦен.ТипЦен];
        
        // Получаем значение (например, из запроса или структуры)
        // И безопасно записываем в строку таблицы
        СтрокаТаб[ИмяКолонкиЦены] = "1 500.00 руб.";
        СтрокаТаб[ИмяКолонкиЕд]   = ВыборкаТЗ.Номенклатура.БазоваяЕдиницаИзмерения;
    КонецЦикла;
КонецЦикла;

Способ 2. Очистка наименований от спецсимволов и пробелов

Если требуется сформировать имя колонки напрямую из реквизитов объекта, необходимо убедиться, что строка не содержит пробелов, знаков препинания и начинается с буквы. Посмотрим на пример вспомогательной функции для подготовки имени колонки:


Функция ПолучитьИдентификаторКолонки(ИсходныйТекст, Префикс = "Колонка_")
    
    Результат = Префикс;
    ДопустимыеСимволы = "абвгдеёжзийклмнопрстуфхцчшщъыьэюяabcdefghijklmnopqrstuvwxyz0123456789_";
    
    НизТекст = НСтр(ИсходныйТекст);
    Для Позиция = 1 По СтрДлина(ИсходныйТекст) Цикл
        Символ = Сред(ИсходныйТекст, Позиция, 1);
        Если Найти(ДопустимыеСимволы, Низ(Символ)) > 0 Тогда
            Результат = Результат + Символ;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Результат;
    
КонецФункции

Использование функции при создании колонок и заполнении данных:


// Создание колонки
ИмяКолонки = ПолучитьИдентификаторКолонки(СтрТаблицаТипЦен.ТипЦен.Наименование);
ТабЗнач.Колонки.Добавить(ИмяКолонки, Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));

// Обращение к колонке при заполнении
СтрокаТаб[ИмяКолонки] = ВыборкаТипЦен.Цена;

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

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

Рекомендуется придерживаться следующих правил оптимизации:

  1. Пакетный запрос. Срез последних цен и значения дополнительных свойств следует получать одним запросом сразу по всему списку номенклатуры и типов цен, используя условие ГДЕ Номенклатура В (&МассивНоменклатуры) И ТипЦен В (&МассивТиповЦен).
  2. Использование Схемы компоновки данных (СКД). Для задач формирования прайс-листов с динамическими колонками оптимально использовать СКД. В настройках отчета достаточно создать таблицу, где в строках указать номенклатуру, в колонках — тип цен, а в ресурсах — цену. СКД сформирует кросс-таблицу автоматически без ручного создания колонок в коде.
← На главную