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

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

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

Решение 1: Прямое обращение через квадратные скобки

На уровне платформы 1С:Предприятие объект записи регистра (будь то регистр сведений, накопления, расчета или бухгалтерии) поддерживает обращение к своим свойствам не только через прямую точку (например, Запись.ИмяПоля), но и посредством квадратных скобок по строковому имени. Этот механизм работает аналогично обращению к элементам универсальных коллекций, таких как Структура, Соответствие или СтрокаТаблицыЗначений.

Рассмотрим пример реализации такого обращения в цикле. Предположим, нам необходимо заполнить показатели неотработанных дней за определенный период месяца:


Для НомерДня = 1 По 31 Цикл
    ИмяПоля = "НеотработанДень" + НомерДня;
    
    // Прямая динамическая запись в поле объекта записи регистра
    Запись[ИмяПоля] = ПолучитьЗначениеНеотработанногоВремени(НомерДня);
КонецЦикла;

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

Анализ возможных ошибок: «Поле объекта не обнаружено»

Если при попытке динамического обращения система выдает ошибку «Поле объекта не обнаружено», нам следует проверить несколько критически важных моментов:

  1. Несоответствие типов данных при конкатенации строк. Убедитесь, что переменная НомерДня корректно преобразуется в строку. Если платформа попытается сложить строку и число без явного или неявного преобразования, возникнет ошибка. Но в конструкции "Строка" + Число платформа 1С производит приведение типов автоматически.
  2. Отсутствие поля в структуре метаданных. Это самая частая причина сбоев. Например, если в текущем месяце 30 дней, а ваш алгоритм пытается обратиться к ресурсу НеотработанДень31, которого физически может не существовать в структуре регистра (или он не предусмотрен для конкретных видов расчетов), система вызовет исключение.
  3. Опечатки в именах полей. Регистр чувствителен к написанию имен. Наличие лишних пробелов, невидимых символов или использование латинских букв вместо кириллических (и наоборот) гарантированно приведет к ошибке.

Решение 2: Безопасное обращение с проверкой существования поля

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

Рассмотрим, как реализовать проверку существования ресурса в регистре сведений по шагам:


ИмяРегистра = "СведенияОВремени";
МетаданныеРегистра = Метаданные.РегистрыСведений[ИмяРегистра];

Для НомерДня = 1 По 31 Цикл
    ИмяПоля = "НеотработанДень" + НомерДня;
    
    // Проверяем наличие поля в ресурсах регистра через метаданные
    Если МетаданныеРегистра.Ресурсы.Найти(ИмяПоля) <> Неопределено Тогда
        Запись[ИмяПоля] = ПолучитьЗначениеНеотработанногоВремени(НомерДня);
    Иначе
        // Поле не найдено в конфигурации, регистрируем предупреждение или пропускаем шаг
        ЗаписьЖурналаРегистрации("ПроверкаПолей", УровеньЖурналаРегистрации.Предупреждение,,, 
            "Поле " + ИмяПоля + " отсутствует в метаданных регистра " + ИмяРегистра);
    КонецЕсли;
КонецЦикла;

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


ИмяПоля = "НеотработанДень" + НомерДня;
Если ОбщегоНазначения.ЕстьРеквизитОбъекта(ИмяПоля, Метаданные.РегистрыСведений.ИмяВашегоРегистра) Тогда
    Запись[ИмяПоля] = Значение;
КонецЕсли;

Решение 3: Использование функции ЗаполнитьЗначенияСвойств

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

Давайте проанализируем этот подход на практическом примере:


// Шаг 1. Создаем структуру и наполняем её нужными свойствами динамически
ДанныеДляЗаписи = Новый Структура;
ДанныеДляЗаписи.Вставить("Месяц", НачалоМесяца(ДеньПериода));
ДанныеДляЗаписи.Вставить("ФизическоеЛицо", ФизическоеЛицоИзБазы);

Для НомерДня = 1 По 31 Цикл
    КлючСвойства = "НеотработанДень" + НомерДня;
    ЗначениеСвойства = ПолучитьЗначениеНеотработанногоВремени(НомерДня);
    ДанныеДляЗаписи.Вставить(КлючСвойства, ЗначениеСвойства);
КонецЦикла;

// Шаг 2. Добавляем новую запись в набор и заполняем её свойства одной командой
НоваяЗапись = НаборЗаписей.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗапись, ДанныеДляЗаписи);

Преимущества данного метода: метод ЗаполнитьЗначенияСвойств работает чрезвычайно быстро и безопасно. Если какого-то свойства из структуры нет в объекте-приемнике (например, отсутствует колонка НеотработанДень31), платформа просто пропустит его заполнение, не вызывая критических ошибок и аварийной остановки выполнения кода.

Решение 4: Высокопроизводительная работа через Таблицу Значений и метод Загрузить()

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

Для оптимизации производительности мы рекомендуем использовать выгрузку структуры набора в ТаблицуЗначений, ее заполнение в оперативной памяти и последующую быструю загрузку обратно в набор.

Разберем данный алгоритм детально:


// Шаг 1. Получаем пустую таблицу значений с точной структурой нашего набора записей
ТаблицаДанных = НаборЗаписей.ВыгрузитьКолонки();

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

// Шаг 3. Загружаем подготовленную таблицу в набор записей регистра и записываем в БД
НаборЗаписей.Загрузить(ТаблицаДанных);
НаборЗаписей.Записать(Истина); // Запись с замещением существующих данных

Почему этот подход эффективнее?

← На главную