Как передать данные для печати ценников с клиента на сервер в 1С 8.3?

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

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

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

Выясняем причину: почему падает код на тонком клиенте

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


&НаКлиенте
Процедура Печать(Команда)
    ТаблицаДанных = Новый ТаблицаЗначений; // Здесь произойдет ошибка!
    // Заполнение таблицы...
КонецПроцедуры

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

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

Рассмотрим несколько надежных решений этой задачи.

Решение 1. Использование табличной части объекта обработки

Самый простой и методологически верный способ — сделать таблицу со списком номенклатуры реквизитом самой обработки. Если на форме мы работаем с табличной частью, которая принадлежит объекту (например, Объект.Товары или Объект.ЗИП), то эти данные уже доступны на сервере без дополнительных ухищрений.

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

Код в модуле формы обработки:


&НаКлиенте
Процедура Печать(Команда)
    
    ТабДок = Новый ТабличныйДокумент;
    
    // Вызываем серверную процедуру для заполнения табличного документа
    СформироватьПечатнуюФормуНаСервере(ТабДок);
    
    // Настраиваем отображение и выводим пользователю
    ТабДок.ОтображатьСетку = Ложь;
    ТабДок.Показать("Печать ценников");
    
КонецПроцедуры

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

Использование метода РеквизитФормыВЗначение позволяет нам легко перекинуть данные из интерфейса в серверный контекст, где мы можем использовать весь стек объектных методов 1С.

Решение 2. Передача данных через массив структур (легковесный способ)

Что делать, если таблица на форме создана динамически или мы не хотим привязываться к реквизитам объекта? В таком случае отличным решением станет использование сериализуемых типов данных, которые без проблем «путешествуют» между клиентом и сервером. К таким типам относятся Массив и Структура.

Мы можем обойти строки нашей таблицы на клиенте, упаковать нужные параметры (например, ссылку на номенклатуру и цену отгрузки) в структуры, поместить их в массив и передать этот массив на сервер.

Посмотрим на пример реализации данного алгоритма:


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

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

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

Решение 3. Передача данных через Временное Хранилище

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

Алгоритм действий выглядит следующим образом: на сервере в контексте формы мы формируем таблицу значений, помещаем её во временное хранилище, передаем на клиент уникальный адрес-ссылку (строку), а затем передаем этот адрес в модуль менеджера. В модуле менеджера мы считываем таблицу обратно.

Рассмотрим код реализации данного метода:


&НаКлиенте
Процедура ПечатьЧерезХранилище(Команда)
    
    ТабДок = Новый ТабличныйДокумент;
    АдресХранилища = "";
    
    // Шаг 1. Переносим данные во временное хранилище на сервере
    ПодготовитьДанныеНаСервере(АдресХранилища);
    
    // Шаг 2. Формируем печатную форму на сервере без передачи тяжелых объектов
    СформироватьПечатнуюФормуПоАдресу(ТабДок, АдресХранилища);
    
    ТабДок.Показать();
    
КонецПроцедуры

&НаСервере
Процедура ПодготовитьДанныеНаСервере(АдресХранилища)
    
    // Создаем серверную ТаблицуЗначений напрямую из ТЧ формы
    ТаблицаДанных = Объект.ЗИП.Выгрузить();
    
    // Помещаем во временное хранилище с привязкой к уникальному идентификатору формы
    АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаДанных, УникальныйИдентификатор);
    
КонецПроцедуры

&НаСервереБезКонтекста
Процедура СформироватьПечатнуюФормуПоАдресу(ТабДок, АдресХранилища)
    
    // В модуле менеджера или общем модуле извлекаем данные
    ТаблицаДанных = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    Если ТипЗнч(ТаблицаДанных) = Тип("ТаблицаЗначений") Тогда
        Обработки.ПечатьЦенников.Ценники(ТабДок, ТаблицаДанных);
    КонецЕсли;
    
КонецПроцедуры

Оптимизация вывода ценников на лист: плиточный (сеточный) вывод

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

Для правильного размещения ценников на листе используют технологию плиточного (сеточного) вывода с контролем заполнения страницы по горизонтали и вертикали.

Давайте подробно разберем, как реализовать этот алгоритм в модуле менеджера вашей обработки:


Процедура Ценники(ТабДок, ТаблицаДанных) Экспорт

    Макет = ПолучитьМакет("МакетЦенника");
    ОбластьЦенника = Макет.ПолучитьОбласть("Ценник");
    
    // Очищаем табличный документ перед заполнением
    ТабДок.Очистить();
    
    // Задаем уникальный ключ параметров печати, чтобы система запоминала настройки принтера
    ТабДок.КлючПараметровПечати = "ПараметрыПечати_Ценники";
    
    // Устанавливаем минимальные поля для экономии места
    ТабДок.ПолеСверху = 5;
    ТабДок.ПолеСнизу = 5;
    ТабДок.ПолеСлева = 5;
    ТабДок.ПолеСправа = 5;
    
    // Создаем массив для проверки возможности вертикального вывода строки ценников
    МассивОбластейДляПроверки = Новый Массив;
    МассивОбластейДляПроверки.Добавить(ОбластьЦенника);
    
    ПервыйЦенникСтроки = Истина;
    
    Для Каждого СтрокаТовара Из ТаблицаДанных Цикл
        
        // Заполняем параметры макета данными товара
        ОбластьЦенника.Параметры.Номенклатура = СтрокаТовара.Номенклатура;
        ОбластьЦенника.Параметры.Цена = СтрокаТовара.Цена;
        
        Если ПервыйЦенникСтроки Тогда
            // Проверяем, поместится ли новая строка ценников по вертикали на текущем листе
            Если Не ТабДок.ПроверитьВывод(МассивОбластейДляПроверки) Тогда
                // Если не помещается, принудительно переносим вывод на новую страницу
                ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
            КонецЕсли;
            
            // Выводим первый ценник в новой строке
            ТабДок.Вывести(ОбластьЦенника);
            ПервыйЦенникСтроки = Ложь;
        Иначе
            // Проверяем, помещается ли очередной ценник по горизонтали в текущей строке
            Если ТабДок.ПроверитьПрисоединение(ОбластьЦенника) Тогда
                // Присоединяем ценник справа
                ТабДок.Присоединить(ОбластьЦенника);
            Иначе
                // Если по ширине листа место закончилось, переходим на новую строку
                // И проверяем, поместится ли она по вертикали
                Если Не ТабДок.ПроверитьВывод(МассивОбластейДляПроверки) Тогда
                    ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
                КонецЕсли;
                
                // Выводим ценник с новой строки методом Вывести()
                ТабДок.Вывести(ОбластьЦенника);
            КонецЕсли;
        EndIf;
        
    КонецЦикла;

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

Проанализируем ключевые методы, использованные в коде:

  1. ТабДок.ПроверитьВывод(МассивОбластей) — проверяет, уместится ли переданный массив областей по высоте страницы при текущих настройках принтера и полей. Возвращает Истина или Ложь. Если возвращается Ложь, мы программно добавляем разделитель страниц методом ВывестиГоризонтальныйРазделительСтраниц().
  2. ТабДок.ПроверитьПрисоединение(Область) — проверяет, помещается ли присоединяемая область по ширине страницы справа от уже выведенных областей. Если возвращается Ложь, текущая строка заполнена, и следующий ценник мы должны начать выводить с новой строки через метод Вывести().
  3. ТабДок.КлючПараметровПечати — позволяет сохранить пользовательские настройки параметров страницы (ориентация, масштаб, выбранный принтер) в системном реестре. При следующей печати ценников пользователю не придется заново настраивать масштаб и поля в диалоговом окне печати.

Подводя итоги

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

← На главную