Переход с обычных форм (например, УПП 1.3, УТ 10.3) на управляемые приложения и Библиотеку Стандартных Подсистем (БСП) в современных конфигурациях (УТ 11, ERP 2, КА 2, Бухгалтерия 3.0) принес множество изменений в привычную разработку. Одно из самых частых затруднений у разработчиков вызывает создание внешних печатных форм (ВПФ). В старых версиях для получения ссылки на документ использовался автоматически заполняемый реквизит СсылкаНаОбъект. В управляемом приложении этот механизм больше не работает.
В этой статье мы подробно разберем, как устроен механизм передачи ссылок в БСП, почему у некоторых разработчиков печатные формы выводятся пустыми, и как правильно написать код внешней печатной формы с нуля. При выполнении комплексного перехода на новую систему для сохранения истории учета используется перенос документов и остатков из УПП 1.3 в ERP.
В управляемом приложении 1С взаимодействие между клиентом и сервером строго разграничено. Концепция БСП предполагает универсальность: одна и та же печатная форма может вызываться сразу для целого списка выделенных в журнале документов (групповая печать). Именно поэтому вместо одной ссылки на объект система всегда передает массив ссылок, даже если пользователь нажал кнопку печати в форме только одного документа.
Все управление интеграцией внешней печатной формы берет на себя механизм дополнительных отчетов и обработок БСП. Для обмена данными используются параметры специальных экспортных процедур и функций, расположенных в модуле объекта внешней обработки. При массовой настройке или тестировании баз с новыми формами применяется инструмент для автоматического обновления и обслуживания баз 1С.
Чтобы система «поняла», к какому документу относится печатная форма и какие команды нужно добавить в подменю «Печать», в модуле объекта обработки должна быть определена экспортная функция СведенияОВнешнейОбработке. Давайте подробно рассмотрим пример ее реализации:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = Новый Структура;
// Определяем вид обработки - ПечатнаяФорма
ПараметрыРегистрации.Вставить("Вид", "ПечатнаяФорма");
// Указываем документы, в которых будет доступна эта печатная форма
МассивНазначений = Новый Массив;
МассивНазначений.Добавить("Документ.ТребованиеНакладная");
ПараметрыРегистрации.Вставить("Назначение", МассивНазначений);
ПараметрыРегистрации.Вставить("Наименование", "Акт установки запчастей (ВПФ)");
ПараметрыРегистрации.Вставить("Версия", "1.1");
ПараметрыРегистрации.Вставить("БезопасныйРежим", Истина);
ПараметрыРегистрации.Вставить("Информация", "Дополнительная печатная форма Акт установки запчастей");
// Создаем таблицу команд
ТаблицаКоманд = ПолучитьТаблицуКоманд();
// Добавляем команду печати
// Параметры: ТаблицаКоманд, Представление, Идентификатор, Использование, ПоказыватьОповещение, Модификатор
ДобавитьКоманду(ТаблицаКоманд,
"Акт установки запчастей",
"АктУстановкиЗапЧастей",
"ВызовСерверногоМетода",
Истина,
"ПечатьMXL");
ПараметрыРегистрации.Вставить("Команды", ТаблицаКоманд);
Возврат ПараметрыРегистрации;
КонецФункции
Обратите внимание на параметр Использование со значением "ВызовСерверногоМетода". Это стандартный и наиболее производительный вариант запуска, при котором формирование печатной формы происходит полностью на стороне сервера без открытия вспомогательных окон.
Вспомогательные процедуры для подготовки таблицы команд выглядят стандартным образом:
Функция ПолучитьТаблицуКоманд()
Команды = Новый ТаблицаЗначений;
Команды.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка"));
Команды.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Строка"));
Команды.Колонки.Добавить("Использование", Новый ОписаниеТипов("Строка"));
Команды.Колонки.Добавить("ПоказыватьОповещение", Новый ОписаниеТипов("Булево"));
Команды.Колонки.Добавить("Модификатор", Новый ОписаниеТипов("Строка"));
Возврат Команды;
КонецФункции
Процедура ДобавитьКоманду(ТаблицаКоманд, Представление, Идентификатор, Использование, ПоказыватьОповещение = Ложь, Модификатор = "")
НоваяКоманда = ТаблицаКоманд.Добавить;
НоваяКоманда.Представление = Представление;
НоваяКоманда.Идентификатор = Идентификатор;
НоваяКоманда.Использование = Использование;
НоваяКоманда.ПоказыватьОповещение = ПоказыватьОповещение;
НоваяКоманда.Модификатор = Модификатор;
КонецProcedures
Для серверных печатных форм (режим "ВызовСерверногоМетода") точка входа — это экспортная процедура Печать в модуле объекта обработки. Давайте проанализируем её сигнатуру:
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Здесь МассивОбъектов содержит ссылки на документы, из которых была вызвана печать.
КонецПроцедуры
Рассмотрим назначение ключевых параметров:
ДокументСсылка.ТребованиеНакладная). Именно его мы используем для построения запросов к базе данных.Если же вы используете открытие формы обработки перед печатью (режимы "ВызовКлиентскогоМетода" или "ОткрытиеФормы"), то в модуле формы обработки процедура печати принимает другие параметры:
&НаКлиенте
Процедура Печать(ИдентификаторКоманды, ОбъектыНазначения) Экспорт
// ОбъектыНазначения - массив ссылок на исходные документы
КонецПроцедуры
Давайте выясним причину, почему у некоторых разработчиков (как в сообщении №7 на форуме) печатная форма выводится на экран, но остается абсолютно пустой. Рассмотрим типичный ошибочный код и исправим его. Для автоматического поиска расхождений в данных после правок кода или доработок учета используется программа сверки бухгалтерских и налоговых проводок.
Ошибка №1: Неправильный обход результатов запроса во вложенных таблицах.
Часто разработчики выбирают данные табличной части через точку от шапки документа:
// ОШИБОЧНЫЙ ЗАПРОС
Запрос.Текст = "ВЫБРАТЬ ТребованиеНакладная.Материалы.(Номенклатура, Количество) ИЗ Документ.ТребованиеНакладная КАК ТребованиеНакладная ...";
При таком подходе результат запроса возвращает строки, где поле Материалы является вложенной выборкой. Если вы попытаетесь обойти их простой конструкцией Выборка.Количество в основном цикле, система вернет пустое значение, так как обращаться нужно к подчиненной выборке.
Ошибка №2: Двойной вызов метода Следующий().
Посмотрите на этот фрагмент кода:
Выборка = Запрос.Выполнить().Выбрать();
Выборка.Следующий(); // Первый сдвиг курсора (встали на 1-ю строку)
Пока Выборка.Следующий() Цикл // Второй сдвиг курсора!
// Этот цикл никогда не выполнится, если в выборке всего один документ!
КонецЦикла;
Вызывая Выборка.Следующий() перед началом цикла Пока, вы сдвигаете указатель на первую запись. Заходя в цикл, вы снова сдвигаете его на вторую запись. Если печатается один документ, цикл просто пропускается, и табличный документ остается без строк.
Ошибка №3: Избыточное разыменование ссылок.
Конструкция МассивОбъектов[0].Ссылка не имеет смысла, так как элемент массива МассивОбъектов[0] уже является готовой ссылкой. Добавление свойства .Ссылка заставляет платформу делать лишнее неявное обращение к базе данных.
Давайте соберем правильный, оптимизированный и надежный код для модуля объекта внешней печатной формы «Акт установки запчастей». В качестве эталона реализации можно изучить пример внешней печатной формы для актуальных версий БСП.
Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
// Проверяем, нужно ли формировать наш макет
ИмяМакета = "АктУстановкиЗапЧастей";
Если УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, ИмяМакета) Тогда
// Формируем табличный документ
ТабДокумент = СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати);
// Выводим его в коллекцию печатных форм
УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
КоллекцияПечатныхФорм,
ИмяМакета,
"Акт установки запчастей",
ТабДокумент
);
КонецЕсли;
КонецПроцедуры
Функция СформироватьПечатнуюФорму(МассивОбъектов, ОбъектыПечати)
ТабДокумент = Новый ТабличныйДокумент;
ТабДокумент.АвтоМасштаб = Истина;
// Получаем макет, сохраненный во внешней обработке
Макет = ПолучитьМакет("АктУстановкиЗапЧастей");
ОбластьШапка = Макет.ПолучитьОбласть("ОбластьШапка");
ОбластьСтрока = Макет.ПолучитьОбласть("ОбластьСтрока");
ОбластьПодвал = Макет.ПолучитьОбласть("ОбластьПодвал");
// Оптимальный запрос: выбираем данные напрямую из табличной части
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("МассивОбъектов", МассивОбъектов);
Запрос.Текст =
"ВЫБРАТЬ
| Материалы.Ссылка КАК Ссылка,
| Материалы.Ссылка.Номер КАК Номер,
| Материалы.Ссылка.Дата КАК Дата,
| Материалы.Номенклатура.Представление КАК НоменклатураПредставление,
| Материалы.Количество КАК Количество,
| Материалы.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
| Материалы.Себестоимость КАК Цена,
| Материалы.Количество * Материалы.Себестоимость КАК Сумма
|ИЗ
| Документ.ТребованиеНакладная.Материалы КАК Материалы
|ГДЕ
| Материалы.Ссылка В(&МассивОбъектов)
|ИТОГИ BY
| Ссылка";
// Выполняем запрос и обходим результаты по группировкам (по документам)
ВыборкаПоДокументам = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоДокументам.Следующий() Цикл
// Заполняем параметры шапки для каждого отдельного документа
ОбластьШапка.Параметры.Заполнить(ВыборкаПоДокументам);
ОбластьШапка.Параметры.НомерДок = ПрефиксацияОбъектовКлиентСервер.ПолучитьНомерНаПечать(ВыборкаПоДокументам.Номер);
ОбластьШапка.Параметры.ДатаДок = Формат(ВыборкаПоДокументам.Дата, "ДФ=dd.MM.yyyy");
ТабДокумент.Вывести(ОбластьШапка);
// Обходим строки табличной части текущего документа
ВыборкаСтрок = ВыборкаПоДокументам.Выбрать();
НомерСтроки = 0;
ИтогоСумма = 0;
Пока ВыборкаСтрок.Следующий() Цикл
НомерСтроки = НомерСтроки + 1;
ОбластьСтрока.Параметры.Заполнить(ВыборкаСтрок);
ОбластьСтрока.Параметры.Сч = НомерСтроки;
ТабДокумент.Вывести(ОбластьСтрока);
ИтогоСумма = ИтогоСумма + ВыборкаСтрок.Сумма;
КонецЦикла;
// Заполняем и выводим подвал
ОбластьПодвал.Параметры.СуммаИтог = ИтогоСумма;
ОбластьПодвал.Параметры.СуммаПрописью = РаботаСЧислами.ЧислоПрописью(ИтогоСумма, "L=ru_RU", "рубль, рубля, рублей, м, копейка, копейки, копеек, ж, 2");
ТабДокумент.Вывести(ОбластьПодвал);
// Добавляем документ в список объектов печати для БСП
УправлениеПечатью.ЗадатьОбластьПечатиДокумента(ТабДокумент, НомерСтроки, ОбъектыПечати, ВыборкаПоДокументам.Ссылка);
// Если документов несколько, выводим каждый на новой странице
ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц();
КонецЦикла;
Возврат ТабДокумент;
КонецФункции
Важно помнить, что некоторые отраслевые конфигурации (например, решения от компании «Рарус», такие как «Альфа-Авто») могут использовать собственные механизмы для регистрации и формирования внешних печатных форм.
В таких конфигурациях может полностью отсутствовать общий модуль УправлениеПечатью. В этом случае вы не сможете использовать стандартный метод БСП УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию. Для подобных систем структуру кода и состав параметров процедуры Печать необходимо брать из встроенной справки или по аналогии с уже существующими в конфигурации внешними обработками. Для быстрого изучения доступных функций подсистем можно использовать справочник по методам БСП с примерами использования.
Для успешной передачи ссылки на документ во внешнюю печатную форму в управляемом приложении 1С достаточно соблюдать три простых правила:
Печать через первый параметр МассивОбъектов.В(&МассивОбъектов).