Как исправить ошибку "Значение не является значением объектного типа (Организация)" при переходе на новый макет печати в 1С?

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

При разработке и кастомизации конфигураций 1С на обычных формах (таких как «Управление торговлей 10.3», «Управление производственным предприятием 1.3» или «Комплексная автоматизация 1.1») программисты часто сталкиваются с необходимостью замены старых печатных форм на новые. Процесс кажется простым: достаточно перенаправить вызов со старой процедуры на новую экспортную функцию в общем модуле. Однако на практике разработчики часто получают неприятную ошибку: «Значение не является значением объектного типа (Организация)».

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

Проанализируем ситуацию по шагам и выработаем надежное, архитектурно правильное решение.

---

Причина 1. Неправильная передача параметров из формы документа

Рассмотрим исходный код вызова печати из формы документа, который привел к ошибке:


Процедура ОсновныеДействияФормыДействиеПечать(Кнопка)
    // Так было изначально (выводился старый макет):
    // УниверсальныеМеханизмы.НапечататьДокументПоУмолчанию(ЭтотОбъект); 
    
    // Так попытались сделать (вызывает ошибку):
    мд_ПечатьДокументов.ПечатьСчета(Тип, ТекОбъект, МассивДокументов); 
КонецПроцедуры

Выясним причину падения платформы. Внутри обработчика нажатия кнопки формы переменные Тип, ТекОбъект и МассивДокументов не были предварительно объявлены и инициализированы. Соответственно, при передаче в функцию общего модуля они принимают значение Неопределено.

Внутри функции общего модуля ПечатьСчета первый параметр (который локально называется ТекДокумент) принимает значение Неопределено. Когда программа пытается выполнить строку:

Отбор.Объект = ТекДокумент.Организация;

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

Решение: Чтобы передать данные текущего документа из модуля его формы, необходимо использовать ключевое слово ЭтотОбъект (или ссылку на него — ЭтотОбъект.Ссылка). Именно этот объект содержит все реквизиты шапки (включая организацию) и табличные части.

Исправленный вызов функции из модуля формы должен выглядеть следующим образом:


Процедура ОсновныеДействияФормыДействиеПечать(Кнопка)
    // Передаем в качестве второго параметра контекст нашего документа
    мд_ПечатьДокументов.ПечатьСчета(Неопределено, ЭтотОбъект, Неопределено); 
КонецПроцедуры

---

Причина 2. Скрытые логические ошибки в алгоритме вывода макета

Даже если мы исправим передачу параметров, оригинальный код функции ПечатьСчета содержит несколько критических архитектурных ошибок, из-за которых на экран будет выводиться абсолютно белый, пустой лист. Разберем эти ошибки подробнее.

  1. Отсутствие вывода областей в табличный документ. В коде инициализируется табличный документ ТабДокумент = Новый ТабличныйДокумент; и получается макет Макет = ПолучитьОбщийМакет("мд_Счет");. Однако в коде нет ни одной строки, которая получала бы области макета (например, Макет.ПолучитьОбласть("Шапка")) и выводила бы их в результирующий табличный документ с помощью метода Вывести().
  2. Перезапись структуры данных в цикле. Внутри цикла по двум копиям документа (для исполнителя и заказчика) строка ДанныеДляПечати = Новый Структура; на каждой итерации полностью стирает данные предыдущего шага.
  3. Тяжелые запросы в теле цикла. Запрос к регистру сведений ЗначенияСвойствОбъектов для получения телефона склада выполняется на каждой итерации цикла. Поскольку склад документа не зависит от того, чей экземпляр печатается (заказчика или исполнителя), этот запрос необходимо выносить за пределы цикла для оптимизации производительности.

Устраним эти недочеты и напишем чистый, работоспособный код формирования печатной формы.

---

Пошаговое руководство по созданию корректной функции печати

Рассмотрим правильный шаблон построения функции ПечатьСчета в общем модуле мд_ПечатьДокументов. Обратите внимание на то, как мы получаем области из общего макета, заполняем параметры и выводим их в результирующий ТабличныйДокумент.


Функция ПечатьСчета(Тип, ТекДокумент, МассивДокументов) Экспорт

    ТабДокумент = Новый ТабличныйДокумент;
    ТабДокумент.ИмяПараметровПечати = "ПАРАМЕТРЫ_ПЕЧАТИ_ЗаказПокупателя_Счет";
    ТабДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
    ТабДокумент.АвтоМасштаб = Истина;
    
    // Получаем макет из общих макетов конфигурации
    Макет = ПолучитьОбщийМакет("мд_Счет");
    
    // ОПТИМИЗАЦИЯ: Выносим получение контактной информации и реквизитов за рамки цикла
    ЮрАдресОрганизации = "";
    ТелефоныОрганизации = "";
    ЮрАдресКонтрагента = "";
    
    // 1. Получаем юридический адрес организации
    ОтборКИ = Новый Структура("Объект, Тип, Вид");
    ОтборКИ.Объект = ТекДокумент.Организация;
    ОтборКИ.Тип = Перечисления.ТипыКонтактнойИнформации.Адрес;
    ОтборКИ.Вид = Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Юридический адрес организации", Истина);
    
    ЮрАдресОрганизации = РегистрыСведений.КонтактнаяИнформация.Получить(ОтборКИ).Представление;
    
    // 2. Получаем телефон склада через запрос
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ЗначенияСвойствОбъектов.Значение КАК Телефон
        |ИЗ
        |	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
        |ГДЕ
        |	ЗначенияСвойствОбъектов.Свойство = &СвойствоТелефон
        |	И ЗначенияСвойствОбъектов.Объект = &НужныйСклад";
        
    Запрос.УстановитьПараметр("СвойствоТелефон", ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоНаименованию("Телефон", Истина));
    Запрос.УстановитьПараметр("НужныйСклад", ТекДокумент.СкладГруппа);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        ТелефоныОрганизации = Выборка.Телефон;
    КонецЕсли;
    
    // Если у склада нет телефона, берем общий телефон организации
    Если ПустаяСтрока(ТелефоныОрганизации) Тогда
        ОтборКИ.Тип = Перечисления.ТипыКонтактнойИнформации.Телефон;
        ОтборКИ.Вид = Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Телефон организации", Истина);
        ТелефоныОрганизации = РегистрыСведений.ContactИнформация.Получить(ОтборКИ).Представление;
    КонецЕсли;
    
    // 3. Получаем юридический адрес контрагента
    ОтборКонтрагента = Новый Структура("Объект, Тип, Вид");
    ОтборКонтрагента.Объект = ТекДокумент.Контрагент;
    ОтборКонтрагента.Тип = Перечисления.ТипыКонтактнойИнформации.Адрес;
    ОтборКонтрагента.Вид = Справочники.ВидыКонтактнойИнформации.НайтиПоНаименованию("Юридический адрес контрагента", Истина);
    ЮрАдресКонтрагента = РегистрыСведений.КонтактнаяИнформация.Получить(ОтборКонтрагента).Представление;
    
    // 4. Расчет реквизитов платежа сторонним модулем
    БанкОрг = ""; НомерСчетаОрг = ""; БИКБанкаОрг = ""; СчетБанкаОрг = "";
    мд_НашМодуль.ПлатежныеРеквизиты(ТекДокумент.Организация.ОсновнойБанковскийСчет, БанкОрг, НомерСчетаОрг, БИКБанкаОрг, СчетБанкаОрг);
    ФирмаРеквизиты = ?(ПустаяСтрока(НомерСчетаОрг), "", "c/d " + СокрЛП(НомерСчетаОрг) + " in " + БанкОрг);
    
    // Начало цикла формирования копий
    Для Ном = 1 По 2 Цикл
        
        // Получаем область "Шапка" из нашего макета (секция должна быть создана в табличном документе макета)
        ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
        
        // Заполняем параметры области напрямую
        ОбластьШапка.Параметры.Экземпляр = ?(Ном = 1, "Экземпляр исполнителя", "Экземпляр заказчика");
        ОбластьШапка.Параметры.НомерДокумента = ТекДокумент.Номер;
        ОбластьШапка.Параметры.ДатаДокумента = Формат(ТекДокумент.Дата, "ДЛФ=ДД");
        ОбластьШапка.Параметры.Организация = ТекДокумент.Организация.НаименованиеПолное;
        ОбластьШапка.Параметры.Контрагент = ТекДокумент.Контрагент.НаименованиеПолное;
        ОбластьШапка.Параметры.ЮрАдресОрганизации = ЮрАдресОрганизации;
        ОбластьШапка.Параметры.ТелефоныОрганизации = ТелефоныОрганизации;
        ОбластьШапка.Параметры.ЮрАдресКонтрагента = ЮрАдресКонтрагента;
        ОбластьШапка.Параметры.ИНН = Строка(ТекДокумент.Организация.ИНН) + "/" + Строка(ТекДокумент.Организация.КПП);
        ОбластьШапка.Параметры.ФирмаРеквизиты = ФирмаРеквизиты;
        
        // Выводим готовую заполненную область в табличный документ
        ТабДокумент.Вывести(ОбластьШапка);
        
        // Если это первый экземпляр, добавляем горизонтальный разделитель для печати на разных листах
        Если Ном = 1 Тогда
            ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц();
        КонецЕсли;
        
    КонецЦикла;
    
    // Выводим готовый заполненный табличный документ на экран
    УниверсальныеМеханизмы.НапечататьДокумент(ТабДокумент, , , "Счет на оплату покупателю", , );
    
КонецФункции

---

Архитектурно правильный подход к интеграции новых макетов

Вызов процедур формирования печатных форм напрямую из обработчиков нажатия кнопок в модуле формы — это пример плохого тона разработки в среде 1С. Такой подход лишает пользователей возможности выполнять групповую печать документов из списков и нарушает стандартную ролевую модель прав доступа.

Сделаем интеграцию нашего нового макета архитектурно правильной, используя стандартные механизмы БСП или типовых конфигураций на обычных формах.

Шаг 1. Регистрация макета в Модуле Объекта документа

Откроем модуль объекта документа (например, Документ.СчетНаОплатуПокупателю) и найдем функцию ПолучитьСтруктуруПечатныхФорм(). Добавим туда наш новый макет:


Функция ПолучитьСтруктуруПечатныхФорм() Экспорт
    СтруктураРег = Новый Структура;
    СтруктураРег.Вставить("мд_Счет", "Счет на оплату (Новый макет)"); 
    Возврат СтруктураРег;
КонецФункции

Шаг 2. Маршрутизация печати в процедуре Печать()

В этом же модуле объекта найдем экспортную процедуру Печать() и свяжем идентификатор макета с вызовом нашей функции из общего модуля:


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

Шаг 3. Использование стандартной кнопки формы

После такой настройки код обработчика кнопки на форме документа может оставаться полностью стандартным:


Процедура ОсновныеДействияФормыДействиеПечать(Кнопка)
    УниверсальныеМеханизмы.НапечататьДокументПоУмолчанию(ЭтотОбъект);
КонецПроцедуры

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

← На главную