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