1. Почему розничный реплениш — хороший стресс-тест для ERP
ERP-демо красивы. Реальные операции — нет.
Розничное пополнение запасов — один из первых процессов, где ERP-системы перестают быть «формами и документами» и становятся движками принятия решений. Он заставляет платформу обрабатывать:
- данные продаж во времени
- корректировки прогноза (сезонность, промо)
- ограничения поставщиков (lead time, MOQ, кратность упаковки)
- логику нескольких складов
- автоматическую генерацию документов
- процессы согласования людьми
- объяснимость решений системы
Это делает его идеальным кейсом для сравнения ERPNext и MyCompany.
Цель этой статьи — не маркетинг. Цель — ответить на практический вопрос уровня CTO:
Сколько инженерных усилий уходит на реализацию реального розничного процесса?
И да — мы будем считать строки кода.
2. Бизнес-спецификация (практическая, не выдуманная)
Сценарий
- 15 розничных магазинов
- 1 центральный склад
- 4 основных поставщика
- ежедневные продажи через POS
- еженедельные промоакции
- смешанная стратегия: трансфер / закупка
Требования
Каждую ночь (02:00):
Для каждого SKU в каждом магазине:
- Рассчитать средние ежедневные продажи (последние 28 дней)
- Скорректировать на промо-коэффициент (если активен)
- Умножить на lead time поставщика
- Вычесть:
- текущий остаток
- входящие заказы поставщику
- входящие трансферы
- Применить:
- MOQ (минимальное количество заказа)
- округление до кратности упаковки
- При наличии дефицита:
- предпочесть трансфер с центрального склада (если есть)
- иначе предложить заказ поставщику
- Создать черновики документов
- Записать объяснение (почему именно такое количество)
- Дать UI-экран:
- просмотр предложений
- утверждение
- отклонение
- пересчёт
Пример объяснимости:
«Расчётная потребность: 94 ед. (ADS 4.7 × 14 дней − 32 остаток − 40 входящих). Округлено до 96 из-за упаковки по 12».
3. Реализация на ERPNext
ERPNext построен на фреймворке Frappe (Python-бэкенд, JavaScript-фронтенд).
Платформа уже поддерживает:
- остатки
- уровни перезаказа
- материальные заявки (Material Requests)
- заказы поставщикам (Purchase Orders)
- трансферы
Но как только добавляются промо, правила для нескольких магазинов, MOQ и кратность упаковки, логика «покупать или переместить» и объяснимость — встроенный реордер быстро заканчивается.
3.1 Архитектурный подход
Типичная реализация:
- кастомный DocType: Replenishment Rule
- задание по расписанию на Python
- кастомный отчёт на серверной стороне
- клиентские действия в UI
- хуки для автоматизации и связки модулей
3.2 Расширение модели данных
{
"doctype": "Replenishment Rule",
"fields": [
{ "fieldname": "item", "fieldtype": "Link", "options": "Item" },
{ "fieldname": "warehouse", "fieldtype": "Link", "options": "Warehouse" },
{ "fieldname": "lead_time_days", "fieldtype": "Int" },
{ "fieldname": "promotion_factor", "fieldtype": "Float" },
{ "fieldname": "moq", "fieldtype": "Int" },
{ "fieldname": "pack_size", "fieldtype": "Int" }
]
}
Типичный объём: ~120–180 строк кода вместе с метаданными.
3.3 Задание по расписанию (основная логика)
# app/replenishment/scheduler.py
import frappe
def nightly_replenishment():
rules = frappe.get_all("Replenishment Rule", fields="*")
for rule in rules:
avg_sales = calculate_average_sales(rule.item, rule.warehouse)
adjusted_sales = avg_sales * (rule.promotion_factor or 1)
required_qty = adjusted_sales * rule.lead_time_days
current_stock = get_stock(rule.item, rule.warehouse)
incoming = get_incoming(rule.item, rule.warehouse)
deficit = required_qty - current_stock - incoming
if deficit > 0:
rounded_qty = round_to_pack(deficit, rule.pack_size, rule.moq)
create_suggestion(rule, rounded_qty)
Вспомогательные функции (запросы остатков, SQL-джойны, отчёты, округление, создание документов, идемпотентность, логирование) добавляют ещё ~180–300 строк.
3.4 Отчёт о предложениях
SELECT
item,
warehouse,
suggested_qty,
explanation
FROM `tabReplenishment Suggestion`
WHERE status = 'Draft';
Типичный объём: ~70–120 строк.
3.5 Клиентское действие «утвердить»
frappe.ui.form.on("Replenishment Suggestion", {
approve(frm) {
frappe.call({
method: "app.replenishment.approve",
args: { name: frm.doc.name },
callback() {
frm.reload_doc();
}
});
}
});
Типичный объём: ~40–80 строк.
3.6 Итог по объёму кода ERPNext
| Компонент | LOC (примерно) |
|---|---|
| Основная логика на Python | 250–400 |
| Отчёты | 70–120 |
| Клиентский JS | 40–80 |
| JSON-метаданные | 120–200 |
| Всего (LOC в репозитории) | 480–800 |
Только бизнес-логика (LOC): ~320–600
4. Реализация на MyCompany
MyCompany построена на lsFusion — декларативной платформе бизнес-логики.
Вместо процедурных заданий вы описываете:
- свойства данных
- вычисляемые выражения
- действия
- формы
4.1 Определение данных
CLASS Item;
CLASS Warehouse;
CLASS Rule;
item = DATA Item (Rule);
warehouse = DATA Warehouse (Rule);
leadTime = DATA INTEGER (Rule);
promotionFactor = DATA NUMERIC[10,2] (Rule);
moq = DATA INTEGER (Rule);
packSize = DATA INTEGER (Rule);
4.2 Вычисляемые свойства
avgSales(Item i, Warehouse w) =
SUM quantity(Sale s)
WHERE s.item = i
AND s.warehouse = w
AND s.date >= currentDate() - 28
/ 28;
requiredQty(Rule r) =
avgSales(item(r), warehouse(r))
* promotionFactor(r)
* leadTime(r);
deficit(Rule r) =
requiredQty(r)
- currentStock(item(r), warehouse(r))
- incoming(item(r), warehouse(r));
4.3 Логика округления
roundedQty(Rule r) =
MAX(
moq(r),
CEIL(deficit(r) / packSize(r)) * packSize(r)
)
IF deficit(r) > 0;
4.4 Действие
generateSuggestions() {
FOR r IN Rule DO {
IF deficit(r) > 0 THEN
NEW Suggestion {
rule = r;
quantity = roundedQty(r);
explanation =
"ADS × LT – stock – incoming = " + deficit(r);
};
}
}
4.5 UI
FORM suggestions
OBJECTS s = Suggestion
PROPERTIES s.rule, s.quantity, s.explanation;
4.6 Итог по объёму кода MyCompany
| Компонент | LOC (примерно) |
|---|---|
| Определения данных | ~25 |
| Бизнес-логика (свойства) | ~60–90 |
| Округление | ~10 |
| Действия | ~30–40 |
| UI | ~20–30 |
| Итого | ~145–195 |
5. Сравнение бок о бок
| Метрика | ERPNext | MyCompany |
|---|---|---|
| Парадигма логики | Процедурная (Python + связки) | Декларативная (свойства + действия) |
| Планирование заданий | Явное задание по расписанию + связки | Модель, ориентированная на действия (обычно меньше связок) |
| Сборка UI | Часто требует клиентских скриптов | Декларативные формы |
| LOC (только логика) | ~320–600 | ~100–150 |
| LOC (весь репозиторий) | ~480–800 | ~145–200 |
6. Архитектурные последствия
Расширения ERPNext часто размазывают логику между Python, JavaScript и метаданными. MyCompany, как правило, централизует бизнес-правила как декларативную модель.
Больше строк кода обычно означает:
- более высокую когнитивную нагрузку
- больше интеграционных «связок»
- больше поверхности для регрессий
Это автоматически не делает одну платформу «лучше». Это говорит о том, какую цену изменений вы покупаете.
7. Экономика разработки с ИИ
Есть и современное измерение, которое нельзя игнорировать: объём и структура кода влияют на то, насколько эффективно ИИ-инструменты помогают, рефакторят и генерируют расширения.
- Стоимость контекста: больше файлов и слоёв-связок обычно увеличивают объём контекста для корректной помощи ИИ — и поднимают стоимость инференса.
- Стоимость верификации: фрагментированная процедурная логика часто требует больше тестовой обвязки и проверок во времени выполнения.
- Стоимость рефакторинга: больше точек связи затрудняет безопасный автоматический рефакторинг (и для людей, и для ИИ).
- Стоимость фиксации знаний: разрозненная логика увеличивает объём документации, усилия на промптинг и время супервизии.
Иначе говоря, LOC — это уже не только прокси для стоимости поддержки людьми. Это всё больше прокси для стоимости эволюции с ИИ.
8. Когда какая платформа выигрывает
Выбирайте ERPNext, если:
- нужен широкий ERP быстро (учёт + закупки + остатки) и хочется крупную экосистему;
- правила пополнения относительно стандартные и меняются редко;
- предпочитаете Python и привычное сообществу решение, а не парадигму «сначала модель».
Выбирайте MyCompany, если:
- конкурентное преимущество — в кастомных бизнес-правилах и быстрых итерациях;
- правила часто меняются, и хочется, чтобы система оставалась объяснимой;
- хочется ERP как конструктор: модель, которую вы развиваете, а не продукт, который вы патчите.
9. Вывод
Подсчёт строк кода — не всё. Но в сложных бизнес-процессах LOC коррелирует с когнитивной нагрузкой, а она — со стоимостью долгосрочной поддержки.
В этом сценарии пополнения:
- ERPNext обычно требует ~в 2–4 раза больше кастомного кода;
- MyCompany часто выражает те же правила на меньшей и более централизованной поверхности логики.
Приложение A. Симуляция git diff (как честно это измерить)
Если хотите, чтобы сравнение «сколько строк» было защитимым, измеряйте его реальным diff репозитория и LOC-инструментом (например, cloc) с прозрачными правилами включения. Ниже — симулированный, но структурно реалистичный пример.
ERPNext (приложение Frappe) — симулированный diff
$ git diff --stat
app/replenishment/hooks.py | 34 +++++++
app/replenishment/scheduler.py | 140 +++++++++++++++++++++
app/replenishment/api.py | 88 +++++++++++++
app/replenishment/utils/sales.py | 74 ++++++++++++
app/replenishment/utils/stock.py | 92 +++++++++++++++
app/replenishment/utils/rounding.py | 38 ++++++++
app/replenishment/doctype/replenishment_rule/replenishment_rule.json | 165 +++++++++++++++++++++++++
app/replenishment/doctype/replenishment_suggestion/replenishment_suggestion.json | 142 +++++++++++++++++++++++
app/replenishment/report/replenishment_suggestions/replenishment_suggestions.py | 76 +++++++++++++
app/replenishment/report/replenishment_suggestions/replenishment_suggestions.js | 58 ++++++++++
app/replenishment/public/js/replenishment_suggestion_form.js | 62 +++++++++++
12 files changed, 971 insertions(+)
Типичная интерпретация:
- LOC только логики = scheduler + utils + API + Python отчётов + минимум JS (~320–600).
- Общий LOC репозитория включает JSON-метаданные (~480–900+ в зависимости от UI/фикстур отчётов).
MyCompany (модуль lsFusion) — симулированный diff
$ git diff --stat
modules/replenishment/Replenishment.lsf | 182 +++++++++++++++++++++++++++
modules/replenishment/ReplenishmentForms.lsf | 54 ++++++++
modules/replenishment/ReplenishmentDocs.lsf | 31 ++++
3 files changed, 267 insertions(+)
Дисциплина, которая делает сравнения LOC осмысленными: одна и та же спецификация, один и тот же метод измерения, прозрачные правила включения.
Быстрая обратная связь
Короткий сигнал помогает нам выбирать темы для следующих материалов.
Похожие материалы
Если вас волнуют практические последствия архитектуры ERP и снижение зависимости от подрядчиков — больше материалов на DevLab Blog: