Как устроен финансовый движок
Движок собирает управленческий P&L двух юрлиц — Thera Interactive DMCC и ИП Качмар С.А. — из первичных документов на Google Drive. Всё, что он делает, подчинено одному правилу: ни одна цифра не появляется из ниоткуда и не исчезает молча. Если данных не хватает, движок останавливается и спрашивает, а не досчитывает за вас.
Зачем и как устроено
Система состоит из трёх слоёв. Разделение не формальное: у каждого своя цена ошибки и своя скорость.
| Слой | Что делает | Цена |
|---|---|---|
| Зеркала | Читает документ, разбирает его, сверяет контрольные суммы, показывает ИИ и замораживает результат в неизменяемый CSV | дорого: ИИ на каждый документ, минуты работы |
| Слой 2 | Из зеркал строит два реестра — инвойсов и транзакций, — применяет статьи, курсы, матчинг, определяет период признания | дёшево: секунды, без ИИ и без сети |
| P&L | Плоская таблица (одна строка = одно событие) и HTML-отчёт | мгновенно |
Порядок работы
- Оператор кладёт документы в папку своего месяца на Drive и называет их по стандарту. Движок в первичку не пишет никогда — у него доступ только на чтение.
- Запуск сборки из консоли: выбрать месяц, нажать «Собрать». Читаются только папки этого месяца.
- Движок задаёт вопросы обо всём, что вызывает сомнение: не тот формат имени, файл в чужой папке, не сошлась контрольная сумма, нет обязательного источника.
- Оператор отвечает прямо в консоли или исправляет причину на Drive.
- Месяц закрывается, когда вопросов не осталось. После закрытия он не пересобирается.
Консоль оператора
Адрес: panel.thera-ltd.com/console.html. Четыре блока:
- Собрать период — выбор месяца и запуск. Галочка «пересобрать заново» снимает защиту заморозки: документы разбираются повторно, и это стоит денег на ИИ.
- Ход работы — живой лог прогона, обновляется каждые пять секунд.
- Месяцы — состояние каждого: открыт, готов, закрыт. У открытых видно, что именно мешает закрыть.
- Вопросы оператору — с текстом, именем файла и папкой. Ответ пишется в очередь на Drive и переживает любые пересборки. Месяцы и типы фильтруются галочками: пустой фильтр показывает всё, чипы с нулём вопросов не выводятся.
Кнопка «сверить имена с Drive». Сверяет имена файлов с первоисточником и снимает вопросы о тех, что уже переименованы. Ничего не пересобирает и не считает — только читает листинг папок, поэтому применима и к закрытым месяцам, где пересборка запрещена. Если выбраны месяцы, сверяет только их. Что она сообщит:
- снято устаревших — файл переименован, претензия снята;
- снято по удалённым — файла на Drive больше нет, и претензия к нему неисполнима. Снимаются вопросы любого типа, не только об именах: так висели три вопроса о выгрузках WPS, удалённых при наведении порядка;
- новые — имена не по стандарту, о которых сборка не спросила. Показываются отдельной строкой, чтобы прирост не выглядел как «нажал кнопку — стало хуже»;
- действительно надо переименовать — имя по-прежнему не по стандарту;
- документ переехал в другую папку — очередь не трогается: период документа определяется разбором, а не листингом, поэтому месяц нужно пересобрать;
- нет на Drive — файл удалён или в корзине.
api.thera-ltd.com/health, подтвердите вход по коду на почту и обновите
страницу.Как признаётся
База — транзакции, а не инвойсы
Решение владельца от 29 июля 2026. Полный комплект инвойсов собрать невозможно: матчинг давал 26 уверенных пар из 128 открытых счетов. Деньги в банке видны все, покрытие статьями стопроцентное — поэтому источником признания стало движение денег. Инвойс уточняет период, но сам по себе расход не создаёт.
Дата начисления
- Есть привязанный инвойс с периодом услуг → берётся период услуг.
- Период услуг не указан → дата инвойса.
- Инвойса нет → дата транзакции.
По сумме оборота около 89% признаётся по начислению, остальное по дате платежа. Основание всегда записано в строке — видно, откуда взялась дата.
Каждое событие ровно один раз
Главный инвариант. Если выручка признана отчётом площадки, приход денег в P&L не идёт. Если ФОТ признан построчно из ведомости, перевод в кошелёк — трансфер, а не расход. Движок проверяет это на каждом прогоне и печатает результат.
Файлы и папки
ГГГГ-ММ Источник.расширение. Например
2026-07 Steam.pdf, 2026-06 Al Ansari.xlsx.Движок проверяет три независимых свидетеля: имя файла, папку и содержимое. Период всегда берётся из содержимого — имя и папка только сверяются с ним. Расхождение не исправляется автоматически, а уходит оператору.
Почему не доверяем имени: 2026-01 MultiPass.pdf однажды содержал
декабрьскую выписку, 2026-02 Google Ads.pdf оказался счётом от Wix,
а имя SIF-выгрузки Al Ansari вообще давало период «1301-09».
Узкий прогон
При сборке месяца открываются только папки этого месяца. Это в разы быстрее и, главное, не даёт правкам задевать закрытые периоды. Обратная сторона принята сознательно: документ, лежащий не в своей папке, в прогон не попадёт — движок скажет «источник отсутствует», и оператор его перенесёт.
Куда движок не смотрит никогда
_TO_DELETE,_КАРАНТИН_удалить, «Архив под разбор», папки со словом «справочные»;- всё за пределами Thera и ИП.
Полнота данных
Отсутствие выписки за месяц — это не «в этом месяце не было расходов», это дыра. Разница принципиальна, и на ней построены все проверки.
Обязательные источники
| Источник | Контрольное тождество |
|---|---|
| Islamic bank | входящий остаток + обороты = исходящий |
| MultiPass | остатки по каждому IBAN отдельно |
| Сбербанк | итого оборотов + исходящий остаток |
| 4Dev | предоплата до и после |
| Deel | выплаты + комиссии = Total Due |
| Al Ansari (SIF) | оклад + переменная = Total по каждому человеку |
| Steam | gross − VAT − возвраты = Net Sales; удержание = 30% × US Share |
| Xsolla | Total payout отчёта |
| Robokassa | остаток + оплаты − комиссии − перечисления = остаток |
| VK Play | доход − возвраты − НДС агентом − вознаграждение = к перечислению |
Границы активности
Источники появляются и исчезают: Steam начал продавать в сентябре 2025, договор ВК с ИП действует с октября 2025, 4Dev заработал в феврале 2026. Спрашивать их отчёты за более ранние месяцы бессмысленно, поэтому в реестре указано, с какого месяца источник существует.
Проверки и закрытие
Каждый документ проходит четыре независимые проверки, и любая может остановить заморозку:
- Контрольное тождество источника — сходится ли документ сам с собой.
- Гейт периода — выписка обязана покрывать ровно свой месяц, с первого по последнее число. Заезд на соседний месяц означает задвоение операций, недобор — потерю.
- ИИ-сверка зеркала с оригиналом. Обязательна на боевом контуре: прогон без неё однажды молча снял сомнения по трём документам, один из которых терял выплату на 400 евро.
- Имя и раскладка — соответствие стандарту.
Закрытие месяца
Месяц можно закрыть, только если по нему нет открытых вопросов, все документы заморожены, все обязательные источники на месте и имена по стандарту. Иначе кнопка недоступна, а попытка закрыть через API отклоняется с перечнем причин.
Закрытие хранится в реестре на Drive и переживает любые пересборки. Закрытый месяц пропускается при сборке — правка парсера или справочника не может переписать историю.
Площадки
Steam
Выручка признаётся отчётом Valve, а не приходом денег: Valve платит примерно через 25 дней после закрытия месяца, и признание по платежу увело бы июньскую выручку в июль. В банк приходит только net payment — без отчёта не видно ни комиссии 30%, ни удержанного налога, ни потребительских налогов.
- Выручка = Net Steam Sales = gross − VAT − возвраты;
- Затраты на дистрибуцию = комиссия Steam (30%);
- Налог = 30% × Total US Share из отчёта того же месяца. Между США и ОАЭ нет соглашения об избежании двойного налогообложения, поэтому ставка полная. Если выплата уже прошла, берётся напечатанная в Payment History сумма — она истина; если нет, считается по формуле и помечается как расчётная.
VK Play
Признание по отчёту площадки. «Доход Разработчика» равен валовым платежам, а возвраты и вознаграждение ВК вычитаются позже — поэтому цепочка у ВК устроена иначе, чем у Steam. Старые платежи на Thera (апрель–октябрь 2025, через ROCK RIM MEDIA) отчётов не имеют и не будут иметь: соглашение тогда было на другое юрлицо. Они признаются по деньгам со сдвигом на месяц назад — ВК платит в следующем месяце за предыдущий.
Robokassa
Выручка берётся только из акта (пункт 9 — оплаты игроков). CSV заявок на вывод его не заменяет и выручки не содержит. НДС не возникает — освобождение по Сколково.
Xsolla
Единственный источник с допуском: расхождение payout до 0,01% не блокирует заморозку, но обязательно порождает вопрос оператору. Причина — накопленное округление: при 1171 транзакции разница в 2,42 евро набегает из сотых долей цента на строку.
Валовые платежи игроков
В строке «Выручка» стоит то, что стало нашим. Потребительские налоги, которые Valve как merchant of record собирает с игрока и сам платит государству, в выручку не входят — по IFRS 15 это суммы, собираемые в пользу третьих лиц. За период это около миллиона долларов.
Чтобы можно было сравнивать себя с рыночными оценками (они считают выручку от цены на витрине, а она VAT-inclusive), в таблице есть справочный блок 00: валовые платежи игроков, потребительские налоги, возвраты. В итоги P&L он не входит. Из доллара, заплаченного игроком в Steam, до компании доходит около 56 центов.
ФОТ
Три канала выплат: кошельки (Deel, 4Dev), зарплатный счёт WPS через Al Ansari, прямые банковские переводы.
Разбивка по людям обязательна: она даёт разрез по отделам. Около 278 тысяч долларов пока идут строкой «без привязки к отделу» — это агрегированные зарплатные реестры, где получатели не детализированы.
Курсы валют
Месячные средние, источник — ЦБ РФ. Подтягиваются автоматически: если за месяц курса нет, движок берёт его сам и дописывает в справочник.
Раньше курсы вносились руками из freecurrencyrates, но тот отдаёт битый сертификат и программно недоступен. Сверка ЦБ с внесёнными вручную значениями за пять месяцев показала расхождение в сотые доли процента — это тот же рынок, разброс лишь от разного набора дней усреднения.
Справочники
Мастер — книга «FE — Engine rules» в папке 03_engine_rules
на Drive. Одиннадцать вкладок: статьи, правила, счета, контрагенты, люди, курсы,
источники, ведомость WPS, решения разбора, два файла переопределений.
Правки оператора живут в книге и переживают пересборки. Локальные копии — рабочие, не источник истины.
Облако и доступ
| Движок | Cloud Run, проект finance-engine-alfa,
регион europe-west1 |
| API | api.thera-ltd.com — Cloudflare Worker,
проксирует в Cloud Run |
| Консоль | panel.thera-ltd.com/console.html |
| Отчёт | panel.thera-ltd.com/reports/pnl-to-date.html
— постоянная ссылка, обновляется после каждого прогона |
| Таблицы | Google Sheets в папке 04_output:
плоская таблица, реестры инвойсов и транзакций |
Доступ — Cloudflare Access, семь адресов. Сервис дополнительно проверяет подпись Cloudflare сам и без неё отвечает отказом. Ключи хранятся в Secret Manager, в образ не попадают.
Прогон живёт в фоновом потоке: запуск возвращает идентификатор сразу, ход виден через консоль. Одновременно идёт только один прогон — два писали бы в один индекс.
Ловушки, на которых уже обжигались
Здесь собрано то, что выглядело правильным и оказалось неверным. Каждый пункт стоил времени или денег.
- Две копии справочников. В папке лежали книга и CSV-файлы; сборка читала CSV, которые отстали на два месяца. Правки оператора не доезжали до слоя зеркал вовсе.
- Прогон без ИИ-сверки молча превращал «сомнительно» в «заморожено»: вердикт затирался в пустоту, а арифметика сходилась и без потерянных строк.
- Вопросы оператору не снимались никогда. Очередь работала как храповик, накапливая претензии к давно исправленному, и её переставали читать.
- Контроль по шапке документа. Ведомость Deel сходилась сама с собой, при этом в зеркало попала одна строка из трёх — 400 евро не попали никуда.
- Обрезка длинного документа при ИИ-сверке: модель честно отказывалась подтверждать полноту, не видя хвоста. Теперь оригинал проверяется по частям.
- Период из регулярки вместо периода услуг. Попытка разложить инвойсы по датам, найденным поиском, дала бы 21 ложную задачу оператору: даты договоров принимались за даты счетов.
- Удержание Steam из истории выплат. Пока сумма бралась из факта платежа, последний месяц любого прогона оставался без налога — Valve платит с задержкой.
- Проверка имени стояла в середине разбора. До неё доходили не все ветки: инвойсы уходят в свою раньше, документы без парсера и с неопределённым периодом — тоже, а фильтр по периоду содержимого отсекал счёт за июнь, лежащий в июльской папке. За июль сборка спросила про 11 файлов из 24 — и месяц закрылся бы с бардаком в именах просто потому, что спросить забыли. Теперь имя проверяется первым делом: оно сверяется с папкой и не зависит ни от парсера, ни от содержимого.
- Месяц вопроса брался из содержимого. Претензия к имени счёта за июнь, лежащего в июльской папке, уезжала в июнь: июль закрывался чистым, а закрытый июнь получал вопрос, который нечем снять. Месяц вопроса об имени идёт по папке.
- Очередь правилась двумя способами разом. Сверка копила снятия в памяти, а заведение вопроса перезаписывало файл очереди целиком — и возвращало снятое обратно. Со стороны выглядело так, что кнопка «ничего не снимает, только добавляет». Правило: очередь читается один раз и пишется один раз.
- Вопрос об имени в закрытом месяце. Претензия к имени снималась только при разборе документа, а разбор закрытого месяца запрещён — и оператор получал вечное задание переименовать файл, который уже переименован. Лечится кнопкой «сверить имена с Drive»: она читает листинг и правит очередь, не трогая цифры.
Открытые вопросы
- Корпоративный налог ОАЭ (9%) не начислен ни за один период.
- По ИП нет ни одной строки налога с дохода. Страховые взносы за сотрудников отражены, самого налога в данных нет.
- Длящиеся услуги признаются одной суммой в первом месяце, а не распределяются.
- ФОТ без привязки к отделу — около 278 тысяч долларов ждут детализации.
- Амортизация и резервы не отражаются.
- Возвраты ВК за март и май (6 584 и 495 рублей) не отражены нигде.