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