Как правильно сформировать и передать тег 1162 (код маркировки) в ККТ АТОЛ из 1С?

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

При интеграции торговых решений на базе 1С с контрольно-кассовой техникой (ККТ) АТОЛ разработчики часто сталкиваются с трудностями при передаче кодов маркировки (тег 1162 «Код товара»). Ошибки обычно проявляются в том, что в чеке печатаются некорректные символы, а оператор фискальных данных (ОФД) или система «Честный Знак» отклоняют чеки из-за неверного формата данных. Например, вместо ожидаемого префикса RE0 в ОФД уходит префикс RE2, либо возникает ошибка о превышении длины GTIN («decoded gtin is over 14 digits»).

В этой статье мы подробно разберем причины возникновения таких ошибок, проанализируем низкоуровневую структуру тега 1162 и рассмотрим готовые программные решения для формирования корректного буфера маркировки как для современной платформы 1С v8.3 (с использованием потоковых методов работы с бинарными данными), так и для устаревших конфигураций на базе 1С v8.2.

---

Почему возникают ошибки кодирования (анализируем структуру тега 1162)

Для того чтобы понять, почему ОФД отображает неверные префиксы (например, FSA или RE2 вместо RE0), необходимо обратиться к структуре представления кода маркировки в ККТ. Согласно требованиям ФНС, для формата GS1 DataMatrix (используется для шин, обуви, одежды, табака и др.) тег 1162 в бинарном представлении должен состоять из строго определенной последовательности байтов:

  1. Префикс типа маркировки (2 байта): для GS1 DataMatrix это шестнадцатеричное значение 0x44 0x4D (в десятичной системе — 17485).
  2. GTIN товара (6 байт): упаковывается как 48-битное целое число в формате Big-Endian (от старшего байта к младшему).
  3. Серийный номер (13 байт или менее): записывается в виде ASCII-символов.

Основная проблема ручной сборки этого пакета в 1С заключается в некорректном выделении памяти под GTIN. Полноценный GTIN содержит 14 цифр. Если записывать его как стандартное целое число платформенными средствами (например, через 64-битное целое), под него выделяется 8 байт вместо положенных 6. Если при этом программист не производит точное смещение байтов или оставляет лидирующие нули в строке GTIN перед преобразованием, вся структура сдвигается на 1–2 байта. В результате ОФД считывает «мусорные» биты, префикс искажается, а система маркировки выдает ошибку декодирования.

---

Вариант 1. Обновление драйвера ККТ АТОЛ (самый простой способ)

Выясним причину, почему на старых версиях драйвера АТОЛ (например, 10.5.x) возникают сбои при передаче готовых строк в XML. В старых релизах драйвер требовал жестко разобранной структуры полей. Начиная с версии драйвера АТОЛ 10.8.0.0 и выше, механизм разбора входящего кода маркировки был существенно доработан.

Если вы используете ККТ АТОЛ на платформе 5.0 (прошивка 7942 и выше) и обновите драйвер до актуального (рекомендуется 10.8.0.0 и новее), касса сможет самостоятельно декодировать марку без сложной ручной подготовки буфера. Достаточно передать параметры в XML-пакет ККТ следующим образом:


ЗаписьXML.ЗаписатьНачалоЭлемента("GoodCodeData");
ЗаписьXML.ЗаписатьАтрибут("StampType", XMLСтрока("17485")); // Префикс для GS1 DataMatrix
ЗаписьXML.ЗаписатьАтрибут("GTIN", XMLСтрока("02900002689075")); // GTIN товара без лидирующих нулей в числовом смысле
ЗаписьXML.ЗаписатьАтрибут("SerialNumber", XMLСтрока(";ZhDv=rd,Kk>p")); // Серийный номер
ЗаписьXML.ЗаписатьАтрибут("MarkingCode", XMLСтрока("RE0CozWhUDM7WmhEdj1yZCxLaz5w")); // Код в Base64 или HEX
ЗаписьXML.ЗаписатьКонецЭлемента();

При этом, как показывает практика разработчиков, ККТ корректно обрабатывает входящий параметр MarkingCode как в формате Base64, так и в HEX-представлении.

---

Вариант 2. Формирование тега 1162 в 1С v8.3 через потоки памяти (для управляемых конфигураций)

Если специфика вашей интеграции требует самостоятельной сборки бинарного буфера для передачи в кассу напрямую или через старые версии драйверов, рассмотрим, как решить эту задачу в 1С v8.3. Мы применим современный механизм платформы — работу с двоичными данными и потоками в памяти.

Для правильного позиционирования байтов мы используем следующий алгоритм. Сначала мы выделим под GTIN 8 байт (через 64-битное целое число), запишем серийный номер, а затем вернемся в начало потока и перезапишем первые 2 байта префиксом типа маркировки (17485). Это автоматически «сожмет» представление GTIN до требуемых 6 байт.

Рассмотрим по шагам реализацию этой функции:


Функция СформироватьДвоичныеДанныеДляТега1162(Префикс, Знач ЗначениеGTIN = Неопределено, Знач СерийныйНомер = Неопределено) Экспорт
    
    // Создаем поток в оперативной памяти для сборки пакета байтов
    Тело = Новый ПотокВПамяти();
    ЗаписьДанных = Новый ЗаписьДанных(Тело);
    
    // Записываем GTIN как 64-битное целое число (8 байт). 
    // Важно использовать порядок байтов BigEndian (от старшего к младшему).
    ЗаписьДанных.ЗаписатьЦелое64(Число(ЗначениеGTIN), ПорядокБайтов.BigEndian);
    
    // Если серийный номер передан, дописываем его символы в поток сразу после GTIN
    Если Не ПустаяСтрока(СерийныйНомер) Тогда
        ЗаписьДанных.ЗаписатьСимволы(СерийныйНомер); 
    КонецЕсли;
    
    ЗаписьДанных.Закрыть();
    
    // Возвращаемся в самое начало нашего потока (позиция смещения 0)
    Тело.Перейти(0, ПозицияВПотоке.Начало);
    
    // Открываем новую запись в этот же поток, чтобы перезаписать первые байты
    ЗаписьДанных = Новый ЗаписьДанных(Тело);
    
    // Записываем 16-битный префикс (2 байта). 
    // Он перекроет первые 2 байта ранее записанного 8-байтового GTIN. 
    // Оставшиеся 6 байт будут содержать корректный GTIN товара.
    ЗаписьДанных.ЗаписатьЦелое16(Префикс, ПорядокБайтов.BigEndian);
    ЗаписьДанных.Закрыть();
    
    // Извлекаем итоговый массив двоичных данных
    РеквизитКодаТовараHEX = Тело.ЗакрытьИПолучитьДвоичныеДанные();
    
    // Кодируем результат в стандартную строку Base64 для передачи в XML ККТ
    Возврат Base64Строка(РеквизитКодаТовараHEX);
    
КонецФункции

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


КодДляКассы = СформироватьДвоичныеДанныеДляТега1162(17485, 466000025632, "Yui8rTd%haswe");
ЗаписьXML.ЗаписатьАтрибут("MarkingCode", XMLСтрока(КодДляКассы));

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

---

Вариант 3. Низкоуровневая сборка тега 1162 в 1С v8.2 (без использования потоков)

Проанализируем ситуацию, когда разработка ведется на устаревших платформах (например, на самописных конфигурациях 1С 8.2), где объекты ПотокВПамяти и ЗаписьДанных отсутствуют. В этом случае программисту приходится вручную переводить числа в шестнадцатеричную систему (HEX), собирать строку байтов и конвертировать ее в двоичные данные через фабрику XDTO.

Ниже представлен проверенный и рабочий алгоритм для платформы 1С v8.2:


// Функция кодирования HEX-строки байтов в Base64 через XDTO
Функция ПолучитьРеквизитКодаТовара_82(ВрКодТовара)
    
    мбКодТовара = ВрКодТовара;
    
    // Получаем из строки HEX-байтов двоичные данные средствами XDTO
    Попытка
        ТипhexBinary    = ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "hexBinary"); 
        ДвоичныеДанные  = ФабрикаXDTO.Создать(ТипhexBinary, мбКодТовара);
    Исключение
        Возврат "";
    КонецПопытки;    
    
    // Кодируем полученные двоичные данные в BASE64
    Попытка
        СтрКод = Base64Строка(ДвоичныеДанные.Значение);
    Исключение
        Возврат "";
    КонецПопытки;        
    
    Возврат СтрКод;
    
КонецФункции

// Основной метод сборки буфера тега 1162
Процедура ПолучитьРеквизитКодаТовараHEX_82(ДанныеМаркировки, Знач ПрефиксМаркировки = 17485)
    
    ГТИН  = ДанныеМаркировки.GTIN;
    Серия = ДанныеМаркировки.СерийныйНомер;
    
    // 1. Преобразуем числовой префикс в шестнадцатеричный вид
    Префикс = Число(ПрефиксМаркировки);
    ПрефиксБайтами = "";
    Пока Префикс > 0 Цикл
        ПрефиксБайтами = Сред("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", Префикс%16 + 1, 1) + ПрефиксБайтами;
        Префикс = Цел(Префикс/16) ;
    КонецЦикла;
    
    // 2. Преобразуем GTIN в число и переводим в HEX
    Попытка
        ГТИНЧислом  = Число(ГТИН);
        ГТИНБайтами = "";
        Пока ГТИНЧислом > 0 Цикл
            ГТИНБайтами = Сред("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", ГТИНЧислом%16 + 1, 1) + ГТИНБайтами;
            ГТИНЧислом = Цел(ГТИНЧислом/16) ;
        КонецЦикла;
    Исключение
        Возврат;
    КонецПопытки;    
    
    // Дополняем HEX-представление GTIN нулями слева до 12 символов (строго 6 байт)
    Пока СтрДлина(ГТИНБайтами) < 12 Цикл
        ГТИНБайтами = "0" + ГТИНБайтами;
    КонецЦикла;    
        
    // 3. Преобразуем серийный номер в HEX-байты через запись во временный файл в кодировке ANSI (Windows-1251)
    ИмяФ = ПолучитьИмяВременногоФайла("txt");
    Т = Новый ТекстовыйДокумент();
    Т.УстановитьТекст(Серия);
    Т.Записать(ИмяФ, КодировкаТекста.ANSI);
    
    ДД = Новый ДвоичныеДанные(ИмяФ);
    СерияБайтами = Строка(ДД);
    СерияБайтами = СтрЗаменить(СерияБайтами, " ", ""); // Удаляем пробелы из строкового представления двоичных данных
    УдалитьФайлы(ИмяФ);
    
    // Собираем итоговую HEX-строку: Префикс (2 байта) + GTIN (6 байт) + Серия (ASCII байты)
    мКодТовара = ПрефиксБайтами + ГТИНБайтами + СерияБайтами;
    
    // Формируем результирующий Base64-код товара
    ДанныеМаркировки.РеквизитКодаТовара = ПолучитьРеквизитКодаТовара_82(мКодТовара);
    
КонецПроцедуры

---

Вспомогательные таблицы префиксов для ККТ

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


Функция ПолучитьПрефиксПоТипуМаркировки(ТипТовара) Экспорт
    
    // Таблица соответствия типов кодов маркировки ККТ
    Если ТипТовара = "DataMatrix" Тогда
        Возврат 17485; // 44h 4Dh - GS1 Data Matrix (Шины, обувь, одежда, лекарства, табак)
    ИначеЕсли ТипТовара = "EAN13" Тогда
        Возврат 17677; // 45h 0Dh - Код товара в формате EAN-13
    ИначеЕсли ТипТовара = "EAN8" Тогда
        Возврат 17672; // 45h 08h - Код товара в формате EAN-8
    ИначеЕсли ТипТовара = "Fur" Тогда
        Возврат 21062; // 52h 46h - Меховые изделия (RFID КИЗ)
    ИначеЕсли ТипТовара = "EGAIS2" Тогда
        Возврат 50452; // C5h 14h - PDF417 для ЕГАИС 2.0
    ИначеЕсли ТипТовара = "EGAIS3" Тогда
        Возврат 50462; // C5h 1Eh - DataMatrix для ЕГАИС 3.0
    Иначе
        Возврат 0;     // Неопределенный тип
    КонецЕсли;
    
КонецФункции

---

Важное предупреждение: переход на ФФД 1.2 и замена тега 1162 на тег 1163

Рассмотренные выше алгоритмы ручной сборки тега 1162 (Код товара) актуальны для фискальных накопителей и касс, работающих по форматам фискальных данных ФФД 1.05 и ФФД 1.1. Если ваше предприятие осуществляет продажу маркированных товаров (включая шины, духи, обувь, одежду и фототехнику), законодательство требует обязательного перехода на ФФД 1.2.

При переходе на ФФД 1.2 ручное формирование тега 1162 становится неработоспособным по следующим причинам:

Рекомендация: При переходе на ФФД 1.2 полностью откажитесь от ручного расчета байтов тега 1162. Обновите интеграционную библиотеку 1С (БПО) и конфигурацию до актуальных релизов, поддерживающих ФФД 1.2, и передавайте в драйвер кассы полный необрезанный код маркировки, считанный 2D-сканером напрямую (со всеми спецсимволами GS1, такими как GS - код 0x1D). Драйвер ККТ АТОЛ версии 10.9 и выше самостоятельно сформирует составной тег 1163 на основании полной считанной строки.

← На главную