Files
GSB-Construction/Modules/ProjectManagement/database/seeders/FgenPpaServiceRoadSeeder.php
Christopher Boyles 3cb68bc6b0
Some checks failed
Tests / PHP 8.3 (push) Has been cancelled
Tests / PHP 8.4 (push) Has been cancelled
Tests / PHP 8.5 (push) Has been cancelled
New Approach Project Creation Methodologies
2026-06-04 20:52:29 +08:00

678 lines
36 KiB
PHP

<?php
namespace Modules\ProjectManagement\Database\Seeders;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Modules\ContractorManagement\Models\Contractor;
use Modules\ProjectManagement\Models\Project;
use Modules\ProjectManagement\Models\ProjectMilestone;
use Modules\ProjectManagement\Models\Task;
use Modules\ProjectManagement\Models\TaskMaterial;
use Modules\ProjectManagement\Models\TaskEquipment;
use Modules\MasterData\Models\Material;
use Modules\Equipments\Models\Equipment;
use Modules\ProjectManagement\Enums\ProjectStatus;
use Modules\ProjectManagement\Enums\TaskStatus;
class FgenPpaServiceRoadSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// 1. Security Check: Only Super Admin can run it in session/web context.
if (auth()->check() && !auth()->user()->hasRole('Super Admin')) {
throw new \Illuminate\Auth\Access\AuthorizationException('Only Super Admin is authorized to run this seeder.');
}
// 2. Setup Contractor
$contractor = Contractor::firstOrCreate(
['email' => 'contractor-fgen@demo.com'],
[
'company_name' => 'FGEN Construction Corp',
'contact_person' => 'Juan dela Cruz',
'phone' => '+63 917 123 4567',
'specialization' => 'Civil Works',
'address' => 'Batangas, Philippines',
'status' => 'active',
'type' => 'main',
]
);
// 3. Setup Customer User (Client)
$customer = User::where('user_type', 'customer')->first();
if (!$customer) {
$customer = User::create([
'name' => 'First Gen Power Corporation',
'email' => 'client-fgen@demo.com',
'password' => bcrypt('password'),
'user_type' => 'customer',
'status' => 'active',
]);
}
// 4. Setup Employees and Roles
$pm = User::where('email', 'pm@fgen.com')->first();
if (!$pm) {
$pm = User::create([
'name' => 'Pedro Penduko',
'email' => 'pm@fgen.com',
'password' => bcrypt('password'),
'user_type' => 'employee',
'status' => 'active',
'contractor_id' => $contractor->id,
]);
$pm->assignRole('Project Manager');
}
$engineer = User::where('email', 'engineer@fgen.com')->first();
if (!$engineer) {
$engineer = User::create([
'name' => 'Engr. Maria Santos',
'email' => 'engineer@fgen.com',
'password' => bcrypt('password'),
'user_type' => 'employee',
'status' => 'active',
'contractor_id' => $contractor->id,
]);
$engineer->assignRole('Site Technical');
}
$foreman = User::where('email', 'foreman@fgen.com')->first();
if (!$foreman) {
$foreman = User::create([
'name' => 'Foreman Tomas',
'email' => 'foreman@fgen.com',
'password' => bcrypt('password'),
'user_type' => 'employee',
'status' => 'active',
'contractor_id' => $contractor->id,
]);
$foreman->assignRole('Construction Supervisor');
}
// 5. Seed Materials
$materialsData = [
'Selected Fill Materials' => ['category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 18.00, 'description' => 'Selected fill materials for compaction and grading.'],
'Water for Compaction' => ['category' => 'Aggregates', 'unit' => 'Kiloliter', 'unit_cost' => 50.00, 'description' => 'Water used for moisture conditioning and compaction.'],
'Reinforced Concrete Pipes (RCP)' => ['category' => 'Concrete & Cement', 'unit' => 'Piece', 'unit_cost' => 150.00, 'description' => 'Reinforced Concrete Pipes (RCP) for drainage lines.'],
'Portland Cement Type I' => ['category' => 'Concrete & Cement', 'unit' => 'Bag (40kg)', 'unit_cost' => 8.50, 'description' => 'Standard portland cement.'],
'River Sand' => ['category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 20.00, 'description' => 'Washed river sand.'],
'Crushed Stone (Gravel) 3/4"' => ['category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 25.00, 'description' => '3/4 inch crushed gravel.'],
'Rebar #4 (1/2" x 20 ft)' => ['category' => 'Steel & Metal', 'unit' => 'Piece', 'unit_cost' => 6.20, 'description' => 'Standard #4 reinforcing bar.'],
'Plywood (CDX) 15/32" (4x8 ft)' => ['category' => 'Timber & Wood', 'unit' => 'Sheet', 'unit_cost' => 22.00, 'description' => 'Plywood for concrete formworks.'],
'Backfill Materials' => ['category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 15.00, 'description' => 'Backfill materials for drainage structures.'],
'Soil Stabilizer' => ['category' => 'Concrete & Cement', 'unit' => 'Bag (40kg)', 'unit_cost' => 12.00, 'description' => 'Chemical stabilizer for subgrade improvement.'],
'Base Gravel (Class 5)' => ['category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 18.00, 'description' => 'Class 5 aggregate base course.'],
'Ready Mix Concrete (30 MPa)' => ['category' => 'Concrete & Cement', 'unit' => 'Cubic Meter', 'unit_cost' => 130.00, 'description' => '30 MPa Ready-mix concrete.'],
'Tie Bars' => ['category' => 'Steel & Metal', 'unit' => 'Piece', 'unit_cost' => 4.50, 'description' => 'Steel tie bars for pavement joints.'],
'Dowel Bars' => ['category' => 'Steel & Metal', 'unit' => 'Piece', 'unit_cost' => 5.50, 'description' => 'Steel dowel bars for load transfer.'],
'Joint Sealant' => ['category' => 'Consumables', 'unit' => 'Tube', 'unit_cost' => 7.00, 'description' => 'Elastomeric joint sealant.'],
'Curing Compound' => ['category' => 'Consumables', 'unit' => 'Pail (20L)', 'unit_cost' => 45.00, 'description' => 'Concrete curing compound.'],
'Riprap Stones' => ['category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 35.00, 'description' => 'Stones for slope protection riprap.'],
'Geotextile Fabric' => ['category' => 'Consumables', 'unit' => 'Roll', 'unit_cost' => 150.00, 'description' => 'Filter fabric for drainage and slope protection.'],
'Traffic Signs' => ['category' => 'Safety & Finishing', 'unit' => 'Piece', 'unit_cost' => 85.00, 'description' => 'Reflective road signs.'],
'Guardrails' => ['category' => 'Safety & Finishing', 'unit' => 'Piece', 'unit_cost' => 110.00, 'description' => 'Metal guardrail beams.'],
'Thermoplastic Paint' => ['category' => 'Safety & Finishing', 'unit' => 'Bag (25kg)', 'unit_cost' => 40.00, 'description' => 'Thermoplastic road marking paint.'],
'Glass Beads' => ['category' => 'Safety & Finishing', 'unit' => 'Bag (25kg)', 'unit_cost' => 25.00, 'description' => 'Reflective glass beads for thermoplastic paint.'],
'Reflectors' => ['category' => 'Safety & Finishing', 'unit' => 'Piece', 'unit_cost' => 12.00, 'description' => 'Cat-eye road reflectors.'],
'Delineators' => ['category' => 'Safety & Finishing', 'unit' => 'Piece', 'unit_cost' => 15.00, 'description' => 'Flexible guide posts.'],
'Topsoil' => ['category' => 'Aggregates', 'unit' => 'Ton', 'unit_cost' => 22.00, 'description' => 'Topsoil for landscape restoration.'],
'Landscaping Materials' => ['category' => 'Aggregates', 'unit' => 'Pack', 'unit_cost' => 50.00, 'description' => 'Grass seeds, turf, and other plantings.'],
];
$materials = [];
foreach ($materialsData as $name => $meta) {
$materials[$name] = Material::where('name', $name)
->where(fn($q) => $q->where('contractor_id', $contractor->id)->orWhereNull('contractor_id'))
->first();
if (!$materials[$name]) {
$baseSku = strtoupper(substr($meta['category'], 0, 3)) . '-' . rand(1000, 9999);
$materials[$name] = Material::create([
'name' => $name,
'contractor_id' => $contractor->id,
'sku' => $baseSku,
'category' => $meta['category'],
'unit' => $meta['unit'],
'unit_cost' => $meta['unit_cost'],
'description' => $meta['description'],
'type' => 'single',
'status' => 'active',
]);
}
}
// 6. Seed Equipment
$equipmentsData = [
'Excavator' => ['hourly_rate' => 1500.00],
'Bulldozer' => ['hourly_rate' => 1600.00],
'Motor Grader' => ['hourly_rate' => 1400.00],
'Vibratory Roller' => ['hourly_rate' => 1200.00],
'Water Truck' => ['hourly_rate' => 1000.00],
'Dump Truck' => ['hourly_rate' => 1200.00],
'Concrete Mixer' => ['hourly_rate' => 350.00],
'Concrete Vibrator' => ['hourly_rate' => 150.00],
'Plate Compactor' => ['hourly_rate' => 200.00],
'Wheel Loader' => ['hourly_rate' => 1300.00],
'Transit Mixer' => ['hourly_rate' => 1100.00],
'Concrete Cutter' => ['hourly_rate' => 250.00],
'Power Trowel' => ['hourly_rate' => 200.00],
'Crane' => ['hourly_rate' => 2500.00],
'Road Marking Machine' => ['hourly_rate' => 300.00],
'Air Compressor' => ['hourly_rate' => 250.00],
'Pickup Truck' => ['hourly_rate' => 400.00],
];
$equipments = [];
foreach ($equipmentsData as $name => $meta) {
$equipments[$name] = Equipment::firstOrCreate(
['name' => $name],
[
'owner_name' => 'Company Owned',
'hourly_rate' => $meta['hourly_rate'],
'status' => 'active',
]
);
}
// 7. Seed Project
$project = Project::firstOrCreate(
['name' => 'Construction of FGEN-PPA Service Road'],
[
'description' => 'Construction of the FGEN-PPA Service Road including site clearing, drainage, subgrade prep, base course, concrete pavement, shoulder protection, safety markings, and cleanup.',
'customer_id' => $customer->id ?? null,
'client_name' => $customer ? $customer->name : 'First Gen Power Corporation',
'contractor_id' => $contractor->id,
'location' => 'Batangas, Philippines',
'status' => ProjectStatus::Planning,
'contract_value' => 15500000.00,
'total_estimated_value' => 15500000.00,
'contract_duration' => 8,
'start_date' => '2026-07-01',
'target_end_date' => '2027-02-28',
'completion_percentage' => 0.00,
'total_capitalization' => 0.00,
]
);
// Sync contractor relation safely
$contractor->projects()->syncWithoutDetaching([
$project->id => ['role' => 'Primary Contractor', 'contract_amount' => $project->contract_value]
]);
// Assign PM to Project
$project->personnel()->syncWithoutDetaching([
$pm->id => ['role' => 'pm'],
$engineer->id => ['role' => 'engineer'],
$foreman->id => ['role' => 'laborer'],
]);
// Transition Project Status to In Progress
if ($project->status !== ProjectStatus::InProgress) {
if ($project->status === ProjectStatus::UnderBidding) {
$project->transitionTo(ProjectStatus::Planning);
}
if ($project->status === ProjectStatus::Planning) {
$project->transitionTo(ProjectStatus::InProgress);
}
}
// 8. Seed Milestones and Tasks
$milestonesConfig = [
[
'name' => 'Milestone 1: Site Clearing and Earthworks',
'description' => 'Site clearing, grubbing, unsuitable soil removal, and embankment compaction.',
'weight' => 10.0,
'tasks' => [
[
'name' => 'Clearing and grubbing using excavators, bulldozers, chainsaws, and dump trucks',
'description' => 'Remove vegetation, roots, and topsoil to clear the road path.',
'est_hours' => 80,
'labor_cost' => 50000.00,
'equipment_cost' => 120000.00,
'materials' => [],
'equipments' => ['Excavator' => 80, 'Bulldozer' => 40, 'Dump Truck' => 80],
],
[
'name' => 'Removal and hauling of unsuitable materials using excavators and dump trucks',
'description' => 'Excavate unsuitable materials and transport them to the designated disposal site.',
'est_hours' => 60,
'labor_cost' => 40000.00,
'equipment_cost' => 90000.00,
'materials' => [],
'equipments' => ['Excavator' => 60, 'Dump Truck' => 120],
],
[
'name' => 'Excavation and embankment works using excavators, bulldozers, graders, and dump trucks',
'description' => 'Perform roadbed cutting and filling to meet design elevation.',
'est_hours' => 120,
'labor_cost' => 80000.00,
'equipment_cost' => 200000.00,
'materials' => [],
'equipments' => ['Excavator' => 120, 'Bulldozer' => 60, 'Motor Grader' => 60, 'Dump Truck' => 120],
],
[
'name' => 'Filling and spreading of selected fill materials using graders and bulldozers',
'description' => 'Spread selected borrow materials on the roadbed in layers.',
'est_hours' => 80,
'labor_cost' => 50000.00,
'equipment_cost' => 130000.00,
'materials' => ['Selected Fill Materials' => 500.0],
'equipments' => ['Motor Grader' => 80, 'Bulldozer' => 40],
],
[
'name' => 'Compaction of embankment and subgrade using vibratory rollers and water trucks',
'description' => 'Apply water and compact the fill layers to the required density.',
'est_hours' => 60,
'labor_cost' => 30000.00,
'equipment_cost' => 100000.00,
'materials' => ['Water for Compaction' => 200.0],
'equipments' => ['Vibratory Roller' => 60, 'Water Truck' => 60],
],
]
],
[
'name' => 'Milestone 2: Drainage Construction',
'description' => 'Drainage excavation, RCP installation, catch basins, and backfilling.',
'weight' => 15.0,
'tasks' => [
[
'name' => 'Excavation for drainage lines and structures',
'description' => 'Excavate trenches for stormwater drainage pipes and structures.',
'est_hours' => 50,
'labor_cost' => 30000.00,
'equipment_cost' => 75000.00,
'materials' => [],
'equipments' => ['Excavator' => 50],
],
[
'name' => 'Installation of reinforced concrete pipes (RCP)',
'description' => 'Lay and align reinforced concrete pipes in the excavated trenches.',
'est_hours' => 80,
'labor_cost' => 60000.00,
'equipment_cost' => 80000.00,
'materials' => ['Reinforced Concrete Pipes (RCP)' => 150.0],
'equipments' => ['Excavator' => 40, 'Dump Truck' => 40],
],
[
'name' => 'Placement of bedding materials',
'description' => 'Place sand or gravel bedding at the bottom of the trenches before laying pipes.',
'est_hours' => 40,
'labor_cost' => 25000.00,
'equipment_cost' => 15000.00,
'materials' => ['Crushed Stone (Gravel) 3/4"' => 80.0, 'River Sand' => 50.0],
'equipments' => [],
],
[
'name' => 'Construction of catch basins and manholes',
'description' => 'Form and pour concrete for catch basins and junction manholes.',
'est_hours' => 100,
'labor_cost' => 90000.00,
'equipment_cost' => 30000.00,
'materials' => ['Portland Cement Type I' => 120.0, 'River Sand' => 15.0, 'Crushed Stone (Gravel) 3/4"' => 25.0, 'Rebar #4 (1/2" x 20 ft)' => 100.0, 'Plywood (CDX) 15/32" (4x8 ft)' => 20.0],
'equipments' => ['Concrete Mixer' => 80, 'Concrete Vibrator' => 80],
],
[
'name' => 'Concrete pouring for drainage structures',
'description' => 'Pour concrete for headwalls, wingwalls, and drainage aprons.',
'est_hours' => 60,
'labor_cost' => 50000.00,
'equipment_cost' => 20000.00,
'materials' => ['Portland Cement Type I' => 80.0, 'River Sand' => 10.0, 'Crushed Stone (Gravel) 3/4"' => 18.0, 'Rebar #4 (1/2" x 20 ft)' => 50.0, 'Plywood (CDX) 15/32" (4x8 ft)' => 15.0],
'equipments' => ['Concrete Mixer' => 40, 'Concrete Vibrator' => 40],
],
[
'name' => 'Backfilling and compaction around drainage facilities',
'description' => 'Backfill around catch basins and pipes, then compact using plate compactors.',
'est_hours' => 40,
'labor_cost' => 20000.00,
'equipment_cost' => 25000.00,
'materials' => ['Backfill Materials' => 120.0],
'equipments' => ['Dump Truck' => 40, 'Plate Compactor' => 40],
],
]
],
[
'name' => 'Milestone 3: Subgrade Preparation',
'description' => 'Final roadbed grading, moisture conditioning, and stabilization.',
'weight' => 10.0,
'tasks' => [
[
'name' => 'Final grading and shaping of roadbed',
'description' => 'Grade the road subgrade to the design crown and cross-slope.',
'est_hours' => 50,
'labor_cost' => 30000.00,
'equipment_cost' => 70000.00,
'materials' => [],
'equipments' => ['Motor Grader' => 50],
],
[
'name' => 'Moisture conditioning',
'description' => 'Sprinkle water or aerate subgrade soil to achieve optimum moisture content.',
'est_hours' => 30,
'labor_cost' => 15000.00,
'equipment_cost' => 30000.00,
'materials' => ['Water for Compaction' => 100.0],
'equipments' => ['Water Truck' => 30],
],
[
'name' => 'Compaction of subgrade layer',
'description' => 'Roll the subgrade to achieve at least 95% maximum dry density.',
'est_hours' => 40,
'labor_cost' => 20000.00,
'equipment_cost' => 50000.00,
'materials' => [],
'equipments' => ['Vibratory Roller' => 40, 'Plate Compactor' => 20],
],
[
'name' => 'Subgrade stabilization (if required)',
'description' => 'Mix in soil stabilizers to improve bearing capacity in soft areas.',
'est_hours' => 60,
'labor_cost' => 45000.00,
'equipment_cost' => 84000.00,
'materials' => ['Soil Stabilizer' => 150.0, 'Base Gravel (Class 5)' => 100.0],
'equipments' => ['Motor Grader' => 40, 'Vibratory Roller' => 30],
],
]
],
[
'name' => 'Milestone 4: Aggregate Base Course Installation',
'description' => 'Delivery, spreading, leveling, and compacting aggregate base course.',
'weight' => 15.0,
'tasks' => [
[
'name' => 'Delivery and stockpiling of aggregate base materials',
'description' => 'Haul and dump aggregate base material at designated intervals on the road.',
'est_hours' => 80,
'labor_cost' => 40000.00,
'equipment_cost' => 96000.00,
'materials' => ['Base Gravel (Class 5)' => 1200.0],
'equipments' => ['Dump Truck' => 80],
],
[
'name' => 'Spreading and leveling of aggregate base course',
'description' => 'Spread base gravel to a uniform thickness using graders.',
'est_hours' => 60,
'labor_cost' => 35000.00,
'equipment_cost' => 110000.00,
'materials' => [],
'equipments' => ['Motor Grader' => 60, 'Wheel Loader' => 30],
],
[
'name' => 'Water application and compaction',
'description' => 'Condition aggregate base course with water and compact thoroughly.',
'est_hours' => 50,
'labor_cost' => 25000.00,
'equipment_cost' => 85000.00,
'materials' => ['Water for Compaction' => 300.0],
'equipments' => ['Vibratory Roller' => 50, 'Water Truck' => 50],
],
[
'name' => 'Layer thickness adjustments',
'description' => 'Correct grade deviations and ensure thickness meets target specifications.',
'est_hours' => 30,
'labor_cost' => 18000.00,
'equipment_cost' => 42000.00,
'materials' => [],
'equipments' => ['Motor Grader' => 30],
],
]
],
[
'name' => 'Milestone 5: Concrete Pavement Construction',
'description' => 'Formworks, rebar/dowel setup, concrete pouring, finishing, curing, and jointing.',
'weight' => 30.0,
'tasks' => [
[
'name' => 'Installation of formworks',
'description' => 'Set up and align steel or timber forms to define the concrete road edge.',
'est_hours' => 80,
'labor_cost' => 60000.00,
'equipment_cost' => 15000.00,
'materials' => ['Plywood (CDX) 15/32" (4x8 ft)' => 100.0],
'equipments' => ['Pickup Truck' => 40],
],
[
'name' => 'Placement of reinforcing steel (if specified)',
'description' => 'Assemble and lay down tie bars and dowel bars at joint assemblies.',
'est_hours' => 80,
'labor_cost' => 70000.00,
'equipment_cost' => 20000.00,
'materials' => ['Rebar #4 (1/2" x 20 ft)' => 200.0, 'Tie Bars' => 250.0, 'Dowel Bars' => 250.0],
'equipments' => ['Pickup Truck' => 20],
],
[
'name' => 'Concrete batching and delivery',
'description' => 'Batch concrete and deliver to site using transit mixers.',
'est_hours' => 120,
'labor_cost' => 80000.00,
'equipment_cost' => 160000.00,
'materials' => ['Ready Mix Concrete (30 MPa)' => 600.0],
'equipments' => ['Transit Mixer' => 120, 'Concrete Mixer' => 40],
],
[
'name' => 'Concrete pouring and finishing',
'description' => 'Pour concrete into forms, consolidate with vibrators, and strike off.',
'est_hours' => 100,
'labor_cost' => 110000.00,
'equipment_cost' => 40000.00,
'materials' => [],
'equipments' => ['Concrete Vibrator' => 100, 'Power Trowel' => 50],
],
[
'name' => 'Concrete curing',
'description' => 'Apply curing compound or pond water to ensure proper hydration.',
'est_hours' => 40,
'labor_cost' => 20000.00,
'equipment_cost' => 40000.00,
'materials' => ['Curing Compound' => 30.0, 'Water for Compaction' => 150.0],
'equipments' => ['Water Truck' => 40],
],
[
'name' => 'Joint cutting and sealing',
'description' => 'Saw contraction joints in hardened concrete and apply joint sealant.',
'est_hours' => 50,
'labor_cost' => 35000.00,
'equipment_cost' => 20000.00,
'materials' => ['Joint Sealant' => 80.0],
'equipments' => ['Concrete Cutter' => 50],
],
]
],
[
'name' => 'Milestone 6: Shoulder and Slope Protection Works',
'description' => 'Shoulder fill placement, riprap construction, and compaction.',
'weight' => 10.0,
'tasks' => [
[
'name' => 'Placement of shoulder materials',
'description' => 'Lay aggregate shoulder material along the edges of the concrete pavement.',
'est_hours' => 60,
'labor_cost' => 35000.00,
'equipment_cost' => 75000.00,
'materials' => ['Selected Fill Materials' => 200.0, 'Base Gravel (Class 5)' => 150.0],
'equipments' => ['Excavator' => 30, 'Dump Truck' => 30, 'Wheel Loader' => 30],
],
[
'name' => 'Construction of riprap or slope protection',
'description' => 'Place riprap stones and geotextile fabric on slopes to prevent erosion.',
'est_hours' => 100,
'labor_cost' => 85000.00,
'equipment_cost' => 90000.00,
'materials' => ['Riprap Stones' => 400.0, 'Geotextile Fabric' => 10.0],
'equipments' => ['Excavator' => 60, 'Dump Truck' => 40],
],
[
'name' => 'Compaction of shoulder areas',
'description' => 'Compact the shoulder aggregate to align with road surface.',
'est_hours' => 40,
'labor_cost' => 20000.00,
'equipment_cost' => 48000.00,
'materials' => [],
'equipments' => ['Vibratory Roller' => 40],
],
]
],
[
'name' => 'Milestone 7: Road Safety and Finishing Works',
'description' => 'Road signs, guardrails, pavement markings, and reflectors.',
'weight' => 5.0,
'tasks' => [
[
'name' => 'Installation of road signs',
'description' => 'Erect regulatory, warning, and guide signs along the road alignment.',
'est_hours' => 30,
'labor_cost' => 20000.00,
'equipment_cost' => 15000.00,
'materials' => ['Traffic Signs' => 15.0],
'equipments' => ['Pickup Truck' => 30],
],
[
'name' => 'Installation of guardrails',
'description' => 'Install steel guardrails in high embankment or curve zones.',
'est_hours' => 50,
'labor_cost' => 35000.00,
'equipment_cost' => 25000.00,
'materials' => ['Guardrails' => 50.0],
'equipments' => ['Pickup Truck' => 40],
],
[
'name' => 'Application of pavement markings',
'description' => 'Apply thermoplastic markings for centerlines and edge lines.',
'est_hours' => 40,
'labor_cost' => 30000.00,
'equipment_cost' => 20000.00,
'materials' => ['Thermoplastic Paint' => 25.0, 'Glass Beads' => 10.0],
'equipments' => ['Road Marking Machine' => 40, 'Air Compressor' => 40],
],
[
'name' => 'Installation of reflectors and delineators',
'description' => 'Fix cat-eye reflectors and flexible delineator posts on the road surface.',
'est_hours' => 30,
'labor_cost' => 15000.00,
'equipment_cost' => 5000.00,
'materials' => ['Reflectors' => 120.0, 'Delineators' => 80.0],
'equipments' => [],
],
]
],
[
'name' => 'Milestone 8: Final Restoration and Site Cleanup',
'description' => 'Removal of excess materials, site grading, restoration, and debris disposal.',
'weight' => 5.0,
'tasks' => [
[
'name' => 'Removal of excess materials',
'description' => 'Load and transport leftover gravel, wood, and other items from the site.',
'est_hours' => 40,
'labor_cost' => 20000.00,
'equipment_cost' => 60000.00,
'materials' => [],
'equipments' => ['Excavator' => 20, 'Dump Truck' => 40, 'Wheel Loader' => 20],
],
[
'name' => 'Site grading and restoration',
'description' => 'Perform cosmetic grading and layout topsoil/vegetation along the roadside.',
'est_hours' => 50,
'labor_cost' => 30000.00,
'equipment_cost' => 70000.00,
'materials' => ['Topsoil' => 100.0, 'Landscaping Materials' => 20.0],
'equipments' => ['Motor Grader' => 30, 'Wheel Loader' => 20],
],
[
'name' => 'Disposal of construction debris',
'description' => 'Haul construction debris and waste materials to authorized landfills.',
'est_hours' => 30,
'labor_cost' => 15000.00,
'equipment_cost' => 36000.00,
'materials' => [],
'equipments' => ['Dump Truck' => 30],
],
]
],
];
$startDate = Carbon::parse($project->start_date);
foreach ($milestonesConfig as $idx => $mConfig) {
$plannedDate = $startDate->copy()->addMonths($idx)->endOfMonth();
$milestone = ProjectMilestone::firstOrCreate(
[
'project_id' => $project->id,
'name' => $mConfig['name']
],
[
'description' => $mConfig['description'],
'planned_date' => $plannedDate->format('Y-m-d'),
'sort_order' => $idx + 1,
'weight_percentage' => $mConfig['weight'],
'is_default' => false,
]
);
foreach ($mConfig['tasks'] as $tIdx => $tData) {
$task = Task::firstOrCreate(
[
'project_id' => $project->id,
'milestone_id' => $milestone->id,
'name' => $tData['name']
],
[
'description' => $tData['description'],
'status' => TaskStatus::Pending,
'sort_order' => $tIdx + 1,
'labor_cost' => $tData['labor_cost'],
'equipment_cost' => $tData['equipment_cost'],
'estimated_hours' => $tData['est_hours'],
'actual_hours' => 0.00,
'start_date' => $startDate->copy()->addMonths($idx)->startOfMonth()->format('Y-m-d'),
'end_date' => $plannedDate->format('Y-m-d'),
'completion_percentage' => 0.00,
]
);
// Assign users to the task
$task->users()->syncWithoutDetaching([$engineer->id, $foreman->id]);
// Seed task materials
foreach ($tData['materials'] as $mName => $qty) {
$material = $materials[$mName];
TaskMaterial::firstOrCreate(
[
'task_id' => $task->id,
'material_id' => $material->id
],
[
'planned_qty' => $qty,
'actual_qty' => 0.00,
'unit_cost' => $material->unit_cost,
]
);
}
// Seed task equipment
foreach ($tData['equipments'] as $eqName => $estHours) {
$eq = $equipments[$eqName];
TaskEquipment::firstOrCreate(
[
'task_id' => $task->id,
'equipment_id' => $eq->id
],
[
'estimated_hours' => $estHours,
'actual_hours' => 0.00,
]
);
}
}
}
// 9. Recalculate Project Capitalization
$project->recalculateCapitalization();
}
}