updateOrInsert( ['key' => 'routing_match_by_snapshot'], ['value' => 'true', 'type' => 'bool', 'updated_at' => now()], ); // Клиент A — уже сменил источник на LIVE-уровне (NewSender), но снимок A ещё на Caranga. $tenantA = Tenant::factory()->create(['balance_rub' => '500.00', 'frozen_by_balance_at' => null]); $projectA = Project::factory()->for($tenantA)->create([ 'is_active' => true, 'signal_type' => 'sms', 'signal_identifier' => null, 'sms_senders' => ['NewSender'], 'sms_keyword' => null, 'delivery_days_mask' => 127, 'daily_limit_target' => 100, 'delivered_today' => 0, 'regions' => [], ]); // Клиент B — не трогал источник, остаётся на общем Caranga. $tenantB = Tenant::factory()->create(['balance_rub' => '500.00', 'frozen_by_balance_at' => null]); $projectB = Project::factory()->for($tenantB)->create([ 'is_active' => true, 'signal_type' => 'sms', 'signal_identifier' => null, 'sms_senders' => ['Caranga'], 'sms_keyword' => null, 'delivery_days_mask' => 127, 'daily_limit_target' => 100, 'delivered_today' => 0, 'regions' => [], ]); foreach ([$projectA, $projectB] as $p) { DB::table('project_routing_snapshots')->insert([ 'snapshot_date' => '2026-05-28', 'project_id' => $p->id, 'tenant_id' => $p->tenant_id, 'daily_limit' => 10, 'delivery_days_mask' => 127, 'regions' => '{}', 'signal_type' => 'sms', 'signal_identifier' => null, 'sms_senders' => json_encode(['Caranga']), 'sms_keyword' => null, // оба снимка на общем Caranga 'expected_volume' => 10, 'delivered_count' => 0, 'created_at' => now(), ]); } $sharedSp = SupplierProject::factory()->create([ 'platform' => 'B3', 'signal_type' => 'sms', 'unique_key' => 'Caranga', ]); $matched = app(LeadRouter::class)->matchEligibleProjects($sharedSp); // оба клиента (A и B) доезжают по общему источнику — по одному на tenant expect($matched)->toHaveCount(2) ->and($matched->pluck('id')->all())->toContain($projectA->id, $projectB->id); Carbon::setTestNow(); });