При разработке механизмов ввода на основании в платформе «1С:Предприятие 8» программисты часто сталкиваются с досадными ошибками, которые на первый взгляд кажутся нелогичными. Одной из самых популярных является ошибка «Поле объекта не обнаружено (Ссылка)» при попытке связать строки табличной части создаваемого документа с документом-источником. В этой статье мы подробно разберем, почему возникает эта ошибка, как устроена объектная модель 1С и как правильно писать лаконичный и быстрый код для заполнения документов.
Рассмотрим ситуацию, с которой столкнулся автор темы. В документе-приемнике в процедуре ОбработкаЗаполнения выполняется обход табличной части документа-источника:
Для Каждого ТекСтрокаТМЗ Из ДанныеЗаполнения.ТМЗ Цикл
НоваяСтрока = ТМЗ.Добавить();
// ... записание других реквизитов
НоваяСтрока.Основание = ТекСтрокаТМЗ.Ссылка; // Ошибка возникает здесь!
КонецЦикла;
В отладчике разработчик видит, что ссылка на документ существует, однако при попытке обратиться к свойству ТекСтрокаТМЗ.Ссылка система аварийно завершает работу.
Причина кроется в особенностях объектной модели платформы «1С:Предприятие»:
ТекСтрокаТМЗ внутри цикла имеет тип ДокументТабличнаяЧастьСтрока. Строка табличной части в оперативной памяти — это простой объект, содержащий только индекс строки и значения ее реквизитов (колонок). Она не является ссылочным типом данных и не хранит в себе ссылку на документ-владелец.Ссылка действительно существует и возвращает ссылку на документ-владелец. Однако в объектной технике (в коде модуля) у строки табличной части такого свойства нет.Поскольку ссылка на документ-источник уже передана в процедуру в качестве параметра ДанныеЗаполнения, обращаться к строке ТЧ за ссылкой нет никакой необходимости.
Для исправления ошибки достаточно напрямую присвоить реквизиту Основание значение параметра ДанныеЗаполнения. Изменим код следующим образом:
НоваяСтрока.Основание = ДанныеЗаполнения;
Некоторые разработчики (как показано в Сообщении 2 форума) предлагают использовать конструкцию ДанныеЗаполнения.Ссылка. Давайте проанализируем, почему это решение работает, но является избыточным.
Если перед циклом мы уже выполнили проверку типа:
Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.ПоступлениеТМЗ") Тогда
то переменная ДанныеЗаполнения гарантированно содержит ссылку. Обращение к свойству .Ссылка у самой ссылки (то есть получение ссылки от ссылки) синтаксически допустимо в 1С, но это лишняя операция, которая не несет никакой практической пользы.
Однако в типовых конфигурациях 1С от фирмы «1С» мы действительно можем встретить код вида ДанныеЗаполнения.Ссылка. Это делается по двум причинам:
ДанныеЗаполнения передадут не ссылку (ДокументСсылка), а сам объект (ДокументОбъект), то обращение к свойству .Ссылка позволит получить корректную ссылку для записи в базу данных. Попытка записать ДокументОбъект непосредственно в реквизит приведет к ошибке несоответствия типов..Ссылка по умолчанию для обеспечения максимальной отказоустойчивости.Теперь, когда мы выяснили причину ошибки, давайте разберем, как сделать наш код более профессиональным, быстрым и соответствующим стандартам разработки (например, БСП).
Вместо того чтобы вручную сопоставлять и переносить каждый реквизит строки (например, Номенклатура, Количество, Цена), мы можем использовать эффективный встроенный метод ЗаполнитьЗначенияСвойств(). Он автоматически найдет свойства с одинаковыми именами в источнике и приемнике и скопирует их значения.
Для Каждого ТекСтрокаТМЗ Из ДанныеЗаполнения.ТМЗ Цикл
НоваяСтрока = ТМЗ.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТекСтрокаТМЗ);
НоваяСтрока.Основание = ДанныеЗаполнения;
КонецЦикла;
Согласно стандартам разработки 1С, перед выполнением заполнения на основании необходимо убедиться в корректности данных источника. Например, не рекомендуется разрешать ввод на основании непроведенных документов или документов, помеченных на удаление.
Рассмотрим финальный вариант качественной реализации процедуры ОбработкаЗаполнения:
Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
// Проверяем тип входящих данных
Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.ПоступлениеТМЗ") Тогда
// Проверка бизнес-логики: документ должен быть проведен
Если Не ДанныеЗаполнения.Проведен Тогда
ВызватьИсключение "Нельзя вводить документ на основании непроведенного документа ""Поступление ТМЗ""!";
КонецЕсли;
// Заполнение реквизитов шапки нового документа
Организация = ДанныеЗаполнения.Организация;
Контрагент = ДанныеЗаполнения.Контрагент;
// Заполнение табличной части
Для Каждого ТекСтрокаТМЗ Из ДанныеЗаполнения.ТМЗ Цикл
НоваяСтрока = ТМЗ.Добавить();
// Быстро копируем совпадающие реквизиты строк
ЗаполнитьЗначенияСвойств(НоваяСтрока, ТекСтрокаТМЗ);
// Записываем ссылку на документ-основание
НоваяСтрока.Основание = ДанныеЗаполнения;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Применяя этот подход, мы не только избавимся от ошибки «Поле объекта не обнаружено (Ссылка)», но и сделаем код устойчивым к изменениям метаданных, а также защитим информационную базу от некорректных действий пользователей.