Files
portal/app/tests/Feature/Admin/AdminDashboardSummaryTest.php
T
Дмитрий 02a8a90e4d feat дашборд: Этап 2 — живые плитки Лиды и Заказ у поставщика
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>
2026-06-27 14:32:31 +03:00

51 lines
1.9 KiB
PHP

<?php
declare(strict_types=1);
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
uses(DatabaseTransactions::class);
beforeEach(function () {
// Чистые счётчики: убираем seed/прошлые данные (FK-порядок: сначала проводки).
DB::table('balance_transactions')->delete();
DB::table('tenants')->delete();
});
it('GET /api/admin/dashboard returns finance + health tiles', function () {
$tenant = DB::table('tenants')->insertGetId([
'subdomain' => 'acme', 'organization_name' => 'Acme', 'contact_email' => 'a@acme.ru',
'status' => 'active', 'is_trial' => false, 'balance_rub' => -500, 'balance_leads' => 0,
'chargeback_unrecovered_rub' => 0, 'created_at' => now(), 'updated_at' => now(),
]);
DB::table('balance_transactions')->insert([
['tenant_id' => $tenant, 'type' => 'topup', 'amount_rub' => 10000, 'created_at' => now()],
['tenant_id' => $tenant, 'type' => 'lead_charge', 'amount_rub' => -3000, 'created_at' => now()],
]);
$res = $this->getJson('/api/admin/dashboard?period=30d');
$res->assertOk();
$res->assertJsonStructure([
'finance' => ['topups_rub', 'charges_rub', 'active_clients', 'new_clients', 'negative_balance_count', 'light'],
'health' => ['light', 'open_incidents', 'last_sync_status'],
'period',
]);
expect($res->json('finance.negative_balance_count'))->toBe(1);
expect($res->json('finance.light'))->toBe('red');
});
it('summary включает плитки leads и supply', function () {
$res = $this->getJson('/api/admin/dashboard?period=30d');
$res->assertOk();
$res->assertJsonStructure([
'finance' => ['light'],
'health' => ['light'],
'leads' => ['light', 'delivered_today', 'stuck', 'unrouted', 'delivery_pct'],
'supply' => ['light', 'demand', 'formula', 'ordered', 'mismatches'],
'period',
]);
});