StockPilot AI
Локальная панель планирования закупок: по шести Excel-выгрузкам (продажи, остатки, товар в пути, MOQ, сезонность) рассчитывает рекомендуемые заказы…
О проекте
Команда: N Turbo
Кейс HackAlem.ai: Логистика — Электрокомплект · ekt.kz
Стадия: Рабочий MVP
Задача кейса: Автоматический расчёт заказов поставщикам для пополнения склада, с утверждением менеджером
Суть
Локальная панель планирования закупок: по шести Excel-выгрузкам (продажи, остатки, товар в пути, MOQ, сезонность) рассчитывает рекомендуемые заказы поставщику со статусами срочности и объяснением расчёта. LLM не используется.
Как устроено
Шесть xlsx импортируются через ExcelJS и связываются по Коду 1С с проверкой контрольных заголовков. Продажи очищаются, в расходных накладных за 24 месяца ищутся выбросы по IQR (порог Q3 + 1.5 × IQR). Регулярный спрос — среднее за 12 месяцев с коррекцией на долю аномалий. Прогноз = спрос × рост (0.5–1.5) × сезонность (0.7–1.3) × (1 + stockout%). Заказ = ceil(max(0, прогноз − свободный остаток − в пути)/кратность) × кратность. При неполных данных заказ блокируется. На выходе статусы срочности, модальное окно с объяснением и экспорт CSV.
Ключевые решения
- IQR anomaly detection
- moving average demand
- YoY growth factor
- seasonality index
- MOQ rounding
- CSV export with formula escaping
Особенности
Аккуратная работа с данными: выбросы по IQR ищутся на уровне накладных, коэффициенты роста и сезонности ограничены, ошибочные колонки AP/AQ исходного отчёта явно исключены. Если данных не хватает, заказ блокируется, а не показывается нулём. Весь проект сдан одним коммитом.
Стек
Next.js · TypeScript · React · ExcelJS
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-25c5642e-n-turbo.
README
README.mdStockPilot AI
Локальный MVP для планирования закупок Systeme Electric. Next.js + TypeScript, React и ExcelJS. Показывает KPI, рекомендации, срочность, аномалии IQR, объяснение расчёта и экспорт CSV. Статистический расчёт выполняется без внешней LLM и без отправки данных третьим лицам.
Запуск
Из корня репозитория, в обычной среде с Node.js 22+ и npm:
npm ci
npm run build
npm run start
Открыть http://127.0.0.1:3000. Для разработки: npm run dev.
На текущем компьютере Node.js доступен в локальной среде Codex, а npm установлен локально в игнорируемой .tools. Скрипт подставляет эти пути, если Node/npm отсутствуют в PATH:
powershell -ExecutionPolicy Bypass -File .\start.ps1 -Production
Без -Production запускается dev-сервер. Скрипт ничего не устанавливает. Если порт 3000 уже занят запущенным StockPilot, достаточно открыть адрес в браузере.
Локальные данные
Положите шесть исходных .xlsx в data/raw/. Excel не включаются в Git. Нужен ровно один файл каждого типа; суффиксы (1), (2) допустимы, одновременные дубликаты отклоняются.
| Префикс файла | Источник |
|---|---|
MOQ | Лист_1: C — Код 1С, D — артикул, E — кратность |
Ежемесячные продажи | Лист_1: B — Код 1С, C — артикул, E:AK — месячные продажи |
Товар в пути | TDSheet: C — Код 1С, E — категория, AZ — свободный остаток, BC — в пути |
Ежемесячные остатки | Лист_1: C — Код 1С, E:AK — месячная история остатков |
Динамика продаж | Лист_1: A — дата, D — Код 1С, H — количество; C — тип документа |
Сезонность | Первый лист: A4:A6 — годы, B:M — месячные агрегаты |
Импорт настроен на проверенную структуру: месячные колонки январь 2024 — сентябрь 2026. Контрольные заголовки проверяются, при изменении формата требуется обновить mapping. Файлы открываются только для чтения на сервере через /api/data. Импорт кэшируется в памяти по именам, размерам и времени изменения файлов; кнопка «Обновить данные» перечитывает изменённые источники. Результаты не сохраняются на диск и не помещаются в public.
Основной ключ — Код 1С, строка с сохранением ведущих нулей и _. Названия не участвуют в объединении. Дубликаты кодов в товарных таблицах и конфликты артикулов отклоняются. Ассортимент — объединение кодов источников. Товары без нужных данных видны со статусом «Проверить данные», заказ для них не вычисляется.
Расчёт
Параметры доступны в «Настроить расчёт». Горизонт по умолчанию — 1 будущий календарный месяц, это пользовательский сценарий, не lead time. Дата данных определяется максимальной датой операций; для текущего набора — 22.09.2026, начало прогноза — октябрь 2026. Неполный сентябрь исключён из базы, прогноз не является рекомендацией покрытия оставшихся дней сентября.
- Очистка. Отрицательные месячные продажи ограничиваются нулём; исходные значения остаются в деталях. Отрицательные операции исключаются только из IQR, повторно из месячных продаж не вычитаются. Пропуски количества операций не становятся нулём. Пустые месяцы по явно включённой настройке принимаются за ноль; при отключении исключаются из среднего.
- IQR. Для каждого кода берутся положительные строки расходных накладных за 24 завершённых месяца, минимум 8 наблюдений. Квартили — линейная интерполяция, порог
Q3 + 1.5 × (Q3 − Q1). Всё выше порога отмечается как аномалия. Строки заказов покупателей не включаются. Номера документов не используются как ID клиентов. - Регулярный спрос. Для каждого месяца:
max(0, monthly_sales) × (1 − anomaly_excess / positive_transaction_quantity). Доля ограничена диапазоном 0–1. При отключённой коррекции множитель равен 1. Затем берётся среднее за последние 12 завершённых месяцев (сентябрь 2025 — август 2026). История операций и месячный отчёт расходятся, поэтому перенос доли аномалий — явное приближение, а не точная сверка продаж. - Рост. Отношение сумм последних трёх полных месяцев к тем же месяцам прошлого года. Используется очищенная история с той же IQR-коррекцией. Множитель ограничен 0,5–1,5. При отсутствующей или нулевой базе применяется 1 с объяснением.
- Сезонность. Для каждого полного прошлого года из файла сезонности месячный агрегат делится на среднемесячный. Индексы полных 2024–2025 годов усредняются; множитель ограничен 0,7–1,3. Неполный 2026 год исключён. Единица агрегатов не подтверждена; это опциональная общая сезонная форма, не персональная сезонность SKU.
- Stockout. По умолчанию 0%, выключено. Слайдер 0–50% задаёт ручное сценарное увеличение, не восстановленные потери. Месячные остатки не преобразуются в выдуманные интервалы отсутствия.
- Прогноз. Для каждого будущего месяца:
regular_demand × growth × seasonality × (1 + stockout_percent / 100). Сумма по горизонту 1–6 месяцев. - Заказ.
ceil(max(0, forecast − free_stock − in_transit) / order_multiple) × order_multiple. Кратность берётся только из MOQ-файла. Резерв повторно не вычитается из уже свободного остатка. Неизвестные/отрицательные остатки, отсутствующая положительная целая кратность или отсутствие базы блокируют заказ.
AP/AQ и коэффициенты товарной сводки не используются: AP ошибочно включает 13 месяцев, AQ делит результат на 12. Расхождения сентябрьских продаж и кратности отмечены в карточках. Кратность не приравнивается к минимальному заказу. Метка «24.09» не используется как подтверждённая ETA: товар в пути вычитается по условию MVP, дата фактической доступности неизвестна.
Срочность и интерфейс
- «Критично»: нужен заказ, свободный остаток равен нулю.
- «Скоро закончится»: нужен заказ, запас в месяцах меньше половины выбранного горизонта.
- «Плановый заказ»: остальной положительный заказ.
- «Запас достаточен»: заказ равен нулю.
- «Проверить данные»: расчёт заказа заблокирован пропусками.
Покрытие считается по свободному остатку и среднему прогнозу, без предположения о lead time. Порог срочности — правило MVP, а не согласованный SLA.
Поиск работает по артикулу, Коду 1С и названию. Быстрые кнопки: ATN000343, BLNZA000011, U734M. Есть фильтр срочности, сортировка, страницы по 20 строк и модальное объяснение с исходной историей. CSV экспортирует все строки текущего фильтра, включая параметры сценария и замечания; UTF-8 BOM, разделитель ;, потенциальные формулы экранируются. Пропущенные значения остаются пустыми, заблокированный заказ не выдаётся за 0.
Проверки
npm test
npm run check
npm run build
git status --short
git ls-files -- data/raw/
git check-ignore -- data/raw/
Тесты проверяют очистку, IQR, пропуски, пределы коэффициентов, горизонт, вычет запаса, округление, stockout-сценарий и CSV. Интеграционный тест читает реальные исходники с суффиксом (2), проверяет три SKU, уникальность кодов и допустимость всех заказов; при отсутствии этого локального набора тест пропускается.
Ограничения MVP
Нет customer ID, lead time, минимального заказа и фактических stockout intervals. Нет подтверждённой ETA поставок. Нельзя точно отделить разовые проектные продажи от регулярных по клиентам. Нет авторизации, БД, сохранения сценариев или отправки заказов; сервер привязан к 127.0.0.1, рассчитан на локальную демонстрацию. Сценарии сбрасываются при обновлении страницы. Годовые агрегаты сезонности имеют неподтверждённую единицу. Отчёты расходятся, поэтому рекомендации требуют проверки закупщиком. Excel не пересчитываются и не изменяются.
Авторы коммитов
История коммитов
- 2026-09-23 17:28Build StockPilot AI MVP for Systeme ElectricNurbolat Abdikarim
Структура репозитория (19 файлов)
.gitignore README.md app/api/data/route.ts app/globals.css app/layout.tsx app/page.tsx components/dashboard.tsx lib/csv.ts lib/engine.ts lib/importer.ts lib/types.ts next-env.d.ts next.config.ts package-lock.json package.json start.ps1 tests/engine.test.ts tests/import.test.ts tsconfig.json
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.