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

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

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

1. Почему дублируются строки табличной части?

Давайте посмотрим на классическую ошибку в построении запроса для печатной формы. Зачастую разработчики пытаются получить все данные (и шапку, и табличную часть) одним запросом, используя ЛЕВОЕ СОЕДИНЕНИЕ. Проанализируем исходный код запроса:


ВЫБРАТЬ
    шапка.Ссылка КАК Ссылка1,
    шапка.Номер КАК Номер,
    шапка.Контрагенты КАК Контрагенты,
    табчасть.Товар КАК Товар,
    табчасть.Количество КАК Количество
ИЗ
    шапка КАК шапка
        ЛЕВОЕ СОЕДИНЕНИЕ табчасть КАК табчасть
        ПО (табчасть.Ссылка = шапка.Ссылка)
СГРУППИРОВАТЬ ПО
    табчасть.Товар,
    табчасть.Количество...

Выясним причину проблемы. Когда мы объединяем шапку и табличную часть таким образом, а затем пытаемся сгруппировать данные по количеству и цене, мы неизбежно сталкиваемся с декартовым произведением (задвоением). Допустим, в документе есть две строки с одинаковым товаром, одинаковой ценой и количеством. При таком соединении и группировке база данных не сможет их различить, или, наоборот, при неправильных связях строки умножатся. В результате вместо ожидаемых двух строк на печать может вывестись больше, а итоги поедут.

Как сделать правильно: Откажемся от соединения шапки и табличной части. Современный и оптимальный паттерн в 1С — это использование пакетных запросов. В первом запросе пакета мы выбираем только данные шапки, а во втором — только данные табличной части. Это полностью исключает задвоение строк и делает код чище.

2. Исправляем ошибки обхода выборки

Рассмотрим еще одну частую ошибку — неправильный обход результата запроса. В исходном коде мы видим следующую конструкцию:


Выборка1 = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);      
Пока Выборка1.Следующий() цикл
    Выборка2 = Выборка1.Выбрать();
    Пока Выборка2.Следующий() цикл
        Выборка = РезультатЗапроса.Выбрать(); // Ошибка здесь!
        Пока Выборка.Следующий() Цикл
            СтрокиТаблицы.Параметры.Заполнить(Выборка);
            Табдок.Вывести(СтрокиТаблицы);
        КонецЦикла;
    КонецЦикла;
КонецЦикла;

Вложенный вызов РезультатЗапроса.Выбрать() внутри цикла сбрасывает текущий контекст и начинает обход данных заново, игнорируя предыдущие группировки. Если мы используем пакетный запрос (как рекомендовано выше), нам вообще не понадобятся сложные выборки по группировкам. Мы просто выполним пакет, получим массив результатов и обойдем их последовательно: сначала шапку, затем табличную часть.

3. Избавляемся от получения данных через точку

Обратим внимание на заполнение параметров шапки макета:


ОбластьШапка.Параметры.Номер = Ссылка.Номер;
ОбластьШапка.Параметры.Дата = Формат(Ссылка.Дата, "ДЛФ=DD");
ОбластьШапка.Параметры.Контрагент = Ссылка.Контрагент;

Здесь кроется серьезная проблема производительности. Каждое обращение к реквизиту через точку (например, Ссылка.Контрагент) вызывает неявный запрос к базе данных. Если таких реквизитов много, система будет тормозить. Все необходимые данные, включая номер, дату и контрагента, нужно получать в том же запросе, который мы используем для подготовки данных. В макете параметр должен совпадать с именем поля в запросе (например, псевдоним шапка.Контрагенты КАК Контрагент).

4. Оптимизация работы с серверным контекстом и БСП

Разберем архитектурные моменты. В коде часто встречается строка:


Обработка = РеквизитФормыВЗначение("Объект");
МакетОбработки = Обработка.ПолучитьМакет("ПечатнаяФорма");

Использование метода РеквизитФормыВЗначение только ради получения макета — это избыточная и ресурсоемкая операция. Серверу приходится конвертировать все данные формы в объект.

Посмотрим на правильный подход. Современные печатные формы разрабатываются по стандартам Библиотеки стандартных подсистем (БСП). Вся логика печати должна находиться в модуле объекта внешней обработки, в экспортной процедуре Печать(). Там метод ПолучитьМакет() доступен напрямую. Документ не нужно извлекать как объект, достаточно передать массив ссылок на печать и обработать их запросом.

5. Форматирование данных: код vs макет

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

То же самое касается представлений ссылок (например, номенклатуры). Использование функции ПРЕДСТАВЛЕНИЕ() на этапе формирования временных таблиц снижает производительность. Группируйте данные по самим ссылкам, а системное поле Представление получайте в самой последней выборке перед выводом в макет.

6. Требования к табличным документам

Не забываем про стандарты интерфейса 1С. Чтобы гарантировать, что табличный документ поместится на страницу принтера по ширине, обязательно задайте свойство:

ТабДок.АвтоМасштаб = Истина;

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

← На главную