О проекте
Команда: PlanP
Кейс HackAlem.ai: Логистика — Электрокомплект · ekt.kz
Стадия: Доведённый продукт
Задача кейса: Автоматический расчёт заказов поставщикам для пополнения склада, с утверждением менеджером
Суть
Сервис планирования закупок для склада Электрокомплекта: считает прогноз спроса, календарь остатков с учётом партий в пути и рекомендуемый объём заказа поставщику с объяснением и выгрузкой CSV.
Как устроено
Импортирует 12 выданных XLSX (openpyxl) и строит снимок данных. Спрос сначала очищается: избыточные крупные документы ограничиваются по медиане/MAD, для месяцев с нулевым остатком оценивается скрытый спрос. Затем сравниваются два прогноза, mean_12 и seasonal_growth, на последних 6 месяцах, и режим auto выбирает метод с меньшей MAE. Поступления раскладываются по дням ETA, рассчитывается N(t) и страховой уровень SS(t), Q = max(SS−N) на горизонте H с учётом MOQ и кратности упаковки. Результат показывается в FastAPI + vanilla JS UI как черновик заказа и выгружается в CSV; при неизвестных параметрах расчёт блокируется, нули не подставляются.
Ключевые решения
- mean_12 forecast
- seasonal × trend forecast
- MAE backtest auto-selection
- median/MAD outlier capping
- stockout lost-demand estimation
- safety stock
- MOQ and pack rounding
- ETA-based stock calendar
Метрики
MAE 19,14 (mean_12) против 22,82 (сезонный) на ATN540126; 161 Python-тест, 73 frontend-проверки; 3045 товаров из 12 XLSX
Особенности
Сделали объяснимый детерминированный расчёт без LLM: backtest двух прогнозов с автовыбором по MAE, коррекция спроса на выбросы и stockout, в интерфейсе видны исходные и скорректированные значения. Воспроизводимый сценарий с переносом ETA (0 → 24 шт.) и bootstrap данных с проверкой SHA256.
Стек
Python 3.13 · FastAPI · uvicorn · openpyxl · vanilla JavaScript · CSS · Docker · Render
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-2dfc4dce-planp.
README
README.mdQORAI — управление запасами
QORAI помогает закупщику решить, что и сколько заказать, к какой дате нужен товар — и почему, соединяя историю продаж, остаток и календарь поставок в объяснимый расчёт.
Преимущества алгоритма
- Сравнивает прогнозы, а не полагается на один. Простое среднее за 12 месяцев и «сезонность × динамика» проверяются на одинаковых прошлых месяцах. Режим
autoвыбирает меньшую историческую MAE — среднюю абсолютную ошибку — при достаточном числе сравнений. - Учитывает, когда товар приедет. Партия в пути попадает в расчётный остаток только в свою дату. Перенос ETA показывает, в какие дни появляется нехватка; наличие заказа поставщику не приравнивается к наличию товара на складе.
- Считает объём под период покрытия. Находит максимальную нехватку до страхового уровня, затем учитывает минимальный заказ поставщика и округляет до упаковки. Дефицит до возможного прихода новой закупки остаётся видимым.
- Объясняет результат до исходного числа. Источник, дата, прогноз, остаток, срок и округление доступны для проверки. CSV формируется из того же серверного результата, без отдельной экспортной математики.
- Не даёт крупному документу незаметно раздуть обычный спрос. Медиана и разброс документных количеств выделяют избыточную часть; её доля корректирует месячную базу. Сырые продажи сохраняются рядом, каждое изменение объясняется.
- Оценивает спрос, который мог скрыться за отсутствием товара. При нулевом начальном остатке и аномально низких продажах сравнивает месяц с прошлой доступной историей и сезонным аналогом. Показывает оценку потерь отдельно: это не выдуманный календарь отсутствия и не подтверждённые потерянные заказы.
Открыть QORAI →
В демо загружены 3045 товаров из 12 выданных XLSX IEK / Systeme Electric. Срез источников — 22 сентября 2026. Работают прогноз, календарь, условный заказ, черновик и CSV. Первая загрузка Render Free может занять несколько минут: при проверке первый запрос данных занял около 183 секунд; следующие запросы используют готовый снимок и работают быстрее.
Быстрая проверка: выберите ATN540126 → откройте «Условия закупки» → проверьте предзаполненные демонстрационные 2 / 30 / 7 / 0 → отметьте оба подтверждения → нажмите «Рассчитать потребность». По другим товарам отсутствующая или недостаточная история может блокировать расчёт: это не означает, что Excel не загрузились.
Основная конкурсная версия: репозиторий, ветка main. Этот файл — единственный основной README; подробные технические документы находятся в docs/.
Одна задержка — другое решение о закупке
Найдите ATN540126. В выданной таблице: остаток 42 шт., бронь 19, свободно 23, в пути 120, кратность упаковки 6. Бронь уже учтена: из 23 её повторно не вычитаем.
Для воспроизведения задайте техническую политику: срок до приёмки L = 2 дня, покрытие H = 30 дней, страховой запас 7 дней, MOQ = 0; включите использование отчётного остатка и режим обычных продаж, метод — auto.
| Дата существующей партии 120 шт. | Рекомендованный дополнительный заказ |
|---|---|
| 24 сентября 2026 | 0 шт. |
| 8 октября 2026 | 24 шт. |
Во втором сценарии на 7 октября расчётный баланс без новой закупки — −8,37 шт., страховой уровень — 14,64 шт. Нехватка 23,01 шт. округляется до 24 шт.: четырёх упаковок по шесть. Первый расчётный дефицит без новой закупки — 3 октября. Расчёт ведётся с полной точностью; числа в объяснении округлены для чтения. Исходные 42/19/23 не меняются, а партия 120 не дублируется. Экспорт использует сохранённый серверный расчёт со статусом provisional.
Это проверенный сценарий на выданном товаре с явно введёнными условиями, а не утверждение о фактических сроках поставщика или доказанной экономии. Даты ETA здесь — действия менеджера в сценарии, не исправление Excel.
Числа выше повторно проверены после коррекции спроса: mean_12 выбран по 6 общим точкам, MAE 19,14 против сезонного 22,82. Предыдущая версия без коррекции давала 36; это исторический результат, не текущая рекомендация. Для демо используйте явную маркировку «Демонстрационные условия» (scenario_mode=demonstration).
Запуск локально
Python 3.13; команды выполняются из корня клона. Helper получает два публичных архива организатора, проверяет закреплённые SHA256 архивов и всех 12 XLSX, безопасно распаковывает их. Собственные бизнес-данные не генерируются; Excel в Git не входят.
Set-Location '<папка клона>/hack-2dfc4dce-planp'
py -3.13 -m pip install -r requirements.txt
py -3.13 scripts/bootstrap_data.py --output-dir data/issued
$env:HACKALEM_DATA_DIR = (Resolve-Path 'data/issued').Path
$env:HACKALEM_DATA_ROOT = $env:HACKALEM_DATA_DIR # совместимость тестов импортёра
py -3.13 -m smartbuyer.serve
Откройте http://127.0.0.1:8765. Если папка 04_Excel уже есть, вместо загрузки укажите её в HACKALEM_DATA_DIR, сохранив подпапки IEK / Systeme electric. На Windows флаг -3.13 выбирает проверенную версию Python, а не другой установленный Python без зависимостей. На Linux/macOS используйте python3.13 вместо py -3.13 и export HACKALEM_DATA_DIR=data/issued.
Первый запрос читает Excel; следующие используют снимок. «Перечитать Excel» обновляет только настроенную папку, при ошибке сохраняет прошлый исправный набор. Повторный bootstrap сверяет уже загруженные файлы и не перезаписывает неизвестные.
CLI — из второго терминала с теми же переменными:
py -3.13 -m smartbuyer.cli --data-dir "$env:HACKALEM_DATA_DIR" --target 2026-10 --sku ATN544045 --format json
py -3.13 -m smartbuyer.cli --data-dir "$env:HACKALEM_DATA_DIR" --target 2026-10 --sku ATN544045 --format csv
Без --sku рассчитываются все позиции. CLI пишет UTF-8 в stdout, ошибки — в stderr с кодом 2; исходники не изменяет. --as-of может уменьшить дату отсечения, но не продвинуть её позже даты источника. CLI экспортирует прогноз; закупочный сценарий доступен через интерфейс/API.
Технологии
Python · openpyxl · FastAPI / uvicorn · vanilla JavaScript / CSS · локальные шрифты Inter. Расчёт детерминированный, без платного LLM, базы данных и внешнего CDN. Один процесс приложения; параметры сценария и рассчитанные результаты хранятся в памяти.
Публичное демо работает на Render Free. Основной репозиторий сдачи — BAITC-Hacks/hack-2dfc4dce-planp; приватная deployment-копия его не заменяет. Команды хостинга, точный Host/Origin и проверенный deploy: DEPLOYMENT. Docker-файлы подготовлены, Docker build не проверен.
Как получается число заказа
Подробный алгоритм: формулы, входы и ограничения.
Прогноз. mean_12 усредняет дневные скорости 12 полных месяцев с учётом длины календаря. seasonal_growth берёт соответствующий месяц прошлого года и умножает на динамику трёх полных месяцев. Неполный сентябрь исключён из обучения. На каждом историческом срезе параметры пересчитываются только по доступному тогда прошлому.
Подготовка спроса. Сначала ограничивается избыточная часть крупных положительных расходных документов (медиана/MAD, минимум 5 документов). Затем при нулевом начальном остатке и продажах ниже половины опорного уровня оценивается возможная потеря: минимум 3 более ранних месяца с положительным остатком, сезонная защита, без повторного обучения на уже восполненных потерях. В интерфейс/API передаются исходное и скорректированное значения, пороги и причины. Это проверяемые оценки на выданных данных, не клиентская идентификация.
Выбор. Оба метода сравниваются на последних шести полных месяцах. auto выбирает меньший MAE при наличии хотя бы трёх общих проверок; иначе использует помеченный непроверенный fallback к среднему, если оно доступно. MAE показан в единицах выбранного товара. Победа на этих месяцах не гарантирует точность будущего прогноза.
Календарь и объём. Для режима обычных продаж стартуем со свободного отчётного остатка AZ; бронь AY уже исключена. Поступления добавляются по ETA, месячный прогноз распределяется по календарным дням с явно указанным допущением равномерности.
N(t) = свободный отчётный остаток + поступления к дате t − прогноз расхода к дате t
SS(t) = страховые дни × прогноз расхода за день
A = дата среза + срок L до приёмки новой закупки
Qraw = max(0, max(SS(t) − N(t))) на H датах начиная с A
Q = 0, если Qraw = 0; иначе pack × ceil(max(Qraw, MOQ) / pack)
Количество закрывает максимальный разрыв до страхового уровня на выбранном горизонте. Риск раньше A новая закупка не исправляет задним числом. Планируемая дата размещения — дата среза сценария; оптимальный крайний срок заказа отдельно не вычислен. Неизвестные параметры не заменяются нулями: вместо точного заказа показывается причина блокировки.
Проверка результата
Текущая версия прошла 161 Python-тест без пропусков (независимый полный прогон, 233,19s), 73 frontend-проверки и desktop browser QA нового demo-пути: Q24, дата03.10, коррекции и маркировка условий. Адресные проверки входят в этот результат, их не нужно прибавлять. Evidence и границы — в QA_REPORT. При первой публикации Render 23.09.2026 подтверждены 3045 товаров / 12 источников и совпадающий CSV. Это проверки реализации, не процент точности модели.
py -3.13 -m pytest -q
node --check web/app.js
node web/app.test.cjs
node tests/frontend_catalog.cjs
node tests/frontend_demo.cjs
Node нужен только для frontend-проверок, не для запуска приложения. frontend_catalog.cjs и frontend_demo.cjs требуют уже запущенный локальный API на http://127.0.0.1:8765. Короткая ручная проверка:
- Проверьте дату источника 22.09.2026 и найдите
ATN540126. - Без политики заказ должен быть заблокирован, а не стать «готовым нулевым».
- Введите условия из сценария выше: перенос ETA с 24.09 на 08.10 меняет Q с 0 на 24 после коррекции спроса.
- Откройте объяснение и календарь, затем выгрузите положительный заказ: в CSV должны сохраниться количество, источник, единица и условный статус.
- Измените параметры или перечитайте Excel: устаревший черновик нельзя отправить в экспорт. Два сценария одного товара не должны удваивать закупку.
Границы MVP
- Из 12 файлов содержимое 10 читается импортёром; две готовые таблицы сезонности только зафиксированы по хешу. Месячные продажи остаются базой: из несверенных операций не вычитаются абсолютные количества, переносится оценочная доля крупного документного избытка. История продаж — не доказанный истинный спрос; результат закупки имеет статус
provisional. - Сроки L/H, страховая политика и недостающий MOQ вводятся менеджером. Реализован сценарий обычных продаж по отчётному свободному остатку, не полный план неизвестных клиентских обязательств. Для IEK остаток начала месяца не выдаётся за текущий.
- Компенсация возможных потерь и очистка крупных документов реализованы как оценки. Нет подтверждённых дневных интервалов отсутствия и clientID: нельзя доказать истинные потери или объединить несколько документов одного клиента. Самообучения, ERP-проводок, онлайн-склада и реальной отправки заказа нет.
- На всём выданном наборе из 3045 товаров текущие условия stockout-компенсации не дали положительных срабатываний. Ветка проверена тестами; реальное восстановление потерянного спроса на этих источниках не заявляется. Пороги ради демонстрации не подгонялись.
- Авторизации в демо нет; используйте только публичный выданный набор. Рестарт Render сбрасывает результаты в памяти. Desktop-first путь проверен; мобильная компоновка ещё не подтверждена браузерной проверкой.
Подробный разбор требований партнёра без завышения готовности: соответствие кейсу. Доказанная экономия и закрытие всех требований не заявляются.
Документация
ТЗ · API · Запуск и остановка · Хостинг · QA · Дизайн QA · Roadmap.
История продуктового плана и план исполнения сохраняют обсуждение будущих возможностей, не заменяют текущую приёмку. HackAlem, трек логистики «Электрокомплект». Hackathon team repository for PlanP.
Авторы коммитов
История коммитов
- 2026-09-23 17:59Merge pull request #4 from BAITC-Hacks/docs/readme-final-20260923emilkuzhantaev-coder
- 2026-09-23 17:58docs: finalize QORAI README and Python 3.13 quick startEmil Kuzhantaev
- 2026-09-23 17:49docs: present QORAI capabilities and product roadmapEmil Kuzhantaev
- 2026-09-23 17:43Merge pull request #3 from BAITC-Hacks/feat/qorai-demo-settings-20260923emilkuzhantaev-coder
- 2026-09-23 17:43docs: record full source coverage and stockout evidence boundaryEmil Kuzhantaev
- 2026-09-23 17:41docs: explain QORAI demand estimates and verified release evidenceEmil Kuzhantaev
- 2026-09-23 17:36feat: add source-backed demand corrections and demo provenanceEmil Kuzhantaev
- 2026-09-23 17:20Merge pull request #2 from BAITC-Hacks/feat/qorai-data-visibility-20260923emilkuzhantaev-coder
Все коммиты (14)
- 2026-09-23 17:19feat: make QORAI source data and algorithm visibleEmil Kuzhantaev
- 2026-09-23 16:54Merge pull request #1 from BAITC-Hacks/docs/logistics-plan-20260923emilkuzhantaev-coder
- 2026-09-23 16:53feat: deliver QORAI inventory planning MVPEmil Kuzhantaev
- 2026-09-23 15:54feat: add source-backed forecasting core and CLIEmil Kuzhantaev
- 2026-09-23 15:20docs: add execution plan and forecasting acceptance casesEmil Kuzhantaev
- 2026-09-23 15:03docs: capture logistics MVP and Stitch design briefEmil Kuzhantaev
Структура репозитория (49 файлов)
.dockerignore .gitignore .python-version Dockerfile README.md docs/AI_HANDOFF.md docs/ALGORITHM.md docs/API_CONTRACT.md docs/CASE_COMPLIANCE.md docs/CHANGELOG_AI.md docs/DEPLOYMENT.md docs/E2E_BOT_REGISTRY.md docs/IMPLEMENTATION_PLAN.md docs/LOGISTICS_PLAN.md docs/PROJECT_STATE.md docs/QA_REPORT.md docs/ROADMAP.md docs/RUNBOOK.md docs/STITCH_DESIGN_BRIEF.md docs/STITCH_PROMPT.txt docs/TECH_SPEC.md docs/design-qa.md requirements.txt scripts/bootstrap_data.py smartbuyer/app.py smartbuyer/cli.py smartbuyer/data.py smartbuyer/demand.py smartbuyer/forecast.py smartbuyer/replenishment.py smartbuyer/serve.py tests/frontend_catalog.cjs tests/frontend_demo.cjs tests/test_api.py tests/test_bootstrap_data.py tests/test_cli.py tests/test_data.py tests/test_demand.py tests/test_forecast.py tests/test_replenishment.py web/app.js web/app.test.cjs web/assets/OFL-Inter.txt web/assets/inter-400.ttf web/assets/inter-500.ttf web/assets/inter-600.ttf web/assets/inter-700.ttf web/index.html web/styles.css
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.