Как в документе заказ покупателя отредактировать макет печатной формы и добавить расчет общего веса?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

В процессе работы с конфигурацией «1С:Управление торговлей 11» (а также в ERP и Комплексной автоматизации) перед разработчиками и аналитиками часто возникает задача кастомизации печатных форм. Типичный пример — необходимость вывода общего веса заказа в печатной форме документа Заказ клиента (в старых редакциях — Заказ покупателя). Этот показатель крайне важен для логистов и служб доставки, чтобы планировать загрузку транспорта.

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

Анатомия хранения весовых характеристик в УТ 11

Прежде чем приступать к кодингу и изменению макетов, проанализируем, как в современных типовых конфигурациях 1С хранятся данные о весе. Просто умножить поле «Количество» на реквизит «Вес» из карточки номенклатуры не всегда возможно по следующим причинам:

  1. Сложная структура реквизитов веса. В справочнике Номенклатура сведения о весе задаются через несколько реквизитов: ВесИспользовать (признак весового учета), ВесЕдиницаИзмерения, а также пару ВесЧислитель и ВесЗнаменатель.
  2. Учет числителя и знаменателя. Вес карточки товара часто задается дробным соотношением. Например, вес может быть указан как «15 кг на 10 штук». Чтобы рассчитать точный вес одной единицы товара, необходимо делить числитель на знаменатель.
  3. Использование упаковок. Если в документе включено использование упаковок номенклатуры (справочник УпаковкиЕдиницыИзмерения), вес может быть привязан к конкретной упаковке (например, коробке, паллете). Расчет логики должен проверять: если в строке документа выбрана упаковка, нужно брать вес упаковки; если нет — базовый вес номенклатуры.

Почему стандартное редактирование макетов в режиме «1С:Предприятие» не решит задачу?

В пользовательском режиме по пути Администрирование -> Печатные формы, отчеты и обработки -> Макеты печатных форм можно отредактировать оформление: изменить шрифты, добавить логотип, поменять местами поля или отредактировать статический текст. Однако добавить туда совершенно новый рассчитываемый параметр, такой как ОбщийВес, без доработки программного кода нельзя.

Если вы просто добавите в ячейку макета заполнение типа Параметр и назовете его ОбщийВесЗаказа, при выводе на печать система никак не заполнит это поле. В коде формирования печатной формы (в модуле менеджера документа) этот параметр не собирается и не передается в область табличного документа. Следовательно, ячейка на печати останется пустой. Нам потребуется вмешательство в программный код.

Способ №1. Использование расширения конфигурации (Рекомендуемый подход)

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

Разберем по шагам, как реализовать доработку через расширение:

  1. Откроем конфигуратор и создадим новое расширение (меню Конфигурация -> Расширения конфигурации -> Добавить).
  2. Заимствуем в расширение документ ЗаказКлиента, его форму документа и макет печатной формы (например, ПФ_MXL_ЗаказКлиента).
  3. В макете внутри нашего расширения находим подходящую область (например, подвал документа или область итогов) и добавляем новую ячейку. В свойствах ячейки устанавливаем свойство Заполнение в значение Параметр, а в поле Параметр пишем ОбщийВесЗаказа.
  4. Заимствуем модуль менеджера документа ЗаказКлиента в расширение для перехвата процедуры печати. Нам потребуется найти процедуру, отвечающую за вывод областей макета.

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


&После("ЗаполнитьПараметрыПечатиЗаказаКлиента")
Процедура Расш1_ЗаполнитьПараметрыПечатиЗаказаКлиента(ПараметрыПечати, ДанныеДокумента, ОбластьМакет)
    
    // Рассчитываем общий вес по табличной части документа
    ИтоговыйВес = 0;
    
    Для Каждого СтрокаТовара Из ДанныеДокумента.Товары Цикл
        
        // Получаем базовый вес из номенклатуры
        ВесНоменклатуры = 0;
        Если ЗначениеЗаполнено(СтрокаТовара.Номенклатура.ВесЧислитель) Тогда
            Знаменатель = ?(СтрокаТовара.Номенклатура.ВесЗнаменатель = 0, 1, СтрокаТовара.Номенклатура.ВесЗнаменатель);
            ВесНоменклатуры = СтрокаТовара.Номенклатура.ВесЧислитель / Знаменатель;
        КонецЕсли;
        
        // Если используется упаковка, берем ее вес
        Если ЗначениеЗаполнено(СтрокаТовара.Упаковка) И ПолучитьФункциональнуюОпцию("ИспользоватьУпаковкиНоменклатуры") Тогда
            ВесУпаковки = СтрокаТовара.Упаковка.Вес;
            Если ВесУпаковки > 0 Тогда
                ВесНоменклатуры = ВесУпаковки;
            КонецЕсли;
        КонецЕсли;
        
        ИтоговыйВес = ИтоговыйВес + (СтрокаТовара.Количество * ВесНоменклатуры);
        
    КонецЦикла;
    
    // Передаем рассчитанное значение в параметр области макета
    Попытка
        ОбластьМакет.Параметры.ОбщийВесЗаказа = ИтоговыйВес;
    Исключение
        // Исключаем ошибку, если в какой-то из областей макета параметр отсутствует
    КонецПопытки;

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

Способ №2. Использование внешней печатной формы (ВПФ)

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

Рассмотрим алгоритм разработки ВПФ:

  1. Создаем новую внешнюю обработку в конфигураторе.
  2. Добавляем макет табличного документа, скопировав его из типового документа ЗаказКлиента. Модифицируем скопированный макет, добавив ячейку-параметр ОбщийВесЗаказа в подвал макета.
  3. В модуле объекта внешней обработки прописываем экспортные функции, требуемые по стандарту БСП (Библиотеки Стандартных Подсистем) для регистрации внешней печатной формы: СведенияОВнешнейОбработке() и процедуру Печать().
  4. В процедуре формирования печатного документа пишем запрос к табличной части проводимого документа для извлечения информации по товарам, упаковкам и весовым характеристикам.

Проанализируем пример запроса внутри внешней обработки, который собирает данные о товарах и рассчитывает вес более оптимально с точки зрения СУБД:


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

Реализация расчета веса непосредственно на форме документа

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

  1. Создаем реквизит формы (или реквизит самого документа) с именем ИтоговыйВес и типом Число (15, 3).
  2. Выводим данный реквизит на форму документа в область подвала (рядом с итоговой суммой документа).
  3. Для табличной части Товары на события ПриИзменении, ТоварыКоличествоПриИзменении и ТоварыУпаковкаПриИзменении вешаем обработчик пересчета.

Разберем пример реализации клиент-серверного расчета веса на форме документа:


&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
    РассчитатьОбщийВесНаФорме();
КонецПроцедуры

&НаКлиенте
Процедура ТоварыУпаковкаПриИзменении(Элемент)
    РассчитатьОбщийВесНаФорме();
КонецПроцедуры

&НаКлиенте
Процедура РассчитатьОбщийВесНаФорме()
    
    ПараметрыСтрок = Новый Массив;
    
    Для Каждого Строка Из Объект.Товары Цикл
        СтруктураДанных = Новый Структура;
        СтруктураДанных.Вставить("Номенклатура", Строка.Номенклатура);
        СтруктураДанных.Вставить("Упаковка", Строка.Упаковка);
        СтруктураДанных.Вставить("Количество", Строка.Количество);
        ПараметрыСтрок.Добавить(СтруктураДанных);
    КонецЦикла;
    
    // Получаем вес серверным вызовом, чтобы не перегружать клиентский контекст обращениями к базе
    Объект.ИтоговыйВес = ПолучитьВесТоваровНаСервере(ПараметрыСтрок);
    
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПолучитьВесТоваровНаСервере(ПараметрыСтрок)
    
    СуммарныйВес = 0;
    
    Для Каждого СтрокаПараметров Из ПараметрыСтрок Цикл
        
        Если НЕ ЗначениеЗаполнено(СтрокаПараметров.Номенклатура) Тогда
            Продолжить;
        КонецЕсли;
        
        // Запрос к свойствам номенклатуры
        ВесЧислитель = СтрокаПараметров.Номенклатура.ВесЧислитель;
        ВесЗнаменатель = СтрокаПараметров.Номенклатура.ВесЗнаменатель;
        Знаменатель = ?(ВесЗнаменатель = 0, 1, ВесЗнаменатель);
        
        ВесЕдиницы = ВесЧислитель / Знаменатель;
        
        // Учет веса упаковки
        Если ЗначениеЗаполнено(СтрокаПараметров.Упаковка) Тогда
            ВесУпаковки = СтрокаПараметров.Упаковка.Вес;
            Если ВесУпаковки > 0 Тогда
                ВесЕдиницы = ВесУпаковки;
            КонецЕсли;
        КонецЕсли;
        
        СуммарныйВес = СуммарныйВес + (СтрокаПараметров.Количество * ВесЕдиницы);
        
    КонецЦикла;
    
    Возврат СуммарныйВес;
    
КонецФункции

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

Резюме

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

← На главную