Agro-Pulse
CLI на Go, который загружает Excel поставщиков IEK и SystemElectric, чистит спрос от выбросов и stockout и считает рекомендуемый заказ по SKU с…
О проекте
Команда: Agro-Pulse
Кейс HackAlem.ai: Логистика — Электрокомплект · ekt.kz
Стадия: Рабочий MVP
Задача кейса: Автоматический расчёт заказов поставщикам для пополнения склада, с утверждением менеджером
Суть
CLI на Go, который загружает Excel поставщиков IEK и SystemElectric, чистит спрос от выбросов и stockout и считает рекомендуемый заказ по SKU с учётом остатка, поступлений и MOQ. Каждая рекомендация сопровождается объяснением.
Как устроено
Загрузчики через excelize нормализуют SKU с сохранением ведущих нулей. Выбросы винзоризуются по внешней границе Тьюки Q3+3·IQR отдельно для каждого SKU, по транзакциям и по десезонализированному месячному ряду. Затем корректируется подтверждённый stockout. Сезонность нормируется, тренд оценивается методом Theil–Sen. Заказ считается как raw = max(0, forecast − stock − incoming), округляется и подгоняется под MOQ (минимум или кратность). Если реальных входов не хватает, результат получает статус needs_data вместо вымышленного числа. Выход: JSON с группами поставщиков, срочностью и объяснениями.
Ключевые решения
- Theil–Sen тренд
- сезонные коэффициенты
- Tukey outer fence (Q3+3·IQR)
- винзоризация
- коррекция stockout
- MOQ-округление
- strict mode needs_data
Особенности
Подробно обоснован выбор порога 3·IQR вместо 1.5 и отказ от z-score для скошенных продаж. Режимы строгий (реальные данные) и синтетическое демо жёстко разделены. На реальных данных IEK (3185 SKU) и SystemElectric (724 SKU) все позиции честно получили needs_data, потому что не хватает подтверждённых входов.
Стек
Go · excelize
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-c8b7404d-agro-pulse.
README
README.mdAgro-Pulse
1. Название проекта
Agro-Pulse — автоматический расчёт заказов поставщикам для пополнения склада. Локальный Go MVP для кейса HackAlem / «Электрокомплект».
Быстрый запуск без Excel: go run ./cmd/app --demo.
2. Проблема
Менеджер отдела закупа вручную консолидирует Excel. Из-за трудоёмкости расчёт выполняется редко; разовые крупные продажи искажают регулярный спрос. Результат — избыточные запасы (overstock), дефицит (stockout) и упущенные продажи. Основной пользователь — менеджер отдела закупа.
3. Решение
Agro-Pulse загружает Excel, нормализует SKU, анализирует продажи, ограничивает влияние крупных выбросов и рассчитывает прогноз с сезонностью и устойчивым трендом. При подтверждённых периодах отсутствия товара ядро корректирует спрос. Из прогноза вычитаются текущий остаток и подтверждённые поступления; результат содержит количество, статус и детерминированное объяснение.
Два режима: строгая обработка реальных данных и изолированная синтетическая
демонстрация. Недостаток обязательных реальных входов даёт needs_data, а не
вымышленное число заказа.
4. Что реализовано
REAL PARTNER DATA (partner_data)
- Загрузчики IEK и SystemElectric, подключённые к одному
cmd/app. - Сохранение значимых ведущих нулей и
_в SKU; контроль пустых и конфликтующих данных. - Диагностика спроса, выбросов, покрытия источников; проблемные SKU остаются в отчёте.
- Категория SystemElectric переносится из источника в рекомендацию; коэффициент категории не придумывается.
- JSON с отдельными группами поставщиков, причинами
needs_dataи объяснениями. - На предоставленных файлах за 22.09.2026 проверено: IEK — 3185 SKU, SystemElectric — 724 SKU; все
needs_data, числовых заказов нет.
DEMO MODE (synthetic_demo)
- Пять сценариев, не требующих Excel, с полными явно заданными входами.
- Числовой заказ, сезонность, тренд, коррекция выбросов и подтверждённого stockout.
- Проверка происхождения входов: смешивание demo и partner data отклоняется.
- Каждый скаляр demo JSON помечен
source; расчёт использует то же ядро.
5. Методология расчёта
Ниже описаны допущения MVP, а не согласованные бизнес-правила заказчика.
- Используются завершённые месяцы до
as-of. Пропуски не превращаются в нули; отрицательные месячные продажи исключаются из оценки с предупреждением. - Регулярный спрос получается после коррекции выбросов. Диагностический baseline — среднее наблюдаемых продаж, regular demand — среднее скорректированных продаж.
- Подтверждённые SKU-коэффициенты сезонности нормируются до среднего 1. История очищается от сезонного масштаба; общий профиль компании не назначается SKU.
- Устойчивый тренд — Theil–Sen: медиана попарных наклонов. Уровень — медиана пересечений с осью. Короткая история сопровождается предупреждением.
- Подтверждённый stockout корректирует историю до прогнозирования. Lost demand не прибавляется к итоговому заказу повторно.
- Прогноз восстанавливает сезонный масштаб и учитывает долю календарного месяца в горизонте. По умолчанию горизонт — один месяц, допустимо 1–24.
- Учитывается валидный снимок на дату расчёта; исторический остаток не заменяет текущий.
Поступления учитываются в интервале
[as-of, конец горизонта)при известных датах и количествах. raw = max(0, forecast − current_stock − incoming). Для штук количество округляется вверх; затем применяется известное правило MOQ: минимум или кратность. В strict mode неподтверждённое правило MOQ блокирует числовой заказ.
Страховой запас не добавляется. Категория и поставщик сохраняются как атрибуты; отдельная модель роста по категориям не реализована.
Формулы
Обозначения: y_t — регулярный спрос месяца t после выбросов и stockout,
s_m — сезонный коэффициент календарного месяца m, t — номер месяца от начала истории.
Нормировка сезонности s_m = S_m / mean(S_1..S_12) среднее s_m = 1
Десезонализация z_t = y_t / s_m(t)
Тренд (Theil–Sen) b = median{ (z_j − z_i) / (t_j − t_i) : i < j }
Уровень a = median{ z_t − b · t }
Прогноз на месяц h F_h = max(0, a + b · t_h) · s_m(h) · доля_месяца_в_горизонте
Прогноз Forecast = Σ_h F_h
Сезонное влияние seasonality_impact = Forecast − Σ_h max(0, a + b · t_h) · доля_h
Сырая потребность raw = max(0, Forecast − current_stock − incoming)
incoming — сумма партий с датой прибытия в [as-of, as-of + months). Если у партии
пустое количество или дата, либо поставка просрочена, incoming считается неизвестным.
MOQ и округление (internal/calculator/calculator.go):
| Правило | Действие |
|---|---|
единица шт, упак, компл | q = ⌈raw⌉; для м дробное количество допустимо |
minimum | q > 0 → max(q, MOQ) |
multiple | q > 0 → ⌈q / MOQ⌉ · MOQ |
| MOQ не задан | без корректировки, предупреждение MOQ_NOT_PROVIDED |
| правило неизвестно | ошибка; в strict — needs_data: moq_rule_unconfirmed |
moq_adjustment = q_после_MOQ − q_до_MOQ.
Срочность
| Значение | Условие |
|---|---|
now | текущий остаток 0 при положительном прогнозе |
within_horizon | при равномерном потреблении внутри месяца остаток уходит в минус до очередного поступления или до конца горизонта |
covered | запаса и поступлений хватает на весь горизонт |
unknown | на реальных данных: политика срочности не утверждена заказчиком |
Поступление считается доступным с начала даты прибытия.
6. Алгоритм исключения выбросов
Используется внешняя граница Тьюки: Q3 + 3 × IQR, где IQR = Q3 − Q1.
Реализация: CapOutliers в internal/analytics/outlier.go, вызов — prepareDemand
в internal/analytics/forecast.go.
Почему этот метод и этот порог
- Распределение продаж сильно скошено. Много мелких отгрузок и редкие очень крупные. Z-score опирается на среднее и стандартное отклонение, а их сам же выброс и раздувает, так что крупная продажа маскирует сама себя. Квартили от хвоста не зависят и не требуют нормального распределения.
- Коэффициент 3, а не 1.5. По Тьюки
1.5·IQR— «подозрительное» значение,3·IQR— «экстремальное» (far out). У электротехники регулярный опт и так даёт длинный хвост, и граница 1.5 срезала бы обычные оптовые отгрузки. Задача — отсечь только разовые аномально крупные продажи. - Граница считается по данным каждого SKU, а не задаётся фиксированным числом штук. Поэтому одна формула подходит и для товара, который продаётся по 10 штук, и для товара, который продаётся по 10 000.
Алгоритм (для каждого SKU)
- Выборка транзакций. Строки «Динамики продаж» с документом «Расходная накладная»
с 2025 года до месяца
as-of(не включая его). Граница строится только по положительным количествам; возвраты и нули в неё не входят. - Минимум данных. Меньше 4 положительных значений — граница не строится,
предупреждение
insufficient_transactions_for_outlier_fence. - Квартили
Q1,Q3— линейная интерполяция по отсортированным значениям;Fence = Q3 + 3·(Q3 − Q1). - Винзоризация. Каждая транзакция
x > Fenceзаменяется наFence, а не удаляется. Факт продажи остаётся, в регулярный спрос не попадает только аномальная часть объёма. - Перенос в месячный ряд. Для месяца считается
excess = Σ(x − Fence)по его выбросам. Если сумма транзакций месяца совпадает с файлом месячных продаж (допуск1e-6 · max(1, |месяц|)),regular = max(0, месяц − excess)иapplied = true. Если нет, месячное значение не меняется: выброс помечаетсяapplied = false, добавляется предупреждениеtransaction_monthly_mismatches. - Месячная проверка. Затем тот же тест применяется к месячному ряду, очищенному
от сезонности (
y_t / s_m). Результат снова умножается наs_m. Так страхуются месяцы без сопоставимых транзакций, а сезонный пик не считается выбросом. - Нули и провалы выбросами не считаются, ими занимается коррекция stockout (раздел 8). Пропуски и отрицательные месяцы исключаются с предупреждением.
Что попадает в отчёт
Каждый выброс записывается в outliers[]:
{ "source": { "file": "Динамика продаж_2025-2026.xlsx", "sheet": "Лист_1", "cell": "H13947" },
"original": 3200, "adjusted": 129,
"method": "transaction_tukey_outer_fence", "applied": false }
method принимает значения transaction_tukey_outer_fence или
monthly_deseasonalized_tukey_outer_fence. В диагностике без сезонности —
monthly_unadjusted_tukey_outer_fence.
Ограничения
- Группировка продаж по клиенту не реализована: в загруженной схеме нет клиентского ID.
- У SKU с регулярным крупным оптом IQR может срабатывать на обычные отгрузки.
Пример: IEK
010500006_— 949 потенциальных выбросов. Для таких SKU порог нужно проверить вместе с заказчиком.
7. Почему крупный разовый заказ не ломает прогноз
Фактический demo LARGE OUTLIER добавляет 100000 единиц к одной продаже обычного размера 31. Обнаруживается один выброс; после коррекции регулярный спрос и прогноз равны 310, заказ — 276, как в NORMAL DEMAND. Выброс остаётся видимым в диагностике и объяснении.
Результаты запуска на 01.01.2026:
| Сценарий | Прогноз | Остаток | В пути | Заказ |
|---|---|---|---|---|
| NORMAL DEMAND | 310 | 45 | 0 | 276 |
| LARGE OUTLIER | 310 | 45 | 0 | 276 |
| GROWING DEMAND | 720 | 45 | 0 | 675 |
| INCOMING GOODS | 310 | 45 | 200 | 72 |
| STOCKOUT | 310 | 0 | 0 | 312 |
GROWING DEMAND сочетает рост и январский сезонный коэффициент 2 (остальные — 1). После нормировки сезонное влияние равно 330, тренд — примерно 21.667 ед./месяц. В остальных сценариях профиль плоский. MOQ — кратность 12; для роста — минимум 400.
8. Stockout
observed demand — фактические продажи; lost demand — оценка упущенного спроса;
adjusted/corrected demand — регулярный спрос плюс эта оценка.
Ядро умножает подтверждённое число дней отсутствия товара на медианный дневной
спрос других наблюдаемых месяцев без stockout.
В demo STOCKOUT за декабрь наблюдалось 0 продаж и подтверждено 31 день отсутствия. Оценка lost demand — 310, скорректированный спрос — 310, прогноз — 310, заказ — 312. Пустая ячейка или месячный нулевой остаток сами по себе не подтверждают период отсутствия товара. Реальные загрузчики не создают такое доказательство автоматически.
9. Архитектура
cmd/app/ единый CLI: demo, IEK, SystemElectric, JSON
internal/model/ входы, результаты и модели адаптера SystemElectric
internal/normalize/ SKU, числа, даты, месяцы
internal/loader/ чтение XLSX и соединение адаптера с общим ядром
internal/analytics/ выбросы, тренд, сезонность, stockout, прогноз
internal/calculator/ остаток, поступления, количество заказа, MOQ
internal/pipeline/ strict/demo orchestration и needs_data
internal/explain/ объяснение из рассчитанных значений
internal/demo/ пять синтетических сценариев и маркированный отчёт
tests/ заготовка под интеграционные тесты (пока без тестов)
Основные действующие тесты находятся рядом с кодом в internal/ и cmd/app/.
Корневой main.go сохранён как прежний IEK entry point; для демонстрации используйте cmd/app.
10. Источники данных
IEK — ожидаемые имена файлов:
Ежемесячные продажи в количественном выражении за последние 2 года.xlsxЕжемесячные остатки продукции за последние 2 года ИЭК.xlsxДинамика продаж_2025-2026.xlsxMOQ ИЭК.xlsxПуть ИЭК 22.09.2026.xlsxСезонность ИЭК.xlsx
SystemElectric — автопоиск по ключевым словам имени, проверенная выгрузка:
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
partner_data обозначает реальные данные; synthetic_demo — отдельные fixtures.
IEK хранит происхождение чисел до файла/листа/ячейки. Мост SystemElectric сохраняет
ключ источника в поле Source.File, а лист и координаты — в доступных issue;
полная адресация каждой преобразованной числовой точки пока отсутствует.
Исходники открываются только для чтения; файлы Excel и результаты с partner data не коммитятся.
11. Технологии
Go 1.25+ (версия из go.mod), Excelize v2, стандартные пакеты Go для CLI,
JSON, статистики и тестирования. LLM и внешние AI API не используются.
12. Установка
git clone https://github.com/BAITC-Hacks/hack-c8b7404d-agro-pulse.git
cd hack-c8b7404d-agro-pulse
go mod download
13. Запуск
Demo без файлов партнёра
go run ./cmd/app --demo
go run ./cmd/app --demo --output demo-report.json
Запуск на реальных данных партнёров
Укажите реальные пути к каталогам с Excel-файлами IEK и SystemElectric.
Linux / WSL
go run ./cmd/app \
--iek-dir "/path/to/Cases/IEK" \
--se-dir "/path/to/Cases/Systeme electric" \
--as-of 2026-09-22 \
--months 1 \
--output partner-report.json
Параметры CLI
| Флаг | Описание |
|---|---|
--demo | пять синтетических сценариев, Excel не нужен |
--iek-dir | папка с шестью XLSX IEK (имена — раздел 10) |
--se-dir | папка с XLSX SystemElectric |
--as-of | дата среза, обязательна для real mode |
--months | горизонт прогноза, 1–24 месяца (по умолчанию 1) |
--output | путь к JSON-отчёту; без флага — stdout |
Пример проверенного запуска в WSL
go run ./cmd/app \
--iek-dir "/mnt/c/Users/assem/OneDrive/Desktop/Cases/IEK" \
--se-dir "/mnt/c/Users/assem/OneDrive/Desktop/Cases/Systeme electric" \
--as-of 2026-09-22 \
--months 1 \
--output partner-report.json
Проверенный совместный запуск успешно формирует единый partner-report.json для двух поставщиков.
Пути необходимо заменить на расположение файлов на конкретном компьютере.
В WSL Windows-диск
C:доступен через/mnt/c/.
Только IEK
go run ./cmd/app \
--iek-dir "/path/to/Cases/IEK" \
--as-of 2026-09-22 \
--output iek-report.json
Только SystemElectric
go run ./cmd/app \
--se-dir "/path/to/Cases/Systeme electric" \
--as-of 2026-09-22 \
--output se-report.json
В именах двух файлов IEK используются два пробела (…2 года ИЭК.xlsx, MOQ ИЭК.xlsx) — не переименовывайте их.
--output создаёт новый JSON-отчёт. Без этого флага JSON выводится в stdout.
14. Как проверить решение
- Выполнить
go run ./cmd/app --demo --output demo-report.json. - Открыть JSON:
scenarios[].inputs.scenario.valueсодержит название сценария. - Сравнить NORMAL DEMAND и LARGE OUTLIER: одинаковый заказ, но разное число выбросов.
- Посмотреть GROWING DEMAND: положительный тренд и ненулевое сезонное влияние.
- Сравнить INCOMING GOODS с NORMAL: поступление 200 снижает заказ с 276 до 72.
- Посмотреть STOCKOUT: lost demand 310 и заказ 312.
- В
metrics.explanation.valueпроверить обоснование каждого результата.
Быстрая таблица в PowerShell после экспорта:
$demo = Get-Content -Raw -Encoding utf8 demo-report.json | ConvertFrom-Json
$demo.scenarios | ForEach-Object {
[pscustomobject]@{
Scenario = $_.inputs.scenario.value
Forecast = $_.metrics.forecast.value
Order = $_.metrics.final_recommended_quantity.value
Explanation = $_.metrics.explanation.value
}
} | Format-Table -Wrap
15. Тестирование
go test ./... -count=1
go vet ./...
go build ./...
Проверяются монотонность заказа при росте остатка/поступлений, неотрицательность,
огромный выброс, подтверждённый stockout, сезонность и рост, MOQ, needs_data,
разделение provenance, соответствие explanation объекту и мост SystemElectric.
Тесты реальных Excel включаются отдельно (PowerShell):
$env:AGROPULSE_CASES_DIR = 'C:/path/to/Cases'
$env:SE_DATA_DIR = 'C:/path/to/Cases/Systeme electric'
go test ./... -count=1
Без переменных тесты реальных файлов пропускаются. Финальная проверка 23.09.2026:
go test ./... -count=1 — PASS, включая тесты реальных IEK и SystemElectric;
go build ./..., go mod tidy, go vet ./... — PASS.
Demo и совместный real CLI выполнены успешно. Предыдущие запуски в этой
Windows-среде блокировались Application Control; финальный прогон прошёл полностью.
При ошибке локального build cache можно задать
$env:GOCACHE = Join-Path $PWD '.gocache'; это не снимает ограничения Application Control.
16. Выходной формат
Real JSON: suppliers[] группирует рекомендации по поставщику. Каждая рекомендация
содержит sku, supplier, forecast, current_stock, incoming,
recommended_quantity, urgency, status, needs_data, explanation.
Недоступное числовое значение сериализуется как null.
Demo использует обёртки value/source/derived_from. Реальный фрагмент из
scenarios[0].metrics (поля derived_from здесь опущены для краткости):
{
"sku": {"value": "DEMO-01", "source": "synthetic_demo"},
"forecast": {"value": 310, "source": "synthetic_demo"},
"current_stock": {"value": 45, "source": "synthetic_demo"},
"incoming": {"value": 0, "source": "synthetic_demo"},
"raw_recommended_quantity": {"value": 265, "source": "synthetic_demo"},
"final_recommended_quantity": {"value": 276, "source": "synthetic_demo"},
"status": {"value": "demo_recommendation", "source": "synthetic_demo"},
"urgency": {"value": "within_horizon", "source": "synthetic_demo"}
}
Поставщик demo находится в inputs.supplier.value, объяснение — в
metrics.explanation.value: «прогноз 310.000, остаток 45.000 … учтено в пути
0.000. Исходная потребность 265.000; заказ 276.000 шт.»
17. Ограничения MVP
- IEK: месячные остатки не являются текущим снимком; правила MOQ и применимость общей сезонности к SKU не подтверждены.
- SystemElectric: свободный остаток используется только если он явно предоставлен; дата из заголовка поступлений не считается подтверждённой датой доставки. Старый адаптер не различает отсутствующее поступление и явный ноль в пустом списке: мост консервативно оставляет такой incoming неизвестным.
- Для обоих поставщиков нет подтверждённого SKU-профиля сезонности и длительности stockout; числовой заказ по текущим выгрузкам блокируется.
- MOQ minimum/multiple, сроки поставки, политика срочности, возвратов и конверсии единиц требуют подтверждения. Strict pipeline выдаёт числовой заказ только в штуках.
- Нет анализа клиентов, отдельной модели по категориям, UI, интеграции с 1С, бэктеста точности или автоматической отправки заказа.
- Доказательство числового end-to-end расчёта — явно синтетические demo-сценарии. Решение не заявляется production-ready.
18. Безопасность и ограничения
Заказ не отправляется поставщику автоматически. Финальное решение проверяет и подтверждает сотрудник. Клиентские данные должны оставаться обезличенными. Коммерческие Excel и выгрузки отчётов нельзя публиковать; обработка выполняется локально, без отправки данных в LLM. Исходные Excel не изменяются.
19. Deployed version
Deployed-версия в рамках MVP не предусмотрена; решение запускается локально.
Авторы коммитов
История коммитов
- 2026-09-23 17:25docs: update final README and run instructionsassrakhimova
- 2026-09-23 17:14docs(readme): align markdown tables for readable source viewAlisherBaitas
- 2026-09-23 17:03chore: remove Go build cacheassrakhimova
- 2026-09-23 17:00docs(readme): add formulas, outlier threshold rationale and CLI flagsAlisherBaitas
- 2026-09-23 16:53final: complete Agro-Pulse MVPassrakhimova
- 2026-09-23 16:09feat: integrate IEK pipeline into mainassrakhimova
- 2026-09-23 15:31Merge branch 'main' of https://github.com/BAITC-Hacks/hack-c8b7404d-agro-pulseagabduli
- 2026-09-23 15:31Адаптер данных SystemElectric и аудит данныхagabduli
Все коммиты (19)
- 2026-09-23 14:54Update main.goassemrs04-boop
- 2026-09-23 14:32Merge branch 'main' of https://github.com/BAITC-Hacks/hack-c8b7404d-agro-pulseagabduli
- 2026-09-23 14:32Добавила excelizeagabduli
- 2026-09-23 14:29docs: add test plan with unit and integration test casesAlisherBaitas
- 2026-09-23 14:14Merge branch 'main' of https://github.com/BAITC-Hacks/hack-c8b7404d-agro-pulseagabduli
- 2026-09-23 14:14created main.goagabduli
- 2026-09-23 14:14Create REQUIREMENTS.mdassemrs04-boop
- 2026-09-23 14:10chore: init Go moduleAlisherBaitas
- 2026-09-23 14:10test: add temporary empty stub for integration testsAlisherBaitas
- 2026-09-23 14:10chore(calculator): add temporary empty stubs for calculator and testsAlisherBaitas
- 2026-09-23 14:09chore(analytics): add temporary empty stubs for outlier, stockout, forecast and testsAlisherBaitas
Структура репозитория (47 файлов)
.gitignore Agro-Pulse/DEMO.md Agro-Pulse/IEK_RUN.md Agro-Pulse/README.md Agro-Pulse/REQUIREMENTS.md DATA_AUDIT_SYSTEMELECTRIC.md README.md REQUIREMENTS.md SYSTEMELECTRIC_DATA_AUDIT.md TEST_PLAN.md cmd/app/main.go cmd/app/main_test.go data/README.md go.mod go.sum internal/analytics/forecast.go internal/analytics/forecast_test.go internal/analytics/outlier.go internal/analytics/outlier_test.go internal/analytics/stockout.go internal/analytics/stockout_test.go internal/calculator/calculator.go internal/calculator/calculator_test.go internal/calculator/inventory.go internal/demo/demo_test.go internal/demo/report.go internal/demo/scenarios.go internal/explain/explain.go internal/explain/explain_test.go internal/loader/iek.go internal/loader/loader.go internal/loader/loader_test.go internal/loader/systemelectric.go internal/loader/systemelectric_core.go internal/loader/systemelectric_core_test.go internal/loader/systemelectric_sources.go internal/loader/systemelectric_test.go internal/loader/systemelectric_xlsx.go internal/model/model.go internal/model/replenishment.go internal/normalize/normalize.go internal/normalize/normalize_test.go internal/pipeline/demo.go internal/pipeline/integration_test.go internal/pipeline/pipeline.go main.go tests/integration_test.go
Ветки (2)
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.