Сообщение, которое ничего не объясняет
«Код маркировки не соответствует ни одному шаблону».
Кто с маркировкой работал — видел. Кассир сканирует товар, программа отказывает, и в сообщении ноль полезной информации. Какому шаблону? Почему не соответствует? Что чинить?
Идёшь на форум. Там несколько тем с этой ошибкой, и советы примерно такие: «снимите галочку настроек обмена ИС МП и поставьте обратно — ошибка уходит». «Появилось после обновления, наверное что-то криво в процедурах обновления». «Проверьте, со скобками код или без».
Самое интересное, что совет с галочкой работает. Люди пишут, что помогло. Но объяснения нет ни у кого, и в следующий раз человек снова будет тыкать наугад.
Я полез разбираться, потому что задолбался угадывать. Оказалось, за этим сообщением стоит не одна ситуация, а шесть.
Шесть, а не одна
В модуле РазборКодаМаркировкиИССлужебныйКлиентСервер есть экспортная функция, которая их и перечисляет:
Функция ИдентификаторыОшибокРазбораКодаМаркировки() Экспорт
ИдентификаторОшибки = Новый Структура;
ИдентификаторОшибки.Вставить("УчетМаркируемойПродукцииНеВедется", "УчетМаркируемойПродукцииНеВедется");
ИдентификаторОшибки.Вставить("ДанныеДляРазбораНекорректны", "ДанныеДляРазбораНекорректны");
ИдентификаторОшибки.Вставить("КодМаркировкиНеСоответствуетНиОдномуШаблону", "КодМаркировкиНеСоответствуетНиОдномуШаблону");
ИдентификаторОшибки.Вставить("КодМаркировкиСоответствуетНесколькимШаблонам", "КодМаркировкиСоответствуетНесколькимШаблонам");
ИдентификаторОшибки.Вставить("КодМаркировкиСоответствуетДругимВидамПродукции", "КодМаркировкиСоответствуетДругимВидамПродукции");
ИдентификаторОшибки.Вставить("НекорректныйФорматКодМаркировки", "НекорректныйФорматКодМаркировки");
Возврат ИдентификаторОшибки;
КонецФункции
Шесть разных исходов. И только один из них — тот самый «не соответствует ни одному шаблону». Остальные пять — это:
- учёт маркируемой продукции не ведётся вообще;
- данные для разбора некорректны;
- код подошёл сразу под несколько шаблонов;
- код подошёл, но под другой вид продукции;
- некорректный формат.
Причём разбор возвращает идентификатор ошибки отдельно от текста. То есть программа знает, что именно случилось, — вопрос лишь в том, доносит ли это до пользователя конкретная форма.
Дальше я пошёл смотреть, в каком порядке эти проверки срабатывают. И первая же оказалась не про код.
Первым делом проверяется не код
Вот начало функции разбора, дословно:
Функция РазобратьКодМаркировки(ДанныеДляРазбора, ВидыПродукции, ПримечаниеКРезультатуРазбора,
Настройки, МодульКонтекста, ПользовательскиеПараметры) Экспорт
ПримечаниеКРезультатуРазбора = Новый Структура("ИдентификаторОшибки, ТекстОшибки, РезультатРазбора", "", "", Неопределено);
ИдентификаторыОшибок = ИдентификаторыОшибокРазбораКодаМаркировки();
Если Настройки.ДоступныеВидыПродукции.Количество() = 0 Тогда
ПримечаниеКРезультатуРазбора.ИдентификаторОшибки = ИдентификаторыОшибок.УчетМаркируемойПродукцииНеВедется;
ПримечаниеКРезультатуРазбора.ТекстОшибки = НСтр("ru = 'Учет маркируемой продукции не ведется.'");
Возврат Неопределено;
КонецЕсли;
Самая первая проверка — есть ли вообще хоть один вид продукции, по которому ведётся учёт. Не «правильный ли код», а «включено ли у вас что-нибудь».
Вот и объяснение народного средства с галочкой. Настройки обмена ИС МП определяют состав ДоступныеВидыПродукции. Если список пустой — код не разбирается никогда, каким бы правильным он ни был. Снял и поставил галочку — список пересобрался — заработало.
Магия кончилась. Совет верный, просто причина у него другая, чем все думают: код тут ни при чём.
Отсюда, кстати, первое практическое следствие. Если ошибка вылезла сразу после обновления или после разворачивания узла РИБ — не ищите проблему в коде на упаковке. Смотрите настройки обмена. В темах на форуме люди прямо пишут, что при формировании узла по магазину галочка не переносится из центральной базы, и её надо ставить руками.
Вторая проверка — пробел
Тоже до всякого разбора формата:
Если СтрНайти(ДанныеДляРазбора, " ") > 0 Тогда
ПримечаниеКРезультатуРазбора.ИдентификаторОшибки = ИдентификаторыОшибок.ДанныеДляРазбораНекорректны;
ПримечаниеКРезультатуРазбора.ТекстОшибки = НСтр("ru = 'Данные для разбора некорректны: код маркировки содержит пробел.'");
Возврат Неопределено;
КонецЕсли;
Пробел внутри кода — отказ сразу. Мелочь, но она объясняет ситуации вида «код скопировали из письма, и он не принимается»: при копировании через почту, мессенджер или PDF пробелы заводятся сами собой, а глазами их в длинной строке не увидишь.
Третья проверка меня развеселила
Если НайденНедопустимыйСимволXML(ДанныеДляРазбора) Тогда
РезультатРазбора = МенеджерОборудованияМаркировкаКлиентСервер.РазобратьСтрокуШтрихкодаGS1(ДанныеДляРазбора);
КонецЕсли;
Читается странно, пока не поймёшь замысел. «Недопустимый символ XML» — это управляющий символ с кодом от 14 до 31, и в коде маркировки такой ровно один: разделитель GS, Символ(29).
То есть типовая использует наличие невидимого символа как признак того, что перед ней GS1-строка с разделителями. Есть управляющий символ — значит код полный, разбираем по-одному. Нет — значит либо разделители потеряны, либо формат вообще без них, разбираем иначе.
Про этот символ я писал прошлую статью — там он был причиной всех бед. Здесь он же работает детектором. Приятно встретить старого знакомого в новой роли.

Рис. 1 — порядок проверок в разборе
Теперь собственно про шаблоны
Дальше начинается то, ради чего сообщение и появляется. Разбор пытается сопоставить код с шаблонами, и вот тут важно понять главное: шаблоны привязаны к товарным группам.
В «Бухгалтерии предприятия» они лежат прямо в коде, в модуле РазборКодаМаркировкиИСМПСлужебный, отдельной процедурой на каждую группу. Их там двадцать две:
`56; Полный список 22 процедур-шаблонов (в публикации — под спойлером)
ОписаниеКодаМаркировкиТабачногоБлока
ОписаниеКодаМаркировкиТабачнойПачки
ОписаниеКодаМаркировкиТабачнойПачкиКодПроверки8
ОписаниеКодаМаркировкиАльтернативногоТабачногоБлока
ОписаниеКодаМаркировкиАльтернативнойТабачнойПачки
ОписаниеКодаМаркировкиАльтернативнойТабачнойПачкиСИдентификаторамиПрименения
ОписаниеКодаМаркировкиОбуви
ОписаниеКодаМаркировкиОбувиКодТНВЭД10
ОписаниеКодаМаркировкиПродукцииИСМП
ОписаниеКодаМаркировкиФотоаппаратов
ОписаниеКодаМаркировкиМолочнаяПродукция
ОписаниеКодаМаркировкиМолочнаяПродукцияБелоруссия
УстаревшийФорматОписаниеКодаМаркировкиМолочнаяПродукция
УстаревшийФорматОписаниеКодаМаркировкиМолочнаяПродукцияСкоропортящаяся
ОписаниеКодаМаркировкиПродукцииУпакованнаяВода
ОписаниеКодаМаркировкиПродукцииПиво
ОписаниеКодаМаркировкиПродукцииНикотиносодержащаяПродукцияГрупповая
ОписаниеКодаМаркировкиПродукцииНикотиносодержащаяПродукцияПотребительская
ОписаниеКодаМаркировкиПродукцииБАДы
ОписаниеКодаМаркировкиПродукцииАнтисептики
ОписаниеКодаМаркировкиЛогистическаяУпаковка18
ОписаниеКодаМаркировкиЛогистическаяУпаковка20
Обратите внимание на две вещи. Во-первых, у одной группы бывает несколько шаблонов — у табака их шесть штук с разными вариантами. Во-вторых, есть отдельные процедуры для устаревших форматов, то есть форматы менялись со временем и старые пришлось оставить.
А теперь ключевое. Список шаблонов — часть конфигурации. Появилась новая обязательная группа — пиво, безалкогольные напитки, ветпрепараты — производитель выпускает коды нового формата, а ваш релиз про этот формат ещё не знает. Код приходит корректный, соответствовать ему просто нечему.
Вот вам и «не соответствует ни одному шаблону» во всей красе: код в порядке, программа в порядке, а между ними пропасть в несколько релизов.
Кстати, устроено это не везде одинаково. Заглянул в «Розницу Аптеку» — конфигурацию другой линейки и посвежее. Там процедур-шаблонов всего четыре, на экзотику вроде натурального меха и логистической упаковки, а обычные группы уехали в справочники:
Справочник.ШаблоныКодовМаркировкиСУЗ
Справочник.ШаблоныРазбораКодовМаркировкиИСМП
Справочник.ТоварныеГруппыГИСМТ
Шаблон стал данными, с реквизитами ДлинаСерийногоНомера, ТипКодаМаркировки и ИдентификаторыПримененияОтсутствуют. Логика перехода понятная: пока групп полтора десятка — держим процедурами, когда их под сотню и меняются они чаще релизов — дешевле таблица.
Практический вывод для диагностики: посмотрите, где шаблоны лежат именно у вас. Если в коде — лечится только обновлением. Если в справочниках — возможно, достаточно обновить их состав.
Как эти шаблоны выглядят на самом деле
Чтобы не рассуждать в воздухе, я сходил в магазин и набрал шесть разных маркированных товаров: воду двух производителей, молоко, банку с крышкой, пачку сигарет и тапки. Дома фотографировал квадратики и распознавал коды прямо из картинок.
Взрослый человек, вечер пятницы, крутит бутылку минералки под настольной лампой, чтобы блик ушёл с кода. Соседи бы не поняли.
Метод, кстати, принципиален. Код надо забирать из изображения сырыми байтами, а не текстом. Отсканируете и перешлёте строку в мессенджер — разделитель Символ(29) по дороге исчезнет, и вы будете изучать уже испорченный код. С фотографии он приезжает целым.
|
Товар |
Процедура типовой |
Схема |
Длина |
GS |
|
Банка с крышкой |
ОписаниеКодаМаркировкиПродукцииИСМП |
01 + 21(13) + 91(4) + 92(44) |
85 |
2 |
|
Минералка №1 |
ОписаниеКодаМаркировкиПродукцииУпакованнаяВода |
01 + 21(13) + 93(4) |
38 |
1 |
|
Минералка №2 |
то же |
то же |
38 |
1 |
|
Молоко |
ОписаниеКодаМаркировкиМолочнаяПродукция |
01 + 21(6) + 93(4) |
31 |
1 |
|
Пачка сигарет |
ОписаниеКодаМаркировкиТабачнойПачки |
14 + 7 + 4 + 4, без идентификаторов |
29 |
0 |
|
Тапки |
ОписаниеКодаМаркировкиОбуви, короткий вариант |
01 + 21(13) |
31 |
0 |

Рис. 2 — шесть кодов и шесть шаблонов типовой
Все шесть сошлись с объявленным в процедурах знак в знак.
Посмотрите на разброс. Длина от 29 до 85. Разделителей два, один или ноль. Серийный номер тринадцать символов — но у молока шесть.
На этом месте я обжёгся лично. После трёх первых образцов у меня сложилась стройная гипотеза, что серийник всегда тринадцать. Молоко её убило. И это не особенность конкретной пачки: в ОписаниеКодаМаркировкиМолочнаяПродукция так и объявлено — шесть. А в устаревшем формате той же молочки было тринадцать. Внутри одной товарной группы длина поменялась между редакциями.
Так что если вы где-то в своём коде зашили «серийник это тринадцать символов после 21» — оно сломается. Не «может сломаться», а сломается, на первой же пачке молока.
Табак: код, в котором нет идентификаторов
Крайний случай, и он объясняет, почему универсального самописного разбора не бывает.
ОписаниеЭлементовКМ.GTIN = ОписаниеЭлементаКодаМаркировки("", "GTIN", 14);
ОписаниеЭлементовКМ.СерийныйНомер = ОписаниеЭлементаКодаМаркировки("", "СерийныйНомер", 7, Алфавит.БуквыЦифрыЗнаки);
ОписаниеЭлементовКМ.МРЦСтрокой = ОписаниеЭлементаКодаМаркировки("", "МРЦСтрокой", 4, Алфавит.БуквыЦифрыЗнакиМРЦ);
ОписаниеЭлементовКМ.КодПроверки = ОписаниеЭлементаКодаМаркировки("", "КодПроверки", 4, Алфавит.БуквыЦифрыЗнаки);
Первый параметр — идентификатор применения. У всех четырёх полей он пустая строка.
Никаких 01, никаких 21. Четырнадцать символов GTIN, семь символов серийника, четыре и ещё четыре. Разбор чисто позиционный, разделители не нужны никому. Живая пачка — ровно двадцать девять знаков, сходится.

Рис. 3 — табачная пачка, позиционный разбор
Любой разбор, который ищет в строке 01 и 21, на табачной пачке либо ничего не найдёт, либо найдёт их случайно внутри GTIN и разложит код в мусор.
И маленькое открытие про МРЦ
Поле МРЦСтрокой использует отдельный алфавит — БуквыЦифрыЗнакиМРЦ, четвёртый и последний в конфигурации. Я сначала решил, что это просто набор допустимых символов, и ошибся.
Это система счисления. Восьмидесятиричная. В алфавите ровно 80 символов, и четыре знака МРЦ — не строка, а число, записанное в этой системе.
Проверил на своей пачке. МРЦ там AD!o. Считаем позиции символов в алфавите, разворачиваем как число по основанию 80, получаем 24 200. Делим на сто — 242 рубля.
А теперь смотрим на бок пачки, где напечатано читаемое: 242P 07.25.
Двести сорок два рубля. Сошлось.

Рис. 4 — как четыре символа превращаются в цену
Признаюсь, этот момент был самым приятным за всё исследование. Четыре непонятных символа AD!o оказались ценой, которую видно глазами на той же упаковке в двух сантиметрах.
Что делать, когда увидите эту ошибку
Порядок диагностики выпадает прямо из порядка проверок в коде.
Сначала — не про код. Откройте настройки обмена ИС МП и проверьте, что учёт по нужной товарной группе включён. Если ошибка появилась после обновления или после создания узла обмена — почти наверняка дело здесь. Это то самое «снять и поставить галочку», только теперь понятно, что происходит: пересобирается список доступных видов продукции.
Потом — пробел. Если код попал в базу копированием из письма, документа или мессенджера — проверьте на пробел. Отказ будет до всякого разбора формата.
Потом — товарная группа. Если товар из группы, которую подключили недавно — пиво, безалкогольные напитки, ветпрепараты, — сверьте релиз. Шаблоны приезжают с обновлением конфигурации, а не с кодами.
И только потом — сам код. Причём проверять его руками не надо, есть готовый вызов:
ПримечаниеКРезультату = Неопределено;
ДанныеРазбора = РазборКодаМаркировкиИССлужебный.РазобратьКодМаркировки(
КодМаркировки, Неопределено, ПримечаниеКРезультату);
Если ДанныеРазбора = Неопределено Тогда
Сообщить(ПримечаниеКРезультату.ИдентификаторОшибки + ": " + ПримечаниеКРезультату.ТекстОшибки);
КонецЕсли;
Три строки, и вы получаете не общее «не соответствует шаблону», а конкретный идентификатор из тех шести. Отличить «учёт не ведётся» от «код подошёл под другой вид продукции» — это уже половина решения.
Границы
Всё, что показано, — из выгрузки БП 3.0, релиз 3.0.111.25, учебная поставка, плюс сверка отдельных мест с «Розницей Аптекой» 3.0.13.357. В вашей конфигурации имена модулей те же, а вот состав шаблонов и место их хранения могут отличаться — собственно, об этом статья.
Образцов шесть. Мало для статистики, достаточно, чтобы сверить с шаблонами.
Одно расхождение назову прямо: код с банки совпал не с процедурой для БАДов, как я ожидал по этикетке, а с ОписаниеКодаМаркировкиПродукцииИСМП — она обслуживает лёгкую промышленность, духи, шины и коляски. Либо товар не тот, за который я его принял, либо в этом релизе БАДы шли по общей схеме. Утверждать не берусь.
И наблюдение, которое специально не превращаю в вывод: у всех пяти кодов с идентификаторами серийник начинается с цифры пять, у табачного — с буквы. Пять из пяти многовато для случайности, но гипотезу «серийник всегда тринадцать» я в этой же статье уже похоронил, так что пусть остаётся зацепкой.
Зачем я всё это выяснял
Началось с раздражения. Сообщение об ошибке, за которым стоит шесть разных ситуаций, а показывают одну формулировку — это неуважение к тому, кто будет разбираться. Особенно когда самая частая причина вообще не про то, что написано в тексте.
Заодно выяснилось приятное: у типовой внутри всё разложено аккуратно. Идентификаторы ошибок вынесены отдельно, порядок проверок логичный, шаблоны описаны по группам, есть даже отдельный алфавит под одно-единственное поле с ценой. Просто до пользователя из всего этого доезжает одна строка.
Сейчас собираю обработку, которая при загрузке файла с кодами показывает не «ошибка», а конкретную причину из этих шести — по каждой строке. Если у вас есть коды товарных групп, которых нет в моём списке, особенно пиво, антисептики или логистическая упаковка — присылайте. Интереснее всего то, что не сходится с шаблоном: это и есть материал для следующего разбора.
Вступайте в нашу телеграмм-группу Инфостарт