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'); });