sharedSupplier = $this->makeSharedSupplierProject(); $this->seedSingleProjectMatrix(); } // ------------------------------------------------------------------------- // Matrix seeding // ------------------------------------------------------------------------- private function seedSingleProjectMatrix(): void { $signals = ['site', 'call']; // regions: empty = all-RF; [82] = Москва; [82, 83] = Москва + СПб $regions = [[], [82], [82, 83]]; $dayMasks = [127, 31]; $limits = [3, 30, 300]; $i = 0; foreach ($signals as $signal) { foreach ($regions as $regionSet) { foreach ($dayMasks as $daysMask) { foreach ($limits as $limit) { $i++; $this->makeSingleProjectCell( index: $i, signal: $signal, regions: $regionSet, daysMask: $daysMask, limit: $limit, ); } } } } } /** * Create one Tenant + User + Project + pivot link for a matrix cell. * * @param array $regions Ordinal subject codes 1..89 (empty = all-RF). */ private function makeSingleProjectCell( int $index, string $signal, array $regions, int $daysMask, int $limit, ): void { $tenant = Tenant::factory()->create(); User::factory()->create(['tenant_id' => $tenant->id]); // Unique signal identifier to avoid UNIQUE constraint violations. $uniqueSuffix = Str::random(6); $signalIdentifier = $signal === 'site' ? "imit-{$index}-{$uniqueSuffix}.test" : '7'.str_pad((string) (9000000000 + $index), 10, '0', STR_PAD_LEFT).$uniqueSuffix; // Pass regions as a PHP int[] — the PostgresIntArray Eloquent cast // converts it to the PostgreSQL literal '{82,83}' or '{}' in set(). $project = $signal === 'site' ? Project::factory() ->asSiteSignal($signalIdentifier) ->create([ 'name' => "IMIT-single-{$index}", 'tenant_id' => $tenant->id, 'regions' => $regions, 'delivery_days_mask' => $daysMask, 'daily_limit_target' => $limit, ]) : Project::factory() ->asCallSignal($signalIdentifier) ->create([ 'name' => "IMIT-single-{$index}", 'tenant_id' => $tenant->id, 'regions' => $regions, 'delivery_days_mask' => $daysMask, 'daily_limit_target' => $limit, ]); DB::table('project_supplier_links')->insert([ 'project_id' => $project->id, 'supplier_project_id' => $this->sharedSupplier->id, 'platform' => $this->sharedSupplier->platform, 'subject_code' => null, ]); } // ------------------------------------------------------------------------- // Shared supplier project factory // ------------------------------------------------------------------------- /** * Create a shared SupplierProject (B2, site signal) used by all matrix cells. * B2 supports both site and call signals (no B1+sms constraint). */ private function makeSharedSupplierProject(): SupplierProject { return SupplierProject::factory()->create([ 'platform' => 'B2', 'signal_type' => 'site', ]); } // ------------------------------------------------------------------------- // Topology helpers — used by Task 13 (TopologyMoneyIntakeTest) // ------------------------------------------------------------------------- /** * G1 topology: one client (Tenant) with one Project linked to TWO different * SupplierProjects (B1 + B2). * * Validates that LeadRouter can route leads from multiple supplier sources * to the same project when the project is linked to multiple suppliers. * * @return array{tenant: Tenant, project: Project, suppliers: list} */ public function seedG1(string $namePrefix = 'IMIT-G1'): array { $tenant = Tenant::factory()->create(); User::factory()->create(['tenant_id' => $tenant->id]); $project = Project::factory() ->asSiteSignal("g1-{$namePrefix}-".Str::random(6).'.test') ->create([ 'name' => "{$namePrefix}-project", 'tenant_id' => $tenant->id, ]); $supplier1 = SupplierProject::factory()->create(['platform' => 'B1', 'signal_type' => 'site']); $supplier2 = SupplierProject::factory()->create(['platform' => 'B2', 'signal_type' => 'site']); foreach ([$supplier1, $supplier2] as $supplier) { DB::table('project_supplier_links')->insert([ 'project_id' => $project->id, 'supplier_project_id' => $supplier->id, 'platform' => $supplier->platform, 'subject_code' => null, ]); } return ['tenant' => $tenant, 'project' => $project, 'suppliers' => [$supplier1, $supplier2]]; } /** * G2 topology: TWO clients (Tenants/Projects) linked to the SAME SupplierProject. * * Validates weighted lottery and fair distribution between competing clients * sharing a single supplier source. * * @param array $overrides1 ProjectFactory overrides for client 1. * @param array $overrides2 ProjectFactory overrides for client 2. * @return array{supplier: SupplierProject, projects: list, tenants: list} */ public function seedG2(array $overrides1 = [], array $overrides2 = []): array { $supplier = SupplierProject::factory()->create(['platform' => 'B2', 'signal_type' => 'site']); $projects = []; $tenants = []; foreach ([$overrides1, $overrides2] as $idx => $overrides) { $tenant = Tenant::factory()->create(); User::factory()->create(['tenant_id' => $tenant->id]); $tenants[] = $tenant; $project = Project::factory() ->asSiteSignal("g2-client-{$idx}-".Str::random(6).'.test') ->create(array_merge([ 'name' => "IMIT-G2-client-{$idx}", 'tenant_id' => $tenant->id, ], $overrides)); $projects[] = $project; DB::table('project_supplier_links')->insert([ 'project_id' => $project->id, 'supplier_project_id' => $supplier->id, 'platform' => $supplier->platform, 'subject_code' => null, ]); } return ['supplier' => $supplier, 'projects' => $projects, 'tenants' => $tenants]; } /** * G4 topology: one client with TWO Projects on the SAME SupplierProject, * each targeting a different region. * * Validates that LeadRouter dispatches leads to the project whose region * matches the lead's resolved subject code. * * @param int $regionA Ordinal subject code for project A (e.g. 82 = Москва). * @param int $regionB Ordinal subject code for project B (e.g. 83 = СПб). * @return array{supplier: SupplierProject, tenant: Tenant, projectA: Project, projectB: Project} */ public function seedG4(int $regionA = 82, int $regionB = 83): array { $tenant = Tenant::factory()->create(); User::factory()->create(['tenant_id' => $tenant->id]); $supplier = SupplierProject::factory()->create(['platform' => 'B2', 'signal_type' => 'site']); $uniqueA = Str::random(6); $uniqueB = Str::random(6); $projectA = Project::factory() ->asSiteSignal("g4-region-{$regionA}-{$uniqueA}.test") ->create([ 'name' => "IMIT-G4-region-{$regionA}", 'tenant_id' => $tenant->id, 'regions' => [$regionA], // PHP int[] — PostgresIntArray cast handles conversion ]); $projectB = Project::factory() ->asSiteSignal("g4-region-{$regionB}-{$uniqueB}.test") ->create([ 'name' => "IMIT-G4-region-{$regionB}", 'tenant_id' => $tenant->id, 'regions' => [$regionB], // PHP int[] — PostgresIntArray cast handles conversion ]); foreach ([$projectA, $projectB] as $project) { DB::table('project_supplier_links')->insert([ 'project_id' => $project->id, 'supplier_project_id' => $supplier->id, 'platform' => $supplier->platform, 'subject_code' => null, ]); } return [ 'supplier' => $supplier, 'tenant' => $tenant, 'projectA' => $projectA, 'projectB' => $projectB, ]; } }