К статье
Практический материал

Разработка и Конфигурация · 13 мин

Как в 1С перебрать реквизиты документа

Как в 1С перебрать все реквизиты документа через метаданные: цикл по Реквизиты, получение значений, работа с табличными частями, примеры кода и типичные ошибки.

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

Решение строится на обращении к метаданным объекта. У каждого документа есть коллекция Метаданные().Реквизиты, по которой можно пройти циклом и для каждого реквизита получить его имя, тип, синоним и текущее значение. Отдельно перебираются стандартные реквизиты (номер, дата, проведение) и табличные части.

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

Как устроен доступ к реквизитам через метаданные

Объект типа «ДокументОбъект» или «ДокументСсылка» хранит описание своей структуры в свойстве Метаданные(). Этот метод возвращает объект конфигурации «ОбъектМетаданных», у которого есть коллекции Реквизиты, ТабличныеЧасти и СтандартныеРеквизиты. Именно они используются для универсального перебора.

Каждый элемент коллекции Реквизиты — это объект «ОписаниеРеквизита» с полезными свойствами: Имя (идентификатор реквизита), Синоним (представление для пользователя), Тип (описание типов значения). Чтобы получить само значение, используется обращение по строковому имени: Документ[Реквизит.Имя].

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

Базовый пример: цикл по всем реквизитам документа

Минимальный рабочий код перебора выглядит так:

Для Каждого Реквизит Из Документ.Метаданные().Реквизиты Цикл

Значение = Документ[Реквизит.Имя];

Сообщить(Реквизит.Имя + " = " + Значение);

КонецЦикла;

Здесь Документ — переменная, содержащая ссылку или объект документа. Цикл пройдёт по всем реквизитам, определённым в конфигураторе для этого вида документов, и выведет имя и значение каждого. Порядок обхода соответствует порядку реквизитов в конфигураторе.

Если нужно вывести не техническое имя, а понятное пользователю представление, используйте свойство Синоним. А свойство Тип позволяет анализировать состав типов реквизита, например чтобы по-разному обрабатывать ссылочные и примитивные значения:

Для Каждого Реквизит Из Документ.Метаданные().Реквизиты Цикл

Значение = Документ[Реквизит.Имя];

Если ЗначениеЗаполнено(Значение) Тогда

Сообщить(Реквизит.Синоним + ": " + Значение

+ " (тип: " + Реквизит.Тип + ")");

КонецЕсли;

КонецЦикла;

Перебор стандартных реквизитов: номер, дата, проведение

Стандартные реквизиты (Номер, Дата, Проведен, ПометкаУдаления, Ссылка и другие) не входят в коллекцию Реквизиты. Для них существует отдельная коллекция СтандартныеРеквизиты, и перебирается она так же:

Для Каждого СтдРеквизит Из Документ.Метаданные().СтандартныеРеквизиты Цикл

Значение = Документ[СтдРеквизит.Имя];

Сообщить(СтдРеквизит.Имя + " = " + Значение);

КонецЦикла;

Этот приём полезен при построении универсальных выгрузок, когда в файл нужно включить служебные поля. Учтите, что состав стандартных реквизитов зависит от настроек документа: например, свойство «Проведен» присутствует только у проводимых документов, а длина и тип номера зависят от настроек нумерации.

Обращение к несуществующему стандартному реквизиту по имени вызовет исключение. Поэтому при универсальном коде безопаснее именно перебирать коллекцию СтандартныеРеквизиты, а не обращаться к полям напрямую.

Перебор табличных частей и их реквизитов

Табличные части перебираются через коллекцию Метаданные().ТабличныеЧасти. Для каждой табличной части доступна собственная коллекция Реквизиты, описывающая её колонки. Типовой код обхода всех строк всех табличных частей выглядит так:

Для Каждого ТабЧасть Из Документ.Метаданные().ТабличныеЧасти Цикл

Для Каждого СтрокаТЧ Из Документ[ТабЧасть.Имя] Цикл

Для Каждого РеквизитТЧ Из ТабЧасть.Реквизиты Цикл

Значение = СтрокаТЧ[РеквизитТЧ.Имя];

Сообщить(ТабЧасть.Имя + "." + РеквизитТЧ.Имя + " = " + Значение);

КонецЦикла;

КонецЦикла;

КонецЦикла;

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

Порядок полного обхода документа
  1. 1
    Получите ссылку или объект документа
  2. 2
    Переберите Метаданные().Реквизиты и прочитайте значения через Документ[Имя]
  3. 3
    Переберите Метаданные().СтандартныеРеквизиты при необходимости
  4. 4
    Переберите Метаданные().ТабличныеЧасти и внутри — строки и их реквизиты
  5. 5
    Проверьте результат на документе с заполненными и пустыми реквизитами

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

Обращение к реквизиту по строковому имени

Ключевой приём, на котором держится весь механизм перебора, — обращение к свойству объекта через квадратные скобки со строковым именем: Документ["Организация"]. Это позволяет работать с реквизитами, имена которых заранее неизвестны или приходят извне — из настроек, файла сопоставления, параметров обработки.

Тот же синтаксис применим к строкам табличных частей (СтрокаТЧ["Номенклатура"]), к записям регистров, к элементам справочников и к строкам результата запроса. Единообразие этого механизма делает универсальные обработки сравнительно простыми.

Главный риск — обращение к несуществующему имени. Если строка содержит опечатку или имя реквизита другого объекта, платформа выбросит исключение «Поле объекта не обнаружено». Поэтому при обращении по внешнему имени полезно сначала проверять существование реквизита:

Если Документ.Метаданные().Реквизиты.Найти("Комментарий") <> Неопределено Тогда

Значение = Документ["Комментарий"];

КонецЕсли;

Типичные ошибки
01
Обращение Документ.Реквизиты.Имя вместо Документ[Реквизит.Имя] — первая конструкция ищет реквизит с именем «Реквизиты» и падает с ошибкой
02
Перебор только коллекции Реквизиты и забытые стандартные реквизиты — Номер и Дата в выгрузку не попадают
03
Попытка записать значения через ссылку вместо объекта — нужно вызвать ПолучитьОбъект(), иначе запись невозможна
04
Обращение к реквизиту по строковому имени без проверки Найти() — исключение при любом расхождении имён после обновления конфигурации

Практический сценарий: универсальное копирование документа

Частая прикладная задача — скопировать документ целиком, не перечисляя реквизиты вручную. Перебор по метаданным решает её компактно: создаётся новый объект, и в него переносятся значения всех реквизитов и строк табличных частей.

НовыйДок = Документы.РеализацияТоваровУслуг.СоздатьДокумент();

Для Каждого Реквизит Из Источник.Метаданные().Реквизиты Цикл

НовыйДок[Реквизит.Имя] = Источник[Реквизит.Имя];

КонецЦикла;

Для Каждого ТабЧасть Из Источник.Метаданные().ТабличныеЧасти Цикл

Для Каждого СтрокаТЧ Из Источник[ТабЧасть.Имя] Цикл

НоваяСтрока = НовыйДок[ТабЧасть.Имя].Добавить();

Для Каждого РеквизитТЧ Из ТабЧасть.Реквизиты Цикл

НоваяСтрока[РеквизитТЧ.Имя] = СтрокаТЧ[РеквизитТЧ.Имя];

КонецЦикла;

КонецЦикла;

КонецЦикла;

НовыйДок.Записать();

Перед применением такого кода проверьте ограничения. Некоторые реквизиты могут быть недоступны для заполнения программно из-за логики обработчиков ОбработкаЗаполнения и ПриЗаписи: типовые документы при записи могут пересчитывать итоги, подставлять значения по умолчанию или проверять условия. Также часть реквизитов может иметь признак «Не редактировать» в форме — на программную запись это не влияет, но бизнес-логика может ожидать иные значения.

Для копирования внутри одного вида документов есть и более простая альтернатива — метод Скопировать() у объекта документа. Универсальный перебор оправдан, когда копировать нужно между разными видами документов с совпадающими именами реквизитов или когда часть полей требуется исключить по условию.

Проверка результата и ограничения подхода

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

Контрольные точки

Как проверить результат

Состав выведенных реквизитов
Норма

Совпадает со списком реквизитов в конфигураторе для этого вида документов

Тревожный признак

Часть реквизитов отсутствует или появляются лишние поля

Значения ссылочных реквизитов
Норма

Выводятся представления ссылок (организация, контрагент)

Тревожный признак

Ошибка обращения или пустые значения у заполненных полей

Табличные части
Норма

Количество обработанных строк совпадает с числом строк в форме документа

Тревожный признак

Строки пропущены или обработаны дважды

Запись изменений
Норма

После Записать() документ открывается с новыми значениями и проводится без ошибок

Тревожный признак

Исключения при записи, отказ проведения, потерянные движения

Учитывайте и границы механизма. Через Метаданные().Реквизиты доступны только реквизиты шапки, определённые в конфигурации. Общие реквизиты, применённые к документу, находятся в отдельной коллекции Метаданные.ОбщиеРеквизиты и в коллекцию документа не входят — их перебор требует отдельного кода с проверкой состава использования.

В управляемых формах на клиенте доступ к Метаданные() у данных формы ограничен: для объекта типа «ДанныеФормыСтруктура» перебор делается не через метаданные, а через реквизиты формы или серверный вызов. Если обработка выполняется в управляемом приложении, размещайте код перебора в серверной процедуре с директивой &НаСервере или &НаСервереБезКонтекста.

Если перебор реквизитов нужен только для вывода в табличный документ или журнал, удобно сначала собрать соответствие «имя — значение», а потом работать с ним. Это упрощает отладку: структуру с результатом легко передать между процедурами и сериализовать в JSON через ЗаписьJSON.
ПЗ
Практическое замечание

Частые вопросы

Как перебрать реквизиты всех документов одного вида?

Сначала выполните запрос или выборку через Документы.ИмяДокумента.Выбрать(), затем для каждой ссылки выполните перебор по Метаданные().Реквизиты. Метаданные одинаковы для всех документов вида, поэтому описание коллекции достаточно получить один раз до цикла — это ускорит обработку.

Почему цикл не выводит реквизиты Номер и Дата?

Это стандартные реквизиты, они не входят в коллекцию Реквизиты. Переберите отдельно Метаданные().СтандартныеРеквизиты или обратитесь к нужным полям напрямую: Документ.Номер, Документ.Дата.

Как получить значение реквизита, если его имя хранится в переменной?

Используйте обращение через квадратные скобки: Документ[ИмяПеременной]. Перед обращением желательно проверить существование реквизита через Метаданные().Реквизиты.Найти(ИмяПеременной), чтобы избежать исключения «Поле объекта не обнаружено».

Можно ли перебрать реквизиты документа в управляемой форме на клиенте?

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

Как перебрать только заполненные реквизиты документа?

Внутри цикла по Реквизиты добавьте проверку ЗначениеЗаполнено(Документ[Реквизит.Имя]) и пропускайте пустые. Учтите, что функция считает незаполненными пустые ссылки, пустые строки, ноль и Неопределено — для числовых реквизитов со значением 0 это может быть нежелательно, тогда проверяйте тип реквизита отдельно.

Попадут ли в перебор общие реквизиты конфигурации?

Нет. Общие реквизиты описываются в ветке Метаданные.ОбщиеРеквизиты и не входят в коллекцию Реквизиты конкретного документа. Для их обхода перебирайте общие реквизиты отдельно и проверяйте через свойство Состав, применён ли реквизит к данному виду документов.

Итог

Что важно запомнить

Перебор реквизитов документа строится на цикле по Документ.Метаданные().Реквизиты с чтением значений через Документ[Реквизит.Имя]. Стандартные реквизиты и табличные части перебираются по отдельным коллекциям. Запись возможна только у объекта, а массовые изменения безопасно проверять сначала на копии базы.