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