Управленческий
учёт
Как устроен продукт: месячная сетка расходов и доходов, автоподтягивание из внешних систем, unit-сценарии с тремя порогами покрытия, open source и SaaS — и где граница честного MVP.
Зачем этот текст
За ~неделю из пустой папки получился рабочий контур управленческого учёта, который я сам веду, плюс публичная упаковка: open source для self-host и SaaS с регистрацией. Это не «ещё один Excel в браузере» и не лекция «зачем считать деньги» — это разбор как собран продукт и какие решения в нём неочевидны.
Три слоя, которые обычно смешивают в одну таблицу:
- Факт — что реально ушло и (когда есть) пришло, по месяцам.
- Сценарий — что будет при таких-то допущениях (оборот, комиссия, аудитория).
- Автоучёт — как внешние системы пишут в ту же сетку, не затирая ручной ввод.
Потом — как один боевой код разрезали на private, MIT-репозиторий и multi-tenant SaaS.
Что унесёте
- Скелет сетки: крупная → мелкая → месяц → запись (плательщик + сумма + комментарий).
- Конвенцию auto-строк — единственный контракт между кроном и ручным вводом.
- Три уровня F стоимости платформы вместо одного «среднего месяца».
- Пол комиссии в агентской схеме: ниже него сделка убыточна при любом обороте.
- Ссылки: SaaS finance.arckep.ru и open source github.com/gmen1057/finance-business — оба MVP.
Что за продукт
Задача
Один экран, где видно:
- расходы и доходы по месяцам, не «списком проводок»;
- кто из плательщиков тянет какую долю;
- какие статьи — «работа платформы», какие — разработка, какие — маркетинг;
- при каких допущениях unit-экономика направлений сходится.
Как устроен UI
- Шапка: Расходы / Доходы — одинаковая механика, разные
kindв БД. - Обзор: только крупные статьи; клик → детализация мелких.
- Ячейка месяц = сумма записей. Клик → список: плательщик, сумма, комментарий.
- Крупная = сумма мелких (не отдельная «магическая» цифра).
- Справочник плательщиков (CRUD с дашборда «Кто платил»); в записи хранится имя, без жёсткого FK на историю.
- Несколько пользователей с одинаковыми правами (в SaaS — org + роли owner/member).
Стек
Next.js 15 (App Router), React 19, PostgreSQL, сессии на jose, пароли bcrypt, валидация zod. Unit-тесты (vitest) + CI на typecheck / test / production build.
Итог
Каркас — не ERP и не банк. Это управленческий слой: факт в сетке + сценарии рядом. Для малого/среднего контура и для своих продуктов — достаточно; для бухгалтерии 1С — нет и не претендует.
Факт и сценарий — два разных модуля
| Факт | Сценарий | |
|---|---|---|
| Вопрос | что уже записано | при GMV X и комиссии Y% — в плюсе? |
| Данные | expense_entries / income_entries | пакеты допущений + ставки + направления |
| Вымысел | нельзя | допущения — норма |
| URL | / (сетка) | /scenarios |
Сценарии не подставляют цифры в факт-доходы. План и факт живут раздельно: иначе через квартал нельзя вспомнить, что было правдой, а что — моделью.
Сценарии отвечают на «что если». Факт — на «что было». Путать — классическая ошибка одной excel-вкладки «на всё».
Автоучёт: крон не убивает ручное
Задача
Часть расходов приходит из внешних систем каждый день. Нужно обновлять их in place и никогда не трогать строки, которые человек ввёл руками.
Как
Конвенция auto-строк:
- в
commentмаркерauto:<источник>:*; - идемпотентный upsert по
(category_id, year, month, comment); - при нулевой сумме — удаление auto-строки;
- строки без
auto:джобы не трогают; - USD → ₽ по курсу из env (для AI-источников у меня зафиксирован 80 — осознанное упрощение, чтобы ряд был сопоставим между месяцами).
В боевом контуре так подтягиваются хостинг, объектное хранилище, рекламный кабинет, расход LLM из соседних сервисов. В open source — examples/sync/: FirstVDS, Yandex S3, Директ, LLM-таблицы. Это референс-реализации под cron/systemd, не App Store коннекторов.
Итог
Один контракт в комментарии. Ручное и автомат в одной сетке. Без второй «теневой» таблицы.
Три уровня F — не один «средний расход»
Задача
Перевести факт расходов в пороги покрытия, о которых можно говорить с продуктом: окупили runtime, окупили ещё разработку, окупили ещё маркетинг.
Почему не одно число
Один «средний месяц» смешивает разную природу:
- F₁ — работа платформы — без этого сервис лежит (хостинг, хранилище, AI в runtime).
- F₂ — разработка — подписки и инструменты; сайт может жить, продукт — нет.
- F₃ — рост — реклама, рычаг по решению.
- Разовое — не размазывать в ежемесячную норму.
- Чужой контур / exclude — выкинуть из F, не «усреднить».
Как в продукте
У мелкой статьи расходов — колонка cost_bucket (f1 | f2 | f3 | one_off | exclude | unclassified). Правится в карточке. Неклассифицированное в F не входит и подсвечивается.
База для F: последний закрытый месяц или медиана 3 мес (переключатель). Незакрытый месяц в среднее не кладётся — иначе база занижается.
Порядок величин на одном закрытом месяце после классификации (структура важнее копеек):
| Уровень | Порядок | Смысл |
|---|---|---|
| F₁ | ~5 тыс. ₽ | платформа жива |
| F₁+F₂ | ~20 тыс. ₽ | + создание |
| F₁+F₂+F₃ | ~28 тыс. ₽ | + рост |
Вал расходов ≠ F. Без бакетов «медиана всех строк» врёт на растущем ряде и на разовых закупках.
Экономика рубля оборота и пол комиссии
Для агентской схемы (клиент платит полную сумму, платформа берёт комиссию, остальное — продавцу) налоговая база — комиссия, не весь оборот. Без оформленного агентского договора модель ниже не применима. Выплаты физлицам без самозанятости/ИП (НДФЛ, взносы) в текущей версии не учтены.
Допущения на входе модели:
| Параметр | Значение |
|---|---|
| Налог | УСН «доходы» 6% с комиссии |
| Эквайринг | ≈ 3,5% со всей суммы от клиента |
Комиссия r | разная по направлениям |
остаётся с рубля оборота = r × 0,94 − 0,035
r_пол = 0,035 / 0,94 ≈ 3,72%
Ниже 3,72% каждая сделка убыточна независимо от объёма — убыток растёт вместе с оборотом. Это не «точка безубыточности платформы», а граница применимости ставки.
| Комиссия | Остаётся с 1 ₽ | Доля комиссии «до вас» |
|---|---|---|
| 5% | 1,2 коп. | 24% |
| 10% | 5,9 коп. | 59% |
| 15% | 10,6 коп. | 71% |
| 20% | 15,3 коп. | 77% |
Эквайринг забирает 3,5 коп. с рубля при любой ставке. Разница 5% vs 15% — не «в три раза», а ~9× по живым деньгам.
В UI:
r_min = (F / GMV + a) / (1 − t)
GMV_min = F / (r × (1 − t) − a)
Плюс: сколько платящих не хватает, три порога F сразу, сравнение до 3 сохранённых сценариев + черновик, вкладка По рынку (reverse-calc от желаемой прибыли по бенчмаркам в БД).
Чего в формуле пока нет (завышает маржу): стоимость выплат продавцу, возвраты, минималка эквайера за операцию.
Направления: вклад, а не «размазать фикс»
Оборот в сценарий — драйверами:
платящих = аудитория × конверсия
оборот направления = платящих × чек × покупок на человека
Стартовый пресет — образец edtech (курсы / вебинары / записи); справочник правится в UI. У направления может быть прямая статья расходов — в вклад линии, не в общую кашу.
Если сервер уже в F₁, алгоритм не дублирует его в прямых затратах направления (частая ошибка excel-моделей).
вклад = оборот × (r×0,94 − 0,035) − прямые затраты линии
сходится, когда сумма вкладов ≥ выбранный F
Общий фикс между направлениями не распределять — иначе круговая зависимость и ложный вердикт «закроем линию», которая на деле кормила часть фикса.
Три контура: private, OSS, SaaS
| Private | Open source | SaaS | |
|---|---|---|---|
| Зачем | боевой учёт + жёсткие синки | self-host, свой Postgres | зарегистрировался, ведёшь org |
| Доступ | команда | MIT, GitHub | https://finance.arckep.ru |
| Multi-tenant | нет | single-tenant | org + owner / member |
| Секреты и бренды | да | вычищены | общие, без одного бренда |
Open source: github.com/gmen1057/finance-business — MIT, Next.js 15 + PostgreSQL, сетка, сценарии, examples/sync/. npm ci → migrate → seed → dev.
SaaS: finance.arckep.ru — email + пароль + название org, изоляция по org_id, owner правит ставки/направления/инвайты/удаление сценариев. Docker Compose на origin.
Честно про MVP
Нет:
- plan/fact P&L бок о бок;
- импорта отчётов эквайера;
- UI истории изменений (таблица в БД есть);
- «умного» прогноза выручки по тренду;
- магазина коннекторов (есть examples).
SaaS — первая итерация multi-tenant: регистрация, RBAC, rate-limit login/register, CSP, fail-closed на API. Без биллинга самой подписки SaaS, без SSO, без bank-grade audit-log.
Гигиена, которая уже в коде:
- Login: всегда
bcrypt.compare(dummy hash, если пользователя нет) — меньше timing-leak по email. - Демо-данные: маркер
auto:demo:sample, снос одной кнопкой, не трогает ручное и боевые auto-строки. - Ошибки БД (в т.ч. unique
23505) → чистые HTTP без дампа схемы клиенту.
Private остаётся отдельным: в OSS не уезжают id кампаний, ключи, имена людей, привязка к одному бренду.
Хронология сборки (неделя)
| Когда | Что |
|---|---|
| 31.07.2026 | Сетка расходов, login, seed категорий |
| 02.08 | Доходы, дашборд плательщиков, автоучёт |
| 03.08 | Сценарии, F-бакеты, сравнение, «По рынку» |
| 05.08 | OSS MIT + SaaS multi-tenant + docker |
Стек и математика сценариев вызрели на боевом контуре, потом санитизированный срез ушёл в public repo, multi-tenant — только в SaaS-контур (в OSS single-tenant self-host).
Развязка
Продукт — это три вещи рядом, а не одна простыня:
факт в сетке · сценарии с F и полом комиссии · auto-строки, которые не едят ручное.
Дальше — упаковка: self-host для тех, кто хочет код, SaaS для тех, кто открывает браузер. Оба — MVP: рамка, а не «замена бухгалтерии».
Ссылки в тексте. Дальше толстеть будет от сверки с эквайером, plan/fact и того, что реально болит в ежедневном вводе — не от желания нарисовать ещё один дашборд.
// Обсуждение
Можно писать анонимно. Укажите email, чтобы получать уведомления об ответах.