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