Многие знают, что при продаже маркированных товаров необходимо передавать информацию о коде маркировки с помощью тега 1162. Про это достаточно много информации.
Для Штрих-М запрограммировать это не составляет большого труда. После операции FNOperation() необходимо добавить несколько строк.
ЗагрузитьВнешнююКомпоненту ("DrvFR.dll");
голеОбъектККМ = СоздатьОбъект("AddIn.DrvFR");
...
голеОбъектККМ.FNOperation();
голеОбъектККМ.MarkingType = MarkingType; //3 - лекарства
голеОбъектККМ.GTIN = GTIN; //14 символов
голеОбъектККМ.SerialNumber = SerialNumber;//13 символов
голеОбъектККМ.FNSendItemCodeData();
Код маркировки включает в себя избыточные данные. Из него необходимо выделить SGTIN длиной 27 символов, который в свою очередь делится на 2 части: первые 14 символов GTIN, остальные 13 символов это серийный номер упаковки (длина серийного номера упаковки указана для группы Лекарства).
Мой вариант функции разбора штрих-кода маркировки:
//******************************************************************************
// глЭтоКодМаркировки(Данные)
//
// Параметры:
// Данные
// SGTIN - выходной параметр для передачи вырезанного из штрихкода SGTIN
// КодТН_ВЭД - выходной параметр
// НомерСерии - выходной параметр
// ДатаИстеченияСрокаГодности - выходной параметр
//
// Возвращаемое значение:
// 1 да, 0 нет
//
// Описание:
//
// Примечание:
// В настройках сканера желательно включить передачу разделителя с кодом 29
//
Функция глЭтоКодМаркировки(Данные,SGTIN,КодТН_ВЭД="",НомерСерии="",ДатаИстеченияСрокаГодности="") Экспорт
Перем лДанные,СчЦ,ЧастьДанных,GTIN,ИндивидуальныйСерийныйНомер;
лДанные = СокрЛП(Данные);
Если СтрДлина(лДанные) >= 27 Тогда
лДанные = СтрЗаменить(лДанные,Симв(29),РазделительСтрок);
Если СтрДлина(лДанные) = 27 Тогда
SGTIN = Данные;
Возврат 1;
КонецЕсли;
Если Лев(лДанные,2) = "01" Тогда
GTIN = Сред(лДанные,3,14);
лДанные = Сред(лДанные,17);
ИндивидуальныйСерийныйНомер = "";//префикс 21 длина 13
КодТН_ВЭД = "";//префикс 240 длина 4
НомерСерии = "";//префикс 10 длина переменная, принимает что от 4 до 10
ДатаИстеченияСрокаГодности = "";//префикс 17 длина 6
Для СчЦ = 1 По СтрКоличествоСтрок(лДанные) Цикл
ЧастьДанных = СтрПолучитьСтроку(лДанные,СчЦ);
флПродолжитьПоиск = 1;
Пока флПродолжитьПоиск = 1 Цикл
Если СтрДлина(ЧастьДанных) <= 2 Тогда
Прервать;
КонецЕсли;
Если Лев(Прав(ЧастьДанных,15),2) = "21" Тогда
ИндивидуальныйСерийныйНомер = Прав(ЧастьДанных,13);
ЧастьДанных = Лев(ЧастьДанных,СтрДлина(ЧастьДанных)-15);
ИначеЕсли Лев(ЧастьДанных,2) = "21" Тогда
ИндивидуальныйСерийныйНомер = Сред(ЧастьДанных,3,13);
ЧастьДанных = Сред(ЧастьДанных,16);
ИначеЕсли Лев(ЧастьДанных,2) = "17" Тогда
ДатаИстеченияСрокаГодности = Сред(ЧастьДанных,3,6);
ЧастьДанных = Сред(ЧастьДанных,9);
ИначеЕсли Лев(ЧастьДанных,3) = "240" Тогда
КодТН_ВЭД = Сред(ЧастьДанных,4,4);
ЧастьДанных = Сред(ЧастьДанных,8);
ИначеЕсли Лев(Прав(ЧастьДанных,7),3) = "240" Тогда
КодТН_ВЭД = Прав(ЧастьДанных,4);
ЧастьДанных = Лев(ЧастьДанных,СтрДлина(ЧастьДанных)-7);
ИначеЕсли Лев(ЧастьДанных,2) = "10" Тогда
Если (СтрДлина(ЧастьДанных) >= 6) И (СтрДлина(ЧастьДанных) <= 12) Тогда //допустим, что длина серии от 4 до 10
НомерСерии = Сред(ЧастьДанных,2);
ЧастьДанных = "";
Иначе
флПродолжитьПоиск = 0;
КонецЕсли;
ИначеЕсли Лев(ЧастьДанных,2) = "91" Тогда
ПроверочныйКлюч = Сред(ЧастьДанных,3,4);
ЧастьДанных = Сред(ЧастьДанных,7);
ИначеЕсли Лев(ЧастьДанных,2) = "92" Тогда
ПроверочныйКод = Сред(ЧастьДанных,3,44);
ЧастьДанных = Сред(ЧастьДанных,47);
Иначе
флПродолжитьПоиск = 0;
КонецЕсли;
КонецЦикла;
КонецЦикла;
Если ПустоеЗначение(ИндивидуальныйСерийныйНомер) = 0 Тогда
SGTIN = GTIN + ИндивидуальныйСерийныйНомер;
Если ПустоеЗначение(SGTIN) = 0 Тогда
Если СтрДлина(SGTIN) = 27 Тогда
Возврат 1;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Если ПустоеЗначение(SGTIN) = 0 Тогда
Если СтрДлина(SGTIN) = 27 Тогда
Возврат 1;
КонецЕсли;
КонецЕсли;
КонецЕсли;
Возврат 0;
КонецФункции // глЭтоКодМаркировки()
Но при продаже лекарственных препаратов всё сложнее.
Когда я для теста сделал первый чек с кодом маркировки, то долго ждал когда же статус упаковки сменится с "in_circulation" (в обороте) на "in_sale" (продан в розницу), но этого так и не произошло.
Вот что мне ответил представитель ОФД:
Так же прошу отметить,что 54-ФЗ возлагает обязанности на оператора фискальных данных только в частности доставки кодов маркировки в составе кассового чека, а не контролировать его прием на стороне ЦРПТ.
Т.е. ОФД нет никакого дела, что именно ты передаёшь, правильно или нет.
Вот какой ответ из Честного Знака привёл один из разработчиков:
Отчёт о выбытии препарата при проведении его через ККТ не поступил в ИС "Маркировку" по причине того, что в отправляемом документе:
- отсутствуют теги 1085 и 1086, которые содержат информацию о месте деятельности, с которого происходит вывод ЛП из оборота;
- отсутствует тег 1191, который указывает именно, что производится именно продажа лекарственного препарата.
Читаем документацию и дополняем обработку.
Тег кассового чека 1084 является структурой и содержит следующие теги:
Код | Наименование Тега | Формат | Размер |
---|---|---|---|
1085 | наименование дополнительного реквизита пользователя | Строка CP-866 | 64 |
1086 | значение дополнительного реквизита пользователя | Строка CP-866 | 256 |
Ниже приведены рекомендации по записи в структуру тега 1084 (дополнительный реквизит пользователя) данных о выбытии лекарственных препаратов.
В теге 1085 необходимо передавать следующие реквизиты:
1. Префикс «mdlp» как признак того, что тег 1086 содержит специфичную для передачи в МДЛП информацию;
2. Опционально 7 символов из диапазона «0..9», указывающих на тип документа, данные которого передаются в теге 1086, в соответствии с ОКУД.
a. Отсутствие этих символов указывает на то, что в чеке передаются данные розничной реализации ЛП по рецепту с частичным дотированием, которые преобразуются в 511 схему данных МДЛП, либо в чеке передаются данные розничной реализации ЛП по рецепту без льготы или отпускаемых без рецепта.
b. Значение «3108805» указывает на то, что в теге 1086 передаются данные льготного рецепта со 100% дотированием (тег 1086 должен содержать реквизит с префиксом «ps»), которые преобразуются в 521 схему данных МДЛП.
c. Другие значения тега 1085 не допускаются.
Все передаваемые в теге 1086 реквизиты необходимо разделять символами-разделителями «&»; каждому реквизиту сопоставить уникальный префикс для упрощения машинной обработки данных:
dn = <номер документа, учитывающего отраслевую специфику>, doc_num, не более 200 символов
dd = <дата документа, учитывающего отраслевую специфику> в формате ГГММДД, doc_date
ps = <номер серии льготного рецепта>, prescription_series
sid = <идентификатор места деятельности субъекта обращения в ИС МДЛП>, subject_id
Для самого простого случая продажи без рецепта в начало добавляем
голеОбъектККМ.CheckType = 0;
голеОбъектККМ.OpenCheck();
голеОбъектККМ.TagNumber = 1084;
голеОбъектККМ.FNBeginSTLVTag();
my_TagID = голеОбъектККМ.TagID;
голеОбъектККМ.TagID = my_TagID;
голеОбъектККМ.TagNumber = 1085;
голеОбъектККМ.TagType = 7;
голеОбъектККМ.TagValueStr = "mdlp";
голеОбъектККМ.FNAddTag();
голеОбъектККМ.TagID = my_TagID;
голеОбъектККМ.TagNumber = 1086;
голеОбъектККМ.TagType = 7;
голеОбъектККМ.TagValueStr = "sid"+subject_id+"&";
голеОбъектККМ.FNAddTag();
голеОбъектККМ.FNSendSTLVTag();
Где subject_id это идентификатор места деятельности.
В теге 1191 передаются следующие реквизиты, порядок их записи описан ниже:
sp = <часть потребительской (маркированной) упаковки, подлежащая выводу из оборота>, sold_part
ss = <сумма субсидии>, subsidy_sum (сумма, компенсируемая из федерального или регионального бюджета, при расчете за данный предмет расчета по льготному рецепту). Используется для заполнения параметра МДЛП «discount».
Идентификатор субъекта обращения (sid) - 14 разрядныйM4; номер (строка длиной 14 байт в CP-866, допустимые символы [«0»-«9»]). Завершающий символ при записи строки данных должен быть «&».
При определении максимальной суммарной длины реквизитов dn и ps необходимо учитывать ограничения длины тега 1086, установленные ФФД (256 байт в версии 1.05). Для включения в реквизиты с префиксом dn или ps символа «&» в передаваемых данных должна содержаться подстрока «&&», которая при обработке и передаче в МДЛП не интерпретируется как разделитель или завершающий символ, а заменяется на символ «&».
Реквизит sp включается в тег 1191 сразу после префикса mdlp без указания в качестве префикса названия реквизита в следующем виде: указывается общее количество отпущенных первичных упаковок (целое число в виде строки, лидирующие нули запрещены) и, далее, количество первичных упаковок в маркированной упаковке (целое число в виде строки, лидирующие нули запрещены), разделенные символом «/» с завершающим символом «&», т.е. при отпуске 4 блистеров из 12 указывается строка «4/12&». Если после префикса «mdlp» указание дробной части отсутствует, упаковка считается выведенной из оборота полностью.
Примеры допустимых значений тега 1191: «mdlp», «mdlp2/12&».
Реквизит ss для льготных ЛП содержит сумму, возмещаемую из регионального или федерального бюджета. В реквизитах чека данные суммы должны указываться суммарно по всем позициям чека , как «сумма встречным предоставлением». Передается только для льготных рецептов с частичным дотированием, для рецептов со 100% льготой этот реквизит передавать в составе ФД не нужно. Реквизит ss всегда должен следовать после первого разделителя «&», название реквизита в качестве префикса не указывается. Если тег 1191 не содержит информации о доле отпуска (отпуск полной упаковки), после префикса «mdlp» перед реквизитом ss добавляется разделитель «&».
Реквизит ss всегда завершается символом «&». Значение реквизита ss – строка, в которой допустимы только символы [0..9], которая интерпретируется как десятичное число с фиксированной точностью 2 знака после десятичного разделителя целой и дробной части.
Примеры допустимых значений тега 1191 при субсидии 123,00 руб.: «mdlp&12300&», «mdlp2/12&12300&».
Добавляем к передаче кода маркировки передачу тега 1191:
Если (ПустоеЗначение(MarkingType) = 0) И (ПустоеЗначение(GTIN) = 0) И (ПустоеЗначение(SerialNumber) = 0) Тогда
голеОбъектККМ.MarkingType = MarkingType; //3 - лекарства
голеОбъектККМ.GTIN = GTIN; //14 символов
голеОбъектККМ.SerialNumber = SerialNumber;//13 символов
голеОбъектККМ.FNSendItemCodeData();
Если СписокТоваров.Количество <> СписокТоваров.ОтоброжениеКоличества Тогда
голеОбъектККМ.TagNumber = 1191;
голеОбъектККМ.TagType = 7;
голеОбъектККМ.TagValueStr = "mdlp"+Окр(СписокТоваров.ОтоброжениеКоличества,0)+"/"+Цел(СписокТоваров.ОтоброжениеКоличества/СписокТоваров.Количество)+"&";//при продаже 1 блистера из 3 должно быть mdlp1/3
голеОбъектККМ.FNSendTagOperation();
Иначе
голеОбъектККМ.TagNumber = 1191;
голеОбъектККМ.TagType = 7;
голеОбъектККМ.TagValueStr = "mdlp";
голеОбъектККМ.FNSendTagOperation();
КонецЕсли;
КонецЕсли;
Если всё прошло удачно, то в личном кабинете Честного Знака в исходящих документах появится документ с типом 10511 - Розничная продажа с использованием ККТ, тип загрузки в систему - ОФД.
По поводу прошивки и драйверов
Версия драйвера использовалась 4.14.0.772
Прошивка "Сборка ПО: 5190" от 29.04.2019.
Никаких лицензий для ККТ не покупалось, было сделано только обновление прошивки через тест драйвера. Почему я хотел бы заострить на этом внимание - нас по телефону на протяжении нескольких недель терроризировал один продажник, уверяя что если мы у него не купим лицензии на все наши кассы, то маркировка у нас работать не будет.
Ссылки на документацию:
Формат записи данных о выбытии ЛП в ФФД (pdf, 537.54 КБ)
Руководство программиста "ШТРИХ-М: Драйвер ККТ" (ZIP, 6.73 MB)