Files
portal/app/tests/Feature/Admin/AdminLeadsTest.php
T
Дмитрий 6536c19c96 feat(дашборд): Этап A — сквозная вложенность Лиды до источника
Экран «Лиды» (/admin/leads): серверный список с фильтрами (дата/канал/поставщик/
статус/поиск) + пагинация (масштаб 10⁴+ лидов). Карточка лида (/admin/leads/{id}):
полная цепочка — ОТКУДА (поставщик B1/B2/B3 + канал + источник + регион) → КОМУ
(сделки клиентов через deals.source_crm_id = supplier_leads.vid). Дашборд: drill
Лиды +топ-10 последних + «Открыть все лиды →». Nav-пункт «Лиды». ПДн-телефон
маскируется (152-ФЗ). Тесты: backend 3 + FE 5 (38 FE всего зелёные).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 10:14:47 +03:00

90 lines
4.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
uses(DatabaseTransactions::class);
function seedLeadTenant(): int
{
return DB::table('tenants')->insertGetId([
'subdomain' => 'acme'.uniqid(), '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(),
]);
}
function seedSupplierProject(string $signal, string $key): int
{
return DB::table('supplier_projects')->insertGetId([
'platform' => 'B1', 'signal_type' => $signal, 'unique_key' => $key,
'current_limit' => 10, 'sync_status' => 'ok', 'created_at' => now(), 'updated_at' => now(),
]);
}
it('GET /api/admin/leads — пагинированный список с фильтром по каналу', function () {
$sp = seedSupplierProject('site', 'okna.ru');
DB::table('supplier_leads')->insert([
['supplier_project_id' => $sp, 'platform' => 'B1', 'raw_payload' => json_encode(['x' => 1]),
'phone' => '+79135397707', 'vid' => 1001, 'received_at' => now()->subHour(),
'processed_at' => now()->subHour(), 'deals_created_count' => 2],
['supplier_project_id' => null, 'platform' => 'B2', 'raw_payload' => json_encode(['x' => 2]),
'phone' => '+79990001122', 'vid' => 1002, 'received_at' => now()->subDays(2),
'processed_at' => null, 'deals_created_count' => 0],
]);
$res = $this->getJson('/api/admin/leads?per_page=10');
$res->assertOk();
$res->assertJsonStructure([
'data' => [['id', 'received_at', 'platform', 'channel', 'source', 'region_code', 'phone_masked', 'deals_created_count', 'status']],
'total', 'page', 'per_page',
]);
expect($res->json('total'))->toBeGreaterThanOrEqual(2);
// фильтр по каналу site → только лид с supplier_project site
$res2 = $this->getJson('/api/admin/leads?channel=site');
expect(collect($res2->json('data'))->pluck('channel')->unique()->all())->toBe(['site']);
});
it('телефон в списке маскируется', function () {
$sp = seedSupplierProject('call', '+74950000000');
DB::table('supplier_leads')->insert([
'supplier_project_id' => $sp, 'platform' => 'B1', 'raw_payload' => json_encode([]),
'phone' => '+79135397707', 'vid' => 2001, 'received_at' => now(), 'deals_created_count' => 0,
]);
$res = $this->getJson('/api/admin/leads?channel=call');
$masked = collect($res->json('data'))->firstWhere('id', '!=', null)['phone_masked'] ?? '';
expect($masked)->not->toContain('9135397'); // середина скрыта
expect($masked)->toContain('**');
});
it('GET /api/admin/leads/{id} — карточка с цепочкой: источник + сделки клиентов', function () {
$sp = seedSupplierProject('site', 'okna.ru');
$leadId = DB::table('supplier_leads')->insertGetId([
'supplier_project_id' => $sp, 'platform' => 'B1', 'raw_payload' => json_encode(['tag' => 77]),
'phone' => '+79135397707', 'vid' => 5005, 'received_at' => now()->subHour(),
'processed_at' => now()->subHour(), 'deals_created_count' => 1, 'resolved_subject_code' => 77,
]);
$tenant = seedLeadTenant();
$project = DB::table('projects')->insertGetId([
'tenant_id' => $tenant, 'name' => 'Проект', 'is_active' => true,
'created_at' => now(), 'updated_at' => now(),
]);
DB::table('deals')->insert([
'tenant_id' => $tenant, 'project_id' => $project, 'source_crm_id' => 5005, 'phone' => '+79135397707',
'status' => 'new', 'is_test' => false, 'received_at' => now()->subHour(), 'created_at' => now(), 'updated_at' => now(),
]);
$res = $this->getJson("/api/admin/leads/{$leadId}");
$res->assertOk();
$res->assertJsonStructure([
'lead' => ['id', 'platform', 'phone_masked', 'received_at', 'region_code', 'status'],
'source' => ['platform', 'channel', 'identifier'],
'deals' => [['tenant_id', 'tenant_name', 'status']],
]);
expect($res->json('source.identifier'))->toBe('okna.ru');
expect($res->json('deals.0.tenant_name'))->toBe('Acme');
});