seed(PricingTierSeeder::class); DB::statement("SELECT set_config('app.current_tenant_id', '0', true)"); }); function runSnapshotRouteJob(int $supplierLeadId): void { (new RouteSupplierLeadJob($supplierLeadId))->handle( app(LeadRouter::class), app(SupplierProjectResolver::class), app(NotificationService::class), app(LedgerService::class), app(LeadDistributor::class), app(RegionTagResolver::class), ); } it('uses snapshot daily_limit, not live daily_limit_target (R-04/R-06)', function (): void { Carbon::setTestNow('2026-05-28 12:00:00', 'Europe/Moscow'); $tenant = Tenant::factory()->create(['balance_rub' => '500.00', 'frozen_by_balance_at' => null]); $project = Project::factory()->for($tenant)->create([ 'is_active' => true, 'delivery_days_mask' => 127, 'daily_limit_target' => 100, // live limit big 'delivered_today' => 4, 'delivered_in_month' => 0, ]); $sp = SupplierProject::factory()->create(['signal_type' => 'site']); linkProjectToSupplier($project, $sp); // Snapshot имеет МАЛЕНЬКИЙ daily_limit=5 — после доставки 1 deal'a должно стать 5. createRoutingSnapshotFromProject( $project, date: '2026-05-28', signalType: 'site', signalIdentifier: $sp->unique_key, dailyLimit: 5, ); $lead = SupplierLead::factory()->create([ 'supplier_project_id' => $sp->id, 'raw_payload' => ['project' => $sp->platform.'_'.$sp->unique_key, 'phones' => ['79161234567']], 'phone' => '79161234567', 'vid' => 1001, ]); runSnapshotRouteJob($lead->id); expect(DB::table('deals')->where('tenant_id', $tenant->id)->count())->toBe(1); // delivered_today инкрементнут на live, delivered_count на snapshot. expect((int) DB::table('projects')->where('id', $project->id)->value('delivered_today'))->toBe(5); $snap = DB::table('project_routing_snapshots') ->where('snapshot_date', '2026-05-28') ->where('project_id', $project->id) ->first(); expect((int) $snap->delivered_count)->toBe(1); Carbon::setTestNow(); }); it('rejects lead when is_active becomes false under lock (R-09)', function (): void { Carbon::setTestNow('2026-05-28 12:00:00', 'Europe/Moscow'); $tenant = Tenant::factory()->create(['balance_rub' => '500.00', 'frozen_by_balance_at' => null]); // Live state: is_active=true для snapshot:backfill, но потом клиент нажмёт «пауза» // (между matchEligibleProjects и handle). Имитируем это inline UPDATE'ом. $project = Project::factory()->for($tenant)->create([ 'is_active' => true, 'delivery_days_mask' => 127, 'daily_limit_target' => 10, 'delivered_today' => 0, 'delivered_in_month' => 0, ]); $sp = SupplierProject::factory()->create(['signal_type' => 'site']); linkProjectToSupplier($project, $sp); // Snapshot НЕ paused (fixed до момента pause'а). createRoutingSnapshotFromProject( $project, date: '2026-05-28', signalType: 'site', signalIdentifier: $sp->unique_key, dailyLimit: 10, ); // ↓ Имитация: клиент paused проект в окне между matchEligible и handle. DB::table('projects')->where('id', $project->id)->update(['is_active' => false]); $lead = SupplierLead::factory()->create([ 'supplier_project_id' => $sp->id, 'raw_payload' => ['project' => $sp->platform.'_'.$sp->unique_key, 'phones' => ['79161234567']], 'phone' => '79161234567', 'vid' => 1002, ]); runSnapshotRouteJob($lead->id); // Snapshot говорит «доставлять», но live is_active=false под lock'ом — НЕ доставляем. expect(DB::table('deals')->where('tenant_id', $tenant->id)->count())->toBe(0); expect((int) DB::table('projects')->where('id', $project->id)->value('delivered_today'))->toBe(0); $snap = DB::table('project_routing_snapshots') ->where('snapshot_date', '2026-05-28') ->where('project_id', $project->id) ->first(); expect((int) $snap->delivered_count)->toBe(0); Carbon::setTestNow(); });