Как перед выполнением команды печати в БСП запустить свою процедуру и передать параметры?

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

При работе с типовыми конфигурациями «1С», использующими Библиотеку стандартных подсистем (БСП), разработчики часто сталкиваются с необходимостью кастомизации стандартных механизмов. Одной из классических задач является печать ценников или этикеток не для всего документа, а только для определенных, выделенных пользователем строк табличной части.

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

Почему стандартный механизм регистрации команд печати имеет ограничения?

Давайте проанализируем, как работает БСП. Когда мы регистрируем команду печати в процедуре ДобавитьКомандыПечати, мы всего лишь описываем метаданные кнопки: ее имя, представление и обработчик, который будет запущен при нажатии. Этот код выполняется в момент инициализации формы, поэтому в процедуре регистрации физически невозможно получить ссылки на текущие выделенные строки — в этот момент пользователь еще даже не открыл форму или ничего в ней не выделил.

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

Способ 1. Использование собственного клиентского обработчика (Рекомендуемый)

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

Шаг 1. Модификация регистрации команды печати

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


&После("ДобавитьКомандыПечати")
Процедура Расш1_ДобавитьКомандыПечати(КомандыПечати)
    
    ПараметрыПечатиЭтикеток = Новый Структура;
    ПараметрыПечатиЭтикеток.Вставить("ИмяПроцедурыПодготовкиСтруктурыДанных", "УправлениеПечатьюРТВызовСервера.ПодготовитьСтруктуруДанныхЦенниковИЭтикеток");
    ПараметрыПечатиЭтикеток.Вставить("УстановитьРежим", "ПечатьЭтикеток");
    ПараметрыПечатиЭтикеток.Вставить("ИмяДокумента", "ОприходованиеТоваров");
    ПараметрыПечатиЭтикеток.Вставить("ЭтикеткаСтрокиНаПрямую", Истина);
    
    КомандаПечати = КомандыПечати.Добавить();
    КомандаПечати.Идентификатор = "ЭтикеткаСтрокиНаПрямую";
    КомандаПечати.Представление = НСтр("ru = 'Этикетка строки (выделенные)'");
    КомандаПечати.ПроверкаПроведенияПередПечатью = Ложь;
    
    // Указываем собственный клиентский обработчик из расширения:
    КомандаПечати.Обработчик = "Расш1_УправлениеПечатьюКлиент.ПечатьЭтикетокВыделенныхСтрок";
    
    КомандаПечати.ДополнительныеПараметры.Вставить("ПараметрыПечатиЦенниковИЭтикеток", ПараметрыПечатиЭтикеток);
    
КонецПроцедуры

Шаг 2. Создание общего клиентского модуля

Создадим в расширении общий модуль с именем Расш1_УправлениеПечатьюКлиент. В свойствах модуля обязательно устанавливаем флаг Клиент (управляемое приложение) и снимаем флаг Сервер.

Обратите внимание: Обработчик БСП должен быть объявлен именно как Функция (а не процедура), так как БСП вызывает его динамически через метод Вычислить(). Возвращать функция ничего не должна (возвращаем Неопределено).

Давайте напишем код этой функции, в котором получим форму-источник, прочитаем выделенные строки и передадим их в структуру параметров:


Функция ПечатьЭтикетокВыделенныхСтрок(ПараметрыКоманды) Экспорт
    
    // 1. Получаем форму, из которой была вызвана команда печати
    ФормаИсточника = ПараметрыКоманды.Источник; 
    
    // 2. Получаем доступ к элементу формы - табличной части (например, "Товары")
    ТаблицаФормы = ФормаИсточника.Элементы.Товары;
    ВыделенныеСтроки = ТаблицаФормы.ВыделенныеСтроки;
    
    // Если пользователь ничего не выбрал, выводим предупреждение и прерываем процесс
    Если ВыделенныеСтроки.Количество() = 0 Тогда
        ПоказатьПредупреждение(, НСтр("ru = 'Не выбраны строки для печати этикеток!'"));
        Возврат Неопределено;
    КонецЕсли;
    
    // 3. Собираем данные выделенных строк в массив структур.
    // Важно: ВыделенныеСтроки табличной части содержат числовые идентификаторы строк,
    // а не сами данные. Получить данные строки можно через метод ДанныеСтроки().
    МассивДанныхСтрок = Новый Массив;
    Для Каждого ИдентификаторСтроки Из ВыделенныеСтроки Цикл
        ДанныеСтроки = ТаблицаФормы.ДанныеСтроки(ИдентификаторСтроки);
        
        Если ДанныеСтроки = Неопределено Тогда
            Продолжить;
        КонецЕсли;
        
        СтруктураСтроки = Новый Структура;
        СтруктураСтроки.Вставить("Номенклатура", ДанныеСтроки.Номенклатура);
        
        // Проверяем наличие характеристики (для учета особенностей конфигурации)
        Если ПолучитьСвойстваОбъекта(ДанныеСтроки, "Характеристика") Тогда
            СтруктураСтроки.Вставить("Характеристика", ДанныеСтроки.Характеристика);
        КонецЕсли;
        
        СтруктураСтроки.Вставить("Количество", ДанныеСтроки.Количество);
        
        МассивДанныхСтрок.Добавить(СтруктураСтроки);
    КонецЦикла;
    
    // 4. Помещаем собранный массив в структуру дополнительных параметров команды
    ПараметрыПечатиЭтикеток = ПараметрыКоманды.ДополнительныеПараметры.ПараметрыПечатиЦенниковИЭтикеток;
    ПараметрыПечатиЭтикеток.Вставить("ДанныеВыделенныхСтрок", МассивДанныхСтрок);
    
    // 5. Передаем управление обратно в стандартный типовой обработчик Розницы
    УправлениеПечатьюРТКлиент.ОбработкаКомандыПечатиЦенниковИЭтикеток(ПараметрыКоманды);
    
    Возврат Неопределено;
    
КонецФункции

// Вспомогательная функция для безопасного чтения свойств строки табличной части
Функция ПолучитьСвойстваОбъекта(Объект, ИмяСвойства)
    Попытка
        Значение = Объект[ИмяСвойства];
        Возврат Истина;
    Исключение
        Возврат Ложь;
    КонецПопытки;
КонецФункции

Способ 2. Перехват выполнения через расширение формы (без общего модуля)

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

Давайте посмотрим, как программно внедриться в параметры команды, которые БСП хранит во временном хранилище формы:


&Перед("Подключаемый_ВыполнитьКоманду")
Процедура Расш1_ПередПодключаемый_ВыполнитьКоманду(Команда, Источник)
    
    // Проверяем, что нажата именно наша команда печати
    Если Найти(Команда.Имя, "ЭтикеткаСтрокиНаПрямую") > 0 Тогда
        
        // Получаем выделенные строки табличной части формы
        ВыделенныеСтроки = Элементы.Товары.ВыделенныеСтроки;
        
        МассивДанныхСтрок = Новый Массив;
        Для Каждого ИдентификаторСтроки Из ВыделенныеСтроки Цикл
            ДанныеСтроки = Элементы.Товары.ДанныеСтроки(ИдентификаторСтроки);
            Если ДанныеСтроки <> Неопределено Тогда
                СтруктураСтроки = Новый Структура;
                СтруктураСтроки.Вставить("Номенклатура", ДанныеСтроки.Номенклатура);
                СтруктураСтроки.Вставить("Количество", ДанныеСтроки.Количество);
                МассивДанныхСтрок.Добавить(СтруктураСтроки);
            КонецЕсли;
        КонецЦикла;
        
        // БСП хранит параметры подключенных команд во временном хранилище формы.
        // Извлечем их, модифицируем и запишем обратно.
        ДействиеКоманды = Элементы.ГруппаПечать.Команды.Найти("АдресКомандПечатиВоВременномХранилище");
        Если ДействиеКоманды <> Неопределено Тогда
            АдресКоманд = ДействиеКоманды.Действие;
            КомандыПечати = ПолучитьИзВременногоХранилища(АдресКоманд);
            
            Для Каждого СтрокаКоманды Из КомандыПечати Цикл
                Если СтрокаКоманды.Идентификатор = "ЭтикеткаСтрокиНаПрямую" Тогда
                    // Внедряем собранные данные в структуру параметров
                    СтрокаКоманды.ДополнительныеПараметры.ПараметрыПечатиЦенниковИЭтикеток.Вставить(
                        "ДанныеВыделенныхСтрок", МассивДанныхСтрок
                    );
                КонецЕсли;
            КонецЦикла;
            
            ПоместитьВоВременноеХранилище(КомандыПечати, АдресКоманд);
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Серверная часть: Обработка переданных параметров

Передать массив выделенных строк с клиента в параметры команды печати — это только половина дела. Стандартная процедура на сервере, которую вы указали при регистрации (УправлениеПечатьюРТВызовСервера.ПодготовитьСтруктуруДанныхЦенниковИЭтикеток), по умолчанию считывает документ целиком из базы данных по его ссылке. Она ничего не знает о переданном нами массиве.

Чтобы сервер обработал именно наши выделенные строки, нам нужно расширить эту функцию. Давайте перехватим ее с помощью аннотации &Вместо в модуле УправлениеПечатьюРТВызовСервера:


&Вместо("ПодготовитьСтруктуруДанныхЦенниковИЭтикеток")
Функция Расш1_ПодготовитьСтруктуруДанныхЦенниковИЭтикеток(ПараметрыПечати, ОбъектыПечати)
    
    // Проверяем, передали ли мы с клиента наш кастомный массив данных
    Если ПараметрыПечати.Свойство("ДанныеВыделенныхСтрок") Тогда
        
        // Здесь мы пишем свой собственный алгоритм заполнения структуры ценников/этикеток.
        // Вместо выполнения тяжелых запросов к базе данных, мы обходим переданный 
        // с клиента массив "ПараметрыПечати.ДанныеВыделенныхСтрок" и формируем 
        // стандартную структуру данных для обработки подсистемой печати.
        
        СтруктураДанных = Новый Структура;
        
        // Пример упрощенного заполнения структуры:
        ТаблицаТоваров = Новый ТаблицаЗначений;
        ТаблицаТоваров.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
        ТаблицаТоваров.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
        
        Для Каждого СтрокаДанных Из ПараметрыПечати.ДанныеВыделенныхСтрок Цикл
            НоваяСтрока = ТаблицаТоваров.Добавить();
            ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДанных);
        КонецЦикла;
        
        СтруктураДанных.Вставить("ТабличнаяЧастьТовары", ТаблицаТоваров);
        // Добавьте другие необходимые реквизиты, требуемые формой печати ценников
        
        Возврат СтруктураДанных;
    Иначе
        // Если нашей структуры нет, значит была вызвана стандартная команда.
        // Передаем управление базовому коду конфигурации.
        Возврат ПродолжитьВызов(ПараметрыПечати, ОбъектыПечати);
    КонецЕсли;
    
КонецФункции

Важные особенности работы с выделенными строками, о которых нужно помнить

Для успешной реализации и стабильной работы кода обратите внимание на следующие нюансы:

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

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

← На главную