При разработке или модификации печатных форм в 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда один и тот же программный код должен обрабатывать разные типы документов. Например, при печати УПД для «Реализации товаров и услуг» нам нужно получить ЗаказКлиента, а для «Корректировки реализации» — ДокументОснование. Если написать обращение «в лоб» через точку, система выдаст ошибку «Поле объекта не обнаружено», как только встретит документ, у которого нет такого реквизита.
В этой статье мы разберем, как правильно выстроить логику условий Если...ИначеЕсли, как избежать падения программы при обращении к реквизитам и как сделать код универсальным для разных видов документов.
Проанализируем типичные ошибки, которые возникли в процессе обсуждения темы. Основная проблема была связана с неправильным использованием знаков препинания в условных операторах. В языке 1С точка с запятой ; является признаком окончания инструкции (оператора).
Важное правило: никогда не ставьте точку с запятой после ключевых слов Тогда, ИначеЕсли или Иначе. Это приводит к тому, что система считает оператор внутри условия пустым и выдает ошибку «Ожидается выражение».
Рассмотрим правильную структуру кода:
Если ЗначениеЗаполнено(Реквизит1) Тогда
// Действие, если условие истинно
ИначеЕсли ЗначениеЗаполнено(Реквизит2) Тогда
// Действие, если первое ложно, а второе истинно
Иначе
// Действие во всех остальных случаях
КонецЕсли;
В сообщениях форума автор использовал ИначеЕсли;, что и вызывало синтаксическую ошибку. Также важно помнить, что каждое Если должно закрываться своим КонецЕсли;.
Выясним причину ошибки «Поле объекта не обнаружено». Когда мы пишем ДокументРеализация.ЗаказКлиента, платформа 1С пытается найти этот реквизит в метаданных конкретного документа. Если ДокументРеализация — это «Корректировка реализации», у которой в конфигураторе нет поля ЗаказКлиента, выполнение кода прервется с ошибкой еще до того, как сработает проверка на заполненность.
Рассмотрим, как правильно реализовать выбор нужного реквизита-основания в зависимости от его наличия в объекте. Нам нужно проверить, какой именно документ перед нами, или безопасно попытаться получить значение.
Посмотрим на пример кода, который позволяет переключаться между ЗаказКлиента и ДокументОснование, если мы заранее уверены в именах реквизитов:
ДокументРеализация = ДанныеПечати.Ссылка.ДокументыОснования[0].ДокументОснование;
// Проверяем наличие Заказа клиента через проверку типа или метаданные
// Но проще всего использовать логику: если это один тип документа, берем один реквизит, если другой - второй.
// В данном примере используем универсальный подход через проверку заполненности:
Попытка
ДокументЗаказ = ДокументРеализация.ЗаказКлиента;
Исключение
ДокументЗаказ = ДокументРеализация.ДокументОснование;
КонецПопытки;
Если НЕ ЗначениеЗаполнено(ДокументЗаказ) Тогда
// Если оба варианта не сработали, возможно, нужно искать глубже
КонецЕсли;
Однако использование Попытка...Исключение — это не самый элегантный способ для бизнес-логики. Давайте разберем более профессиональный метод.
Проанализируем ситуацию с точки зрения использования Библиотеки Стандартных Подсистем (БСП), которая есть почти во всех современных конфигурациях (ERP, УТ 11, КА, БП 3.0). В БСП есть отличная функция ОбщегоНазначения.ЗначениеРеквизитаОбъекта().
Преимущество этого метода: если реквизита не существует в метаданных документа, функция просто вернет Неопределено вместо того, чтобы вызвать критическую ошибку «Поле объекта не обнаружено».
Разберем по шагам, как переписать проблемный код из темы форума:
// Получаем ссылку на документ-основание
СсылкаОснование = ДанныеПечати.Ссылка.ДокументыОснования[0].ДокументОснование;
// Безопасно получаем значения реквизитов
Заказ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаОснование, "ЗаказКлиента");
Если Заказ = Неопределено Тогда
// Если реквизита "ЗаказКлиента" нет, пробуем "ДокументОснование"
Заказ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(СсылкаОснование, "ДокументОснование");
КонецЕсли;
// Теперь работаем с результатом
Если ЗначениеЗаполнено(Заказ) Тогда
ДокументЗаказ = Заказ;
ТекстОснования = "Счет № " + ПрефиксацияОбъектовКлиентСервер.ПолучитьНомерНаПечать(ДокументЗаказ.Номер, Ложь, Истина)
+ " от " + Формат(ДокументЗаказ.Дата, "ДЛФ=DD");
Иначе
// Логика, если основание не найдено
КонецЕсли;
Теперь объединим все условия в один логический блок, который решит задачу автора темы. Нам нужно проверить наличие данных о доверенности и правильно сформировать строку основания, не допуская падения системы.
// 1. Сначала определим документ-основание (Реализация или Корректировка)
ДокументРеализация = ДанныеПечати.Ссылка.ДокументыОснования[0].ДокументОснование;
// 2. Определяем "Заказ", учитывая разную структуру документов
// Используем функцию ЕстьРеквизитОбъекта для проверки метаданных
МетаданныеОбъекта = ДокументРеализация.Метаданные();
Если МетаданныеОбъекта.Реквизиты.Найти("ЗаказКлиента") <> Неопределено Тогда
ДокументЗаказ = ДокументРеализация.ЗаказКлиента;
ИначеЕсли МетаданныеОбъекта.Реквизиты.Найти("ДокументОснование") <> Неопределено Тогда
ДокументЗаказ = ДокументРеализация.ДокументОснование;
Иначе
ДокументЗаказ = Неопределено;
КонецЕсли;
// 3. Формируем текст на печать
Если ЗначениеЗаполнено(ДокументЗаказ) Тогда
НомерДатаСчета = "Счет № " + ПрефиксацияОбъектовКлиентСервер.ПолучитьНомерНаПечать(ДокументЗаказ.Номер, Ложь, Истина)
+ " от " + Формат(ДокументЗаказ.Дата, "ДЛФ=DD");
// Проверяем заполнение доверенности
ДоверенностьЗаполнена = ЗначениеЗаполнено(ДанныеПечати.ДоверенностьНомер)
И ЗначениеЗаполнено(ДанныеПечати.ДоверенностьДата)
И (ЗначениеЗаполнено(ДанныеПечати.ДоверенностьВыдана)
Или ЗначениеЗаполнено(ДанныеПечати.ДоверенностьЛицо));
Если ДоверенностьЗаполнена Тогда
ШаблонДоверенности = НСтр("ru = 'по доверенности №%1 от %2, выданной %3 %4'");
ТекстДоверенности = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
ШаблонДоверенности,
ДанныеПечати.ДоверенностьНомер,
Формат(ДанныеПечати.ДоверенностьДата, "ДЛФ=DD"),
ДанныеПечати.ДоверенностьВыдана,
ДанныеПечати.ДоверенностьЛицо);
ТекстОснования = НомерДатаСчета + "; " + ТекстДоверенности;
Иначе
// Если доверенность не заполнена, выводим только счет
ТекстОснования = НомерДатаСчета;
КонецЕсли;
КонецЕсли;
Для того чтобы ваш код был устойчив к ошибкам и корректно работал с разными типами документов, придерживайтесь следующих правил:
Тогда, Иначе и ИначеЕсли не ставьте точку с запятой. Завершайте блок оператором КонецЕсли;.Объект.Реквизит), убедитесь, что этот реквизит существует в данном типе документа.ОбщегоНазначения.ЗначениеРеквизитаОбъекта избавляют от необходимости писать сложные проверки и защищают от аварийного завершения программы.ЗаказПокупателя и ЗаказКлиента), выносите логику поиска в отдельную переменную в начале процедуры.Следуя этим рекомендациям, вы сможете создавать надежные механизмы печати, которые не «падают» при добавлении новых типов документов в цепочку оснований.