Как программно добавить колонку в дерево значений 1С и избежать ошибки "Неверное имя колонки"?

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

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

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

Причина 1. Повторное выполнение кода (Дублирование имени колонки)

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

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

Решение: Перед добавлением новой колонки всегда проверяйте, существует ли она уже в коллекции, с помощью метода Найти().

Рассмотрим безопасный шаблон добавления колонки:


ИмяИскомойКолонки = "Ро";
Если ОстаткиТоваров.Колонки.Найти(ИмяИскомойКолонки) = Неопределено Тогда
    // Колонка еще не существует, добавляем её
    ОстаткиТоваров.Колонки.Добавить(ИмяИскомойКолонки, Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(12, 2)));
КонецЕсли;

Причина 2. Ошибка в конструкторе ОписаниеТипов (Лишние запятые)

Другая распространенная причина ошибок — невнимательность при заполнении параметров конструктора ОписаниеТипов. Обратите внимание на типичную ошибочную строку кода:


ОстаткиТоваров.Колонки.Вставить(5, "Ро", Новый ОписаниеТипов("Число",,, Новый КвалификаторыЧисла(12, 2)),,);

Давайте разберем по шагам параметры конструктора ОписаниеТипов:

  1. Первый параметр — типы (строка или массив типов).
  2. Второй параметр — квалификаторы числа.
  3. Третий параметр — квалификаторы строки.
  4. Четвертый параметр — квалификаторы даты.

В ошибочном примере из-за лишних запятых объект КвалификаторыЧисла(12, 2) передается в качестве четвертого параметра, то есть на место квалификаторов даты! Платформа некорректно инициализирует объект типа данных, что впоследствии приводит к ошибкам при обращении к методам работы с колонками дерева.

Решение: Уберите лишние запятые. Квалификаторы числа должны идти строго вторым параметром:


// Правильная инициализация описания типов чисел
КвалификаторыДробногоЧисла = Новый КвалификаторыЧисла(12, 2);
ТипКолонки = Новый ОписаниеТипов("Число", КвалификаторыДробногоЧисла);

// Теперь можно безопасно добавить колонку в дерево
ОстаткиТоваров.Колонки.Добавить("Ро", ТипКолонки);

Причина 3. Выход за границы коллекции при использовании метода Вставить()

Метод Вставить(<Индекс>, <Имя>, ...) предназначен для добавления колонки в строго определенную позицию дерева значений. Если в вашем дереве на данный момент всего 4 колонки (индексы от 0 до 3), а вы пытаетесь вставить новую колонку под индексом 5, платформа может выдать ошибку из-за выхода за границы доступного диапазона индексов.

Решение: Если порядок колонок не критичен для внутренней логики расчетов, всегда используйте метод Добавить(). Он автоматически добавляет колонку в самый конец коллекции, что исключает ошибки с индексами.


// Безопасное добавление в конец коллекции
ОстаткиТоваров.Колонки.Добавить("Ро", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(12, 2)));

Как вывести программно добавленную колонку на форму?

Если вы работаете в режиме обычного приложения (обычные формы), просто добавить колонку в объект данных ДеревоЗначений в памяти недостаточно — пользователь её не увидит. Чтобы новая колонка отобразилась на форме в табличном поле, необходимо принудительно обновить элементы интерфейса.

Для этого после программного изменения колонок объекта вызовите метод СоздатьКолонки() у соответствующего табличного поля формы:


// Добавляем колонку в данные
Если ОстаткиТоваров.Колонки.Найти("Ро") = Неопределено Тогда
    ОстаткиТоваров.Колонки.Добавить("Ро", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(12, 2)));
КонецЕсли;

// Перерисовываем колонки в табличном поле на форме
ЭлементыФормы.ТабличноеПолеОстаткиТоваров.СоздатьКолонки();

Оптимизация кода и работа с запросами в циклах

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

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


// 1. Проверяем и добавляем колонку, если её нет
Если ОстаткиТоваров.Колонки.Найти("Ро") = Неопределено Тогда
    ОстаткиТоваров.Колонки.Добавить("Ро", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(12, 2)));
КонецЕсли;

// 2. Собираем массив уникальных ссылок на номенклатуру из дерева значений
МассивНоменклатуры = Новый Массив;
Для Каждого СтрокаДерева Из ОстаткиТоваров.Строки Цикл
    Если ЗначениеЗаполнено(СтрокаДерева.Номенклатура) Тогда
        Если МассивНоменклатуры.Найти(СтрокаДерева.Номенклатура) = Неопределено Тогда
            МассивНоменклатуры.Добавить(СтрокаДерева.Номенклатура);
        КонецЕсли;
    КонецЕсли;
КонецДля;

// 3. Выполняем ОДИН запрос ко всей выборке номенклатуры
Если МассивНоменклатуры.Количество() > 0 Тогда
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	РезервОстатки.Номенклатура КАК Номенклатура,
        |	РезервОстатки.КоличествоОстаток КАК Остаток
        |ИЗ
        |	РегистрНакопления.Резерв_Ольга.Остатки(, Номенклатура В (&МассивНоменклатуры)) КАК РезервОстатки";
    
    Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
    ТаблицаРезультатов = Запрос.Выполнить().Выгрузить();
    
    // 4. Заполняем добавленную колонку в дереве значений из таблицы результатов
    Для Каждого СтрокаДерева Из ОстаткиТоваров.Строки Цикл
        СтрокаОстатка = ТаблицаРезультатов.Найти(СтрокаДерева.Номенклатура, "Номенклатура");
        Если СтрокаОстатка <> Неопределено Тогда
            СтрокаДерева.Ро = СтрокаОстатка.Остаток;
        Иначе
            СтрокаДерева.Ро = 0;
        КонецЕсли;
    КонецДля;
КонецЕсли;

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

← На главную