При динамической работе с коллекциями данных в 1С (такими как ТаблицаЗначений или ДеревоЗначений) разработчики часто сталкиваются с необходимостью программно расширить структуру колонок. Однако при вызове методов Добавить() или Вставить() нередко возникает неприятная ошибка: «Неверное имя колонки» или аварийное завершение метода. На первый взгляд код кажется абсолютно правильным, но платформа упорно отказывается его выполнять.
В этой статье мы подробно разберем, почему возникает эта ошибка, выявим скрытые синтаксические нюансы платформы «1С:Предприятие» и научимся правильно и безопасно добавлять колонки в дерево значений.
Давайте проанализируем ситуацию, в которой выполняется добавление колонки. Если ваш код находится в процедуре, которая вызывается интерактивно (например, при активизации строки списка номенклатуры в событии ПриАктивизацииСтроки), он будет выполняться многократно при каждом перемещении курсора пользователем.
При первом вызове процедуры колонка успешно создается и добавляется в объект. При повторном вызове (когда пользователь перешел на другую строку) платформа пытается добавить колонку с точно таким же именем еще раз. Поскольку имена колонок в пределах одного дерева значений должны быть уникальными, платформа выдает ошибку «Неверное имя колонки».
Решение: Перед добавлением новой колонки всегда проверяйте, существует ли она уже в коллекции, с помощью метода Найти().
Рассмотрим безопасный шаблон добавления колонки:
ИмяИскомойКолонки = "Ро";
Если ОстаткиТоваров.Колонки.Найти(ИмяИскомойКолонки) = Неопределено Тогда
// Колонка еще не существует, добавляем её
ОстаткиТоваров.Колонки.Добавить(ИмяИскомойКолонки, Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(12, 2)));
КонецЕсли;
Другая распространенная причина ошибок — невнимательность при заполнении параметров конструктора ОписаниеТипов. Обратите внимание на типичную ошибочную строку кода:
ОстаткиТоваров.Колонки.Вставить(5, "Ро", Новый ОписаниеТипов("Число",,, Новый КвалификаторыЧисла(12, 2)),,);
Давайте разберем по шагам параметры конструктора ОписаниеТипов:
В ошибочном примере из-за лишних запятых объект КвалификаторыЧисла(12, 2) передается в качестве четвертого параметра, то есть на место квалификаторов даты! Платформа некорректно инициализирует объект типа данных, что впоследствии приводит к ошибкам при обращении к методам работы с колонками дерева.
Решение: Уберите лишние запятые. Квалификаторы числа должны идти строго вторым параметром:
// Правильная инициализация описания типов чисел
КвалификаторыДробногоЧисла = Новый КвалификаторыЧисла(12, 2);
ТипКолонки = Новый ОписаниеТипов("Число", КвалификаторыДробногоЧисла);
// Теперь можно безопасно добавить колонку в дерево
ОстаткиТоваров.Колонки.Добавить("Ро", ТипКолонки);
Метод Вставить(<Индекс>, <Имя>, ...) предназначен для добавления колонки в строго определенную позицию дерева значений. Если в вашем дереве на данный момент всего 4 колонки (индексы от 0 до 3), а вы пытаетесь вставить новую колонку под индексом 5, платформа может выдать ошибку из-за выхода за границы доступного диапазона индексов.
Решение: Если порядок колонок не критичен для внутренней логики расчетов, всегда используйте метод Добавить(). Он автоматически добавляет колонку в самый конец коллекции, что исключает ошибки с индексами.
// Безопасное добавление в конец коллекции
ОстаткиТоваров.Колонки.Добавить("Ро", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(12, 2)));
Если вы работаете в режиме обычного приложения (обычные формы), просто добавить колонку в объект данных ДеревоЗначений в памяти недостаточно — пользователь её не увидит. Чтобы новая колонка отобразилась на форме в табличном поле, необходимо принудительно обновить элементы интерфейса.
Для этого после программного изменения колонок объекта вызовите метод СоздатьКолонки() у соответствующего табличного поля формы:
// Добавляем колонку в данные
Если ОстаткиТоваров.Колонки.Найти("Ро") = Неопределено Тогда
ОстаткиТоваров.Колонки.Добавить("Ро", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(12, 2)));
КонецЕсли;
// Перерисовываем колонки в табличном поле на форме
ЭлементыФормы.ТабличноеПолеОстаткиТоваров.СоздатьКолонки();
Посмотрим на ситуацию шире. Выполнение тяжелых операций, таких как запросы к базе данных внутри цикла по строкам дерева значений в обработчиках событий (например, при изменении или активизации строки), существенно снижает производительность системы. Интерфейс программы начнет "зависать" при перемещении курсора по списку.
Давайте перепишем алгоритм правильно. Вместо выполнения запроса в цикле по каждой строке, мы соберем все необходимые ссылки в массив, выполним один пакетный запрос, а затем быстро распределим результаты по строкам нашего дерева значений.
// 1. Проверяем и добавляем колонку, если её нет
Если ОстаткиТоваров.Колонки.Найти("Ро") = Неопределено Тогда
ОстаткиТоваров.Колонки.Добавить("Ро", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(12, 2)));
КонецЕсли;
// 2. Собираем массив уникальных ссылок на номенклатуру из дерева значений
МассивНоменклатуры = Новый Массив;
Для Каждого СтрокаДерева Из ОстаткиТоваров.Строки Цикл
Если ЗначениеЗаполнено(СтрокаДерева.Номенклатура) Тогда
Если МассивНоменклатуры.Найти(СтрокаДерева.Номенклатура) = Неопределено Тогда
МассивНоменклатуры.Добавить(СтрокаДерева.Номенклатура);
КонецЕсли;
КонецЕсли;
КонецДля;
// 3. Выполняем ОДИН запрос ко всей выборке номенклатуры
Если МассивНоменклатуры.Количество() > 0 Тогда
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РезервОстатки.Номенклатура КАК Номенклатура,
| РезервОстатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.Резерв_Ольга.Остатки(, Номенклатура В (&МассивНоменклатуры)) КАК РезервОстатки";
Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
ТаблицаРезультатов = Запрос.Выполнить().Выгрузить();
// 4. Заполняем добавленную колонку в дереве значений из таблицы результатов
Для Каждого СтрокаДерева Из ОстаткиТоваров.Строки Цикл
СтрокаОстатка = ТаблицаРезультатов.Найти(СтрокаДерева.Номенклатура, "Номенклатура");
Если СтрокаОстатка <> Неопределено Тогда
СтрокаДерева.Ро = СтрокаОстатка.Остаток;
Иначе
СтрокаДерева.Ро = 0;
КонецЕсли;
КонецДля;
КонецЕсли;
Применение такого подхода позволит вашему приложению работать максимально быстро, без лишних обращений к базе данных и без критических ошибок при динамическом построении интерфейса.