Как программно записать значение в реквизит табличной части 1С, если его имя формируется динамически?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

Анализ основной проблемы и типичной ошибки

Рассмотрим ситуацию, описанную автором темы. Имеется документ ГодовойГрафикРемонтов, данные из которого должны транслироваться в МесячныйГрафикРемонтов. В табличной части присутствуют колонки для каждого дня месяца. Основная трудность заключается в том, что при обходе результата запроса разработчик пытается присвоить значение напрямую в выборку запроса:


// ОШИБОЧНЫЙ ПОДХОД
Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
    Результат.День08 = "Значение"; // Ошибка: Поле объекта недоступно для записи
КонецЦикла;

Проанализируем причину ошибки: объект ВыборкаИзРезультатаЗапроса предназначен только для чтения данных из базы. Для изменения данных нам необходимо обратиться к самому объекту документа или к реквизиту формы, который содержит данные табличной части.

Решение 1: Использование синтаксиса квадратных скобок

В языке 1С коллекции (строки табличных частей, структуры, соответствия) позволяют обращаться к своим свойствам не только через точку (Строка.ИмяРеквизита), но и через квадратные скобки, передавая имя реквизита в виде строки (Строка["ИмяРеквизита"]). Это и есть ключ к решению задачи.

Разберем по шагам, как это реализовать в процедуре &НаСервере:


&НаСервере
Процедура ЗаполнитьПоДнямНаСервере(Параметр_Месяц)
    
    // 1. Формируем запрос для получения данных из годового графика
    Запрос = Новый Запрос;
    // ... (текст запроса из примера автора)
    
    РезультатЗапроса = Запрос.Выполнить().Выбрать();
    
    Пока РезультатЗапроса.Следующий() Цикл
        
        // 2. Находим или создаем строку в табличной части текущего документа (Объект)
        // Предположим, мы ищем строку по оборудованию
        ПараметрыОтбора = Новый Структура("Оборудование", РезультатЗапроса.Оборудование);
        НайденныеСтроки = Объект.СписокОборудования.НайтиСтроки(ПараметрыОтбора);
        
        СтрокаТЧ = Неопределено;
        Если НайденныеСтроки.Количество() > 0 Тогда
            СтрокаТЧ = НайденныеСтроки[0];
        Иначе
            СтрокаТЧ = Объект.СписокОборудования.Добавить();
            ЗаполнитьЗначенияСвойств(СтрокаТЧ, РезультатЗапроса);
        КонецЕсли;
        
        // 3. Вычисляем день и формируем имя колонки
        // Важно: учитываем ведущий ноль (День01, а не День1)
        ДеньЧислом = День(РезультатЗапроса.ДатаПоследнегоРемонта);
        ИмяПоляДня = "День" + Формат(ДеньЧислом, "ЧГ=0; ЧВН=");
        
        // 4. ЗАПИСЬ ЗНАЧЕНИЯ
        // Используем квадратные скобки для динамического обращения
        СтрокаТЧ[ИмяПоляДня] = "ППР"; // Здесь записываем нужное значение
        
    КонецЦикла;

КонецПроцедуры

Решение 2: Форматирование имени реквизита с ведущим нулем

Как мы заметили в коде выше, критически важно правильно сформировать строковое имя реквизита. Если в метаданных колонка называется День08, а ваш код сформирует строку "День8", система выдаст ошибку "Поле объекта не обнаружено".

Рассмотрим подробнее функцию Формат() для этих целей:

Используйте строку формата "ЧГ=0; ЧВН=":

Пример формирования:


Для НомерДня = 1 По 31 Цикл
    ИмяРеквизита = "День" + Формат(НомерДня, "ЧГ=0; ЧВН=");
    // На выходе получим: "День01", "День02" ... "День31"
КонецЦикла;

Решение 3: Оптимизация поиска строк (Кэширование)

Если в документе МесячныйГрафикРемонтов сотни или тысячи строк, использование метода НайтиСтроки() внутри цикла по результату запроса может существенно замедлить работу (алгоритм сложности O(n*m)). Проанализируем, как ускорить этот процесс.

Рекомендуется предварительно создать Соответствие, где ключом будет уникальный идентификатор оборудования, а значением — сама строка табличной части:


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

// Теперь в цикле запроса поиск будет мгновенным
Пока РезультатЗапроса.Следующий() Цикл
    СтрокаТЧ = КартаСтрок.Получить(РезультатЗапроса.Оборудование);
    Если СтрокаТЧ <> Неопределено Тогда
        СтрокаТЧ[ИмяПоляДня] = "Значение";
    КонецЕсли;
КонецЦикла;

Важные архитектурные замечания

Проанализируем ситуацию, упомянутую в сообщении №7 форума: "необходимо завести 365 колонок". С точки зрения производительности и гибкости системы 1С, создание огромного количества реквизитов (День01...День31 или тем более 365 дней) в одной таблице считается "антипаттерном".

Почему это плохо:

  1. Жесткость структуры: При изменении бизнес-логики (например, переход на учет по часам) придется переделывать все формы и запросы.
  2. Размер БД: Каждая колонка — это отдельное поле в таблице SQL. Пустые значения все равно занимают место.
  3. Сложность отчетов: Выводить данные из 365 колонок в отчет СКД крайне неудобно.

Как сделать правильно:

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

Резюме

Для записи значения в динамический реквизит используйте конструкцию СтрокаКоллекции["ИмяРеквизита"] = Значение. Не забывайте о строгом соответствии имен (включая ведущие нули) и старайтесь использовать кэширование (объект Соответствие) для повышения производительности серверных процедур.

← На главную