Расчёты: стоимость, аналитика и категории
Расчёты — это подсистема, которая сама вычисляет стоимость договора: применяет наценки и скидки, считает налоги и платные опции, фиксирует движения в кассе и собирает данные для аналитики. Оператору не нужно считать цену в уме или в калькуляторе — система делает это по заранее описанным правилам.
Стоимость заказа редко равна простой сумме прайса: к ней добавляются надбавки за срочность, скидки постоянным клиентам, налоги, доплаты за опции, учёт собранных предоплат. Если считать это вручную, появляются ошибки, а цена становится непрозрачной — клиент не понимает, откуда сумма, и сам сотрудник через неделю не вспомнит. Расчёты позволяют описать все эти правила один раз, а дальше система применяет их сама — при сохранении договора, изменении состава или смене статуса.
В основе подсистемы — отдельный расчёт: одна инструкция вида «откуда взять исходное значение → что с ним сделать → куда записать итог». Из набора таких расчётов и складывается финальная цена. Подсистема отвечает именно за калькуляцию сделки и не ведёт долгосрочный баланс клиента или бухгалтерию. Дальше — как устроен один расчёт, что он оставляет после себя (результаты и их группировку по категориям), в каком порядке всё это считается и по каким правилам.
С чем связаны расчёты
Расчёт не работает сам по себе — он опирается на данные связанных сущностей и оставляет следы в нескольких местах.
- С договором — расчёты вычисляют его стоимость, хотя сам договор в расчётах напрямую не участвует: данные берутся из связанных с ним сущностей.
- С товаром или услугой — расчёт может работать на уровне отдельной позиции договора, а не всего договора целиком.
- С фильтрами договора и товаров — их значения служат и источником данных, и триггером запуска расчёта.
- С кассовыми записями — расчёт с кассовым назначением фиксирует приход или расход в кассе.
- Со статусом и городом — они ограничивают, где расчёт выполняется и когда его результат виден.
- С задачей — если расчёт выполнен в контексте задачи, его результат дублируется с привязкой к ней для аналитики в разрезе сотрудников.
Как устроен один расчёт
Каждый расчёт собирается по единой схеме: откуда взять исходное значение → какое действие применить и на сколько → куда записать результат. Ключевая настройка — назначение; расчёт всегда делает что-то одно из трёх:
- Расчёт стоимости — прибавляет, вычитает, умножает, делит или переопределяет стоимость договора либо отдельного товара или услуги. Самый частый случай: наценки, скидки, налоги. Оставляет результат, видимый в детализации стоимости.
- Кассовый расчёт — не трогает стоимость, а фиксирует движение в кассе (приход или расход) с указанием категории кассы и владельца счёта: Клиент, Сотрудник или Общее. Записи результата не оставляет — след остаётся только в кассе.
- Статистический расчёт — фиксирует значение в статистике и никогда не меняет стоимость. Применяется, например, для подсчёта предоплат, собранных курьером, или как промежуточное вычисление для других расчётов.
Дальше задаются исходное значение и операция над ним:
- Источник значения — текущая стоимость, количество или размер товара, суммарное количество или размер по выбранным товарам, накопленное значение категории или значение фильтра договора.
- Действие — плюс, минус, умножить, делить, уравнять (переопределить) или округлить с заданным шагом и порогом дотягивания вверх.
- Значение и его тип — число (берётся как есть), процент (доля от исходного значения) или категория (берётся накопленное значение указанной категории).
Когда расчёт срабатывает, задают условия запуска и область действия: фильтры-триггеры определяют, при каких значениях фильтров расчёт попадает в прогон; города — где он действует (вне списка не выполняется вовсе); статусы — когда его результат виден; дополнительные условия сравнивают стоимость, количество, размер, значение фильтра, статус или накопленную категорию с заданным порогом. Порядок расчёта задаёт его место в общей цепочке. Готовый расчёт можно скопировать и донастроить под похожий случай.
Результаты расчётов
Когда расчёт срабатывает, он оставляет результат — зафиксированное значение того, что и где произошло. Результаты не нужны для пересчёта цены: они хранят историю того, из чего сложилась стоимость, и питают финансовую аналитику. Благодаря им видна структура выручки, строятся отчёты по продажам, доп. услугам и скидкам, и всегда можно объяснить, почему договор стоит именно столько.
По каждому результату хранится: к какому договору или задаче он относится, какой расчёт его породил, категория группировки, числовое значение, дата и время фиксации и признак видимости. Если расчёт повлиял на стоимость, в результате хранится не итоговая цена, а дельта — например, +100 или −350, — так виден вклад каждого шага по отдельности.
Видимостью результата управляет не он сам, а статус договора: если текущий статус входит в набор статусов, заданных в настройке расчёта, результат виден и учитывается в суммарной стоимости своей категории; если не входит — результат скрыт и в сумму категории не попадает, хотя сам расчёт всё равно выполняется и запись создаётся. Расчёт с кассовым назначением результата не создаёт — его след остаётся только в кассе.
Категории расчёта
Каждый расчёт принадлежит одной категории — это справочник, которым администратор группирует результаты. Категория решает две задачи. Первая — превратить детализацию стоимости из плоского списка в структуру: «Услуги», «Скидки», «Надбавки», «Остаточная стоимость». Вторая — позволить строить многошаговые формулы: один расчёт накапливает промежуточный итог в категории, следующий берёт это значение как вход и считает дальше.
Сама категория — чистый справочник из названия и порядка отображения; никаких правил применения, флагов влияния на цену или формул она не содержит — всё задаётся на уровне расчётов, ссылающихся на неё. Категория появляется в видимой детализации только через свои видимые результаты: если все результаты категории на договоре скрыты неподходящим статусом, сама категория в детализации не показывается, хотя её накопленное значение остаётся доступным другим расчётам как промежуточная точка. Поэтому одну и ту же категорию можно использовать и как видимую группу, и как скрытое промежуточное звено цепочки.
Как считается стоимость договора
Расчёты обрабатываются строго в заданной последовательности — это нужно, чтобы один расчёт мог опираться на результаты предыдущих и корректно работать с процентами. Сначала срабатывают расчёты, привязанные к фильтрам товаров и услуг в составе договора, и только затем — расчёты самого договора: если в договоре есть хотя бы одна позиция, итоговая стоимость явно базируется на стоимости позиций.
Пример прогона. В договоре две позиции химчистки с фильтром «Тип чистки» = «Чистка пятен», а у самого договора фильтр «Скидка» = «Повторное обращение». Система сначала прогоняет расчёты товаров: каждая позиция получает +1000 рублей, стоимость договора становится 2000 рублей, появляются два результата в категории «Стоимость химчистки». Затем срабатывает расчёт договора «Скидка для повторного обращения»: −10%, итог — 1800 рублей, а в категории «Скидки» появляется результат на 200 рублей. Оператор и клиент видят не только цену, но и её структуру.
Правила и ограничения
Чтобы расчёты давали воспроизводимый и непротиворечивый результат, действуют строгие правила:
- расчёт выполняется только при совпадении условий;
- порядок выполнения расчётов задаётся явно;
- сначала срабатывают расчёты по фильтрам товаров или услуг в договоре, затем — по фильтрам самого договора;
- один расчёт делает что-то одно: эффект — единичная настройка, а не комбинация;
- кассовый расчёт не изменяет цену договора и не создаёт записи результата — его след остаётся только в кассе;
- изменения исходных данных приводят к пересчёту;
- при пересчёте договора ранее созданные результаты и связанные кассовые записи обновляются, а не дублируются;
- результат всегда относится к одному расчёту и не пересчитывается автоматически; удаление расчёта не удаляет уже созданные результаты;
- ограничение по городу и ограничение по статусу действуют по-разному: вне разрешённого города расчёт не выполняется вовсе, а при неподходящем статусе расчёт выполняется, но его результат не виден — не попадает в кассу, статистику и сумму своей категории;
- категория не имеет собственных ограничений по статусам и городам — всё задаётся на уровне расчётов; переименование категории применяется сразу ко всем договорам, поскольку это справочник без версионирования.
Типичные сценарии
В детализации стоимости одного договора обычно лежит сразу несколько результатов: базовая стоимость товара (+3000), наценка за срочность (+450) и скидка постоянному клиенту (−350). Каждая строка — отдельный результат со своей категорией и дельтой; вместе они показывают оператору и руководителю, из чего сложилась цена.
Курьер собрал предоплату. Статистический расчёт фиксирует её сумму в статистике, не меняя стоимость договора, — позже по этим результатам сводят, кто и сколько собрал.
Компании нужно отразить движение денег, не пересчитывая цену. Кассовый расчёт записывает приход или расход в кассу с нужным владельцем счёта, а стоимость договора остаётся прежней.
Примеры настройки
Скидка 10% за повторное обращение. Назначение — расчёт стоимости; источник значения — текущая стоимость договора; действие — минус; значение — 10, тип значения — процент; триггер — фильтр «Скидка» со значением «Повторное обращение»; категория — «Скидки». При срабатывании стоимость договора уменьшается на 10%.
Фиксированная наценка за услугу. Назначение — расчёт стоимости; источник значения — текущая стоимость; действие — плюс; значение — 1000, тип значения — число; триггер — фильтр товара или услуги «Тип чистки» со значением «Чистка пятен»; категория — «Стоимость химчистки». Расчёт прибавляет фиксированные 1000 рублей за каждую такую позицию.
Расчёты можно выстраивать в цепочки: более поздний расчёт берёт накопленное значение категории как исходное и применяет к нему своё действие — так считаются проценты от промежуточных сумм. Для цепочки важен порядок: расчёт, читающий значение категории, должен стоять после тех, что её наполняют.
Частые вопросы и подводные камни
- Расчёт «сработал», но его не видно в детализации стоимости — почему? Скорее всего, статус договора не входит в набор статусов расчёта. Расчёт выполняется независимо от статуса, но результат становится видимым и учитывается в сумме категории только при подходящем статусе.
- Чем отличается ограничение по городу от ограничения по статусу? В городе вне списка расчёт не выполняется вообще — следа не остаётся. При неподходящем статусе расчёт выполняется, но результат скрыт: не попадает в кассу, статистику и сумму категории.
- В каком порядке выполняются расчёты и почему это важно? Сначала — расчёты по фильтрам товаров или услуг, затем — по фильтрам договора; внутри порядок задаётся явно. Процент считается от суммы, накопленной к моменту срабатывания, поэтому перестановка расчётов меняет итог.
- Что будет с результатами при пересчёте или переименовании категории? При пересчёте прежние результаты и кассовые записи обновляются, а не дублируются. Переименование категории применяется сразу ко всем договорам — это справочник без версионирования.
- Почему категория не появляется в детализации, хотя расчёты на неё ссылаются? Категория показывается только через свои видимые результаты. Если все результаты категории на договоре скрыты неподходящим статусом, категория в детализацию не попадает, хотя её накопленное значение остаётся доступным другим расчётам.
Диагностика: расчёт ведёт себя не так, как ожидалось
Если расчёт не дал ожидаемого эффекта, причина почти всегда в одном из условий срабатывания. Что проверить по порядку:
- совпадает ли фильтр-триггер со значением фильтра в договоре или товаре;
- входит ли город договора в список городов расчёта — иначе расчёт не выполняется вовсе;
- выполнены ли дополнительные условия расчёта;
- подходит ли статус договора — если нет, расчёт выполнился, но результат скрыт.
Если итоговая стоимость отличается от ожидаемой, проверьте порядок расчётов: процент считается от суммы, накопленной к моменту срабатывания. А «исчезнувшая» после пересчёта строка детализации означает, что условия расчёта перестали выполняться, а не потерю данных. Если не сходится цепочка через категорию — убедитесь, что читающий категорию расчёт стоит после наполняющих её.