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