Как проверить наличие реквизита в документе в 1С:Предприятие 7.7?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При разработке универсальных отчетов, обработок или выгрузок в среде 1С:Предприятие 7.7 разработчики часто сталкиваются с необходимостью динамического анализа структуры метаданных объектов. Типичная ситуация: в цикле перебираются документы различных видов (например, «Реализация», «Поступление», «Счет»), и для каждого из них нужно получить значение определенного реквизита, например, Сумма или Контрагент. Если у какого-то вида документа данный реквизит отсутствует, платформа немедленно прерывает выполнение программы с критической ошибкой Поле агрегатного объекта не обнаружено.

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

Выясняем причину: почему код «из 1С 8.х» не работает в 1С 7.7?

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


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

При компиляции такого фрагмента в 1С 7.7 мы получим сразу две ошибки. Выясним их причины:

  1. Переменная не определена (Неопределено). В платформе 1С 7.7 отсутствует тип данных Неопределено и соответствующая системная константа. Для проверки переменных на пустоту или незаполненность в этой версии используется встроенная функция ПустоеЗначение().
  2. Различия в модели метаданных. Метод Метаданные() у прикладного объекта в 1С 7.7 возвращает объект метаданных верхнего уровня, но его структура, свойства и методы кардинально отличаются от объектной модели 1С 8.х. Поиск элементов по идентификатору через метод Найти() здесь просто не реализован.

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

Способ №1: Использование конструкции «Попытка — Исключение» (Быстрый метод)

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

Проанализируем пример реализации такого подхода:


СуммаДокумента = 0;
Попытка
    // Пытаемся напрямую прочитать значение реквизита
    СуммаДокумента = Док.Сумма;
Исключение
    // Если реквизита "Сумма" в документе нет, управление передается сюда
    СуммаДокумента = 0;
КонецПопытки;

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

Способ №2: Динамическое обращение через «ПолучитьАтрибут»

В платформе 1С 7.7 для всех прикладных объектов (документов, справочников) существует штатный метод ПолучитьАтрибут(). Он принимает в качестве параметра строковое имя реквизита и возвращает его значение. В сочетании с конструкцией перехвата ошибок мы получаем очень гибкое решение:


ИмяРеквизита = "Сумма";
ЗначениеРеквизита = 0;
Попытка
    ЗначениеРеквизита = Док.ПолучитьАтрибут(ИмяРеквизита);
Исключение
    // Сюда мы попадем, если реквизита с таким именем нет в объекте
    ЗначениеРеквизита = 0;
КонецПопытки;

Такой подход незаменим, когда имя анализируемого реквизита заранее неизвестно и передается в алгоритм в виде переменной (например, при настройке универсального отбора пользователем).

Способ №3: Проверка существования реквизита через метаданные конфигурации

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

  1. Общие реквизиты документов (задаются для всех документов информационной базы сразу).
  2. Реквизиты шапки конкретного вида документа.
  3. Реквизиты табличной части конкретного вида документа.

Для проверки существования реквизита в метаданных используется специальный метод Выбран(), который применяется к соответствующему объекту метаданных. Если реквизит существует, метод возвращает 1 (Истина), если нет — 0 (Ложь).

Разберем универсальную функцию, которую можно разместить в глобальном модуле конфигурации или в модуле вашего отчета:


// Функция проверяет, есть ли у документа указанного вида конкретный реквизит
// Возвращает: 1 - если реквизит найден, 0 - если реквизит отсутствует
Функция глЕстьРеквизитДокумента(ИмяРеквизита, ВидДокумента) Экспорт
    
    Если (ПустоеЗначение(ВидДокумента) = 1) ИЛИ (ПустоеЗначение(ИмяРеквизита) = 1) Тогда
        Возврат 0;
    КонецЕсли;
    
    // 1. Проверяем наличие в общих реквизитах документов
    Если Метаданные.ОбщийРеквизитДокумента(ИмяРеквизита).Выбран() = 1 Тогда
        Возврат 1;
        
    // 2. Проверяем наличие в реквизитах шапки конкретного вида документа
    ИначеЕсли Метаданные.Документ(ВидДокумента).РеквизитШапки(ИмяРеквизита).Выбран() = 1 Тогда
        Возврат 1;
        
    // 3. Проверяем наличие в реквизитах многострочной (табличной) части
    ИначеЕсли Метаданные.Документ(ВидДокумента).РеквизитТабличнойЧасти(ИмяРеквизита).Выбран() = 1 Тогда
        Возврат 1;
    КонецЕсли;
    
    // Реквизит не найден ни в одной из областей
    Возврат 0;
    
КонецФункции

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


ВидДок = Док.Вид(); // Получаем строковый вид текущего документа
Если глЕстьРеквизитДокумента("Сумма", ВидДок) = 1 Тогда
    ТЗ.Сумма = Док.Сумма;
Иначе
    ТЗ.Сумма = 0; // Или обрабатываем ситуацию отсутствия реквизита иначе
КонецЕсли;

Особый случай: Обработка реквизитов табличной части

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

Если нам нужно посчитать общую сумму по документу, у которого реквизит Сумма расположен в табличной части, мы должны действовать по следующему алгоритму:


СуммаДок = 0;
ВидДок = Док.Вид();

// Проверяем, есть ли реквизит в табличной части
Если Метаданные.Документ(ВидДок).РеквизитТабличнойЧасти("Сумма").Выбран() = 1 Тогда
    
    // Проверяем, включен ли итог по этой колонке в метаданных для быстрого получения значения
    Если Метаданные.Документ(ВидДок).РеквизитТабличнойЧасти("Сумма").ИтогПоКолонке = 1 Тогда
        СуммаДок = Док.Итог("Сумма");
    Иначе
        // Если итог по колонке не рассчитывается автоматически, обходим строки вручную
        Док.ВыбратьСтроки();
        Пока Док.ПолучитьСтроку() = 1 Цикл
            СуммаДок = СуммаДок + Док.Сумма;
        КонецЦикла;
    КонецЕсли;
    
ИначеЕсли Метаданные.Документ(ВидДок).РеквизитШапки("Сумма").Выбран() = 1 Тогда
    // Если реквизит находится в шапке
    СуммаДок = Док.Сумма;
КонецЕсли;

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

Способ №4: Секретный «хак» платформы 7.7 — функция «Шаблон»

Опытные разработчики 1С 7.7 часто применяли недокументированную, но очень полезную особенность работы системной функции Шаблон(). Эта функция динамически вычисляет выражения, переданные в квадратных скобках внутри строкового параметра. Главное преимущество этого метода заключается в том, что при обращении к несуществующему реквизиту платформа не выдает критическую ошибку, а просто возвращает пустую строку.

Посмотрим, как выглядит этот лаконичный прием на практике:


// Если реквизита "Сумма" нет, выражение вернет пустую строку без вызова системной ошибки
СуммаСтрокой = Шаблон("[Док.Сумма]"); 

Если ПустаяСтрока(СуммаСтрокой) = 1 Тогда
    Сумма = 0;
Иначе
    Сумма = Число(СуммаСтрокой);
КонецЕсли;

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


ЗначениеОбъекта = ЗначениеИзСтрокиВнутр(Шаблон("[ЗначениеВСтрокуВнутр(Док.Контрагент)]"));

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

Рекомендации по выбору метода

Выбор конкретного решения зависит от архитектуры вашего отчета и требований к быстродействию:

← На главную