Мой Best Practics. Красивый код в печатных формах

Публикация № 1261956

Разработка - Печать - Универсальные печатные формы

Внешняя печатная форма Красивый код шаблон

Мой Best Practics. Красивый код в печатных формах. Формирование печатных форм.

В процессе анализа своего старого кода иногда удивляешься: кто это написал? ну кто так пишет? Всегда хочется это все переделать и оптимизировать. 

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

Попытаюсь выложить здесь свой "дзен". Хочу поговорить про красоту кода во внешних печатных формах для библиотеки стандартных подсистем.

Версия БСП особо роли не играет. Стандарты написания внешних печатных форм 1С не меняет уже очень давно. Мне кажется еще с 8.0, независимо от типов форм: обычные или управляемые.

Итак, поехали.

Макет печатной формы.

Для себя я выработал несколько правил:

1. Если в печатной форме доступен только один макет - называю его так же как и имя внешней обработки.

2. В общем случае, когда в макете выводится некоторая коллекция - всегда определяю минимум 4 именованные области строк:

  • Заголовок - Область выше таблицы
  • Шапка - Шапка таблицы. Всегда выделяю отдельной областью. В случае чего позволит повторять шапку на каждой странице 
  • Строка - Строка коллекции
  • Подвал - Строки ниже таблицы

Если в таблице присутствуют итоги - добавляю 5ю область Итог. Имена областей практически во всех печатных формах одинаковые.

3. Имена полей всегда стараюсь писать без сокращений. К примеру, НоменклатураПоставщика вместо НомПост. Допускаю сокращение, если невозможно назвать поле без сокращений короче 30-35 символов.

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

Формирование табличного документа

Чаще всего формирование печатной формы выполняю в функции с именем СформироватьПечатнуюФорму, хотя никто не мешает назвать ее как угодно. Для унификации принял для себя такое правило.

В минимальном варианте в качестве параметров этой функции достаточно одного: МассивОбъектов. В своей конфигурации я много встречал кода других программистов, которые в качестве параметра этой функции передавали только одну ссылку. Я считаю это грубой ошибкой, поэтому хочу отдельно обратить на это внимание.

Функционально можно разделить функцию СформироватьПечатнуюФорму на несколько областей:

  • Инициализация переменных
  • Запрос
  • Обход запроса и формирование табличного документа.

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

ТабличныйДокумент = Новый ТабличныйДокумент;

Макет = ПолучитьМакет("Макет");

ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");

ОбластьШапка = Макет.ПолучитьОбласть("Шапка");

ОбластьСтрока = Макет.ПолучитьОбласть("Строка");

ОбластьИтог = Макет.ПолучитьОбласть("Итог");

ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");

Отдельная переменная для каждой области макета позволит избежать путаницы при заполнении табличного документа.

Запрос.

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

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

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

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

Заполнение табличного документа.

Запрос построен, теперь надо вывести данные в табличный документ.

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

В минимальном варианте код обхода результата запроса примерно такой:

ВыборкаСсылка = Запрос.Выполнить().Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаСсылка.Следующий() Цикл
 ЗаполнитьЗначенияСвойств(ОбластьЗаголовок.Параметры, ВыборкаСсылка);
 ТабличныйДокумент.Вывести(ОбластьЗаголовок);
 ТабличныйДокумент.Вывести(ОбластьШапка);
 Выборка = ВыборкаСсылка.Выбрать();
 Пока Выборка.Следующий() Цикл
  ЗаполнитьЗначенияСвойств(ОбластьСтрока.Параметры, Выборка);
  ТабличныйДокумент.Вывести(ОбластьСтрока);
 КонецЦикла;
 ЗаполнитьЗначенияСвойств(ОбластьИтог.Параметры, ВыборкаСсылка);
 ТабличныйДокумент.Вывести(ОбластьИтог);
 ЗаполнитьЗначенияСвойств(ОбластьПодвал.Параметры, ВыборкаСсылка);
 ТабличныйДокумент.Вывести(ОбластьПодвал);
 ТабличныйДокумент.ВывестиГоризонтальныйРазделительСтраниц();
КонецЦикла;

// Ну и в конце формирования обязательно задаем параметры страницы
ТабличныйДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
ТабличныйДокумент.Автомасштаб = Истина;

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

Отладка.

Ну и в завершение пару слов про отладочную форму обработки.

Я использую следующие реквизиты формы:

  • Ссылка - ссылка на объект, для которого формируется печатная форма
  • ТабличныйДокумент - для просмотра сформированной формы
  • Команда формы Сформировать

На форму вывожу Ссылку и ТабличныйДокумент. Кнопку Сформировать в командную панель.

У формы устанавливаю свойство АвтоматическоеСохранениеДанныхВНастройках = Использовать, 

у кнопки "Сформировать" устанавливаю галочку КнопкаПоУмолчанию

У реквизита "ссылка" ставлю галочку "Сохранение"

Ну и код формы всегда одинаков:


&НаСервере
Процедура СформироватьНаСервере()
	МассивОбъектов = Новый Массив;
	МассивОбъектов.Добавить(Ссылка);
	ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
	ТабличныйДокумент = ОбработкаОбъект.СформироватьПечатнуюФорму(МассивОбъектов);
КонецПроцедуры

&НаКлиенте
Процедура Сформировать(Команда)
	СформироватьНаСервере();
КонецПроцедуры

Заключение.

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

Тестировалось на конфигурации Управление торговлей 11, Но можете использовать на любой конфигурации, основанной на библиотеке стандартных подсистем начиная с версии 2.1 и выше.

Скачать файлы

Наименование Файл Версия Размер
Мой Best Practics. Красивый код в печатных формах.: Шаблон печатной формы

.epf 9,64Kb
3
.epf 9,64Kb 3 Скачать

Специальные предложения

Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. forseil 209 08.07.20 17:41 Сейчас в теме
Надо как в ЗУПе, обработку, к ней отчет на СКД, сбор запроса кусками, вот где красота
myoker; Риник; SpartakM; TMV; ubnkfl; Terve!R; Ta_Da; amd1986; +8 Ответить
2. Rustig 1717 08.07.20 18:17 Сейчас в теме
(0), (1)
я делаю так, когда вижу запрос кусками: для определенных входных параметров собираю через отладчик единый текст запроса, копирую в буфер, закрываю отладочный режим, копирую текст запроса тут же в модуль, комментирую, подписываю что за запрос.

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

При этом я храню исходный полный текст запроса и измененный текст запроса без кусков - оба могут пригодиться.

Вот бы был порядок, если разработчики вставляли полный текст запросов без сборки по кускам в определенных местах описания алгоритма, чтобы не тратить время на сборку....
andy_zhav; +1 Ответить
5. AlX0id 09.07.20 08:12 Сейчас в теме
(2)
Вот бы был порядок, если разработчики вставляли полный текст запросов без сборки по кускам в определенных местах описания алгоритма, чтобы не тратить время на сборку....

Так от хорошей жизни запрос кусками не собирают же ж.. Все варианты не вставишь.
Мне обычно помогает отладка отложенных объектов из инструментов разработчика в сложносочиненных запросах.
Риник; Aleksandr55555; +2 Ответить
3. kuzyara 1141 09.07.20 05:02 Сейчас в теме
Все описанное вроде бы очевидно, но спасибо за грамотное изложение.
Я ещё вот такой приёмчик использую: visualizing-code-to-fail-faster
vano-ekt; SpartakM; Alexsur; YPermitin; pm74; Rustig; artbear; +7 Ответить
4. andy_zhav 191 09.07.20 07:57 Сейчас в теме
(3) да, это очевидно, но рассчитано на новичков. В последнее время часто вижу код, от которого волосы дыбом становятся. Это выводит меня из равновесия. Решил хоть так исправить это :-)
7. Di_Denis 305 21.07.20 17:56 Сейчас в теме
Я делаю так, последнее время
Процедура Печать(МассивОбъектов, ПараметрыПечати, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт

	// Печать накладной на приход.
	НужноПечататьМакет = УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "Накладная");
	Если НужноПечататьМакет Тогда
		УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
			КоллекцияПечатныхФорм,
			"Накладная",
			НСтр("ru = 'Накладная на приход товаров'"),
			ПечатнаяФормаПриходТоваров(МассивОбъектов, ОбъектыПечати),
			,
			"Документ.РЗ_АктОбмера.ПФ_MXL_НакладнаяНаПриходТовара");
	КонецЕсли;
	
	НужноПечататьМакет = УправлениеПечатью.НужноПечататьМакет(КоллекцияПечатныхФорм, "СортировочныйЛист");
	Если НужноПечататьМакет Тогда
		УправлениеПечатью.ВывестиТабличныйДокументВКоллекцию(
			КоллекцияПечатныхФорм,
			"СортировочныйЛист",
			НСтр("ru = 'Сортировочный лист'"),
			ПечатнаяФормаСортировочныйЛист(МассивОбъектов, ОбъектыПечати),
			,
			"Документ.РЗ_АктОбмера.ПФ_MXL_СортировочныйЛист");
	КонецЕсли;

		
		
КонецПроцедуры

#Область СлужебныеПроцедурыИФункции

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

		
		ТаблицаТовары = Шапка.Товары.Выгрузить();
		ДанныеПечати.Вставить("Количество", ТаблицаТовары.Итог("Количество"));
		
		МассивОбластейМакета = Новый Массив;
		МассивОбластейМакета.Добавить("Заголовок");
		МассивОбластейМакета.Добавить("ШапкаТаблицы");
		МассивОбластейМакета.Добавить("Строка");
		МассивОбластейМакета.Добавить("Подвал");
		МассивОбластейМакета.Добавить("Подписи");
		
		Для Каждого ИмяОбласти Из МассивОбластейМакета Цикл
			ОбластьМакета = Макет.ПолучитьОбласть(ИмяОбласти);
			Если ИмяОбласти <> "Строка" Тогда
				ЗаполнитьЗначенияСвойств(ОбластьМакета.Параметры, ДанныеПечати);
				ТабличныйДокумент.Вывести(ОбластьМакета);
			Иначе
				Для Каждого СтрокаТаблицы Из ТаблицаТовары Цикл
					ОбластьМакета.Параметры.Заполнить(СтрокаТаблицы);
					ОбластьМакета.Параметры.Номенклатура = "" + СтрокаТаблицы.Номенклатура + " ("+ РЗ_Лесозавод.ОпределитьХарактеристикуНоменклатуры(СтрокаТаблицы.Номенклатура,СтрокаТаблицы.Номенклатура.Порода,СтрокаТаблицы.Сорт,СтрокаТаблицы.Длина,СтрокаТаблицы.Диаметр,Ложь)+")";
					ТабличныйДокумент.Вывести(ОбластьМакета);
				КонецЦикла;
			КонецЕсли;
		КонецЦикла;

		УправлениеПечатью.ЗадатьОбластьПечатиДокумента(ТабличныйДокумент, НомерСтрокиНачало, ОбъектыПечати, Шапка.Ссылка);
	
	КонецЦикла;
	
	Возврат ТабличныйДокумент;
	
	
КонецФункции

#КонецОбласти



Показать
8. andy_zhav 191 22.07.20 09:43 Сейчас в теме
(7) НужноПечататьМакет тоже использую. Но только для встроенных печатных форм. Во внешних печатных формах имеет смысл использовать только если имеется больше одного макета в обработке и больше одной команды печати.

По поводу формирования табличного документа - очень сильно напоминает код из типовых конфигураций. Более сложный, чем мой и менее гибкий. Я предлагаю использовать по максимуму команду ЗаполнитьЗначенияСвойств и получение данных в запросе с использованием итогов.
Ваша процедура выглядела бы так:

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


Не уверен что это производительней, но в плане универсальности точно лучше.
9. Cyberhawk 127 31.07.20 18:28 Сейчас в теме
в одной транзакции обращения к базе данных
Это что такое?
10. andy_zhav 191 31.07.20 23:05 Сейчас в теме
(9)имею в виду одним запросом или пакетом запросов
Оставьте свое сообщение

См. также

Публикаций не найдено

Попробуйте расширить область поиска, проверьте поисковый запрос и повторите попытку.

Или закажите индивидуальную разработку вашего решения.

Создать заказ на разработку