При формировании нестандартных отчетов, прайс-листов и аналитических выборок часто возникает задача сформировать таблицу значений с заранее неизвестным количеством колонок (например, когда список колонок строится по типам цен, складам или периодам). В процессе реализации разработчики нередко сталкиваются с ошибками «Неверное имя колонки» и «Получение элемента по индексу для значения не определено». Разберем по шагам причины возникновения этих ошибок и выясним, как правильно создавать динамические колонки и заполнять их значениями в цикле.
Проанализируем две типичные ошибки, которые допускаются при добавлении динамических колонок в объект ТаблицаЗначений:
ТабЗнач.Колонки.Добавить() в качестве первого параметра ожидает строку, которая должна отвечать правилам именования переменных в 1С (идентификатор). Если передать туда ссылку на элемент справочника (например, СтрТаблицаТипЦен.ТипЦен), платформа либо преобразует ее в строковое представление с пробелами и спецсимволами, либо выдаст исключение. При последующем обращении СтрокаТаб[СтрТаблицаТипЦен.ТипЦен] обращение по ссылке как по индексу невозможно.Код), который часто начинается с нулей (например, "000000001") или содержит дефисы, возникнет системная ошибка «Неверное имя колонки», так как имя колонки не может начинаться с цифры.СправочникСсылка.ТипыЦенНоменклатуры, а при заполнении строки в нее записывается форматированная строка с ценой и символом валюты, возникнет ошибка или значение не запишется.Рассмотрим наиболее надежный и гибкий подход. Чтобы избежать проблем с недопустимыми символами в наименованиях или кодах справочников, создадим уникальные программные имена колонок с префиксом, а связь между ссылкой и именем колонки сохраним в объекте Соответствие.
Разберем по шагам программный код:
// 1. Создаем таблицу значений и соответствие для хранения имен колонок
ТабЗнач = Новый ТаблицаЗначений;
ТабЗнач.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТабЗнач.Колонки.Добавить("НаименованиеТовар", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));
// Соответствие: Ключ - СсылкаНаТипЦен, Значение - ИмяКолонкиВТаблице
СоответствиеКолонокЦен = Новый Соответствие;
СоответствиеКолонокЕдИзм = Новый Соответствие;
ИндексКолонки = 0;
Для Каждого СтрТипЦен Из ТаблицаТипЦен Цикл
ИндексКолонки = ИндексКолонки + 1;
// Формируем гарантированно валидный идентификатор колонки
ИмяКолонкиЦены = "ЦенаТип_" + Формат(ИндексКолонки, "ЧГ=0");
ИмяКолонкиЕд = "ЕдИзмТип_" + Формат(ИндексКолонки, "ЧГ=0");
// Добавляем колонки с указанием понятного заголовка
ТабЗнач.Колонки.Добавить(ИмяКолонкиЦены, Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)), СокрЛП(СтрТипЦен.ТипЦен.Наименование));
ТабЗнач.Колонки.Добавить(ИмяКолонкиЕд, Новый ОписаниеТипов("СправочникСсылка.КлассификаторЕдиницИзмерения"), "Ед. изм.");
// Сохраняем связку
СоответствиеКолонокЦен.Вставить(СтрТипЦен.ТипЦен, ИмяКолонкиЦены);
СоответствиеКолонокЕдИзм.Вставить(СтрТипЦен.ТипЦен, ИмяКолонкиЕд);
КонецЦикла;
// 2. Заполнение таблицы данными
Пока ВыборкаТЗ.Следующий() Цикл
СтрокаТаб = ТабЗнач.Добавить();
СтрокаТаб.Номенклатура = ВыборкаТЗ.Номенклатура;
СтрокаТаб.НаименованиеТовар = ВыборкаТЗ.Номенклатура.Наименование;
// Обходим типы цен и обращаемся к динамическим колонкам по имени из соответствия
Для Каждого СтрТипЦен Из ТаблицаТипЦен Цикл
ИмяКолонкиЦены = СоответствиеКолонокЦен[СтрТипЦен.ТипЦен];
ИмяКолонкиЕд = СоответствиеКолонокЕдИзм[СтрТипЦен.ТипЦен];
// Получаем значение (например, из запроса или структуры)
// И безопасно записываем в строку таблицы
СтрокаТаб[ИмяКолонкиЦены] = "1 500.00 руб.";
СтрокаТаб[ИмяКолонкиЕд] = ВыборкаТЗ.Номенклатура.БазоваяЕдиницаИзмерения;
КонецЦикла;
КонецЦикла;
Если требуется сформировать имя колонки напрямую из реквизитов объекта, необходимо убедиться, что строка не содержит пробелов, знаков препинания и начинается с буквы. Посмотрим на пример вспомогательной функции для подготовки имени колонки:
Функция ПолучитьИдентификаторКолонки(ИсходныйТекст, Префикс = "Колонка_")
Результат = Префикс;
ДопустимыеСимволы = "абвгдеёжзийклмнопрстуфхцчшщъыьэюяabcdefghijklmnopqrstuvwxyz0123456789_";
НизТекст = НСтр(ИсходныйТекст);
Для Позиция = 1 По СтрДлина(ИсходныйТекст) Цикл
Символ = Сред(ИсходныйТекст, Позиция, 1);
Если Найти(ДопустимыеСимволы, Низ(Символ)) > 0 Тогда
Результат = Результат + Символ;
КонецЕсли;
КонецЦикла;
Возврат Результат;
КонецФункции
Использование функции при создании колонок и заполнении данных:
// Создание колонки
ИмяКолонки = ПолучитьИдентификаторКолонки(СтрТаблицаТипЦен.ТипЦен.Наименование);
ТабЗнач.Колонки.Добавить(ИмяКолонки, Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
// Обращение к колонке при заполнении
СтрокаТаб[ИмяКолонки] = ВыборкаТипЦен.Цена;
Обратите внимание на критическую архитектурную ошибку в исходном коде: выполнение запроса внутри циклов обхода строк и колонок (Для каждого СтрТаблицаТипЦен внутри Пока ВыборкаТЗ.Следующий). Выполнение запроса в цикле создает огромную нагрузку на СУБД и замедляет работу системы в десятки и сотни раз.
Рекомендуется придерживаться следующих правил оптимизации:
ГДЕ Номенклатура В (&МассивНоменклатуры) И ТипЦен В (&МассивТиповЦен).