При интеграции торговых решений на базе 1С с контрольно-кассовой техникой (ККТ) АТОЛ разработчики часто сталкиваются с трудностями при передаче кодов маркировки (тег 1162 «Код товара»). Ошибки обычно проявляются в том, что в чеке печатаются некорректные символы, а оператор фискальных данных (ОФД) или система «Честный Знак» отклоняют чеки из-за неверного формата данных. Например, вместо ожидаемого префикса RE0 в ОФД уходит префикс RE2, либо возникает ошибка о превышении длины GTIN («decoded gtin is over 14 digits»).
В этой статье мы подробно разберем причины возникновения таких ошибок, проанализируем низкоуровневую структуру тега 1162 и рассмотрим готовые программные решения для формирования корректного буфера маркировки как для современной платформы 1С v8.3 (с использованием потоковых методов работы с бинарными данными), так и для устаревших конфигураций на базе 1С v8.2.
---
Для того чтобы понять, почему ОФД отображает неверные префиксы (например, FSA или RE2 вместо RE0), необходимо обратиться к структуре представления кода маркировки в ККТ. Согласно требованиям ФНС, для формата GS1 DataMatrix (используется для шин, обуви, одежды, табака и др.) тег 1162 в бинарном представлении должен состоять из строго определенной последовательности байтов:
0x44 0x4D (в десятичной системе — 17485).Основная проблема ручной сборки этого пакета в 1С заключается в некорректном выделении памяти под GTIN. Полноценный GTIN содержит 14 цифр. Если записывать его как стандартное целое число платформенными средствами (например, через 64-битное целое), под него выделяется 8 байт вместо положенных 6. Если при этом программист не производит точное смещение байтов или оставляет лидирующие нули в строке GTIN перед преобразованием, вся структура сдвигается на 1–2 байта. В результате ОФД считывает «мусорные» биты, префикс искажается, а система маркировки выдает ошибку декодирования.
---
Выясним причину, почему на старых версиях драйвера АТОЛ (например, 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-представлении.
---
Если специфика вашей интеграции требует самостоятельной сборки бинарного буфера для передачи в кассу напрямую или через старые версии драйверов, рассмотрим, как решить эту задачу в 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 строкой с ведущим нулем, функция Число() очистит его, что обеспечит корректное математическое преобразование в байты.
---
Проанализируем ситуацию, когда разработка ведется на устаревших платформах (например, на самописных конфигурациях 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; // Неопределенный тип
КонецЕсли;
КонецФункции
---
Рассмотренные выше алгоритмы ручной сборки тега 1162 (Код товара) актуальны для фискальных накопителей и касс, работающих по форматам фискальных данных ФФД 1.05 и ФФД 1.1. Если ваше предприятие осуществляет продажу маркированных товаров (включая шины, духи, обувь, одежду и фототехнику), законодательство требует обязательного перехода на ФФД 1.2.
При переходе на ФФД 1.2 ручное формирование тега 1162 становится неработоспособным по следующим причинам:
Рекомендация: При переходе на ФФД 1.2 полностью откажитесь от ручного расчета байтов тега 1162. Обновите интеграционную библиотеку 1С (БПО) и конфигурацию до актуальных релизов, поддерживающих ФФД 1.2, и передавайте в драйвер кассы полный необрезанный код маркировки, считанный 2D-сканером напрямую (со всеми спецсимволами GS1, такими как GS - код 0x1D). Драйвер ККТ АТОЛ версии 10.9 и выше самостоятельно сформирует составной тег 1163 на основании полной считанной строки.