AAYMA
RU
Войти

Электрокомплект — планирование закупок

Сервис на Go считает рекомендации по заказам поставщикам IEK и Systeme Electric.

Рабочий MVP
Команда «11» 7💬 0 комментариевс 19 сентября 2026 г.

О проекте

Команда: 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 90799 56131324

Данные сохраняются в data/warehouse.json. Если склад уже существует, его содержимое сохраняется; перейти со старого демо на исходные данные можно кнопкой «Данные Excel», подтвердив замену.

Остановка — Ctrl+C. После изменения кода или интерфейса перезапустите go run .: статические файлы включаются в приложение при сборке.

Что посмотреть на сайте

  1. Нажмите «Пример расчёта» — в карточке показано «Нужно → Есть → Приедет → Заказать». Продажи, сезонность и корректировки доступны в «Подробном расчёте».
  2. В «Параметрах расчёта» настройте дату, период анализа, сроки поставщиков и страховой запас. Для встроенного набора используются дата 23.09.2026 и история 365 дней.
  3. Проверьте рекомендации, поиск по названию, артикулу или коду 1С и фильтры поставщиков. Откройте товар, чтобы увидеть обоснование и помесячные корректировки.
  4. В «Рекомендациях» доступны «Все товары» и «Требуют внимания»: блокировки заказа, риск дефицита и уточнение данных. Карточки подскажут следующий шаг; общие предупреждения раскрываются через «Подробнее». Поступления — в «Товарах в пути».
  5. Нажмите «Экспорт CSV». Выгружаются все готовые строки заказа, независимо от фильтров таблицы, с обоснованием и BOM для Excel.

В контрольном примере IVR21-1-25 целевой запас равен 7, доступный остаток — 0, в пути — 5, рекомендация — 2 единицы. Подробный сценарий показа: демо на 3 минуты.

Формирование рекомендаций и экспорт не отправляют заказ поставщику и не создают новую поставку. Решение принимает менеджер.

Как устроен расчёт

Приложение поддерживает два вида истории:

ДанныеМетод
Встроенный набор, кнопка «Данные Excel»Месячные продажи, корректировки всплесков, компенсация возможного отсутствия товара, тренд и сезонность
Прямой импорт XLSX и JSON с дневными продажамиПолные календарные дни до даты расчёта, исключения и возвраты, коррекция всплесков и сезонность

Для месячного расчёта:

  1. Берутся завершённые месяцы, пересекающие выбранный период. Неполный сентябрь 2026 исключается. Месяцы учитываются целиком.

  2. Аномальная часть документных продаж уменьшает соответствующий месячный итог пропорционально. Месячные и дневные источники не суммируются.

  3. Спрос приводится к дневному значению без сезонности. Всплески корректируются по медиане и MAD — медиане абсолютных отклонений. При четырёх и более положительных наблюдениях порог равен max(3 × медиана, медиана + 3 × 1,4826 × MAD).

  4. Если известный начальный остаток нулевой, а спрос не превышает 20% медианы нормальных периодов с запасом, он восстанавливается до этой медианы. При неизвестном остатке такая компенсация не применяется.

  5. При наличии шести месяцев сравниваются медианы последних и предыдущих трёх. Коэффициент тренда ограничен диапазоном 0,75–1,25. Затем применяются месячные коэффициенты будущего периода.

  6. Рассчитываются запас и заказ:

    горизонт = срок поставки + период закупки + страховой запас, дней
    целевой запас = округление вверх прогнозного спроса и страхового запаса
    доступный остаток = остаток − резерв
    потребность = 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_ADDR127.0.0.1:8080Адрес сервера
DATA_FILEdata/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/Конвертеры и браузерные тесты

Хранилище рассчитано на один процесс на файл, без межпроцессных блокировок и истории изменений. Для нескольких экземпляров потребуется другое хранилище. Смена даты расчёта не восстанавливает прошлое состояние склада; при приёмке одновременно обновляйте остатки и уменьшайте количество в пути.

Звёзды
⭐ 0
Форки
0
Язык
Go
Последний push
в прошлом месяце
Коммиты
29
Авторы
3
Файлы
62
Ветки
2

Авторы коммитов

zbralin 14markowinss 13Lamperush 2

История коммитов

  • 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 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.