Skip to main content
CRM полагается на очередь фоновых задач для двух типов работы: периодические задачи (запускаются по расписанию) и пересчёты по требованию (запускаются при изменении аренды, продажи или оплаты). Часть работы выполняется в фоне, часть — сразу, прямо во время вашего действия. Ниже разбираемся, что и когда исполняется.
Важная особенность: часть тяжёлых пересчётов — это не фоновые задачи, а обычный синхронный код. Центральный пересчёт аренды, пересчёт оплаты аренды, пересчёт продажи, посев штрафов, построение расписания платежей и пересчёт занятости инвентаря вызываются синхронно внутри обработки запроса и увеличивают время ответа. «Задачами» в терминах очереди являются только те пункты, что отмечены как асинхронные (см. таблицу ниже).Это осознанный остаток, а не недоделка:
  • центральный пересчёт аренды возвращает те самые суммы, которые уходят в ответ. Если вынести его в фон, клиент увидит устаревшие цены и долг, пока фоновая обработка не догонит. Это уже смена контракта денежного API, а не оптимизация;
  • пересчёт занятости инвентаря управляет бронированием. Устаревшая доступность грозит двойной бронью, поэтому он остаётся немедленным. Известный запас: за одно изменение аренды он выполняется два-три раза, хотя достаточно одного.
А вот побочные эффекты смены статуса аренды — уведомления и лиды воронки — из обработки запроса вынесены: они выполняются фоновой задачей после того, как изменение зафиксировано в базе.

Задачи и компании

Каждая фоновая задача выполняется в данных той компании, из которой она была поставлена — система запоминает компанию в момент постановки и восстанавливает её перед выполнением, без ручной настройки. Отдельно устроены кросс-компанийные задачи: они не привязаны к одной компании, а сами проходят по всем компаниям по очереди. Это начисление автоштрафов, автосписание, массовый пересчёт прибыли, а также обслуживание компаний и биллинг. Для них работа «вне компании» — штатный режим, и они объявлены такими явно.
Задача, которой компания обязательна, при потере контекста компании завершается ошибкой. Раньше такая задача молча выполнялась «вне компании» — и не падала, потому что в общей области базы лежат таблицы с теми же названиями: чтения и записи попадали в них, ошибки не возникало никогда, и утечка оставалась незаметной. Теперь требование объявляется на уровне самой задачи: если оно объявлено, а компания не определилась, задача падает с понятной ошибкой и видна в мониторинге.Есть также общий строгий режим (по умолчанию выключен): при его включении требование компании применяется ко всем задачам компанийных модулей сразу — режим предназначен для проверки на тестовом окружении перед включением в рабочем. Задачи обслуживания компаний и биллинга в общей области базы работают штатно и в строгом режиме, как и оба кросс-компанийных обхода — начисление автоштрафов и автосписание: они помечены как не требующие компании.Пока строгий режим выключен и требование не объявлено, задача без контекста компании по-прежнему выполняется, но оставляет предупреждение в журнале.

Расписание периодических задач

Расписание хранится в базе данных и редактируется в том числе из админки. Планировщик берёт задания из базы. Из статического конфига к CRM относятся две задачи (остальные — интеграции и ассистент):
Помимо статического конфига, во время работы приложения в базе динамически создаются записи расписания: одноразовые/периодические уведомления о ТО, одноразовые алерты «просрочка близко» и записи для начисления автоштрафов (по ним итерирует задача начисления автоштрафов). Они не видны в статическом конфиге, но исполняются тем же планировщиком.

Каталог фоновых процессов CRM

Легенда столбца «Тип»: периодическая — запускается по расписанию; задача — асинхронная задача из очереди; функция — обычный синхронный код (вызывается прямо в запросе, несмотря на расположение среди задач).

Как это работает

Центральный пересчёт аренды

Это самая тяжёлая функция модуля: она заново считает всю финансовую картину аренды одним проходом по инвентарю и услугам и сохраняет аренду. Вызывается синхронно из десятка мест (изменение аренды, статуса, инвентаря, скидок, залога, доставки, бонусов, а также из начисления штрафа). Основные шаги:
1

Ранний выход по удалённой аренде

Если аренда помечена как удалённая — снимаются запланированные списания, и функция завершается.
2

Расписание и режим подсчёта

Если у компании включена функция расписания транзакций, строится список дат аренды, оставшихся дат и оставшейся длительности, и подсчёт ведётся по дням; иначе — по длительности.
3

Расчёт по каждой позиции: цена, штраф, скидка

Для каждой строки инвентаря считаются стоимость позиции, сумма автоматического штрафа и скидки. Формула штрафа зависит от настройки округления периода просрочки (вверх/вниз), шага начисления штрафа, буфера просрочки и шага округления суммы штрафа — см. ниже.
4

Агрегация и запись позиций

Суммы сворачиваются в «Сумму за инвентарь», «Инвентарь со скидкой», сумму штрафов и т.д., затем строки инвентаря и услуг обновляются пакетно, если значения изменились.
5

Итоговые поля аренды и налог

Считаются «Сумма без скидок», «Итоговая сумма к оплате», «Сумма налога», «Сумма штрафов» (= автоматический штраф + ручной штраф), «Размер скидки»; по периоду просрочки статус переводится между «В аренде» и «Просрочена», и аренда сохраняется.
6

Скидки, склад, расписание, прибыль

Пересчитываются применённые скидки и занятость склада; при включённой функции строится расписание платежей; и, если плановый заработок разошёлся с фактической ценой, ставится асинхронная задача пересчёта прибыли по аренде.
Ключевые формулы: Стоимость одной позиции инвентаря (аренда): Число тарифных периодов округляется вверх, затем умножается на цену за период:
Автоматический штраф за просрочку:
Итоговая цена аренды с налогом:
Центральный пересчёт аренды поддерживает автопродление через ограниченный цикл повторов: если срок аренды истёк («сейчас» позже планового окончания) и аренда в статусе «В аренде» или «Просрочена», плановое окончание сдвигается на новый период, а пересчёт запускается заново. Число повторов жёстко ограничено (до 15 проходов); если за это число проходов расчёт не сошёлся (например, при некорректных данных, когда длительность автопродления равна нулю и окончание не продвигается), в журнал пишется предупреждение и процедура завершается с последним результатом — бесконечный цикл исключён.

Начисление автоштрафов по расписанию

Запускается каждую минуту. Работает партиями до 100 записей за раз, отсортированных по времени последнего запуска (сначала те, что ещё ни разу не запускались).
1

Отбор просроченных

Выбираются записи штрафов, где подключена интеграция аренды, начисление включено, наступил срок, и с момента прошлого запуска прошло не меньше заданного интервала.
2

Группировка по компаниям

Записи группируются по компаниям — задача обрабатывает каждую компанию по очереди.
3

Начисление или очистка

Для каждой аренды: если она удалена или не в статусе «В аренде» — удаляются все её записи для начисления штрафов; иначе выполняется начисление штрафа, которое запускает центральный пересчёт аренды и, если аренда просрочена, ставит уведомление о просрочке.
4

Отметка о запуске

У обработанных записей проставляется время последнего запуска.
Посев штрафов — это «посевная» функция: при создании аренды она создаёт/обновляет записи для начисления штрафов (по одной на каждый уникальный срок окончания у инвентаря) и, отдельно, регистрирует одноразовые записи расписания для алерта «скоро просрочка», если у компании настроены триггеры перехода из статуса «В аренде» в состояние близкой просрочки с задержкой больше одной минуты.

Расписание платежей

Работает только при включённой функции расписания транзакций. Три уровня:
  • Построение расписания — строит список дат аренды и вычисляет выходные дни по правилам цикла рабочих и выходных дней и по кастомным пометкам расписания. Первый день по умолчанию считается выходным (не оплачивается), если не включена функция платы за первый день.
  • Раскладка по дням — раскладывает цену инвентаря и скидки по дням пропорционально (цена дня = стоимость позиции ÷ число дней аренды), распределяет оплату, добавляет штрафы к дню их создания и синхронизирует записи расписания (создание/обновление/удаление).
  • Разнос фактических оплат — «жадно» разносит фактические оплаты по дням расписания с переносом переплаты на предыдущие дни, ограничивая каждый день его плановой суммой.
Логика разноса переплаты по дням расписания:

Прибыль: пересчёт по аренде, по продаже и массовый

Пересчёт прибыли по аренде — тонкая обёртка: при необходимости сперва синхронизирует автоматические штрафы под пересчитанные суммы, затем пересчитывает прибыль по аренде с полным набором цен. Пересчёт прибыли по продаже просто пересчитывает прибыль по конкретной продаже. Массовый пересчёт прибыли — ремонтная задача: перебирает все компании (кроме служебных) и пересчитывает прибыль по продажам, задачам мастерской и арендам, где расхождение между плановым заработком и ценой больше единицы (для продаж и аренд сравнивается с итоговой суммой к оплате, для задач мастерской — с ценой). Может фильтроваться по конкретной компании или конкретной аренде. Диаграмма зависимостей пересчёта аренды:
При отладке «почему сумма аренды не пересчиталась» первым делом проверяйте, дошёл ли вызов до центрального пересчёта аренды (он синхронный) и поставилась ли из него задача пересчёта прибыли — постановка происходит только при расхождении планового заработка и фактической цены. Если фоновая обработка остановлена, немедленные пересчёты всё равно отработают, а асинхронная часть (прибыль, автоматические штрафы через очередь, уведомления и лиды по смене статуса) — нет.