Я столкнулся с банальной проблемой: у меня на складе оказалось несколько наименований настольных игр и аксессуаров, которые я выпускаю под собственной маркой «СИМПЛРОБОТ» и которые попали под маркировку «Честный Знак». Тиражи небольшие (по 500 штук), но каждая единица товара должна теперь иметь код маркировки (КМ), и с этим кодом нужно что-то делать: наносить на упаковку, проверять статус, заводить коды при продажах через маркетплейсы и подавать отчёты о вводе и выводе из оборота. И к 1 сентября 2026 года весь товар на складе должен быть промаркирован, и после этой даты продавать настольные игры для детей до 14 лет без маркировки нельзя.
Посчитав, что платные облачные решения обходились примерно в 2000–4000 рублей в месяц (тот же «Мой склад» с опциями маркетплейсов и маркировки или 1С), а у меня ассортимент небольшой, товаров немного на складе и смысла платить такие суммы нет, я решил написать (точнее, навайбкодить + поправить ручками) свое приложение. Благо захотелось попробовать MiMo Code от Xiaomi, который до сих пор можно заюзать бесплатно, и понимание, чего я хочу в итоге, также имелось.
Проект открыт под MIT лицензией и расположен на GitHub: github.com/akdengi/sklad-cz
Дисклеймер
Я не профессиональный программист, поэтому код, как и положено написанному ИИ, ужасный, интерфейс не самый интуитивный, и «затачивалось» всё под мои потребности в маркировке настольных игр для детей. И я знаю, что код на python лучше запускать в виртуальном окружении, но опять же для меня это не критично.
История проекта
Приложение развивалось поэтапно. Начал я с простого: мне нужно было завести коды маркировки, присвоить товарам, напечатать этикетки. На этом этапе была реализована базовая функциональность: SKU, единицы товара, печать DataMatrix.
Далее я решил добавить интеграцию с API «Честный Знак» для проверки статусов маркировки. Появилась возможность пакетной проверки всех кодов в базе, кэширование токена, диагностика сертификатов.
Поработав так неделю, я понял, что приложение может быть полезнее, если учесть не только маркировку, но и весь процесс: склад > продажа > вывод из оборота. Были добавлены склады (физические и виртуальные), быстрая продажа, управление выбытием, дашборд со статистикой.
Сейчас идет «полировка» приложения в процессе эксплуатации, например, из последних фич добавлена оффлайн проверка КМ по их формату и GTIN.
На каждом этапе приложение оставалось простым и понятным. Никаких абстракций ради абстракций, никаких «возможностей на потом». Каждая фича — это решение конкретной задачи, с которой я столкнулся в реальной работе.
Что получилось
Получилось кроссплатформенное веб-приложение на Flask, которое работает на Windows и (по идее) в Linux. Под последним я не проверял, так как ноутбук у нас на складе издательства с Windows. Запускается одной командой python run.py и открывается в браузере по адресу http://127.0.0.1:5000.
Основные реализованные возможности:
Управление товарами (SKU) — карточки товаров с GTIN-14, EAN-13, артикулом, тиражом, ТН ВЭД и флагом «с маркировкой / без маркировки». Для товаров без маркировки (например, сувенирные аксессуары) код ЧЗ не обязателен, и приложение это учитывает на всех экранах.
Склады — физические (реальные склады) и виртуальные (маркетплейсы: «Озон», «Яндекс Маркет», «Интернет-магазин»). При продаже код автоматически переносится с физического склада на виртуальный.
Жизненный цикл единицы товара — товар может двигаться по алгоритму работы с КМ: не указан > эмитирован > нанесён > в обороте > продан / выбыл. Каждый переход фиксируется, и по каждой единице видна полная история.
Продажа — быстрое оформление по коду ЧЗ (для маркированных товаров) или по EAN-13/артикулу (для немаркированных). При этом генерируется код для вставки в поле при сборке товара на маркетплейсе или в вашем интернет-магазине, а оттуда вам нужно ввести номер заказа и цену, после этого автоматически заполняются данные для отчёта о выводе из оборота и сам товар ставится в продажу и на вывод.
Вывод из оборота — помощь и отслеживание выбытия КМ: тип операции, причина, документы, адрес, цена, статус отчёта. Для маркированных товаров — 3-дневный дедлайн на подачу отчёта с предупреждениями на дашборде.
Печать этикеток — генерация PDF, ZPL (Zebra) и TSPL (TSC) или печать непосредственно из браузера. Для маркированных товаров реализован вывод: DataMatrix + данных SKU + EAN-13 кода. Для немаркированных — только текстовых данных + EAN-13. Поддержка популярных размеров этикеток, нумерация диапазонами (#1-#50), горизонтальная и вертикальная ориентация. Можно скопировать и распечатать любой КМ, который есть у вас уже в базе, что обязательно для поставок на маркетплейсы, где код должен быть и на товаре, и на упаковке.
Импорт и экспорт — загрузка кодов ЧЗ из CSV/TXT и PDF, экспорт в JSON и CSV. Полный экспорт/импорт базы данных для резервного копирования.
Удалённая синхронизация — загрузка и скачивание базы данных через SSH/SFTP на любой недорогой VPS. Локальные бэкапы с ротацией (до 10 штук).

Архитектура и реализация
В основе Python 3.11 + Flask + SQLAlchemy + SQLite на бэкенде. На фронтенде — vanilla JavaScript, Bootstrap 5, никаких сборщиков и фреймворков. Всё в одном HTML-файле с SPA-навигацией по 11 вкладкам. Почему такой простой стек? Потому что приложение решает конкретную задачу, а не строит платформу. Flask — минимальная нагрузка, SQLite — нулевая настройка, vanilla JS — нет зависимостей от npm/webpack. Запуск на любой машине за 30 секунд.
Генерация DataMatrix сделана через библиотеку treepoem (обёртка над BWIPP + Ghostscript). Код формируется с parsefnc=True, FNC1 передаётся как ^FNC1 и реализованы вставки кодов разделителей GS. Скажу сразу, это был первый «подводный камень» при написании, так как я замучался сначала с работой с GS, а потом с FNC1 кодом, чтобы генерировало корректный дата-матрикс.

Интеграция с API «Честный Знак» и работой с электронной подписью ИП на ключе реализована через CAdES-BES подпись. На Windows — через COM-интерфейс «КриптоПро» (CAdESCOM / CAPICOM), на Linux — через pycades. Приложение проверяет статусы маркировки (эмитирован, нанесён, в обороте, выбыл) пакетами по 900 кодов. Токен авторизации кэшируется и автоматически обновляется при истечении.
Геморой с ЧЗ
Именно геморрой. Так как, чтобы работать с API «Честного знака», нужна авторизация по электронной цифровой подписи, а она находится у меня на USB-шном RuTocken. Никаких токенов авторизации в ЛК «Честного знака» не получить, только работать непосредственно с закрытой и открытой частью ЭЦП через «КриптоПро».
Для начала нам нужны отпечаток сертификата (SHA-1), ИНН организации и URL самого API ЧЗ https://markirovka.crpt.ru/api/v3/true-api (по умолчанию у меня стоит 3-я версия, но скоро придется переходить на 4-ю). Тут были проблемы как с нахождением информации по API, так и в принципе, как это всё интегрировать. Скажу только, что я потратил на работоспособную версию несколько дней. Проблема большинства «окологосударственных» решений как в качестве документации, так и в ее логичности.
После проверки наличия отпечатка сертификата и пин-кода мы получаем auth key от сервера и выполняем логин в ЧЗ.
GET /auth/key → Получаем { uuid, data }
Подпись data приватным ключем сертификата → CAdES-BES подпись (не PKCS#7!)
POST /auth/simpleSignIn { uuid, data: signature, unitedToken: true, inn } → Получаем JWT-токен
Токен кешируется в uuidtoken (глобальная переменная). Используется как Bearer в заголовке Authorization
Пока что я реализовал только запрос статуса кода, но (возможно) позже реализую также и работу с отчетами (то есть ввод/вывод/возврат из оборота).
Полное описание работы с ЧЗ через API в программе можно найти в главе документации: https://github.com/akdengi/sklad-cz/blob/master/docs/15-api-chestny-znak.md
Почему нет автоматических отчётов о выбытии в ЧЗ
Посмотрев программу, у вас может возникнуть вопрос: «Почему ты не сделал автоматическую подачу отчётов через API?» Ответ простой.
API «Честный Знак» меняется постоянно. Каждые несколько месяцев выходят обновления, меняются форматы запросов, добавляются новые обязательные поля. Для автоматической подачи отчёта нужно точно знать структуру запроса, обрабатывать десятки кодов ошибок, учитывать валидацию на стороне сервера. Одна ошибка — и отчёт отклонён, а товар «завис» в промежуточном статусе.
При небольших тиражах и продажах (не более сотни единиц в месяц) ручная подача через личный кабинет «Честный Знак» занимает от силы минут пять. Программа помогает собрать все нужные данные (тип операции, документы, адрес, цена), показывает статус, а сам отчёт подаётся в ЛК вручную. Это надёжнее и проще.
Кроме того, автоматизация отчётов — это юридическая ответственность. Если программа отправит неверный отчёт, проблема будет на совести владельца бизнеса, а не разработчика ПО. При моих объёмах ручной контроль — это разумный компромисс.

Как заносить КМ в программу?
Тут есть два варианта: вносить коды, сканируя их поштучно через вкладку «Сканирование». Здесь главное, чтобы ваш сканер корректно передавал GS разделители.

Второй вариант — вносить коды из txt/csv файлов. Я поступаю здесь таким образом:
В приложении «Маркировка» просто заказываю КМ и сразу ввожу их в оборот. Далее смотрю в Отчеты СУЗ, и в конкретном отчете о нанесении можно найти JSON со всеми кодами.

Коды копирую из json структуры и сохраняю в csv или txt и далее «скармливаю» товаро-учетной системе. Она сама уберет лишнее и добавит разделители. Главное — верно указать SKU (хотя в последних версиях я добавил проверку по GTIN, и на неправильный добавить КМ программа уже не даст) и склад.
Обязательно проверяйте коды перед распечаткой для нанесения или продажи мобильным приложением «ЧЗ.Бизнес». Это позволит избежать ошибок, если вдруг код импортируется сбойным или вы забыли обновить статус товара и пытаетесь продать экземпляр, который или не введен в оборот, или, наоборот, уже выведен из него.

В заключение
Товароучёт + Честный Знак — это не «ещё одна CRM» и не коммерческий продукт. Это инструмент, который писался под собственные нужды, но, надеюсь, будет полезен ещё кому-нибудь. Увы, маркировка в ближайшее время похоже захватит все товары, и раз отменять ее явно не будут, то нужно к ней приспосабливаться и это можно сделать и своими силами.
