'Super Admin']); Permission::firstOrCreate(['name' => 'projects.access']); $this->user = User::factory()->create([ 'status' => 'active', 'user_type' => 'admin', ]); $this->user->assignRole($role); // Standard Material $this->material = Material::create([ 'name' => 'Review Cement Bag', 'sku' => 'CEM-REV-101', 'unit' => 'bag', 'unit_cost' => 350.00, 'category' => 'cement', 'type' => 'raw', 'status' => 'active', ]); // Standard Labor $this->labor = Labor::create([ 'name' => 'Review Foreman', 'category' => 'skilled', 'hourly_rate' => 200.00, 'status' => 'active', ]); // Standard Equipment $this->equipment = Equipment::create([ 'name' => 'Review Excavator', 'owner_name' => 'Company Owned', 'hourly_rate' => 1200.00, 'status' => 'active', ]); // Create Project $this->project = Project::create([ 'name' => 'Review Test Project', 'code' => 'PRJ-REV-2026', 'status' => ProjectStatus::Planning, 'project_type' => 'standard', 'is_unprofitable' => false, 'contract_value' => 600000.00, 'current_wizard_step' => 6, ]); // Setup materials estimates $this->project->materialsEstimates()->create([ 'material_id' => $this->material->id, 'estimated_qty' => 50, 'unit_cost' => 350.00, // Total = 17,500 ]); // Setup task, labor & equipment allocations $task = $this->project->tasks()->create([ 'name' => 'Main Site Excavation', 'status' => 'pending', 'labor_cost' => 0, 'estimated_hours' => 10, 'actual_hours' => 0, 'completion_percentage' => 0, 'sort_order' => 1, ]); $task->taskLabors()->create([ 'labor_id' => $this->labor->id, 'estimated_hours' => 20, // Total = 4,000 ]); $task->taskEquipments()->create([ 'equipment_id' => $this->equipment->id, 'estimated_hours' => 10, // Total = 12,000 ]); // Grand total estimation cost: 17,500 + 4,000 + 12,000 = 33,500 } public function test_project_show_returns_estimation_totals(): void { $response = $this->actingAs($this->user)->get(route('projects.show', $this->project->ulid)); $response->assertStatus(200); $page = $response->original->getData()['page']; $props = $page['props']; $this->assertEquals('overview', $props['tab']); $this->assertArrayHasKey('estimationTotals', $props); $this->assertEquals(17500.0, $props['estimationTotals']['materials']); $this->assertEquals(4000.0, $props['estimationTotals']['labor']); $this->assertEquals(12000.0, $props['estimationTotals']['equipment']); $this->assertEquals(33500.0, $props['estimationTotals']['sum']); } public function test_approval_chain_breakdown_data_computes_project_totals_correctly(): void { // Create approval chain for the project $chain = ApprovalChain::create([ 'approvable_type' => Project::class, 'approvable_id' => $this->project->id, 'status' => 'in_review', 'initiator_id' => $this->user->id, 'notes' => 'Review project budget estimations.', ]); // Seed permission for approvals index/show Permission::firstOrCreate(['name' => 'approvals.access']); $response = $this->actingAs($this->user)->get(route('approvals.show', $chain->ulid)); $response->assertStatus(200); $page = $response->original->getData()['page']; $props = $page['props']; $this->assertArrayHasKey('breakdownData', $props); $this->assertEquals('PRJ-REV-2026', $props['breakdownData']['document_number']); $this->assertEquals(33500.0, $props['breakdownData']['total_cost']); } }