Электрокомплект — планирование закупок
Сервис на Go считает рекомендации по заказам поставщикам IEK и Systeme Electric.
О проекте
Команда: 11
Кейс HackAlem.ai: Логистика — Электрокомплект · ekt.kz
Стадия: Рабочий MVP
Задача кейса: Автоматический расчёт заказов поставщикам для пополнения склада, с утверждением менеджером
Суть
Сервис на Go считает рекомендации по заказам поставщикам IEK и Systeme Electric. Спрос очищается от всплесков, учитываются сезонность, тренд, сроки поставки, MOQ и кратность. Каждая позиция сопровождается обоснованием, результат выгружается в CSV, решение за менеджером.
Как устроено
Реальные Excel-выгрузки (продажи, остатки, товары в пути, сезонность, MOQ) импортируются и встраиваются в бинарник, склад хранится в JSON. Спрос приводится к дневному без сезонности. Всплески режутся по медиане и MAD с порогом max(3×медиана, медиана+3×1,4826×MAD). Учитывается компенсация отсутствия товара и тренд, ограниченный диапазоном 0,75–1,25. Далее считается целевой запас на горизонт «срок поставки + период закупки + страховой запас», потребность = max(0, цель − доступный остаток − поставки), затем округление по MOQ и кратности. REST API с ETag, веб-интерфейс показывает цепочку «Нужно → Есть → Приедет → Заказать».
Ключевые решения
- median/MAD spike filtering
- seasonality coefficients
- trend clamp
- safety stock
- MOQ/multiplicity rounding
- XLSX structure-based classification
Особенности
Тип отчёта и поставщик определяются по структуре листов и колонок, а не по имени файла. Два режима расчёта: месячный и дневной. Заказ блокируется, если данные неподтверждены. Реальный датасет встроен в один Go-бинарник, для запуска достаточно go run.
Стек
Go · HTML/CSS/JavaScript · JSON storage · Docker · Python
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-2a953101-11.
README
README.mdЭлектрокомплект — планирование закупок
Сервис помогает менеджеру определить, что, сколько и у какого поставщика заказать. Он объединяет продажи, остатки и товары в пути, корректирует разовые всплески спроса и рассчитывает пополнение с учётом сезонности, сроков поставки и правил заказа.
Результат — рекомендации по поставщикам, понятное обоснование по каждому товару и экспорт в CSV. В интерфейсе можно проследить весь расчёт: от исходных продаж до итогового количества.
Стек: Go · HTML/CSS/JavaScript · JSON-хранилище · Docker. Интерфейс и подготовленный набор Excel-данных встроены в бинарник. Для обычного запуска не нужны Node.js, Python или отдельная база данных.
Быстрый запуск
Требуется Go 1.23+. Из корня проекта:
go run .
Откройте http://127.0.0.1:8080. При первом запуске новый склад автоматически заполняется подготовленными данными IEK и Systeme Electric:
| Товаров | Месячных записей | Строк поставок | Коэффициентов сезонности |
|---|---|---|---|
| 3 907 | 99 561 | 313 | 24 |
Данные сохраняются в data/warehouse.json. Если склад уже существует, его содержимое сохраняется; перейти со старого демо на исходные данные можно кнопкой «Данные Excel», подтвердив замену.
Остановка — Ctrl+C. После изменения кода или интерфейса перезапустите go run .: статические файлы включаются в приложение при сборке.
Что посмотреть на сайте
- Нажмите «Пример расчёта» — в карточке показано «Нужно → Есть → Приедет → Заказать». Продажи, сезонность и корректировки доступны в «Подробном расчёте».
- В «Параметрах расчёта» настройте дату, период анализа, сроки поставщиков и страховой запас. Для встроенного набора используются дата 23.09.2026 и история 365 дней.
- Проверьте рекомендации, поиск по названию, артикулу или коду 1С и фильтры поставщиков. Откройте товар, чтобы увидеть обоснование и помесячные корректировки.
- В «Рекомендациях» доступны «Все товары» и «Требуют внимания»: блокировки заказа, риск дефицита и уточнение данных. Карточки подскажут следующий шаг; общие предупреждения раскрываются через «Подробнее». Поступления — в «Товарах в пути».
- Нажмите «Экспорт CSV». Выгружаются все готовые строки заказа, независимо от фильтров таблицы, с обоснованием и BOM для Excel.
В контрольном примере IVR21-1-25 целевой запас равен 7, доступный остаток — 0, в пути — 5, рекомендация — 2 единицы. Подробный сценарий показа: демо на 3 минуты.
Формирование рекомендаций и экспорт не отправляют заказ поставщику и не создают новую поставку. Решение принимает менеджер.
Как устроен расчёт
Приложение поддерживает два вида истории:
| Данные | Метод |
|---|---|
| Встроенный набор, кнопка «Данные Excel» | Месячные продажи, корректировки всплесков, компенсация возможного отсутствия товара, тренд и сезонность |
| Прямой импорт XLSX и JSON с дневными продажами | Полные календарные дни до даты расчёта, исключения и возвраты, коррекция всплесков и сезонность |
Для месячного расчёта:
-
Берутся завершённые месяцы, пересекающие выбранный период. Неполный сентябрь 2026 исключается. Месяцы учитываются целиком.
-
Аномальная часть документных продаж уменьшает соответствующий месячный итог пропорционально. Месячные и дневные источники не суммируются.
-
Спрос приводится к дневному значению без сезонности. Всплески корректируются по медиане и MAD — медиане абсолютных отклонений. При четырёх и более положительных наблюдениях порог равен
max(3 × медиана, медиана + 3 × 1,4826 × MAD). -
Если известный начальный остаток нулевой, а спрос не превышает 20% медианы нормальных периодов с запасом, он восстанавливается до этой медианы. При неизвестном остатке такая компенсация не применяется.
-
При наличии шести месяцев сравниваются медианы последних и предыдущих трёх. Коэффициент тренда ограничен диапазоном 0,75–1,25. Затем применяются месячные коэффициенты будущего периода.
-
Рассчитываются запас и заказ:
горизонт = срок поставки + период закупки + страховой запас, дней целевой запас = округление вверх прогнозного спроса и страхового запаса доступный остаток = остаток − резерв потребность = max(0, целевой запас − доступный остаток − поставки в горизонте) заказ = положительная потребность с учётом минимума и кратности
Просроченные поставки отмечаются отдельно и не уменьшают потребность. Положительные заказы группируются по поставщику. Расчёт сопровождается историей корректировок, предупреждениями и текстовым объяснением.
В дневном режиме дни без продаж внутри известной истории считаются нулевыми; текущий неполный день исключается. Возвраты и ручные исключения сохраняются в исходных данных, но не увеличивают регулярный спрос. Месячная компенсация отсутствия и расчёт тренда в этом режиме не применяются.
Данные и импорт
Для знакомства достаточно встроенного набора — вручную загружать исходные книги не требуется. Он подготовлен из выгрузок на 22.09.2026. Состав источников и результаты преобразования доступны в отчёте импорта.
Чтобы загрузить свои данные, нажмите «Загрузить данные»:
- Excel: выберите полный комплект из шести
.xlsxдля одного поставщика или 12 для обоих. Проверяются только обнаруженные поставщики; полный комплект второго не требуется. Для каждого неполного комплекта выводится отдельный русский список недостающих отчётов. Для предоставленных выгрузок укажите дату2026-09-22и начало истории2025-01-01. - JSON: выберите один набор данных или ранее скачанную резервную копию.
- Проверьте результат и подтвердите «Заменить данные». Импорт заменяет весь склад; перед заменой можно скачать текущие данные.
В IEK/ и systemElectric/ находятся дневные продажи, месячные продажи, месячные остатки, сезонность, MOQ и товары в пути. MOQ ИЭК теперь находится в папке IEK/: MOQ ИЭК.xlsx. Файлы можно добавлять по очереди через «Добавить файлы», сохраняя уже выбранные.
Тип отчёта и поставщик определяются по структуре листов и колонок; имя файла служит дополнительным признаком. Для общих структур динамики продаж и сезонности учитывается обнаруженный комплект. Если поставщика нельзя определить однозначно или имя противоречит структуре, проверка просит уточнить поставщика в имени файла. Файл без имени поставщика не считается IEK автоматически.
Поддерживается структура предоставленных выгрузок; произвольные Excel-таблицы и старый формат .xls не поддерживаются. Лимит загрузки — 12 файлов и 64 МиБ; сервер читает сохранённые значения формул без их выполнения. Не добавляйте повторно копии уже выбранных книг; одного MOQ ИЭК.xlsx достаточно.
Полный комплект Systeme Electric в systemElectric/ содержит шесть файлов (написание Syseme сохранено из исходной выгрузки):
MOQ SystemElectric.xlsxДинамика продаж_Syseme Electric_2025-2026.xlsxЕжемесячные остатки SystemElectric 2024-2026.xlsxЕжемесячные продажи в кол-м выражении SystemElectric 2024-2026.xlsxСезонность SystemElectric 2024-2026.xlsxТовар в пути_SystemElectric на 22.09.2026.xlsx
Для JSON обязательны массивы suppliers, products, sales, stock, shipments; пустые коллекции задаются как []. Месячный формат дополнительно использует monthly, seasonality, source_date. Каждый товар должен иметь остаток и ссылку на существующего поставщика. Даты — YYYY-MM-DD, количества — в одной базовой единице товара. Пример: dataset.json; полная модель: model.go.
Допущения и качество данных
Встроенный месячный набор допускает предварительные заказы с предупреждениями: срок поставки принят равным 14 дням, при неизвестных правилах заказа используется расчёт по одной единице, при отсутствующем остатке — предварительный ноль. Остатки и количества в пути округлены вниз до целых базовых единиц; дробные продажи сохраняются. Отрицательные месячные продажи и остатки ограничены нулём.
Прямой импорт XLSX сохраняет дробные количества и применяет более строгие проверки: неподтверждённые сроки, непроверенные или явно устаревшие остатки и нерешённые проблемы товара блокируют заказ. Предварительное количество видно в suggested_quantity, но строка не попадает в заказы и CSV. Поэтому результаты двух способов загрузки могут различаться.
В исходниках IEK остатки — начальное сальдо на 01.09.2026. У части товаров Systeme Electric есть текущие остатки на 22.09.2026. Перед реальной закупкой нужны актуальные остатки, подтверждённые сроки и проверка единиц, особенно для бухт и метров. Месячная компенсация дефицита — эвристика: точные дни отсутствия товара по этим данным неизвестны.
Обновление встроенного набора
После изменения исходных книг выполните из корня проекта:
python3 scripts/import_excel.py
go run .
Python-конвертер использует стандартную библиотеку и обновляет internal/realdata/dataset.json.gz и отчёт импорта. Перезапустите приложение, затем нажмите «Данные Excel», чтобы заменить уже сохранённый склад новым набором.
Для отдельной дневной выгрузки и подробной сверки источников доступен python3 scripts/import_supplier_workbooks.py: результат записывается в data/supplier-import.json и data/supplier-import-report.json. Описание этого преобразования — в проверке исходных книг.
API
| Метод | Маршрут | Назначение |
|---|---|---|
GET | /healthz, /readyz | Доступность и готовность сервиса |
GET | /api/v1/dataset | Текущий снимок, ревизия и ETag |
PUT | /api/v1/dataset | Полная замена набора с If-Match |
GET | /api/v1/excel-dataset | Встроенный набор без изменения склада |
POST | /api/v1/import/xlsx | Предпросмотр Excel: multipart/form-data, поля files, as_of, history_start |
POST | /api/v1/recommendations | Рекомендации и объяснения по товарам |
POST | /api/v1/recommendations.csv | Экспорт готовых заказов |
Пример расчёта для запущенного локального сервера:
curl --fail-with-body http://127.0.0.1:8080/api/v1/recommendations \
-H 'Content-Type: application/json' \
-d '{"as_of":"2026-09-23","lookback_days":365,"review_period_days":14,"safety_stock_days":7}'
История допускает 7–730 дней, период закупки — 1–365, страховой запас — 0–365. Пустой запрос {} для встроенного набора использует дату после source_date и 365 дней истории; без source_date — сегодня по UTC и 90 дней. Период закупки и страховой запас по умолчанию равны 14 и 7 дням.
Для PUT отправляйте сам набор, без оболочки ответа GET, и текущий ETag в If-Match, включая кавычки. При конфликте ревизий возвращается 412: перечитайте данные перед повторной записью. Отсутствующий If-Match даёт 428. Ответы расчёта содержат X-Dataset-Revision; интерфейс проверяет его перед экспортом.
Настройки и Docker
| Переменная | По умолчанию | Назначение |
|---|---|---|
HTTP_ADDR | 127.0.0.1:8080 | Адрес сервера |
DATA_FILE | data/warehouse.json | Файл хранилища |
SEED_EXCEL | Не задана | 1 включает автозаполнение нового склада при явном DATA_FILE; без явного DATA_FILE оно включено автоматически |
API_KEY | Пусто | Общий ключ доступа, минимум 24 символа, без пробелов по краям |
CORS_ORIGIN | Пусто | Разрешённый origin отдельного веб-клиента |
Для работы вне loopback-адреса нужен API_KEY. Передавайте его как Authorization: Bearer <ключ> или введите в настройках подключения сайта. Интерфейс хранит ключ только в памяти вкладки.
Запуск через Docker Compose:
export API_KEY="$(openssl rand -hex 32)"
docker compose up --build -d
Откройте http://127.0.0.1:8080 и введите ключ. Контейнер запускается от непривилегированного пользователя, автоматически заполняет новый склад и хранит данные в томе warehouse-data. docker compose down останавливает сервис с сохранением тома.
Для отдельного пустого склада задайте новый путь явно: DATA_FILE=data/empty-warehouse.json SEED_EXCEL=0 go run .. Существующий файл при этом не очищается.
Разработка и проверки
go test -race ./...
go vet ./...
go build -o bin/backend .
python3 -m unittest discover -s scripts -p 'test_*.py'
Браузерные тесты требуют Node.js 20+ и Chromium. Они запускают отдельный сервер с временным складом:
npm ci
npx playwright install chromium
npm run test:syntax
npm run test:browser
Для сценариев только IEK, только Systeme Electric и обоих поставщиков с исходными книгами:
SUPPLIER_WORKBOOK_DIR="$PWD" go test ./internal/workbook -v
SUPPLIER_WORKBOOK_DIR="$PWD" npm run test:browser
Перед ними соберите bin/backend командой выше. Для установленного Chrome можно задать CHROME_BIN=/path/to/chrome. Сценарий реального товара проверяется командой go test ./internal/realdata -run TestLiveDemoSKUTrace -v.
| Каталог | Содержимое |
|---|---|
| internal/planning/ | Дневной и месячный расчёт, модель данных |
| internal/workbook/ | Чтение XLSX и прямой импорт |
| internal/realdata/ | Встроенный набор и аудит источников |
| internal/httpapi/, internal/storage/ | API, CSV и атомарное сохранение снимков |
| internal/webui/ | Интерфейс, встроенный через go:embed |
| scripts/, tests/ | Конвертеры и браузерные тесты |
Хранилище рассчитано на один процесс на файл, без межпроцессных блокировок и истории изменений. Для нескольких экземпляров потребуется другое хранилище. Смена даты расчёта не восстанавливает прошлое состояние склада; при приёмке одновременно обновляйте остатки и уменьшайте количество в пути.
Авторы коммитов
История коммитов
- 2026-09-23 17:49fix: removed unnecessary fileszbralin
- 2026-09-23 17:48feat(ui): simplify warnings, categorize issues, and streamline product detailszbralin
- 2026-09-23 17:43merge: объединить origin/main и исправления Excel-импортаmarkowinss
- 2026-09-23 17:36fix(import): исправить определение поставщиков и валидацию Excel-комплектовmarkowinss
- 2026-09-23 17:28fix: remove same info from side menuzbralin
- 2026-09-23 17:28fix: readme changezbralin
- 2026-09-23 17:22bugfixmarkowinss
- 2026-09-23 17:21clean readme.mdzbralin
Все коммиты (29)
- 2026-09-23 17:15delete unnessecary fileszbralin
- 2026-09-23 17:02fix(ui): исправить перенос обоснований, убрать кликабельность профиля и уточнить единицу счётчика всплесковmarkowinss
- 2026-09-23 16:57move moq to a folderzbralin
- 2026-09-23 16:47feat: автоматически заполнять новый склад даннымиmarkowinss
- 2026-09-23 16:40merge: объединить Excel-расчёты и импорт XLSX из origin/mainmarkowinss
- 2026-09-23 16:34feat: автоматически заполнять новый склад данными из Excelmarkowinss
- 2026-09-23 16:08• feat: support XLSX uploads alongside JSON with import preview and validationzbralin
- 2026-09-23 15:33add readme in ruszbralin
- 2026-09-23 15:22Backend:zbralin
- 2026-09-23 15:18add instruction fileszbralin
- 2026-09-23 15:04feat: подключить реальные Excel и подготовить демо закупокmarkowinss
- 2026-09-23 14:34add system electric instructionsLamperush
- 2026-09-23 14:33add instruction filesLamperush
- 2026-09-23 14:25port message for conviniencezbralin
- 2026-09-23 14:20filesmarkowinss
- 2026-09-23 14:14feat: add purchasing dashboard integrated with Go backendzbralin
- 2026-09-23 14:12filesmarkowinss
- 2026-09-23 13:58ignore libreoffice lock filesmarkowinss
- 2026-09-23 13:56filesmarkowinss
- 2026-09-23 13:46feat: add warehouse replenishment backend with spike filteringzbralin
- 2026-09-19 23:36testmarkowinss
Структура репозитория (62 файлов)
.dockerignore .gitignore Dockerfile IEK/MOQ ИЭК.xlsx IEK/Динамика продаж_2025-2026.xlsx IEK/Ежемесячные остатки продукции за последние 2 года ИЭК.xlsx IEK/Ежемесячные продажи в количественном выражении за последние 2 года.xlsx IEK/Путь ИЭК 22.09.2026.xlsx IEK/Сезонность ИЭК.xlsx Makefile README.md compose.yaml docs/REGRESSION_AUDIT.md docs/SUPPLIER_DATA_REVIEW.md examples/dataset.json examples/recommendation.json go.mod internal/httpapi/import.go internal/httpapi/import_test.go internal/httpapi/server.go internal/httpapi/server_test.go internal/planning/model.go internal/planning/monthly.go internal/planning/monthly_test.go internal/planning/planner.go internal/planning/planner_test.go internal/planning/supplier_data_test.go internal/realdata/data.go internal/realdata/data_test.go internal/realdata/dataset.json.gz internal/realdata/import-report.json internal/storage/store.go internal/storage/store_test.go internal/webui/public/app.js internal/webui/public/index.html internal/webui/public/styles.css internal/webui/web.go internal/webui/web_test.go internal/workbook/classify.go internal/workbook/classify_test.go internal/workbook/import.go internal/workbook/import_test.go internal/workbook/reader.go internal/workbook/testdata/iek/MOQ ИЭК.xlsx internal/workbook/testdata/iek/Динамика продаж ИЭК.xlsx internal/workbook/testdata/iek/Ежемесячные остатки ИЭК.xlsx internal/workbook/testdata/iek/Ежемесячные продажи ИЭК.xlsx internal/workbook/testdata/iek/Путь ИЭК 22.09.2026.xlsx internal/workbook/testdata/iek/Сезонность ИЭК.xlsx main.go package-lock.json package.json scripts/import_excel.py scripts/import_supplier_workbooks.py scripts/test_import_supplier_workbooks.py systemElectric/MOQ SystemElectric.xlsx systemElectric/Динамика продаж_Syseme Electric_2025-2026.xlsx systemElectric/Ежемесячные остатки SystemElectric 2024-2026.xlsx systemElectric/Ежемесячные продажи в кол-м выражении SystemElectric 2024-2026.xlsx systemElectric/Сезонность SystemElectric 2024-2026.xlsx systemElectric/Товар в пути_SystemElectric на 22.09.2026.xlsx tests/browser.test.mjs
Ветки (2)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.