command->info('Seeding Customers...'); $customers = []; for ($i = 1; $i <= 3; $i++) { $customers[] = User::firstOrCreate( ['email' => "customer{$i}@demo.com"], [ 'name' => "Demo Customer {$i}", 'password' => bcrypt('password'), 'user_type' => 'customer', ] ); } $this->command->info('Seeding Contractors...'); $specializations = ['General Building', 'MEP', 'Civil Works', 'Landscaping', 'Interior Fit-out']; $contractors = []; for ($i = 1; $i <= 5; $i++) { $contractors[] = Contractor::firstOrCreate( ['email' => "contractor{$i}@demo.com"], [ 'company_name' => "Demo Contractor {$i}", 'contact_person' => $faker->name, 'phone' => $faker->phoneNumber, 'specialization' => $specializations[array_rand($specializations)], 'address' => $faker->address, 'tax_id' => $faker->numerify('TAX-####-####'), 'payment_terms' => 'Net 30', 'rating' => 4.5, 'status' => 'active', 'type' => 'sub', 'parent_id' => \Modules\ContractorManagement\Models\Contractor::where('type', 'main')->value('id') ?? null, ] ); } $this->command->info('Seeding Materials Catalog...'); $materialNames = ['Portland Cement 40kg', 'Steel Rebar 12mm', 'Concrete Hollow Blocks 4"', 'Sand (per cu.m)', 'Gravel (per cu.m)', 'Plywood 1/2"', 'PVC Pipe 2"']; $materials = []; foreach ($materialNames as $mName) { $materials[] = Material::firstOrCreate( ['name' => $mName], [ 'sku' => strtoupper(Str::random(8)), 'category' => 'Construction', 'unit' => $faker->randomElement(['pcs', 'bags', 'cu.m', 'length']), 'unit_cost' => $faker->randomFloat(2, 50, 1000), 'status' => 'active', 'type' => 'single', ] ); } $this->command->info('Seeding Main Warehouse...'); $warehouse = Warehouse::firstOrCreate( ['code' => 'WH-CEN-01'], [ 'name' => 'Central Depot', 'address' => $faker->address, 'type' => 'main', 'status' => 'active', ] ); $this->command->info('Seeding Document Categories...'); $docCategories = []; foreach (['Architectural', 'Structural', 'MEP', 'Permits', 'Contracts'] as $catName) { $docCategories[] = DocumentCategory::firstOrCreate( ['name' => $catName], ['description' => "Standard $catName category"] ); } $this->command->info('Seeding Projects & Tasks...'); $statuses = [ ProjectStatus::Planning, ProjectStatus::InProgress, ProjectStatus::Completed, ProjectStatus::OnHold ]; $projectNames = [ 'Skyline Tower Construction', 'Riverside Commercial Complex', 'Green Valley Residential Estate', 'Harbor View Bridge Expansion', 'Downtown Plaza Renovation', 'Metro Hospital Annex', 'Tech Park Office Building', 'Sunrise Shopping Mall', 'Highway 95 Repaving', 'Central Station Modernization' ]; $taskNames = [ 'Site Clearing and Preparation', 'Excavation and Trenching', 'Foundation Concrete Pouring', 'Steel Framework Erection', 'Plumbing Rough-in', 'Electrical Wiring Installation', 'HVAC System Setup', 'Roofing and Waterproofing', 'Interior Drywall Installation', 'Painting and Finishing' ]; for ($i = 1; $i <= 10; $i++) { $contractor = $contractors[array_rand($contractors)]; $customer = $customers[array_rand($customers)]; $startDate = $faker->dateTimeBetween('-1 year', '-1 month'); $endDate = (clone $startDate)->modify('+' . $faker->numberBetween(3, 24) . ' months'); $projectName = $projectNames[$i - 1]; $project = Project::firstOrCreate( ['name' => $projectName], [ 'description' => "Construction project for {$projectName}.", 'customer_id' => $customer->id, 'client_name' => $customer->name, 'contractor_id' => $contractor->id, 'location' => $faker->address, 'status' => $statuses[array_rand($statuses)], 'contract_value' => $faker->randomFloat(2, 50000, 5000000), 'contract_duration' => $faker->numberBetween(3, 24), 'start_date' => $startDate->format('Y-m-d'), 'target_end_date' => $endDate->format('Y-m-d'), 'completion_percentage' => $faker->randomFloat(2, 0, 100), 'total_capitalization' => 0, ] ); // Sync contractor relation safely $contractor->projects()->syncWithoutDetaching([ $project->id => ['role' => 'Primary Contractor', 'contract_amount' => $project->contract_value] ]); $pmRoleExists = \Spatie\Permission\Models\Role::where('name', 'Project Manager')->exists(); if ($pmRoleExists) { $pm = User::role('Project Manager')->first(); if ($pm) { $project->personnel()->syncWithoutDetaching([$pm->id => ['role' => 'pm']]); } } // Seed Tasks for this Project $selectedTasks = $faker->randomElements($taskNames, 5); foreach ($selectedTasks as $t => $taskName) { Task::firstOrCreate( ['project_id' => $project->id, 'name' => $taskName], [ 'description' => "Execution of {$taskName}", 'status' => $faker->randomElement([TaskStatus::Pending, TaskStatus::InProgress, TaskStatus::Completed]), 'estimated_hours' => $faker->numberBetween(10, 100), 'start_date' => $startDate->format('Y-m-d'), 'end_date' => $endDate->format('Y-m-d'), ] ); } // Seed Milestones if ($project->milestones()->count() === 0) { $defaults = [ ['name' => 'Mobilization', 'weight_percentage' => 5], ['name' => 'Earthworks & Foundation', 'weight_percentage' => 15], ['name' => 'Structural Works', 'weight_percentage' => 25], ['name' => 'Roofing & Waterproofing', 'weight_percentage' => 15], ['name' => 'Architectural Finishing', 'weight_percentage' => 20], ['name' => 'MEP Rough-In', 'weight_percentage' => 10], ['name' => 'Final Inspection & Punch List', 'weight_percentage' => 5], ['name' => 'Turnover', 'weight_percentage' => 5], ]; $duration = $project->contract_duration; $count = count($defaults); foreach ($defaults as $idx => $milestone) { $plannedDate = null; if ($startDate && $duration && $duration > 0) { $daysOffset = (int) round(($idx + 1) / $count * $duration * 30); // duration is in months, approx * 30 days $plannedDate = (clone $startDate)->modify("+{$daysOffset} days"); } $project->milestones()->create([ 'name' => $milestone['name'], 'weight_percentage' => $milestone['weight_percentage'], 'sort_order' => $idx, 'is_default' => true, 'planned_date' => $plannedDate, ]); } } // Seed Daily Reports for this Project (if InProgress) if ($project->status === ProjectStatus::InProgress) { for ($r = 1; $r <= 2; $r++) { DailyReport::firstOrCreate( ['project_id' => $project->id, 'report_number' => "DR-{$project->id}-{$r}"], [ 'user_id' => $customers[0]->id, 'report_date' => $faker->dateTimeBetween($startDate, 'now')->format('Y-m-d'), 'weather' => $faker->randomElement(['Sunny', 'Cloudy', 'Rainy']), 'temperature' => $faker->numberBetween(20, 35) . 'C', 'work_accomplished' => "Completed general works for the day.", ] ); } } // Seed Documents for this Project for ($d = 1; $d <= 3; $d++) { $cat = $docCategories[array_rand($docCategories)]; Document::firstOrCreate( ['project_id' => $project->id, 'title' => "Document {$d} for Project {$i}"], [ 'category_id' => $cat->id, 'status' => $faker->randomElement(['Pending', 'Approved']), 'description' => "Sample attached document", 'file_size' => $faker->numberBetween(1024, 10485760), 'version_count' => 1, 'mime_type' => 'application/pdf', ] ); } // Seed Project Inventory $selectedMats = $faker->randomElements($materials, 3); foreach ($selectedMats as $mat) { ProjectInventory::firstOrCreate( ['project_id' => $project->id, 'material_id' => $mat->id], [ 'on_hand_qty' => $faker->numberBetween(10, 500), 'unit_cost' => $mat->unit_cost, ] ); } } $this->command->info('Seeding Bidding Packages...'); $bidStatuses = [\Modules\BiddingManagement\Enums\BidPackageStatus::Open, \Modules\BiddingManagement\Enums\BidPackageStatus::Evaluating, \Modules\BiddingManagement\Enums\BidPackageStatus::Awarded]; $allProjects = Project::all(); if ($allProjects->count() > 0) { for ($b = 1; $b <= 3; $b++) { $proj = $allProjects->random(); $package = \Modules\BiddingManagement\Models\BidPackage::firstOrCreate( ['title' => "Bid Package {$b} - {$proj->name}"], [ 'project_id' => $proj->id, 'created_by' => $customers[0]->id, 'description' => "Tender document for phase {$b}", 'status' => $faker->randomElement($bidStatuses), 'evaluation_mode' => \Modules\BiddingManagement\Enums\EvaluationMode::Simple, 'submission_deadline' => $faker->dateTimeBetween('now', '+1 month'), 'validity_period' => clone $faker->dateTimeBetween('+2 months', '+3 months'), ] ); // Invite contractors $invitedContractors = $faker->randomElements($contractors, 2); foreach ($invitedContractors as $ic) { $invitation = \Modules\BiddingManagement\Models\BidInvitation::firstOrCreate( ['bid_package_id' => $package->id, 'contractor_id' => $ic->id], ['status' => 'invited', 'invited_at' => now()] ); // Add Submission \Modules\BiddingManagement\Models\BidSubmission::firstOrCreate( ['bid_invitation_id' => $invitation->id], [ 'submitted_price' => $faker->randomFloat(2, 10000, 500000), 'completion_days' => $faker->numberBetween(30, 180), 'status' => \Modules\BiddingManagement\Enums\BidSubmissionStatus::Submitted, 'submitted_at' => now(), ] ); } } } $this->command->info('Demo Data seeded successfully across all modules!'); } }