Как исправить ошибку «В обработчике печати не был сформирован табличный документ» в 1С

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

При разработке внешних печатных форм (ВПФ) на базе Библиотеки Стандартных Подсистем (БСП) программисты часто сталкиваются с неприятной ошибкой: «В обработчике печати не был сформирован табличный документ». На первый взгляд код кажется абсолютно верным, данные выбираются без ошибок, но система упорно отказывается выводить печатную форму на экран.

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

Причина №1. Опечатка в идентификаторе команды (Ошибка «ш/щ»)

Давайте внимательно изучим код регистрации внешней обработки и процедуру печати из примера автора. Обратите внимание на регистрацию команды в функции СведенияОВнешнейОбработке:


ДобавитьКоманду(ТаблицаКоманд, "Заказ поставщику ПФ", "ЗаказПоставшику", "ВызовСерверногоМетода", Истина, "ПечатьMXL");

А теперь посмотрим, как этот документ регистрируется в коллекции печатных форм в процедуре Печать:


УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(КоллекцияПечатныхФорм, "ЗаказПоставщику", "Заказ поставщику ПФ", ТабличныйДокумент);

Заметили разницу? В функции регистрации используется идентификатор "ЗаказПоставшику" (через букву «ш»), а в процедуре вывода в коллекцию — "ЗаказПоставщику" (через букву «щ»).

Почему это приводит к ошибке?

Механизм БСП работает следующим образом: при нажатии кнопки печати система ищет в зарегистрированных командах идентификатор запущенной команды (в нашем случае это зарегистрированный "ЗаказПоставшику"). Затем она выполняет процедуру Печать, ожидая, что в переданную коллекцию КоллекцияПечатныхФорм будет добавлен табличный документ именно с этим идентификатором. Но наш код добавляет в коллекцию документ с именем "ЗаказПоставщику". В итоге БСП видит, что для запрошенной команды "ЗаказПоставшику" табличный документ так и не был сформирован. Это и вызывает исключение.

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

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

1. Несоответствие параметров при вызове функции формирования

Посмотрим, как в процедуре Печать вызывается функция формирования табличного документа:

ТабличныйДокумент = СформироватьПечатнуюФорму(МассивОбъектов[0], ИмяМакета);

А теперь обратим внимание на сигнатуру самой функции СформироватьПечатнуюФорму:

Функция СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати)

Вместо коллекции ОбъектыПечати (которая используется БСП для сопоставления объектов метаданных и областей макета) автор передает строковую переменную ИмяМакета со значением "Макет". Если внутри функции или механизмов БСП произойдет обращение к методам коллекции ОбъектыПечати, система аварийно завершит работу с ошибкой несоответствия типов.

2. Обращение к несуществующему свойству Ссылка

В процедуру Печать БСП передает первый параметр МассивОбъектов. Автор берет его первый элемент МассивОбъектов[0] (который сам по себе является ссылкой на документ типа ДокументСсылка.ЗаказПоставщика) и передает его в функцию под именем МассивОбъектов. Внутри функции мы видим следующую строку:

ОбластьШапка.Параметры.ЗаказПоставщику = МассивОбъектов.Ссылка;

Поскольку в переменную МассивОбъектов была передана одиночная ссылка, обращение к свойству .Ссылка у ссылки избыточно. В некоторых случаях это может работать, но если тип переданного значения по какой-то причине изменится (например, при отладке будет передан другой тип данных), программа выдаст ошибку «Поле объекта не обнаружено (Ссылка)».

3. Игнорирование групповой печати

Архитектура БСП изначально проектируется с учетом того, что пользователь может выделить в списке несколько документов и отправить их на печать одновременно. В этом случае в процедуру Печать в параметре МассивОбъектов придет массив из нескольких ссылок.

Жесткая привязка к первому элементу массива МассивОбъектов[0] лишает пользователей возможности групповой печати. Будет распечатан только один документ, сколько бы строк в списке пользователь ни выбрал.

4. Проблема с версиями БСП (количество параметров)

В зависимости от версии БСП, используемой в вашей конфигурации, процедура Печать в модуле объекта внешней печатной формы может требовать разное количество параметров. В современных конфигурациях (например, ERP 2.5, УТ 11.5, КА 2.5) процедура ожидает 5 параметров:


Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт

В коде автора параметров всего 4:


Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт

Если запустить такой код в новой версии конфигурации, БСП передаст вторым параметром структуру параметров печати, которая в коде автора запишется в переменную КоллекцияПечатныхФорм. Из-за этого вызов метода ВывестиТабличныйДокументВКоллекцию завершится фатальной ошибкой выполнения.

Как правильно отлаживать внешние печатные формы

Многие начинающие разработчики считают, что отлаживать ВПФ невозможно, так как они запускаются во временном хранилище. На самом деле существует два отличных и простых способа отладки.

Способ №1. Использование тестовой формы («Файл -> Открыть»)

Этот способ позволяет запускать и отлаживать печатную форму как обычную обработку через конфигуратор.

  1. Добавьте во внешнюю обработку временную управляемую форму. Сделайте её основной формой обработки.
  2. Разместите на форме реквизит с типом ссылки на тестируемый документ (например, СсылкаНаДокумент с типом ДокументСсылка.ЗаказПоставщика) и кнопку «Печать».
  3. В модуле формы в обработчике команды кнопки напишите следующий код:

&НаСервере
Процедура ПечатьНаСервере()
    
    // Создаем аналоги параметров БСП
    МассивОбъектов = Новый Массив;
    МассивОбъектов.Добавить(Объект.СсылкаНаДокумент);
    
    КоллекцияПечатныхФорм = УправлениеПечатью.ПодготовитьКоллекциюПечатныхФорм("ЗаказПоставщику");
    ОбъектыПечати = Новый СписокЗначений;
    ПараметрыВывода = УправлениеПечатью.ПодготовитьСтруктуруПараметровВывода();
    
    // Вызываем экспортную процедуру печати нашей обработки
    Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода);
    
    // Получаем сформированный документ и показываем его
    Если КоллекцияПечатныхФорм.Количество() > 0 Тогда
        ТабДок = КоллекцияПечатныхФорм[0].ТабличныйДокумент;
        // Сохраняем во временное хранилище или передаем на клиент для отображения
        АдресХранилища = ПоместитьВоВременноеХранилище(ТабДок, УникальныйИдентификатор);
    КонецЕсли;
    
КонецПроцедуры

Теперь вы можете открыть обработку в режиме «1С:Предприятие» через меню Файл -> Открыть, выбрать нужный документ в реквизите формы и нажать кнопку. В Конфигураторе при этом сработают любые точки останова (клавиша F9), установленные в модуле объекта.

Способ №2. Метод отладочной «заглушки»

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

  1. Создайте копию вашей обработки и сохраните её на жесткий диск, например, по пути C:\Distr\MyPrintForm.epf.
  2. В оригинальной обработке, зарегистрированной в справочнике «Дополнительные отчеты и обработки», полностью очистите код процедур и оставьте только перенаправление вызова:

Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
    // Загружаем актуальный код обработки напрямую с диска
    ПутьКФайлу = "C:\Distr\MyPrintForm.epf";
    ТекущаяОбработка = ВнешниеОбработки.Создать(ПутьКФайлу, Ложь);
    ТекущаяОбработка.Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода);
КонецПроцедуры

Откройте файл MyPrintForm.epf в Конфигураторе, поставьте точки останова в нужных местах кода. Запустите печать из интерфейса программы. Система обратится к зарегистрированной обработке, та загрузит файл с диска, и управление перейдет в открытый в Конфигураторе файл, активировав отладчик.

Другие частые причины возникновения ошибки

Если вы проверили все идентификаторы и исправили опечатки, но ошибка все равно сохраняется, обратите внимание на следующие моменты:

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

Пример исправленного и оптимизированного кода внешней печатной формы

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

Код модуля объекта внешней обработки:


Функция СведенияОВнешнейОбработке() Экспорт
    
    ПараметрыРегистрации = Новый Структура;
    МассивНазначений = Новый Массив;
    МассивНазначений.Добавить("Документ.ЗаказПоставщику");
    
    ПараметрыРегистрации.Вставить("Вид", "ПечатнаяФорма"); 
    ПараметрыРегистрации.Вставить("Назначение", МассивНазначений);
    ПараметрыРегистрации.Вставить("Наименование", "Заказ поставщику ПФ (Исправленная)"); 
    ПараметрыРегистрации.Вставить("Версия", "1.2");
    ПараметрыРегистрации.Вставить("БезопасныйРежим", Ложь);
    ПараметрыРегистрации.Вставить("Информация", "Печатная форма документа Заказ поставщику");
    
    ТаблицаКоманд = ПолучитьТаблицуКоманд();
    // Исключили опечатку в идентификаторе "ЗаказПоставщику"
    ДобавитьКоманду(ТаблицаКоманд, "Заказ поставщику ПФ", "ЗаказПоставщику", "ВызовСерверногоМетода", Истина, "ПечатьMXL");
    
    ПараметрыРегистрации.Вставить("Команды", ТаблицаКоманд);
    
    Возврат ПараметрыРегистрации;
    
КонецФункции

Функция ПолучитьТаблицуКоманд()
    Команды = Новый ТаблицаЗначений;
    Команды.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка"));
    Команды.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Строка")); 
    Команды.Колонки.Добавить("Использование", Новый ОписаниеТипов("Строка")); 
    Команды.Колонки.Добавить("ПоказыватьОповещение", Новый ОписаниеТипов("Булево"));
    Команды.Колонки.Добавить("Модификатор", Новый ОписаниеТипов("Строка"));
    Возврат Команды;
КонецФункции

Процедура ДобавитьКоманду(ТаблицаКоманд, Представление, Идентификатор, Использование, ПоказыватьОповещение = Ложь, Модификатор = "")
    НоваяКоманда = ТаблицаКоманд.Добавить();
    НоваяКоманда.Представление = Представление;  
    НоваяКоманда.Идентификатор = Идентификатор;
    НоваяКоманда.Использование = Использование;
    НоваяКоманда.ПоказыватьОповещение = ПоказыватьОповещение;
    НоваяКоманда.Модификатор = Модификатор;
КонецПроцедуры

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

Функция СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати)
    
    ТабличныйДокумент = Новый ТабличныйДокумент;
    ТабличныйДокумент.АвтоМасштаб = Истина;
    
    Макет = ПолучитьМакет("Макет");
    
    // Запрос адаптирован под выборку по ВСЕМУ массиву объектов (групповая печать)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	ЗаказПоставщикуТовары.Ссылка КАК Ссылка,
        |	ЗаказПоставщикуТовары.Номенклатура.Наименование КАК НоменклатураНаименование,
        |	ЗаказПоставщикуТовары.Номенклатура.Артикул КАК НоменклатураАртикул,
        |	ЗаказПоставщикуТовары.КоличествоУпаковок КАК КоличествоУпаковок,
        |	ЗаказПоставщикуТовары.Количество КАК Количество
        |ИЗ
        |	Документ.ЗаказПоставщику.Товары КАК ЗаказПоставщикуТовары
        |ГДЕ
        |	ЗаказПоставщикуТовары.Ссылка В(&МассивОбъектов)
        |ИТОГИ ПО
        |	Ссылка";
        
    Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
    
    РезультатЗапроса = Запрос.Выполнить();
    Если РезультатЗапроса.Пустой() Тогда
        // Если данных нет, выведем хотя бы пустой лист с предупреждением
        ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
        ТабличныйДокумент.Вывести(ОбластьШапка);
        Возврат ТабличныйДокумент;
    КонецЕсли;
    
    ВыборкаДокументов = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    
    ПервыйДокумент = Истина;
    
    Пока ВыборкаДокументов.Следующий() Цикл
        
        // При групповой печати каждый документ выводится на новой странице
        Если Не ПервыйДокумент Тогда
            ТабличныйДокумент.ВывестиГоризонтальныйРазделительСтраниц();
        КонецЕсли;
        ПервыйДокумент = Ложь;
        
        // Запоминаем позицию начала вывода текущего документа
        НомерСтрокиНачало = ТабличныйДокумент.ВысотаТаблицы + 1;
        
        // Выводим шапку документа
        ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
        ОбластьШапка.Параметры.ЗаказПоставщику = ВыборкаДокументов.Ссылка;
        ТабличныйДокумент.Вывести(ОбластьШапка);
        
        // Выводим строки товаров
        ВыборкаСтрок = ВыборкаДокументов.Выбрать();
        Пока ВыборкаСтрок.Следующий() Цикл
            ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
            ЗаполнитьЗначенияСвойств(ОбластьСтрока.Параметры, ВыборкаСтрок);
            ТабличныйДокумент.Вывести(ОбластьСтрока);
        КонецЦикла;
        
        // Регистрируем область печати для БСП
        УправлениеПечатью.ЗадатьОбластьПечатиДокумента(
            ТабличныйДокумент, 
            НомерСтрокиНачало, 
            ОбъектыПечати, 
            ВыборкаДокументов.Ссылка);
            
    КонецЦикла;
    
    Возврат ТабличныйДокумент;
    
КонецФункции
← На главную