Селлер женской одежды со своим швейным производством: четыре кабинета на Wildberries и Ozon, свой склад готовой продукции и цех из двух бригад по мастерам. Система отвечает на три вопроса недели: что довезти со своего склада на площадки, что шить и в каком направлении цеха, как идёт неделя по факту. Рядом деньги, которые сходятся с учётной системой заказчика до копейки, сезонность из собственной истории продаж и карточка артикула с двумя себестоимостями.
У заказчика собственное швейное производство, свой склад готовой продукции и четыре витрины на двух маркетплейсах. Каждую неделю планировщик руками собирал ответы: что довезти на площадки, что поставить в пошив, хватит ли людей. Деньги при этом считались в двух местах и не сходились.
Дважды в неделю на каждый кабинет: номенклатура площадки, остатки 1С, остатки площадки, склад продавца, лента заказов. В ленте нет штрихкода, ключ клеился из артикула и размера, ошибки подстановки руками менялись на нули. Резерв между кабинетами обнулялся, и одна штука могла уйти в два кабинета.
Один артикул продаётся с четырёх витрин. Сколько его шить, видно только из суммы заказов всех кабинетов. А выкуп у одежды далеко не стопроцентный: часть заказов возвращается на склад и продаётся снова.
Норма часов на изделие и сколько людей у мастера. Расценки в рублях были, но переводить их в часы через среднюю ставку слишком грубо. Требование заказчика прямое: на плане нужны часы, деньги не нужны.
Суммы сходились до рубля, а база показа была другой. Учётная система считает выручку по чекам покупателей, а портал делил маржу на цену продавца, которая выше чеков в 1,6-2,4 раза. Маржа на портале выходила в разы ниже, чем в 1С.
Прайс фабрики для отчётов и листы калькуляций технолога, где после итога идут коэффициенты и число, вбитое руками. Какая из цифр правильная, зависело от того, кто спрашивает.
Сезонность не была заведена ни в учётной системе, ни в Excel. У каждого артикула свой сезон: костюмные и льняные брюки из одной категории продаются в разные месяцы. А площадка отдаёт не больше года истории.
Ночью Celery стягивает четыре кабинета двух маркетплейсов в PostgreSQL: заказы по дням, воронку, отчёт площадки по размерам, остатки, начисления и рекламу. Остатки склада, часы отдела нормирования и спецификации приходят файлами. Модули считают подсорт, план, спрос и деньги на лету из последних снимков. Люди работают в React-портале, менеджеры видят ту же правду в Google-таблице, отклонения собираются на странице Сигналы.
Подсорт стал первой вкладкой производства: это ежедневный вопрос, а не помесячная таблица. Строка это один штрихкод одного размера одного кабинета. Таблица нигде не хранится: она считается на лету из последних снимков остатков и заказов и повторяет Excel-расчёт планировщика формула в формулу.
Словами: довезти столько, чтобы хватило на неделю продаж и на полке осталось ещё три штуки, но не больше, чем лежит на складе. У каждой цифры на экране есть подстановка: заказы за 3 дня 6 шт, 2,0 в день × 7 дней = прогноз 14; остаток 6 - 14 = дефицит -8; неснижаемый 3 - (-8) = 11; на складе 53 - резерв 0 = 53; подсорт min(11, 53) = 11.
Файл расчёта это не выгрузка чисел: восемь листов лист в лист с файлом планировщика, формулы строки совпадают символ в символ, справа колонки сверки с порталом и лист с легендой. Тест вычисляет формулы книги как Excel и требует совпадения с порталом. Человек сверяет систему в привычной раскладке и видит ошибку в любую сторону.
Воронка сходится с кабинетом, но не знает размеров. Список заказов знает размеры, но меньше воронки. Решение: отчёт площадки по размерам день за днём и ежедневная сверка с воронкой. В день без отчёта размер берёт свои заказы по списку, а долями делится только разница. Инварианты держит тест на 300 случайных случаях.
Кнопка Сформировать список для склада создаёт документ сразу открытым. Открытый документ резервирует свои штуки от других кабинетов и закрывается сам, когда приходит снимок 1С свежее его даты. Одна штука больше не уходит в два кабинета.
Фоновая задача со статусом и процентом, замок от двойного клика, зависшая сборка через 10 минут читается как ошибка с кнопкой Повторить. Перед сборкой берутся свежие остатки площадки; отказ площадки сборку не роняет, файл собирается по последнему снимку, а причина сказана словами. 5 500 строк собираются за 6,6 секунды.
До применения окно говорит, что будет: применится столько-то размеров, столько-то размеров прошлого снимка станут нулём. Замена целиком осознанная: выгрузка учётной системы перечисляет только то, что есть. Файл чужого склада получает отказ, пустая загрузка не применяется.
Архивные артикулы в подсорте по умолчанию включены. Архив значит больше не шьём, а остатки распродаются, и довозить их надо: почти половина заказов приходится на модели, снятые с производства.
Чего расчёт не видит, написано прямо на экране: поставки на склады площадки, товар в пути к покупателю, второй кабинет той же площадки. Без снимка площадки остаток показан прочерком с пометкой, а в арифметике взят нулём со словами принят за 0: иначе такую строку нельзя было бы довезти. Строка с дефицитом и пустым складом получает пометку нечем крыть. Это кандидат в пошив и мост к плану.
Решение шить или нет принимает человек по деньгам и признакам артикула. Задача плана разложить уже принятое решение по неделям и направлениям цеха, посчитать размер партий, сроки и нехватку ткани и уйти на фабрику файлом.
Партия, сшитая в последнюю неделю трёхнедельного горизонта, приезжает на площадку за край расчёта, и движок видел после прихода нулевой спрос. Поэтому недель расчёта = горизонт + срок прихода + окно покрытия = 3 + 2 + 6 = 11, а партии ставятся только в первые три. Точный перебор заодно ускорился: 1 141 вариант на товар вместо 20 489.
Точному перебору нужен критерий сравнения, поэтому движок считает прибыль как раньше, а наружу ответ уходит копией без денежных ключей на любой глубине. Тексты почему переписаны штуками, часами и датами. В книге второй замок: тест обходит все ячейки и не пускает слов про рубли, прибыль и маржу.
Отмеченное ставится обязательно, даже если по остатку шить ещё рано: тогда в первую неделю горизонта со словами шьём по решению человека. Единственный отказ, когда партия не влезла в мощность горизонта.
Каждый расчёт хранит вход движка целиком, и правка пересчитывается тем же входом без похода в базу. Двое правят один расчёт и получают две ветки: никто не затирает чужое.
Python-движок сверен с движком макета на шести изделиях: та же раскладка, те же партии, то же число узлов перебора. Даже округление JavaScript воспроизведено как floor(x + 0.5).
В книге итог часов считается от точных часов, а не сложением округлённых строк. Иначе файл спорил бы с экраном на десятую долю часа.
Честно о границах. Партии в работе расчёт пока не видит ни приходом, ни занятыми часами, поэтому отметки с них снимаются, а колонка В работе предупреждает о повторной отметке. Календаря заказов ткани со статусами тоже нет: нехватка ткани и дата заказать до считаются и видны ромбиками на ганте, а сам заказ ведут люди.
После встречи с фабрикой планируется только пошив, двумя направлениями по мастерам. Раскрой, ОТК, утюг и комплектовка выключены из загрузки, но их рубли по-прежнему идут в себестоимость. Жёстко расписывать участки вредно: фабрика теряет возможность маневрировать.
Пример на условных числах: 20 человек по 8 часов, 5 смен, резерв 3% дают 20 × 8 × 5 × 0,97 = 776 часов в неделю. Изделие с нормой 2,03 часа в партии 180 штук займёт 365 часов, почти половину недели направления.
Часы на штуку считает одна функция для движка, советника, дашборда, карточки товара, спецификаций и каталога. Второго способа посчитать часы в системе нет, а у каждой цифры подписан источник: из спецификации или перевод по ставке.
Раскладка недели оставляет закреплённые партии на месте, а остальные по убыванию часов отправляет туда, где после неё останется больше свободных часов. Советник прогоняет ту же функцию: не влезает целиком, урезает шагом и пишет почему; не влезает минимальная партия, переносит на следующую неделю.
Иначе новая партия недели переложила бы уже начатую в другое направление и переписала бы историю фактических часов. Закрепить можно и кнопкой, и переводом расчёта в рабочий план.
Лист ищется по шапке, порядок столбцов любой, часы с запятой, модель без цвета ложится на все цвета каталога. Новинка без часов попадает в список без часов, а не в ошибки. Колонка оплаты не читается вовсе.
Пустота честнее нуля. Если у направления не указано число людей, мощности нет: ячейка серая с причиной, а не ноль и не 100%, и направление не входит в сумму Пошив всего, о чём сказано словами. Участок со ставкой на проверке считается, но цифра серая и в решениях не используется.
Сделанный план надо вести: что раскроено и отшито, что опаздывает, сколько часов реально ушло. Бывает и обратное: фабрика уже шьёт то, чего в плане не было, и это тоже заводится списком прямо из Excel.
Отдельно он не хранится: одна правда. Отметок по этапу сколько угодно, кроить можно частями. Штук больше остатка этапа или дата в будущем получают отказ словами.
У каждой партии написано, где она сейчас и насколько отстаёт: пошив по плану до 13.09, сделано 40 из 100. Клик по клетке недели раскрывает, из каких партий сложились её часы.
Раскладка по размерам правится в раскрытой партии, рядом сумма 230 из 240, и кнопка Сохранить выключена, пока сумма не сошлась с партией.
Спрос собирается из фактических заказов всех четырёх кабинетов по ключу товара. Дальше на него действуют три множителя: выкуп, доли размеров и сезон. Каждый считается лесенкой: ручное главнее автоматического, товар главнее категории, а когда данных нет, система так и говорит.
Воронка площадки дописывает выкуп ещё около двух недель, поэтому последние 14 дней в окно не берутся: иначе свежие дни всегда выглядели бы провалом. Ноль выкупов при живых заказах это нет данных, а не 0%: иначе движок объявил бы убыточной любую партию артикула.
Невыкупленное возвращается на склад через три недели и продаётся снова. На учебной партии 240 штук при выкупе 50% эта поправка меняет знак прибыли партии: без неё советник ставил бы убыточные партии всем.
Доли берутся руками, из своих заказов за 180 дней, из своих продаж, из профиля категории или поровну. Партия раскладывается по размерам: сначала закрывается нехватка каждого размера за месяц прихода, остаток по долям, кратно 10, и сумма всегда равна партии. Нехватка 48-го не закрывается излишком 54-го.
Перенос помесячного расчёта потребности проверен на входах и ожиданиях самой книги: 13 786 пар размер-месяц в полном прогоне. Округление один раз и только от половины вверх, как ROUND в Excel. Помесячный экран потом уступил место ежедневному подсорту, а движок живёт в плане, раскладке партий и карточке товара.
История копится сама каждую ночь: отчёт аналитики площадки за год приходит одним файлом за секунды вместо часов запросов, а старые месяцы не удаляются никогда, так что через год появляется второй круг сезона. Коэффициент месяца это дневной темп месяца к среднему дневному темпу полных месяцев: по суммам февраль проигрывал бы марту три дня. Меньше шести полных месяцев значит мало истории, а не выдуманный профиль. Новинка свою историю не берёт никогда: категория даёт форму сезона, но не уровень спроса.
Категории Ozon находят пару на Wildberries правилом по основам слов: сдвоенные буквы схлопываются, окончания снимаются, кандидат берётся только короче исходного имени. Уточнение можно отбросить, но нельзя придумать: майка не становится майкой спортивной. Пару находят 23 категории из 29, остальным донора выбирает человек.
Артикулы наложены на правую ось в процентах от своего среднего: в штуках линия артикула легла бы плоской чертой у нуля рядом с категорией, а сравнивают как раз форму сезона. Год к году идёт по оси двенадцати месяцев: январь с январём.
Двенадцать единиц закрыли бы дорогу к истории категории, поэтому осознанное не учитывать сезон это отдельная кнопка. Коэффициенты по всем категориям разом не пишутся: шумные категории надо смотреть глазами.
Показ перестроен так, как считает учётная система заказчика: одна лестница для обеих площадок на дашборде, в аналитике и в РНП. Прибыль и перечисленные деньги от смены показа не изменились ни на копейку: поменялась база процентов, а не деньги.
Строка свода, категория и сектор круга открывают статьи площадки, из которых сложилась сумма. Новая статья помечена чипом с датой и поднимает сигнал. Администратор относит статью к нужной строке, и перенос действует на всю историю.
Обе цифры берутся из одной колонки витрины, и фронт сравнивает их в целых копейках: в плавающей точке расхождение в копейку просто пропадало бы.
Десять сумм и формула итого к оплате проверяются в целых копейках, расхождение это красный сигнал с названием поля. Перечисленное собирается по дню операции отчёта, поэтому сумма за неделю равна итогу площадки до копейки.
Очередь с тишиной 120 секунд, каждый перенос отодвигает срок, а пересчёт идёт теми же функциями, что ночная сборка: второй формулы прибыли в системе не появляется.
Деление поровну и пропорционально идёт в целых копейках: минус 5 копеек на три части это 1, 2 и 2 копейки, и сумма частей всегда равна целому.
Седьмого сентября сравнение идёт с 1-7 августа, а не со всем августом: иначе в начале месяца все карточки показывали бы минус 75%.
Про один артикул приходилось ходить по пяти вкладкам. Карточка ничего не хранит: она собирает цифры из модулей-источников на каждый запрос. Плашки, себестоимость цепочкой, трудоёмкость, партия и совет, сезон, движение за 12 недель, размеры и партии.
В действующую себестоимость пишет только явная кнопка на одном артикуле. Две цифры живут по своим назначениям, а расхождение между ними подсвечено.
Первая версия себестоимости ложится на всю историю, следующие только вперёд. Ноль и скачок больше чем в 10 раз откладываются на проверку: ноль превратил бы убыточный товар в сверхприбыльный. Новая цена сама пересчитывает витрины, и в шапке виден ход пересчёта.
Архивный артикул исчезает из планирования, но его остатки продолжают считаться в экономике и довозиться подсортом. Кто, когда и откуда перевёл в архив, видно в журнале.
Внутри работает нормальная теория ограничений и честная комбинаторика. На экране их терминов нет: люди в цехе говорят другими словами, и обучать их словарю ради интерфейса незачем.
| В коде и в ТЗ | В интерфейсе |
|---|---|
| ключ товара, item_key | Артикул |
| спецификация, BOM | Из чего шьём |
| норма расхода | Ткани на 1 штуку, м |
| логистическое плечо | Ткань едет, дней |
| точка заказа | Заказать до |
| мощность участка | Часов в неделю |
| решение о запуске | галочка шьём |
| жадная раскладка по срочности | кнопка Собрать план советником |
| ветви и границы | кнопка Точный расчёт |
| дефицит без покрытия | пометка нечем крыть |
Цифра с пунктиром открывает формулу словами и строку подстановки. Цифра, которую нельзя объяснить за десять секунд, в этом интерфейсе считается недоделанной.
Воронка в шапке каждого столбца, для постраничных таблиц отбор идёт на сервере. Тест-сторож роняет сборку, если в системе появилась таблица без фильтров.
Артикулы везде идут в порядке фабрики: платья, сарафаны, юбки, брюки, костюмы, дальше номер модели и цвет. Одна функция на фронте и на сервере. Вкладки стали адресами, первая колонка липкая, на телефоне таблицы превращаются в карточки.
| Было | Стало |
|---|---|
| Подсорт на каждый кабинет из пяти отчётов и подстановок, резерв между кабинетами обнулялся. | Подсорт на лету по каждому штрихкоду, книга с живыми формулами планировщика и сверкой с порталом, резерв между кабинетами документом. |
| Спрос считали по кабинету, выкуп одной константой, сезон в голове. | Заказы четырёх кабинетов по ключу товара, выкуп, размеры и сезон лесенкой источников, история копится сама. |
| Что шить и когда, решалось таблицей. | Человек отмечает, что шьём, советник раскладывает по неделям и направлениям, план уходит на фабрику файлом без денег. |
| Загрузку цеха считали в голове. | Два направления с мощностью в часах, партия целиком в одном, план против факта по неделям на дашборде. |
| Маржа на портале в разы ниже, чем в 1С. | Лестница денег как в 1С: выручка по чекам, статьи расходов с расшифровкой, каждый отчёт площадки сверен до копейки. |
| Себестоимость в двух местах и спор о цифрах. | Две себестоимости по назначению в одной карточке артикула, расхождение подсвечено. |
| Почему такая цифра, никто не мог объяснить. | У каждой цифры как посчитано с подстановкой, у плана блок Почему так и что не вошло. |
Бизнес-аналитик и строитель систем. Начинаю с бизнес-процесса и контракта данных, потом автоматизирую. Здесь ручная сборка недели из пяти Excel-файлов заменена системой, которая считает подсорт, план и деньги и умеет объяснить каждую цифру.