refactor(billing-v2): runwayDays = affordable_leads ÷ avg-leads-per-day
This commit is contained in:
@@ -101,7 +101,7 @@ class BillingController extends Controller
|
||||
'current_tier' => $conversion['current_tier'],
|
||||
'next_tier' => $conversion['next_tier'],
|
||||
'delivered_in_month' => (int) ($tenant->delivered_in_month ?? 0),
|
||||
'runway_days' => $this->runwayDays($tenant),
|
||||
'runway_days' => $this->runwayDays($tenant, $conversion['leads']),
|
||||
'tiers_preview' => $tiersPreview,
|
||||
'tariff' => $tenant->tariff === null ? null : [
|
||||
'code' => $tenant->tariff->code,
|
||||
@@ -186,27 +186,35 @@ class BillingController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Прогноз «на сколько дней хватит баланса» — оценочный UX-показатель.
|
||||
* Прогноз «на сколько дней хватит affordable_leads» — оценочный UX-показатель.
|
||||
*
|
||||
* = balance_rub / (рублёвые списания за 30 дней / 30). NULL, если списаний
|
||||
* не было. Float здесь допустим: грубая оценка для шапки, НЕ мутация
|
||||
* баланса (мутации баланса — строго bcmath, см. BillingTopupService).
|
||||
* Отрицательный баланс → 0 (тенант уже в минусе, runway не может быть < 0).
|
||||
* Billing v2 Spec A: считаем по affordable_leads (выход BalanceToLeadsConverter)
|
||||
* делённому на среднюю скорость списания за 30 дней (count(lead_charges)/30).
|
||||
* Раньше формула была balance_rub / per-day-rub-spend — после унификации
|
||||
* единицы измерения «лиды» более показательны и устраняют дрейф между
|
||||
* рублёвой шапкой и тарифной ступенью.
|
||||
*
|
||||
* - affordable_leads ≤ 0 → 0 (тенант не может купить ни одного лида).
|
||||
* - leadsLast30Days = 0 → null (нет истории, не от чего считать).
|
||||
* - иначе → floor(affordable_leads / (leadsLast30Days / 30)).
|
||||
*/
|
||||
private function runwayDays(Tenant $tenant): ?int
|
||||
private function runwayDays(Tenant $tenant, int $affordableLeads): ?int
|
||||
{
|
||||
$spent = abs((float) DB::table('balance_transactions')
|
||||
->where('tenant_id', $tenant->id)
|
||||
->where('type', BalanceTransaction::TYPE_LEAD_CHARGE)
|
||||
->where('created_at', '>=', now()->subDays(30))
|
||||
->sum('amount_rub'));
|
||||
if ($affordableLeads <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($spent <= 0.0) {
|
||||
$leadsLast30Days = (int) DB::table('lead_charges')
|
||||
->where('tenant_id', $tenant->id)
|
||||
->where('charged_at', '>=', now()->subDays(30))
|
||||
->count();
|
||||
|
||||
if ($leadsLast30Days <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$perDay = $spent / 30.0;
|
||||
$avgPerDay = $leadsLast30Days / 30.0;
|
||||
|
||||
return max(0, (int) floor((float) $tenant->balance_rub / $perDay));
|
||||
return max(0, (int) floor($affordableLeads / $avgPerDay));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user