Не считывается дополнительный реквизит в 1С: как найти ошибку и получить значение?

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

Часто при доработке печатных форм в 1С:Бухгалтерия 3.0 возникает задача вывести информацию из дополнительных реквизитов, например, ОКТМО контрагента в счете на оплату. Однако даже простой, на первый взгляд, код может не работать, возвращая пустое значение. Разберем по шагам, почему это происходит и как правильно получить данные из дополнительного реквизита.

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

Способ 1: Прямой поиск в табличной части (не сработал изначально)

Первая мысль разработчика — найти свойство по наименованию, а затем найти его значение в табличной части ДополнительныеРеквизиты объекта. Посмотрим на пример такого кода.


ОКТМОпок = "";
// Находим ссылку на само свойство в плане видов характеристик
СвойствоОКТМО = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ОКТМО");

// Проверяем, что свойство с таким наименованием вообще существует в системе
Если СвойствоОКТМО <> ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.ПустаяСсылка Тогда
    
    // Получаем ссылку на контрагента из данных печатной формы
    КонтрагентСсылка = СведенияОДокументе.Покупатель.Ссылка;
    
    // Ищем строку с нужным свойством в табличной части контрагента
    НайденнаяСтрока = КонтрагентСсылка.ДополнительныеРеквизиты.Найти(СвойствоОКТМО, "Свойство");
    
    Если НайденнаяСтрока <> Неопределено Тогда
        ОКТМОпок = СокрЛП(НайденнаяСтрока.Значение);
    КонецЕсли;
КонецЕсли;

Если ОКТМОпок = "" Тогда
    Сообщить("ОКТМО покупателя не установлено");
Иначе
    ПредставлениеПокупателя = ПредставлениеПокупателя + ", ОКТМО " + СокрЛП(ОКТМОпок);
КонецЕсли;

Аналогичный подход — перебор всех строк табличной части в цикле. Он более громоздкий, но выполняет ту же задачу.


ОКТМОпок = "";
СвойствоОКТМО = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ОКТМО");

Если НЕ СвойствоОКТМО.Пустая() Тогда
    КонтрагентСсылка = СведенияОДокументе.Покупатель.Ссылка;
    Для Каждого СтрокаРеквизита Из КонтрагентСсылка.ДополнительныеРеквизиты Цикл
        Если СтрокаРеквизита.Свойство = СвойствоОКТМО Тогда
            ОКТМОпок = СокрЛП(СтрокаРеквизита.Значение);
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

В рассматриваемой проблеме оба этих варианта не давали результата. Это первый признак того, что проблема не в коде, а в данных. Чаще всего это означает, что ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ОКТМО") находит не тот реквизит, который мы ожидаем. Но прежде чем копать в эту сторону, рассмотрим более правильный и современный способ получения значений.

Способ 2: Использование стандартной функции из БСП

В типовых конфигурациях на базе Библиотеки стандартных подсистем (БСП) существует специальный механизм для работы с дополнительными реквизитами. Он находится в общем модуле УправлениеСвойствами. Рекомендуемый способ — использовать его функцию ЗначениеСвойства.

Синтаксис функции: УправлениеСвойствами.ЗначениеСвойства(Объект, Свойство)

Попробуем применить этот метод:


КонтрагентСсылка = СведенияОДокументе.Покупатель.Ссылка;

// Вариант А: передаем ссылку на свойство
СвойствоОКТМО = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ОКТМО");
ОКТМОпок = УправлениеСвойствами.ЗначениеСвойства(КонтрагентСсылка, СвойствоОКТМО);

// Вариант Б: передаем наименование реквизита (не рекомендуется, может быть неоднозначным)
ОКТМОпок = УправлениеСвойствами.ЗначениеСвойства(КонтрагентСсылка, "ОКТМО");

К сожалению, в исходной ситуации и этот способ не сработал. Но он навел на интересную деталь. У каждого дополнительного реквизита, кроме видимого пользователю "Наименования", есть и внутреннее "Имя" для разработчика. Оно уникально и генерируется автоматически, например ОКТМО_a54535befac54423.

Это имя можно увидеть и изменить, открыв форму дополнительного реквизита в режиме конфигуратора или в пользовательском режиме (Администрирование -> Общие настройки -> Дополнительные реквизиты и сведения). В нижней части формы будет поле "Имя".

Если передать это уникальное имя в функцию, она сработает даже при наличии проблем с наименованием:


КонтрагентСсылка = СведенияОДокументе.Покупатель.Ссылка;
// Используем уникальное имя для разработчика
ОКТМОпок = УправлениеСвойствами.ЗначениеСвойства(КонтрагентСсылка, "ОКТМО_a54535befac54423");

Важный момент: этот подход решает проблему "здесь и сейчас", но создает трудности с переносимостью. Если вы перенесете эту внешнюю печатную форму в другую базу, сгенерированное имя реквизита там будет другим, и код перестанет работать. Чтобы избежать этого, рекомендуется при создании реквизита вручную задавать осмысленное и одинаковое "Имя" во всех базах, где планируется использовать разработку (например, просто "ОКТМОКонтрагента").

Истинная причина проблемы: дубликат неиспользуемого реквизита

Несмотря на то, что способ с уникальным именем сработал, он не объяснил, почему не работали первоначальные варианты, которые должны были быть корректными. Финальная разгадка оказалась в "мусоре" в данных.

Проблема заключалась в том, что в базе существовало два дополнительных реквизита с одинаковым наименованием "ОКТМО". Один из них был рабочим и видимым, а второй когда-то был создан, помечен как неиспользуемый и скрыт из обычного списка.

Функция НайтиПоНаименованию("ОКТМО") непредсказуемо возвращала ссылку на первый попавшийся реквизит, которым оказывался тот самый, старый и неиспользуемый. Естественно, у контрагентов не было значения для этого "фантомного" реквизита, поэтому все попытки получить его проваливались.

Чтобы найти и устранить такие дубли, выполним следующие шаги:

  1. Перейдите в раздел "Администрирование" – "Общие настройки".
  2. Откройте ссылку "Дополнительные реквизиты и сведения".
  3. Найдите и выберите в левой части окна нужный объект (например, справочник "Контрагенты").
  4. В правой части окна со списком реквизитов нажмите кнопку "Еще" и выберите пункт "Показывать неиспользуемые".
  5. Внимательно просмотрите обновленный список. Если вы увидите реквизиты-дубликаты по наименованию, удалите тот, который является устаревшим и неиспользуемым (помечен серым цветом).

После удаления "фантомного" дубликата все ранее рассмотренные способы получения значения начинают работать корректно!

Итог: рабочие варианты кода после исправления данных

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

Рекомендуемый вариант:


ОКТМОпок = "";
КонтрагентСсылка = СведенияОДокументе.Покупатель.Ссылка;
// Находим свойство по наименованию (теперь оно находится однозначно)
СвойствоОКТМО = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ОКТМО");

// Получаем значение через стандартную функцию
ОКТМОпок = СокрЛП(УправлениеСвойствами.ЗначениеСвойства(КонтрагентСсылка, СвойствоОКТМО));

Альтернативный вариант (перебор в цикле):


ОКТМОпок = "";
СвойствоОКТМО = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("ОКТМО");

Если НЕ СвойствоОКТМО.Пустая() Тогда
    КонтрагентСсылка = СведенияОДокументе.Покупатель.Ссылка;
    Для Каждого СтрокаРеквизита Из КонтрагентСсылка.ДополнительныеРеквизиты Цикл
        Если СтрокаРеквизита.Свойство = СвойствоОКТМО Тогда
            ОКТМОпок = СокрЛП(СтрокаРеквизита.Значение);
            Прервать;
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

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

← На главную