Создание печатных форм в среде 1С:Предприятие 8.3 — одна из самых частых задач, с которой сталкивается начинающий разработчик. Особенно актуально это для печатных форм договоров, где большая часть документа представляет собой статичный юридический текст, в который необходимо динамически подставлять данные из информационной базы: реквизиты организации, контрагента, номера, даты и суммы. При реализации этой задачи на управляемых формах (например, в конфигурациях "Управление торговлей 11", "ERP" или "Бухгалтерия предприятия 3.0") программисты часто совершают типовые ошибки. В этой статье мы подробно разберем, как правильно спроектировать печатную форму, почему возникает популярная ошибка "Несоответствие типов" при заполнении параметров макета и как реализовать печать по стандартам разработки 1С.
В сообщениях на форуме автор столкнулся с ошибкой: Ошибка при вызове метода контекста (Заполнить) по причине: Несоответствие типов (параметр номер '1'). Строка кода, вызвавшая сбой, выглядела следующим образом:
Заголовок.Параметры.Заполнить(Выборка.Номер);
Выясним причину этой ошибки. Метод Заполнить(), вызванный у коллекции Параметры области макета, предназначен для автоматического заполнения параметров табличного документа. Он берет имена параметров, объявленные в макете, сопоставляет их с именами свойств переданного в метод объекта и присваивает соответствующие значения. На вход этот метод ожидает объект, содержащий пары "Ключ - Значение". Таким объектом может выступать:
ВыборкаИзРезультатаЗапроса).Структура).Автор же попытался передать в метод Заполнить() конкретное одиночное значение — Выборка.Номер (которое является обычной строкой или числом). Платформа 1С не смогла извлечь структуру свойств из этого значения, что и привело к ошибке несоответствия типов.
Для исправления этой ситуации у нас есть два пути:
Способ 1. Заполнение параметров вручную (покоординатно). Мы напрямую обращаемся к каждому конкретному параметру области макета и присваиваем ему значение из выборки:
Заголовок.Параметры.Номер = Выборка.Номер;
Шапка.Параметры.Дата = Выборка.Дата;
Способ 2. Автоматическое заполнение через передачу всей строки выборки. Мы передаем в метод Заполнить() весь объект Выборка целиком. Система самостоятельно найдет в выборке поля, имена которых совпадают с параметрами макета, и заполнит их:
Заголовок.Параметры.Заполнить(Выборка);
Шапка.Параметры.Заполнить(Выборка);
Текст.Параметры.Заполнить(Выборка);
Второй способ гораздо лаконичнее и удобнее, особенно когда параметров много. Главное условие — имена параметров в макете табличного документа должны в точности совпадать с именами полей (псевдонимами КАК), которые возвращает ваш запрос.
Второй важный нюанс в коде автора — закомментированное условие ГДЕ в запросе. Без этого условия печать работать корректно не будет. Проанализируем, что произойдет: программа выполнит запрос ко всей таблице справочника договоров, получит абсолютно все договоры из базы данных и в цикле выведет их один за другим в один табличный документ. Пользователь вместо печати одного выбранного договора получит огромную "простыню" из всех документов системы.
Чтобы печатать только тот договор, который выбрал пользователь на форме, в запросе обязательно должно присутствовать условие фильтрации по ссылке:
Запрос.Текст =
"ВЫБРАТЬ
| ДоговорыКонтрагентов.Номер КАК Номер,
| ДоговорыКонтрагентов.Дата КАК Дата,
| ДоговорыКонтрагентов.Организация КАК Организация,
| ДоговорыКонтрагентов.Контрагент КАК Контрагент,
| ДоговорыКонтрагентов.КонтактноеЛицо КАК КонтактноеЛицо
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
|ГДЕ
| ДоговорыКонтрагентов.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Параметр Ссылка должен передаваться в процедуру печати с клиента, чтобы система знала, какой именно элемент справочника мы отправляем на принтер.
В управляемом приложении 1С действует жесткое правило разделения контекста. Вывод табличного документа на экран (метод Показать()) возможен только на стороне клиента (в директиве &НаКлиенте), так как серверная часть платформы не имеет доступа к интерфейсу пользователя. В то же время получение макета, работа с базой данных и выполнение запросов могут происходить только на сервере (в директиве &НаСервере или в серверных модулях).
Рассмотрим правильную схему вызова печатной формы с формы справочника:
&НаКлиенте
Процедура КомандаПечатьДоговора(Команда)
// Получаем ссылку на текущий элемент на форме
СсылкаНаДоговор = Объект.Ссылка;
Если Не ЗначениеЗаполнено(СсылкаНаДоговор) Тогда
ПоказатьПредупреждение(, "Сначала необходимо записать элемент справочника!");
Возврат;
КонецЕсли;
// Вызываем серверную функцию для формирования документа
ТабДок = СформироватьДоговорНаСервере(СсылкаНаДоговор);
// Выводим результат пользователю на клиенте
ТабДок.Показать("Печать договора");
КонецПроцедуры
&НаСервереБезКонтекста
Функция СформироватьДоговорНаСервере(Ссылка)
ТабДок = Новый ТабличныйДокумент;
// Получаем макет, сохраненный в справочнике
Макет = Справочники.ДоговорыКонтрагентов.ПолучитьМакет("МакетДоговора");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДоговорыКонтрагентов.Номер КАК Номер,
| ДоговорыКонтрагентов.Дата КАК Дата,
| ДоговорыКонтрагентов.Организация КАК Организация,
| ДоговорыКонтрагентов.Контрагент КАК Контрагент
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
|ГДЕ
| ДоговорыКонтрагентов.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить.Выбрать();
ОбластьТекст = Макет.ПолучитьОбласть("ТекстДоговора");
Если Выборка.Следующий() Тогда
// Заполняем параметры области макета из выборки запроса
ОбластьТекст.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОбластьТекст);
КонецЕсли;
Возврат ТабДок;
КонецФункции
В этой схеме мы четко разделили логику: на сервере подготовили макет и наполнили его данными, а на клиенте отобразили готовый табличный документ пользователю.
В современных конфигурациях 1С (таких как УТ 11, ERP, КА 2, БП 3) напрямую добавлять кнопки на формы объектов считается плохим стилем разработки. Программа должна оставаться типовой для последующих беспроблемных обновлений. Все механизмы печати стандартизированы через БСП.
Чтобы печатная форма корректно регистрировалась системой и автоматически появлялась в меню "Печать" как на форме списка, так и на форме элемента, разработку следует вести в модуле менеджера объекта. Выполним настройку по шагам:
Шаг 1. Регистрация команды печати в модуле менеджера. В модуле менеджера справочника ДоговорыКонтрагентов создаем или дополняем экспортную процедуру ДобавитьКомандыПечати():
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
КомандаПечати = КомандыПечати.Добавить();
КомандаПечати.Идентификатор = "ДоговорСКонтрагентом";
КомандаПечати.Представление = "Договор (шаблон БСП)";
КомандаПечати.ПроверятьПроведенностьПередПечатью = Ложь;
КомандаПечати.Порядок = 10;
КонецПроцедуры
Шаг 2. Реализация логики печати в процедуре Печать(). Там же, в модуле менеджера, размещаем стандартную процедуру управления печатью БСП. Она принимает массив объектов (что позволяет печатать несколько выделенных договоров сразу):
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "ДоговорСКонтрагентом") Тогда
ТабДок = СформироватьПечатнуюФормуДоговора(МассивОбъектов, ОбъектыПечати);
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
КоллекцияПечатныхФорм,
"ДоговорСКонтрагентом",
"Договор (шаблон БСП)",
ТабДок
);
КонецЕсли;
КонецПроцедуры
Шаг 3. Функция формирования табличного документа. Напишем вспомогательную функцию генерации макета. Обратите внимание на обработку массива ссылок:
Функция СформироватьПечатнуюФормуДоговора(МассивОбъектов, ОбъектыПечати)
ТабДок = Новый ТабличныйДокумент;
ТабДок.КлючПараметровПечати = "ПараметрыПечати_Договор";
Макет = ПолучитьМакет("Печать");
ОбластьТекст = Макет.ПолучитьОбласть("ТекстДоговора");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДоговорыКонтрагентов.Ссылка КАК Ссылка,
| ДоговорыКонтрагентов.Номер КАК Номер,
| ДоговорыКонтрагентов.Дата КАК Дата,
| ДоговорыКонтрагентов.Организация КАК Организация,
| ДоговорыКонтрагентов.Контрагент КАК Контрагент
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
|ГДЕ
| ДоговорыКонтрагентов.Ссылка В(&МассивОбъектов)";
Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
Выборка = Запрос.Выполнить.Выбрать();
ПервыйДокумент = Истина;
Пока Выборка.Следующий() Цикл
Если Не ПервыйДокумент Тогда
// Добавляем разделитель страниц при печати нескольких договоров
ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
КонецЕсли;
ПервыйДокумент = Ложь;
// Запоминаем позицию начала вывода текущего объекта для БСП
НомерСтрокиНачала = ТабДок.ВысотаТаблицы + 1;
// Заполняем параметры области макета из текущей строки выборки
ОбластьТекст.Параметры.Заполнить(Выборка);
ТабДок.Вывести(ОбластьТекст);
// Регистрируем область печати для БСП
УправлениеПечатью.ЗадатьОбластьПечатиДокумента(ТабДок, НомерСтрокиНачала, ОбъектыПечати, Выборка.Ссылка);
КонецЦикла;
Возврат ТабДок;
КонецФункции
Благодаря этому подходу печатная форма встроится в конфигурацию максимально бесшовно и будет использовать все преимущества типовых механизмов: вывод на принтер, отправку по электронной почте, сохранение в форматы PDF, Excel или Word напрямую из стандартной формы предварительного просмотра.
Если перед вами стоит задача сохранить конфигурацию на поддержке без возможности редактирования (без снятия "замочка"), лучшим выбором будет разработка Внешней печатной формы (ВПФ). Она представляет собой внешний файл обработки с расширением .epf.
Чтобы БСП смогла распознать внешнюю обработку как печатную форму, в ее модуле объекта обязательно должна присутствовать экспортная функция СведенияОВнешнейОбработке(). Рассмотрим пример структуры модуля внешней обработки:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.1");
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиПечатнаяФорма();
ПараметрыРегистрации.Назначение.Добавить("Справочник.ДоговорыКонтрагентов");
ПараметрыРегистрации.Наименование = "Договор (внешний шаблон)";
ПараметрыРегистрации.Версия = "1.0";
ПараметрыРегистрации.БезопасныйРежим = Ложь;
НоваяКоманда = ПараметрыРегистрации.Команды.Добавить();
НоваяКоманда.Представление = "Печать договора (внешняя)";
НоваяКоманда.Идентификатор = "ВнешнийДоговор";
НоваяКоманда.Использование = ДополнительныеОтчетыИОбработкиКлиентСервер.ТипЗапускаВызовСерверногоМетода();
НоваяКоманда.ПоказыватьОповещение = Ложь;
Возврат ПараметрыРегистрации;
КонецФункции
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Сюда помещается аналогичная логика формирования печатной формы,
// описанная для модуля менеджера
КонецПроцедуры
После сохранения этой обработки в файл, администратор базы данных сможет просто загрузить ее в режиме "1С:Предприятие" через раздел НСИ и администрирование -> Дополнительные отчеты и обработки. Кнопка вызова появится на формах справочника автоматически.
В случаях, когда договор имеет сложную структуру, многостраничный текст и специфическое форматирование, обычный табличный макет MXL может оказаться неудобным для верстки. Платформа 1С предлагает современные альтернативные инструменты решения этой задачи:
.docx) или OpenOffice (в формате .odt). В этих документах параметры размечаются в специальном формате, например, {v8 Контрагент} или {v8 НомерДоговора}. Программа с помощью функций БСП считывает двоичные данные файла, заменяет параметры на реальные значения из базы данных 1С и отдает пользователю готовый текстовый документ. Огромным преимуществом этого подхода является то, что он работает непосредственно на сервере (в том числе под управлением ОС Linux) без использования тяжеловесных COM-соединений с MS Office.