Как правильно проверить составной тип документа и заполнить реквизит строки в 1С?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

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

Анализ проблемы: ошибки логики и потеря контекста строки

Проанализируем типовую ситуацию, с которой столкнулся разработчик. В обработчике события ПриИзменении для поля табличного поля был написан следующий фрагмент:


ТекущаяСтрока = ЭлементыФормы.Отражение.ТекущаяСтрока;
Если Не ЗначениеЗаполнено(ТекущаяСтрока.Источник) Или Не ТипЗнч(ТекущаяСтрока.Источник) = Тип("ДокументСсылка.СметаЗатрат") Тогда 
    Возврат; 
КонецЕсли;

Если Не ЗначениеЗаполнено(ТекущаяСтрока.ЦФО) Или Не ТекущаяСтрока.ЦФО = ТекущаяСтрока.Источник.ЦФО Тогда 
    ТекущаяСтрока.ЦФО = ТекущаяСтрока.Источник.ЦФО; 
КонецЕсли;

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

При выборе документа с типом ДокументСсылка.АктВыполненныхСметЗатрат автозаполнение реквизита не срабатывало, а точка останова в блоке обработки акта даже не активизировалась. Выясним причину такого поведения:

  1. Нарушение логики условий отсечения (ранний возврат). Первое условие проверяло: если тип не равен смете затрат, происходит Возврат. Когда пользователь выбирает акт, условие ТипЗнч(...) = Тип("ДокументСсылка.СметаЗатрат") дает значение Ложь, а с учетом отрицания Не превращается в Истина. В результате процедура немедленно завершает работу, а управление до проверки акта вообще не доходит.
  2. Исключение «Поле объекта не обнаружено». При попытке исправить условие и сразу обратиться к ТекущаяСтрока.Источник платформа выдает критическую ошибку. Это происходит в тех случаях, когда переменная ТекущаяСтрока не инициализирована (равна Неопределено) или в табличном поле свойство ТекущаяСтрока возвращает индекс/идентификатор вместо строки данных. В обычном приложении для надежного доступа к полям строки таблицы следует использовать свойство ТекущиеДанные либо выполнять проверку на Неопределено.

Решение 1. Прямое позитивное условие (наиболее понятный подход)

Вместо множественных условий с отрицаниями и операторами Возврат намного проще и нагляднее проверить допустимость данных в одном позитивном блоке Если ... Тогда. Рассмотрим пример:


ДанныеСтроки = ЭлементыФормы.Отражение.ТекущиеДанные;

Если ДанныеСтроки <> Неопределено И ЗначениеЗаполнено(ДанныеСтроки.Источник) Тогда
    ТипИсточника = ТипЗнч(ДанныеСтроки.Источник);
    
    Если ТипИсточника = Тип("ДокументСсылка.СметаЗатрат") 
       Или ТипИсточника = Тип("ДокументСсылка.АктВыполненныхСметЗатрат") Тогда
        
        ДанныеСтроки.ЦФО = ДанныеСтроки.Источник.ЦФО;
    КонецЕсли;
КонецЕсли;

Разберем преимущества этого кода:

  1. Мы сначала защищаемся от ошибки отсутствия строки проверкой ДанныеСтроки <> Неопределено.
  2. Значение типа вычисляется один раз в локальную переменную ТипИсточника, что ускоряет выполнение и улучшает читаемость.
  3. Условие не содержит двойных отрицаний и прямо выражает бизнес-логику: если выбран один из разрешенных типов, заполняем реквизит ЦФО.

Решение 2. Корректный паттерн с досрочным выходом (Guard Clause)

Если в процедуре после проверки типов выполняется большой объем вычислений, часто применяют паттерн досрочного выхода из процедуры (ранний Возврат). Чтобы объединить несколько неподходящих типов по законам логики (законам де Моргана), операцию отрицания равенства нужно связать оператором И, а не ИЛИ. Посмотрим на реализацию:


ДанныеСтроки = ЭлементыФормы.Отражение.ТекущиеДанные;

// Проверяем наличие строки и заполненность источника
Если ДанныеСтроки = Неопределено Или Не ЗначениеЗаполнено(ДанныеСтроки.Источник) Тогда
    Возврат;
КонецЕсли;

ТипИсточника = ТипЗнч(ДанныеСтроки.Источник);

// Если тип не является ни сметной, ни актом — прерываем выполнение
Если ТипИсточника <> Тип("ДокументСсылка.СметаЗатрат") 
   И ТипИсточника <> Тип("ДокументСсылка.АктВыполненныхСметЗатрат") Тогда
    Возврат;
КонецЕсли;

// Основное действие: перенос реквизита
ДанныеСтроки.ЦФО = ДанныеСтроки.Источник.ЦФО;

Обратим внимание: скобки и правильная связка условий ТипИсточника <> Тип1 И ТипИсточника <> Тип2 гарантируют, что процедура прервется только тогда, когда документ не совпал ни с одним из разрешенных вариантов.

Решение 3. Использование массива допустимых типов

Когда количество типов документов превышает два или три, перечисление конструкций с операторами Или загромождает код. В таком случае список целевых типов удобно вынести в коллекцию. Рассмотрим пример с использованием объекта Массив:


ДанныеСтроки = ЭлементыФормы.Отражение.ТекущиеДанные;
Если ДанныеСтроки = Неопределено Или Не ЗначениеЗаполнено(ДанныеСтроки.Источник) Тогда
    Возврат;
КонецЕсли;

ДопустимыеТипы = Новый Массив;
ДопустимыеТипы.Добавить(Тип("ДокументСсылка.СметаЗатрат"));
ДопустимыеТипы.Добавить(Тип("ДокументСсылка.АктВыполненныхСметЗатрат"));

Если ДопустимыеТипы.Найти(ТипЗнч(ДанныеСтроки.Источник)) <> Неопределено Тогда
    ДанныеСтроки.ЦФО = ДанныеСтроки.Источник.ЦФО;
КонецЕсли;

Такой подход делает код легко расширяемым: для поддержки нового документа достаточно добавить еще один элемент в массив без изменения логических выражений.

Решение 4. Универсальная проверка через метаданные

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


ДанныеСтроки = ЭлементыФормы.Отражение.ТекущиеДанные;
Если ДанныеСтроки = Неопределено Или Не ЗначениеЗаполнено(ДанныеСтроки.Источник) Тогда
    Возврат;
КонецЕсли;

ДокументСсылка = ДанныеСтроки.Источник;
МетаданныеДокумента = ДокументСсылка.Метаданные();

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

Данный способ защищает систему от аварийной остановки, если в составной тип реквизита Источник попадет документ, не содержащий реквизита ЦФО.

Важные рекомендации по производительности и надежности

  1. Различие между ТекущаяСтрока и ТекущиеДанные: в табличных полях интерфейса обычных форм свойство ТекущиеДанные возвращает строку источника данных (например, СтрокаТабличнойЧасти), предоставляя прямой доступ к реквизитам. Свойство ТекущаяСтрока может возвращать системный идентификатор строки, из-за чего попытка обращения через точку вызывает ошибку.
  2. Оптимизация чтения данных: прямое обращение ДокументСсылка.ЦФО приводит к неявному считыванию всего объекта из базы данных в память. Если процедура вызывается в цикле или критична к скорости, рекомендуется получать значение отдельного реквизита через функцию типового общего модуля (например, ОбщегоНазначения.ПолучитьЗначениеРеквизита(Ссылка, "ЦФО")).
← На главную