02a8a90e4d
Backend: AdminDashboardController +leads/+supply эндпоинты, summary дополнен плитками leads/supply; сверка заказа вынесена в чистый сервис SupplyReconciliation (спрос → формула computeOrder=max(max,⌈Σ/3⌉) → факт → рассинхрон). Лиды: доставлено сегодня / зависшие 4ч+ / нераспределённые / % доставки — cross-tenant под pgsql_admin. Frontend: плитки Лиды и Заказ оживлены (убраны заглушки «Этап 2»), drill с KPI и таблицей групп спрос→формула→факт→совпадает. Тесты: SupplyReconciliation unit 3/3, Leads/Supply/Summary feature, admin-срез 87 зелёных, фронт 10/10. stan 0, pint/eslint/type-check/build чисто. phpstan-baseline перегенерирован (getJson false-positive на новых тестах). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
40 lines
1.8 KiB
PHP
40 lines
1.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
uses(DatabaseTransactions::class);
|
|
|
|
it('GET /api/admin/dashboard/leads возвращает KPI лидов', function () {
|
|
$tenant = DB::table('tenants')->insertGetId([
|
|
'subdomain' => 'leadsacme', 'organization_name' => 'Acme', 'contact_email' => 'a@acme.ru',
|
|
'status' => 'active', 'is_trial' => false, 'balance_rub' => 0, 'balance_leads' => 0,
|
|
'chargeback_unrecovered_rub' => 0, 'created_at' => now(), 'updated_at' => now(),
|
|
]);
|
|
|
|
$supplierProjectId = DB::table('supplier_projects')->insertGetId([
|
|
'platform' => 'B1', 'signal_type' => 'site', 'unique_key' => 'leads-x.ru',
|
|
'current_limit' => 10, 'sync_status' => 'ok', 'created_at' => now(), 'updated_at' => now(),
|
|
]);
|
|
|
|
DB::table('supplier_leads')->insert([
|
|
['supplier_project_id' => $supplierProjectId, 'platform' => 'B1', 'raw_payload' => '{}',
|
|
'phone' => '79990000001', 'received_at' => now(), 'processed_at' => now(), 'deals_created_count' => 1],
|
|
['supplier_project_id' => $supplierProjectId, 'platform' => 'B1', 'raw_payload' => '{}',
|
|
'phone' => '79990000002', 'received_at' => now()->subHours(6), 'processed_at' => null, 'deals_created_count' => null],
|
|
]);
|
|
|
|
$res = $this->getJson('/api/admin/dashboard/leads');
|
|
|
|
$res->assertOk();
|
|
$res->assertJsonStructure([
|
|
'light',
|
|
'kpi' => ['delivered_today', 'stuck', 'unrouted', 'unassigned_deals', 'delivery_pct'],
|
|
]);
|
|
expect($res->json('kpi.stuck'))->toBe(1); // 1 зависший (6ч, не обработан)
|
|
expect($res->json('kpi.unrouted'))->toBe(1); // 1 в очереди
|
|
expect($res->json('light'))->toBe('red'); // есть зависший
|
|
});
|