Как вывести номер строки и единицу измерения товара в чеке ККТ в 1С:Розница

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

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

Выясняем причину проблемы: где формируется нефискальный чек

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

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

Решение проблемы №1: Объединение номера строки и наименования товара

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

Рассмотрим по шагам, как изменить код. Найдем в цикле получения позиций следующую строку:


Наименование = ?(ПозицияЧека.Свойство("Наименование"), ПозицияЧека.Наименование, "");

Сразу после нее добавим логику конкатенации номера строки и названия товара. Используем функцию Формат с параметром ЧГ=, чтобы исключить разделение групп тысяч в номере строки, если позиций в чеке окажется много:

Наименование = Формат(ИндексМассива + 1, "ЧГ=") + ". " + Наименование;

После этой несложной доработки чек примет гораздо более компактный вид: номер строки и название товара будут выводиться строго на одной строчке.

Решение проблемы №2: Добавление единиц измерения к количеству товара

Следующая задача — вывести единицу измерения товара (например, "шт" или "кг") рядом с количеством товара в формате: 1.000 шт x 5.00 руб = 5.00 руб.

Попытка напрямую обратиться к справочнику номенклатуры внутри модуля МенеджерОборудованияКлиентСервер вызовет ошибку компиляции Переменная не определена (Справочники). Выясним причину этой ошибки. Модуль МенеджерОборудованияКлиентСервер компилируется в том числе на тонком клиенте, где непосредственный доступ к объектным данным базы данных (к которым относится менеджер справочников Справочники) невозможен.

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

Шаг 1. Создание серверной функции получения единицы измерения

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

Добавим в конец модуля МенеджерОборудованияВызовСервера следующую экспортную функцию:


Функция ПолучитьЕдиницуИзмеренияНоменклатуры(НаименованиеТовара) Экспорт
    
    // Выполняем поиск номенклатуры по наименованию
    НоменклатураСсылка = Справочники.Номенклатура.НайтиПоНаименованию(НаименованиеТовара);
    
    Если ЗначениеЗаполнено(НоменклатураСсылка) Тогда
        Возврат " " + СокрЛП(НоменклатураСсылка.ЕдиницаИзмерения);
    КонецЕсли;
    
    Возврат "";
    
КонецФункции

Шаг 2. Разбор критической ошибки связывания данных

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


// ОШИБОЧНЫЙ КОД!
Наименование = Формат(ИндексМассива + 1, "ЧГ=") + ". " + Наименование;
Единицы = МенеджерОборудованияВызовСервера.ПолучитьЕдиницуИзмеренияНоменклатуры(Наименование);

То серверный метод НайтиПоНаименованию попытается найти в базе данных номенклатуру с названием "1. Молоко". Естественно, поиск завершится неудачей, и единица измерения возвращена не будет. Поиск по базе должен осуществляться строго по оригинальному, "чистому" наименованию товара до момента наложения маски с номером строки.

Шаг 3. Итоговая сборка рабочего кода формирования строки товара

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


Если ПозицияЧека.Sвойство("ФискальнаяСтрока") Тогда
    
    // 1. Получаем исходное наименование товара
    ОригинальноеНаименование = ?(ПозицияЧека.Свойство("Наименование"), ПозицияЧека.Наименование, "");
    
    // 2. Получаем единицу измерения с сервера по чистому наименованию
    ЕдиницаИзмерения = "";
    Если ЗначениеЗаполнено(ОригинальноеНаименование) Тогда
        ЕдиницаИзмерения = МенеджерОборудованияВызовСервера.ПолучитьЕдиницуИзмеренияНоменклатуры(ОригинальноеНаименование);
    КонецЕсли;
    
    // 3. Формируем красивое наименование с номером строки для печати
    НаименованиеДляПечати = Формат(ИндексМассива + 1, "ЧГ=") + ". " + ОригинальноеНаименование;
    
    // Строим строку наименования с переносом по ширине ленты
    ТекстСтроки = ПостроитьПолеПереносом(НаименованиеДляПечати, ШиринаСтроки) + Символы.ПС;
    
    // 4. Расчет цены и формирование строки количества с единицей измерения
    Количество = ?(ПозицияЧека.Свойство("Количество"), ПозицияЧека.Количество, 1);
    Сумма = ?(ПозицияЧека.Свойство("Сумма"), ПозицияЧека.Сумма, 0);
    ЦенаДляПечати = Окр(Сумма / Количество, 2);
    
    // Форматируем строку товара, внедряя полученную единицу измерения
    СтрокаТовара = Формат(Количество, "ЧРД=.;ЧЦ=12;ЧДЦ=3;ЧН=0.000;ЧГ=0") + ЕдиницаИзмерения 
                 + " х " + Формат(ЦенаДляПечати, ФорматЧисла) + " руб." 
                 + " =" + Формат(Сумма, ФорматЧисла) + " руб.";
                 
    ТекстСтроки = ТекстСтроки + ВыстроитьПоля(, СтрокаТовара, ШиринаСтроки) + Символы.ПС;

Анализ производительности: почему поиск в цикле — это антипаттерн

Описанный выше подход с использованием метода НайтиПоНаименованию решает задачу, но с технической точки зрения имеет существенный недостаток. Вызов серверного метода и выполнение поискового запроса к СУБД в цикле для каждой строки чека (проблема "N+1 запросов") может привести к задержкам при печати длинных чеков. На кассовом узле важна каждая доля секунды.

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

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

Решение без программирования: встроенный конструктор шаблонов

Не забываем, что современные версии программы "1С:Розница" предоставляют администраторам мощный графический редактор шаблонов чеков. Если ваш принтер чеков поддерживает работу с XML-шаблонами БПО, то изменять общий код конфигурации вообще не требуется.

Достаточно перейти в раздел Администрирование -> Печатные формы, отчеты и обработки -> Шаблоны этикеток, ценников и чеков ККМ, создать или отредактировать действующий шаблон чека КСО/ККТ. В визуальном конструкторе можно перетаскивать поля, настраивать их ширину, отключать перенос строк для номера позиции и выводить предопределенные реквизиты номенклатуры (включая единицы измерения по классификатору) без написания единой строки кода.

← На главную