Производство
ENRU
Кейс - заказная ERP

Производство перестало планироваться на глаз.

Селлер женской одежды со своим швейным производством: четыре кабинета на двух маркетплейсах, пять производственных участков, ткани с плечом поставки до 38 дней. Система считает заказ на производство целиком: какие изделия, какими партиями, на каких неделях кроить и шить, какие ткани и когда заказывать, что и когда везти на склады площадок.

FastAPIPostgreSQL 16CeleryReact 18 TypeScriptAPI двух маркетплейсовGoogle SheetsTelegram
58
таблиц данных в 20 модулях бэкенда
84
API-эндпоинта, у каждого модуля свой README
400+11
автотестов бэкенда и фронта, прогон ночью
4
кабинета на двух маркетплейсах в одном контуре
5
участков производства с раздельной мощностью
14 недель
горизонт планирования, пересчёт по кнопке
01 Контекст и боль

Цех работает, а решение "что запускать" принимается вручную

У заказчика собственное швейное производство и четыре витрины на двух маркетплейсах. Производство физически одно, а спрос приходит из четырёх мест. Каждую неделю кто-то садился и вручную решал, что подвинуть и куда вставить.

Спрос в четырёх кабинетах, производство одно

Один и тот же артикул продаётся с четырёх витрин. Чтобы понять, сколько его шить, нужно сложить заказы всех кабинетов, а потом развезти готовое обратно пропорционально их продажам. В таблицах это делалось руками и разъезжалось.

Ткань едет дольше, чем шьётся изделие

Плечо поставки ткани доходит до 38 дней, а цикл от кроя до склада площадки укладывается в три недели. Значит, решение о заказе ткани принимается за полтора месяца до того, как товар встанет на полку. Пропустил точку заказа - пропустил сезон.

Мощность цеха не была цифрой

Сколько часов в неделю реально даёт раскрой, швея, утюг, ОТК и комплектовка, не знал никто. План ставился по ощущению, узкое место всплывало в середине недели, когда двигать уже поздно.

Себестоимость жила в файлах технолога

Нормы расхода ткани и рубли по операциям лежали в личных калькуляциях. Один и тот же артикул в разных файлах стоил по-разному, и любой расчёт прибыли начинался со спора о цифрах.

02 Архитектура

Четыре кабинета на входе, недельный план на выходе

Ночью Celery стягивает четыре кабинета двух маркетплейсов в PostgreSQL. Справочники производства и расчётное ядро превращают заказы в партии по неделям. Люди работают в React-портале, менеджеры видят ту же правду в Google-таблице, срочное приходит в Telegram. Внешние системы только читаются: система не пишет на площадки ничего.

НОЧНОЙ КОНВЕЙЕР · ВРЕМЯ МСК Маркетплейс А 2 кабинета продавца заказы · остатки · воронка синк ночью Маркетплейс Б 2 кабинета продавца заказы · финансы · реклама отчёты по заданию Файлы технолога нормы расхода ткани рубли по операциям загрузка шаблоном xlsx Слой синков Celery Beat, журнал прогонов идемпотентный импорт память о заказанном отчёте: после перезапуска доскачивается rate-limit на каждый кабинет границы суток по МСК только чтение внешних систем PostgreSQL 16 58 таблиц · 28 миграций Redis 7: кэш и брокер бэкап перед каждым выкатом Ядро планирования specs: ткани, спецификации, участки, параметры plan_engine: модель спроса, перебор с отсечением, версии production: партии, статусы, заказы тканей, выгрузки Портал производства План · Загрузка · Сырьё Раскрой · Пошив · Доставка 5 цветовых схем, светлая и тёмная Google-витрина та же правда для менеджеров рендер в 07:35, до пулла Telegram точка заказа ткани пройдена участок перегружен на неделе ЖЕЛЕЗНОЕ ПРАВИЛО Система ничего не пишет во внешние системы. Заказы тканей, поставки и цены остаются за людьми: система считает и объясняет, но решение и кнопку оставляет человеку.
20модулей бэкенда, у каждого свой README
28миграций схемы, выкат только аддитивный
07:35рендер Google-витрины, чтобы не столкнуться с пуллом
0методов записи во внешние API
03 Модель спроса

Сколько будет заказов, если ничего не менять

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

База: заказы, а не выкупы

Темп считается по заказам: выкуп приходит позже и смещает картину назад на две недели. Для решения "запускать ли партию" важна скорость спроса сегодня.

Сезонность коэффициентом месяца

У категории свой профиль по месяцам, у отдельного артикула можно задать свой. Коэффициент виден в таблице отдельной колонкой: человек видит, что именно подняло план.

Ручная поправка с подписью

Менеджер может задать темп и множитель руками, с комментарием. Поправка не растворяется в расчёте: в блоке "Почему" она стоит отдельной строкой с автором.

Границы суток - московские. Сервер живёт в UTC, а бизнес считает день по Москве. Пока это не было зафиксировано, вечерние заказы попадали в следующие сутки и темп продаж прыгал на ровном месте. Все периоды аналитики режутся по московскому дню.

04 Расчётное ядро

Недельная сетка, партии и объяснение каждой цифры

Ядро расставляет партии по неделям так, чтобы прибыль горизонта была максимальной, а участки не перегружались. У каждой партии три недели: крой, пошив, доставка. Партия кратна минимальному запуску: настил меньше него не окупает раскрой.

Недельный план: партии по неделям с этапами крой, пошив и доставка, заказы тканей строкой ниже
Вкладка План: сводка горизонта, предупреждения движка, партии цветными блоками по неделям и строки заказа тканей под ними. Клик по партии открывает карточку с расчётом и ответом на вопрос "почему именно эта неделя".

Что считает ядро

Перебор вариантов запуска с отсечением заведомо худших веток, затем перебалансировка по участкам. Результат это версия расчёта: её можно сравнить с рабочей и только потом сделать рабочей.

Версии, а не перезапись

Каждый прогон сохраняется отдельной версией с прибылью и числом партий. Прежний рабочий план не исчезает: незапущенные партии закрываются, запущенные живут дальше своим жизненным циклом.

Предупреждения вместо тихой подстановки

Нет плеча у ткани, нет спецификации у артикула, ставка участка на проверке: движок говорит об этом списком наверху экрана. Молчаливая подстановка значения по умолчанию здесь недопустима: план выглядел бы честным, будучи выдуманным.

Потерянные продажи считаются деньгами

То, что не успеваем сшить до того, как полка встанет, показывается рублями рядом с прибылью горизонта. Это единственный способ сравнить "запустить сейчас" и "подождать" в одних единицах.

05 Загрузка участков

Успеют ли участки сшить то, что поставлено на неделю

Пять участков, у каждого своя мощность и своя ставка. Загрузка считается на лету при каждом запросе: посчитанные часы и проценты нигде не хранятся, иначе у загрузки было бы две правды.

часов на штуку по участку = рубли операции из спецификации / ставка участка, ₽ за час
мощность участка, ч в неделю = людей × часов в смене × смен в неделю × (1 − резерв) − сторонние часы
нагрузка участка за неделю, ч = сумма по партиям недели (штук × часов на штуку)
загрузка, % = нагрузка / мощность × 100

Пример: 3 человека по 8 часов, 5 смен, резерв 12%, сторонним 0 часов - это 3 × 8 × 5 × 0,88 = 105,6 часа. Пошив юбки 495 ₽ при ставке швеи 2 202 ₽ за час - это 0,225 часа, то есть 13,5 минуты на штуку; партия 240 штук займёт 54 часа.

Загрузка участков по неделям со светофором процентов и таблица что шить
Вкладка Загрузка: проценты по неделям со светофором (больше 100 красный, от 85 жёлтый) и таблица "Что шить" с остатком, скоростью заказов, маржой и датой, позже которой запускать уже поздно.

Пустота честнее нуля. Если у участка не указано число людей, мощности нет: ячейка серая с причиной "у участка Утюг не указано число людей", а не ноль и не 100%. Если сторонние часы съели всю мощность, часы партий считаются, а процента нет, и в шапке участка цифра остаётся с расшифровкой. Причин может быть несколько, и в ячейке они собираются в одну фразу.

5участков: раскрой, пошив, утюг, ОТК, комплектовка
>100%красная ячейка: на этой неделе не успеть
85-100%жёлтая зона: запаса на переделку уже нет
0сохранённых расчётных часов: считаются на лету
06 Ткани и точка заказа

Календарь заказа считается обратным ходом от недели кроя

Ткань едет от 9 до 38 дней в зависимости от поставщика. Система берёт неделю кроя, вычитает плечо поставки и запас времени и получает дату, позже которой заказывать уже поздно. Строки с пройденной точкой заказа стоят красными наверху списка.

Таблица тканей с плечом поставки и календарь заказа с датами заказать до
Вкладка Сырьё: слева ткани с плечом, ценой, остатком и потребностью по рабочему плану, справа календарь заказов со сроком "заказать до" и списком партий, ради которых заказ нужен.

Заказ размещён и ткань пришла

Две кнопки у строки календаря: первая создаёт заказ с плановой датой прихода, вторая подтверждает приход фактической датой и метрами. Расхождение плана и факта видно и попадает в следующий пересчёт.

Запас времени отдельным параметром

К плечу поставки прибавляется запас на срыв срока и на брак. Это настройка, а не константа в коде: срок поставщика меняется звонком, а выкат кода занимает время.

Сигнал уходит в Telegram

Когда точка заказа проходит, менеджер получает сообщение. Ждать, пока кто-то откроет вкладку, здесь нельзя: пропущенный заказ ткани стоит месяца.

07 Жизнь партии

От строки плана до остатка на складе площадки

Рабочий план один. Партия проходит шесть состояний, и на каждом переходе система забирает факт: дату и количество. Расхождение с планом видно сразу и участвует в следующем расчёте.

Планпартия предложена расчётом и принята в работу
Ткань заказаназаказ размещён, известна плановая дата прихода
Раскроенафакт: дата и сколько штук раскроено
Отшитафакт по пошиву, партия готова к отгрузке
Отгруженауехала на склад площадки, указан кабинет назначения
Принята складомисчезает из плана и живёт дальше как обычный остаток

Куда везти, считается, но правится руками. По умолчанию готовая партия делится между кабинетами пропорционально их заказам за период. Менеджер может переписать раскладку: у него бывают причины, которых нет в данных, например приёмка закрыта на конкретном складе.

08 Язык интерфейса

Никаких буферов, барабанов и узких мест на экране

Внутри работает нормальная теория ограничений. На экране её терминов нет: люди в цехе говорят другими словами, и обучать их словарю ради интерфейса незачем.

В коде и в ТЗВ интерфейсе
ключ товара, item_keyАртикул
спецификация, BOMИз чего шьём
норма расходаТкани на 1 штуку, м
логистическое плечоТкань едет, дней
производственный циклОт кроя до склада, недель
буферЗапас времени
точка заказаЗаказать до
мощность участкаЧасов в неделю
перебор с отсечениемкнопка "Рассчитать план"
маржа на единицуЗаработок с 1 штуки

У каждой расчётной цифры есть подсказка "как посчитано": формула словами и числа подстановки. Цифра, которую нельзя объяснить за десять секунд, в этом интерфейсе считается недоделанной.

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

Что изменилось у заказчика

БылоСтало
Спрос считали по кабинету, а производство одно. Заказы четырёх кабинетов складываются по ключу товара, а готовая партия развозится обратно пропорционально их продажам.
"Что запускать" решал человек с таблицей. Расчёт предлагает партии по неделям и называет прибыль горизонта и потерянные продажи рублями. Решение остаётся за человеком, но теперь оно с цифрой.
Мощность цеха была ощущением. Пять участков с раздельной мощностью и загрузкой по неделям, красная ячейка видна до того, как неделя началась.
Ткань заказывали, когда вспоминали. Календарь заказа обратным ходом от недели кроя с датой "заказать до" и сигналом в Telegram.
Себестоимость жила в личных файлах. Спецификации загружаются шаблоном и становятся единственным источником норм и рублей операции для всех расчётов.
"Почему такая цифра" никто не мог объяснить. У каждой расчётной цифры подсказка с формулой словами и числами подстановки, а у партии блок "Почему".

Кирилл Седов

Бизнес-аналитик и строитель систем. Начинаю с бизнес-процесса и контракта данных, потом автоматизирую. Здесь ручной разбор "что подвинуть и куда вставить" заменён расчётом, который умеет себя объяснить.

Связаться в LinkedIn