Почему возникает ошибка «Итератор для значения не определен» при обходе метаданных и как правильно обезличить справочник в 1С?

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

В процессе разработки на платформе 1С:Предприятие 8 программисты часто сталкиваются с необходимостью массовой обработки данных: очистки, модификации или обезличивания (деперсонализации). При написании универсальных механизмов, использующих метаданные, нередко возникает ошибка «Итератор для значения не определен». Проанализируем, почему это происходит, и разработаем корректный алгоритм для решения задачи на примере справочника «Работники».

Выясняем причину ошибки итератора

Ошибка «Итератор для значения не определен» означает, что встроенный язык 1С пытается выполнить цикл Для каждого ... Из ... Цикл применительно к объекту, который не является коллекцией. Рассмотрим фрагмент кода, вызвавший проблему у автора темы:


Для Каждого МетаданноеСправочника Из Метаданные.Справочники.Работники Цикл
    // Код обработки
КонецЦикла;

Разберем ситуацию: объект Метаданные.Справочники — это коллекция всех справочников в конфигурации. К ней можно применять цикл. Однако, обращаясь через точку к конкретному справочнику (Метаданные.Справочники.Работники), мы получаем конкретный объект метаданных типа ОбъектМетаданных: Справочник. Этот объект описывает структуру справочника «Работники» (его имя, синоним, состав реквизитов и табличных частей), но сам по себе он не является списком или массивом. Попытка «перебрать» описание одного справочника и приводит к ошибке итератора.

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

Корректная работа с метаданными и выборкой данных

Рассмотрим, как правильно организовать код, чтобы обезличить данные в конкретном справочнике. Мы не будем перебирать все справочники конфигурации, а сосредоточимся на «Работниках». Нам потребуется получить описание метаданных для доступа к реквизитам и запустить выборку самих элементов справочника для изменения данных.

Рассмотрим пошагово исправленный алгоритм:

  1. Получаем объект метаданных: Вместо цикла просто присваиваем переменной описание нашего справочника.
  2. Создаем выборку: Используем метод Выбрать() менеджера справочника.
  3. Обходим элементы: В цикле Пока Выборка.Следующий() получаем объект для редактирования.
  4. Обезличиваем данные: Проходим по реквизитам, определенным в метаданных, и меняем строковые значения.

Проанализируем пример кода, который реализует эту логику:


// 1. Получаем метаданные конкретного справочника
МетаданноеОбъекта = Метаданные.Справочники.Работники;
Счетчик = 1;

// 2. Создаем выборку всех элементов справочника
Выборка = Справочники.Работники.Выбрать();

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

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

    // 5. Отключаем проверки и записываем
    ОбъектСправочника.ОбменДанными.Загрузка = Истина;
    ОбъектСправочника.Записать();
    
    Счетчик = Счетчик + 1;
    
КонецЦикла;

Обработка табличных частей: исправляем логические ошибки

Особое внимание стоит уделить обработке табличных частей (ТЧ). В исходном коде автора была допущена критическая ошибка: внутри цикла по строкам табличной части попытка обращения к данным шла через Спровочник[Реквизит.Имя], что приводило к обращению к основному реквизиту объекта, а не к колонке таблицы. Кроме того, переменная Реквизит использовалась некорректно.

Разберем, как правильно обрабатывать ТЧ. Нам нужно организовать вложенный цикл: сначала по самим табличным частям, затем по строкам каждой ТЧ, и, если нужно универсально менять данные, — по реквизитам этих строк.

Посмотрим на пример правильной реализации:


// Внутри цикла по объектам справочника
Для Каждого МетаДанныеТЧ Из МетаданноеОбъекта.ТабличныеЧасти Цикл
    
    ТабличнаяЧасть = ОбъектСправочника[МетаДанныеТЧ.Имя];
    
    Для Каждого СтрокаТЧ Из ТабличнаяЧасть Цикл
        
        // Перебираем реквизиты (колонки) табличной части
        Для Каждого РеквизитТЧ Из МетаДанныеТЧ.Реквизиты Цикл
            
            // Если колонка строковая — обезличиваем её
            Если ТипЗнч(СтрокаТЧ[РеквизитТЧ.Имя]) = Тип("Строка") Тогда
                СтрокаТЧ[РеквизитТЧ.Имя] = "ТЧ_" + РеквизитТЧ.Имя + "_" + Счетчик;
            КонецЕсли;
            
        КонецЦикла;
        
    КонецЦикла;
    
КонецЦикла;

Использование такой структуры гарантирует, что программа «заглянет» в каждую ячейку каждой строки всех табличных частей справочника и заменит персональные данные на сгенерированные строки.

Оптимизация: использование ОбменДанными.Загрузка и Транзакции

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

1. Свойство ОбменДанными.Загрузка = Истина

Установка этого флага перед вызовом метода Записать() сообщает системе, что запись происходит в режиме «загрузки». Это отключает:

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

2. Группировка записей в транзакции

Каждая операция Записать() вне транзакции инициирует отдельную транзакцию на уровне SQL-сервера, что очень медленно. Для ускорения рекомендуется обрабатывать данные порциями. Рассмотрим, как применить метод НачатьТранзакцию():


НачатьТранзакцию();
Попытка
    // Цикл обработки 1000 элементов
    // ...
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    Сообщить("Ошибка при записи порции данных: " + ОписаниеОшибки());
КонецПопытки;

Оптимальный размер порции обычно составляет от 500 до 2000 элементов. Это позволяет значительно сократить время выполнения скрипта без чрезмерной нагрузки на лог транзакций базы данных.

Резюме по решению задачи

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

Следуя этим рекомендациям, вы сможете создавать надежные и быстрые инструменты для манипуляции данными в любой конфигурации 1С.

← На главную