Роялти5 мин чтения

Где в расчёте роялти теряются копейки — и наш код, которым это чинится

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

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

Мы разбираем три места, где сумма роялти уезжает, и выкладываем код, которым мы эти места закрыли у себя.

Копейка, которой нет в компьютере

Число 0,1 в компьютере хранится не как 0,1. Оно хранится как ближайшая двоичная дробь, и она чуть больше. Поэтому почти в любом языке программирования 0,1 + 0,2 даёт не 0,3, а 0,30000000000000004.

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

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

Лечится это тем, что деньги вообще не хранятся дробным числом. 1 480 000 рублей 00 копеек — это целое число 148 000 000 копеек. Ставка 5,5 % — это целое число 550 сотых процента. Умножение целых даёт целое, и единственное деление происходит один раз, в самом конце.

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

Отдельно стоит зафиксировать в договоре, как округляется половина копейки. Вариантов три: вверх (в пользу управляющей компании), вниз (в пользу франчайзи) и к чётному (меньше накапливает перекос на больших объёмах). Пока это не записано, каждая сторона считает по-своему и обе правы.

Прогрессивная шкала, которая значит две разные вещи

В договоре написано: “6 % с выручки до миллиона, свыше миллиона — 4 %”.

Это можно прочитать двумя способами.

Первый. Первый миллион считается по 6 %, всё что сверх — по 4 %. Как в подоходном налоге. Выручка 2 000 000 даёт 60 000 + 40 000 = 100 000 ₽.

Второй. Выручка попала в верхнюю ступень, значит вся она считается по 4 %. Те же 2 000 000 дают 80 000 ₽.

Разница — 20 000 ₽ на одной точке за один месяц. А на выручке ровно в миллион первый способ даёт 60 000 ₽, второй — 40 000 ₽: полтора раза.

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

Проверьте свой договор прямо сейчас. Если там нет слов “к части выручки, превышающей”, формулировку стоит дописать.

Цифра, которую нельзя прочитать однозначно

Выручка приезжает в отчёте не числом. Она приезжает строкой, и вот что в ней бывает:

  • 1 234 567,89 — из 1С, причём пробелы там неразрывные, и обычное “убрать пробелы” их не убирает;
  • 1 234 567.89 — из другой выгрузки, точкой;
  • (1 234,00) — из бухгалтерской выгрузки, где скобки означают минус;
  • 1 234 567,89 ₽, …руб., …RUB — руками.

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

Правило то же, что и с договором: лучше отвергнуть строку и попросить исправить, чем угадать. Отчёт сдаётся раз в месяц, лишний вопрос стоит дешевле неверного начисления.

Что мы выложили

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

  • royalty-calc — расчёт роялти. Деньги целыми копейками, ставки целыми сотыми процента, четыре правила округления на выбор, произвольные формулы на точных дробях. На выходе не только сумма, но и построчное объяснение, как она получилась.
  • revenue-report — разбор отчёта о выручке: суммы во всех видах выше, период словом (“сентябрь 2026”) и диапазоном, CSV-выгрузка из русского Excel. Проверка возвращает сразу все ошибки отчёта, а не первую попавшуюся.

Обе на TypeScript, без внешних зависимостей, с тестами — 243 теста на двоих.

Если GitHub у вас не открывается, тот же код лежит на российских площадках — GitFlic и GitVerse, вплоть до хеша коммита.

Схемы из договоров и виды расчёта

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

Как написано в договореЧто считает библиотека
Процент от выручкипроцент от выручки
Фиксированная суммафиксированная сумма
Ступени по выручкепрогрессивная шкала, режим задаётся явно
Минимальная гарантияпроцент не ниже фиксированного минимума
Роялти с точкификсированная сумма на точку
Роялти с площадиставка за единицу объёма (за м²)
Процент с оборота закупокформула по переменным отчёта
Комбинированнаяфиксированная часть плюс процент

Девятый вариант — “роялти не платится” — в библиотеке тоже есть отдельной схемой. Это не мелочь: в сетях бывают точки на каникулах, точки сотрудников и пилотные площадки, и обрабатывать их отсутствием схемы неудобнее, чем явным “ноль”.

Почему мы это отдали

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

Пример из нашей же разработки. В библиотеке есть возможность задать произвольную формулу — что-то вроде (выручка - возвраты) * 0.05. Формулу пишет пользователь, поэтому разбирает её наш собственный код, а список разрешённых функций закрыт. Тест, написанный до реализации, проверил формулу constructor(1) — и она прошла проверку, потому что список лежал в обычном объекте, а у любого объекта есть унаследованное свойство constructor. Ошибка нашлась за минуту, потому что её искали. В закрытом коде её искал бы только автор.

Если вы найдёте схему расчёта, которой у нас нет, или формат выгрузки, который библиотека не поняла, — напишите об этом в issue репозитория или на почту. Живой случай из чужой практики полезнее звезды.


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

Посмотреть, как это работает в кабинете

Оставьте контакты — покажем на вашей сети: ваша схема роялти, ваши точки, ваши документы. Без презентаций на сорок слайдов.