Тарифный движок маркетплейса: как сохранить историю расчётов и провести пересчёт

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

Цена заказа, комиссия площадки, скидка и стоимость доставки могут считаться разными модулями. Результат стоит отделить от самого правила. Правило отвечает на вопрос «как считать». Результат фиксирует, что именно получилось для конкретного объекта в конкретный момент.
Для каждого завершённого расчёта полезен паспорт из пяти частей.

Этот набор не требует единственной технологии. Это граница домена: данные, от которых зависит сумма, должны быть отличимы от данных, которые появились позже.
В PostgreSQL диапазоны показаны как способ хранить период действия значений и проверять пересечения. Для тарифов это полезная техническая идея, а не обязательный стек: у одной категории и одного типа правила не должно одновременно действовать две конфликтующие версии, если бизнес не задал явный приоритет.
Обсудить с командой Qtim карту сущностей и событий можно до начала реализации: это помогает определить входные факты, версию правила и события, которые запускают проверку.
Публикация правила — это отдельный процесс, а не редактирование процента
Представим синтетический сценарий. Для категории меняется комиссия с 12% на 13% с 1 ноября. Редактор создаёт черновик, добавляет период действия и передаёт его на проверку. Новая версия не меняет существующую: она получает собственный идентификатор и проходит путь draft → validated → published.
- У версии заполнены все входные параметры: область применения, ставка, валюта, условия и дата действия.
- Для того же ключа нет неразрешённого пересечения периодов.
- Набор контрольных примеров даёт ожидаемые суммы до публикации.
- Публикация оставляет аудит: кто утвердил версию, когда она стала активной и по какому основанию.
Полезно развести две даты. effective_at отвечает за бизнес-момент, когда применяется правило. recorded_at показывает, когда система узнала об этой версии. Решение о ретроактивности стоит фиксировать в аудите, иначе пересчёт затронет объекты по неочевидному критерию.
Preview проверяет будущую логику, проводка фиксирует деньги
До публикации команда должна иметь возможность увидеть расчёт без создания финансовой записи. Preview получает те же входные факты и версию правила, но возвращает только результат и trace.
Для первой итерации нужен набор golden cases: таблица с ожидаемым исходом для границ домена.

Отраслевой Katalon посвящён автоматизации и качеству и опирается на ответы более 1 500 QA-специалистов. Он не доказывает конкретную архитектуру тарифного движка. Для этой задачи он важен как напоминание: проверка формулы в интерфейсе не покрывает выбор версии, границы действия и последствия повторного запуска.
Trace нужен и для тестов, и для поддержки. В нём достаточно сохранить короткую цепочку: «входная сумма → применимое правило → ставка → округление → итог». В продакшене это сокращает путь от вопроса о сумме до проверяемого ответа.
Массовый пересчёт — это задание с границами, а не кнопка «обновить всё»
Пересчёт нужен, когда бизнес решил изменить прошлые результаты, исправить источник данных или подготовить новый сценарий без немедленного финансового эффекта. Для этих целей требуются разные режимы.
- Выборка объектов и её снимок: какие заказы, статусы и период входят в запуск.
- Причина запуска и целевая версия правила.
- Режим: dry run, расчёт для сравнения или создание отдельной корректировки.
- Идентификатор запуска и идентификатор каждой порции.
- Счётчики: найдено, обработано, пропущено, требует разбора, завершено.
- Результат сверки: совпали ли суммы, версии и ожидаемое число объектов.
Порции ограничивают радиус ошибки. Вместо одного запуска на весь период система обрабатывает предсказуемый набор идентификаторов, записывает статус, а затем переходит к следующему набору. Размер порции зависит от времени расчёта, блокировок, лимитов внешних систем и требований к откату.
Повтор одного и того же запуска не должен незаметно создавать второй финансовый эффект. Поэтому идентичность операции проектируют отдельно от формулы: через ключ запуска, ключ порции и ключ результата. Это проектная рекомендация, а не единственный стандарт реализации.
В нашем кейсе Юркас — интернет-магазине дверей и материалов — в Qtim развивали ядро товарного конфигуратора: доменную логику выносили в отдельные модули, а массовый пересчёт цен — в фоновые очереди. Этот же разрыв между правилами и фоновым исполнением нужен тарифному движку, чтобы пересчёт не смешивался с выбором версии правила.
После выполнения job нужен не только статус done. Нужна сверка: сколько объектов попало в выборку, на скольких расчёт изменился, где появились исключения, какие записи требуют человека.
Версия правила, версия кода и корректировка — разные сущности
- Версия бизнес-правила: ставка, область действия и период.
- Версия алгоритма: код, который интерпретирует правило и строит trace.
- Запуск пересчёта: выборка, причина, параметры и ход выполнения.
- Финансовая корректировка: отдельный эффект, который меняет состояние баланса или выплаты.
Новая версия правила может применяться только к будущим заказам. Исправленная версия алгоритма может потребовать диагностики исторических данных. А корректировка денег может требовать отдельного согласования и обратимой записи.
Можно хранить только актуальную ставку и пересчитывать по ней при необходимости, когда история не влияет на деньги, отчётность и споры. Как только прошлый результат нужно объяснить или сверить, актуальная ставка перестаёт быть достаточным источником.
Discovery начинается с одного спорного расчёта
- Какой момент выбирает версию правила: создание заказа, оплата, отгрузка, закрытие периода или другой бизнес-факт?
- Какие входные значения после расчёта нельзя менять молча?
- Какой ключ определяет конфликт правил и какой приоритет разрешён бизнесом?
- Кто создаёт, проверяет и публикует версию?
- Что видит пользователь в preview и какие ошибки останавливают проводку?
- Какие объекты допускают пересчёт и какой режим меняет деньги?
- Какая сверка доказывает, что job завершился корректно?
Правило для решения: исторический расчёт должен ссылаться на воспроизводимый набор фактов и версию правила; пересчёт должен иметь границы, идентификатор и сверку результата.
Когда в продукте уже есть спорные выплаты, меняющиеся комиссии или ручные исключения, в Qtim разбирают контур e-commerce: выбирают доменные границы, состав первой итерации и порядок безопасных изменений.



