Разработка и Конфигурация · 12 мин
Как обойти реквизиты объекта 1С: практическое руководство
Как программно обойти все реквизиты объекта 1С через метаданные: перебор реквизитов справочника, документа, табличных частей, примеры кода и типичные ошибки.
Обход реквизитов объекта через метаданные — стандартный приём во встроенном языке 1С, когда нужно обработать все поля объекта без жёсткого перечисления их имён в коде. Такой подход используют при универсальном копировании объектов, сравнении данных, заполнении по шаблону, выгрузке в произвольные форматы и написании процедур печати.
Основа механизма — коллекция Метаданные и свойство Реквизиты у объекта метаданных. Цикл по этой коллекции даёт доступ к имени, синониму, типу и другим характеристикам каждого реквизита, а значение читается из самого объекта по имени через квадратные скобки: Объект[Реквизит.Имя].
Ниже разобраны конкретные сценарии: обход реквизитов шапки справочника и документа, работа с табличными частями, учёт стандартных реквизитов, проверка заполненности и типичные ошибки, которые приводят к падению кода или неверным данным.
Как устроен механизм обхода реквизитов
Каждый объект конфигурации (справочник, документ, план видов характеристик и т.д.) имеет метод Метаданные(), который возвращает описание его структуры. У этого описания есть коллекции: Реквизиты — явно добавленные разработчиком поля, СтандартныеРеквизиты — служебные поля вроде ссылки, кода, наименования, даты и номера, ТабличныеЧасти — списки табличных частей.
Элемент коллекции Реквизиты — это объект ОбъектМетаданныхРеквизит со свойствами Имя, Синоним, Тип, Обязательный, ПроверкаЗаполнения и другими. Имя реквизита используется как индекс для доступа к значению: Объект[Реквизит.Имя] эквивалентно прямому обращению Объект.Контрагент, но работает для любого поля без знания его имени заранее.
Важно понимать разницу между объектом и ссылкой. Если у вас есть только ссылка, метод Метаданные() тоже доступен, но чтение значений реквизитов через ссылку выполняется точечно (Ссылка[Имя]) и при массовом обходе порождает отдельные запросы к базе. Для пакетной обработки лучше получить объект методом ПолучитьОбъект() или выбрать данные запросом.
Обход реквизитов шапки: базовый код
Минимальный рабочий пример — перебор всех реквизитов документа с выводом имени и значения:
Для Каждого Реквизит Из Объект.Метаданные().Реквизиты Цикл
Значение = Объект[Реквизит.Имя];
Сообщить(Реквизит.Имя + " = " + Значение);
КонецЦикла;
Тот же цикл решает практическую задачу копирования значений между однотипными объектами:
Для Каждого Реквизит Из Источник.Метаданные().Реквизиты Цикл
Приемник[Реквизит.Имя] = Источник[Реквизит.Имя];
КонецЦикла;
При копировании между объектами разных видов (например, из «Заказа покупателя» в «Реализацию») прямой обход опасен: состав реквизитов не совпадает, и обращение к несуществующему полю вызовет ошибку. Перед записью проверяйте наличие реквизита у приёмника через Найти в коллекции метаданных или используйте структуру соответствия полей.
МДПриемник = Приемник.Метаданные();
Для Каждого Реквизит Из Источник.Метаданные().Реквизиты Цикл
Если МДПриемник.Реквизиты.Найти(Реквизит.Имя) <> Неопределено Тогда
Приемник[Реквизит.Имя] = Источник[Реквизит.Имя];
КонецЕсли;
КонецЦикла;Обход табличных частей
Табличные части не входят в коллекцию Реквизиты — их нужно перебирать отдельно через Метаданные().ТабличныеЧасти. Для каждой табличной части доступна вложенная коллекция Реквизиты, описывающая её колонки.
Для Каждого ТЧ Из Объект.Метаданные().ТабличныеЧасти Цикл
Для Каждого Строка Из Объект[ТЧ.Имя] Цикл
Для Каждого Колонка Из ТЧ.Реквизиты Цикл
Значение = Строка[Колонка.Имя];
КонецЦикла;
КонецЦикла;
КонецЦикла;
Такой тройной цикл удобен для универсальной очистки, проверки заполнения или выгрузки всех табличных частей объекта. Если нужна только одна конкретная табличная часть, универсальный обход избыточен — обращайтесь к ней по имени напрямую, это быстрее и читаемее.
При копировании табличных частей между объектами помните, что построчное присваивание значений не переносит данные автоматически: сначала очистите табличную часть приёмника (Приемник.Товары.Очистить()), затем добавляйте строки через Добавить() и заполняйте реквизиты строки в цикле по колонкам.
Стандартные реквизиты и системные поля
Коллекция Реквизиты не включает стандартные поля: ссылку, код, наименование, дату, номер, пометку удаления, проведение. Если задача требует их обработки (например, универсальная проверка «заполнено ли наименование у любого справочника»), обходите коллекцию СтандартныеРеквизиты:
Для Каждого Реквизит Из Объект.Метаданные().СтандартныеРеквизиты Цикл
Значение = Объект[Реквизит.Имя];
КонецЦикла;
Учтите ограничения: часть стандартных реквизитов недоступна для записи (ссылка, пометка удаления у полученного из базы объекта меняется только методом УстановитьПометкуУдаления). Попытка присвоить им значение в универсальном цикле копирования приведёт к ошибке — исключайте системные поля из обработки или оборачивайте запись в проверку.
Практические сценарии применения
Универсальная проверка заполнения
Обход реквизитов позволяет проверить обязательные поля без жёсткого списка. Свойство ПроверкаЗаполнения метаданных показывает, требует ли реквизит заполнения, а функция ЗначениеЗаполнено() — фактическое состояние:
Для Каждого Реквизит Из Объект.Метаданные().Реквизиты Цикл
Если Реквизит.ПроверкаЗаполнения = ПроверкаЗаполнения.ВыдаватьОшибку
И Не ЗначениеЗаполнено(Объект[Реквизит.Имя]) Тогда
Сообщить("Не заполнен реквизит: " + Реквизит.Синоним);
КонецЕсли;
КонецЦикла;
Сравнение двух версий объекта
Для аудита изменений обходят реквизиты старой и новой версии и сравнивают значения. Сравнивайте через ЗначениеВСтрокуВнутр или поэлементно, если типы простые: прямое сравнение ссылок разных сеансов корректно, а вот табличные части сравнивайте построчно — оператор «=» для них не работает.
Выгрузка в произвольную структуру
Обход удобен для переноса данных в структуру или соответствие перед сериализацией: ключ — имя реквизита, значение — его содержимое. Для ссылочных полей сохраняйте представление или уникальный идентификатор, иначе при выгрузке получите нечитаемые COM-объекты.
- 1Получите метаданные источника и приёмника через Метаданные()
- 2Переберите коллекцию Реквизиты источника
- 3Проверьте наличие одноимённого реквизита у приёмника через Найти()
- 4Присвойте значение: Приемник[Имя] = Источник[Имя]
- 5Отдельно скопируйте табличные части построчно
- 6Запишите приёмник и проверьте результат в форме объекта
Производительность и ограничения
Обход через метаданные сам по себе быстр, но контекст применения решает всё. Чтение значений через ссылку (Ссылка[Имя]) в цикле по тысяче объектов означает тысячу обращений к базе. Для массовой обработки используйте запрос с выборкой нужных полей или выгрузку в таблицу значений — метаданные при этом применяют только для формирования текста запроса.
Второе ограничение — права доступа. Если у пользователя нет права просмотра отдельного реквизита (настройка на уровне RLS или ролей), обращение к нему в цикле может вернуть пустое значение или вызвать ошибку в зависимости от конфигурации. Универсальные процедуры, работающие под разными пользователями, должны это учитывать.
Третий момент — управляемые и обычные формы. Код обхода одинаково работает на сервере в обоих режимах, но на клиенте в тонком клиенте доступ к метаданным ограничен: Метаданные() у объекта формы недоступен. Универсальные процедуры обхода размещайте в серверных методах.
Типичные ошибки при обходе реквизитов
Первая и самая частая ошибка — обращение к реквизиту приёмника без проверки его существования. Конфигурации развиваются, поля переименовывают и удаляют, и жёсткий цикл ломается на первом расхождении. Проверка через Найти() занимает одну строку и снимает проблему.
Вторая ошибка — смешение объекта и ссылки в одном коде. Метод ПолучитьОбъект() забывают вызвать, и попытка записи Ссылка[Имя] = Значение падает, потому что ссылка доступна только для чтения. Обратная ситуация — вызов ПолучитьОбъект() там, где достаточно чтения через ссылку, — приводит к лишней блокировке объекта в базе.
Третья ошибка — игнорирование типов при сравнении или копировании. Реквизит составного типа может хранить Неопределено, NULL-подобные значения или ссылки разных видов. При универсальной обработке проверяйте тип значения через ТипЗнч(), если дальнейшая логика зависит от него.
Как проверить результат
После отработки универсальной процедуры проверку выполняют на контрольном примере: возьмите объект с максимально заполненными полями, включая табличные части, и сравните состояние до и после обработки. Для копирования — откройте приёмник в форме и визуально сверьте ключевые поля, для проверки заполнения — убедитесь, что сообщения выводятся именно по пустым обязательным реквизитам.
Полезный приём — отладочный вывод: в цикле обхода временно выводите имя реквизита, его тип и значение. Это сразу показывает, какие поля реально попали в обработку, и выявляет пропущенные табличные части или стандартные реквизиты.
Как проверить результат
В отладочном выводе присутствуют все ожидаемые реквизиты и табличные части
Часть полей отсутствует — вероятно, не обойдены табличные части или стандартные реквизиты
Поля совпадают с источником, табличные части перенесены построчно
Поля пустые или с ошибками — нет проверки наличия реквизита у приёмника
Процедура отрабатывает без ошибок, пустые значения обработаны корректно
Падение на значениях Неопределено или составных типах
Что важно запомнить
Обход реквизитов строится на цикле по Объект.Метаданные().Реквизиты с чтением значений по имени. Табличные части и стандартные реквизиты — отдельные коллекции. Запись выполняйте только в объект (не ссылку), проверяйте наличие реквизита у приёмника и не трогайте системные поля. Массовую обработку делайте запросом, а не циклом по ссылкам.
Частые вопросы
Как обойти реквизиты, если есть только ссылка, а не объект?
Метод Метаданные() доступен и у ссылки, поэтому цикл по коллекции реквизитов работает. Значения читаются как Ссылка[Реквизит.Имя], но каждое обращение — это обращение к базе. Для разового чтения это допустимо, для массового — используйте запрос. Записывать значения через ссылку нельзя: нужен ПолучитьОбъект().
Почему в цикле не видно табличных частей документа?
Табличные части не входят в коллекцию Реквизиты. Они перебираются через Метаданные().ТабличныеЧасти, а колонки каждой из них — через вложенную коллекцию Реквизиты табличной части. Для полного обхода объекта нужны оба цикла.
Как проверить, существует ли реквизит с нужным именем у объекта?
Используйте метод Найти у коллекции метаданных: Объект.Метаданные().Реквизиты.Найти("ИмяРеквизита"). Если возвращено Неопределено — реквизита нет. Это стандартная защита универсальных процедур от расхождений структуры между объектами и конфигурациями.
Можно ли обойти реквизиты на клиенте в управляемом приложении?
Нет, в тонком и веб-клиенте доступ к метаданным объекта на клиенте ограничен. Процедуры обхода размещайте в серверных методах общего модуля или модуля объекта, а на клиент передавайте уже готовый результат — например, структуру с именами и значениями полей.
Как скопировать табличную часть из одного документа в другой в цикле?
Очистите табличную часть приёмника методом Очистить(), затем в цикле по строкам источника добавляйте строки методом Добавить() и заполняйте колонки вложенным циклом по Реквизиты табличной части с проверкой наличия колонки у приёмника. Метод Загрузить() с выгрузкой в таблицу значений подходит, только если структуры колонок совпадают.
Почему при сравнении двух объектов в цикле табличные части всегда «разные»?
Оператор сравнения не работает для табличных частей как целых — их нужно сравнивать построчно и поколоночно вложенными циклами, либо выгрузить обе в таблицы значений и сравнить их содержимое. Также учитывайте порядок строк: одинаковые данные в разном порядке формально дают разные таблицы.