'Super Admin']); Permission::firstOrCreate(['name' => 'projects.access']); $this->user = User::factory()->create([ 'status' => 'active', 'user_type' => 'admin', ]); $this->user->assignRole($role); $this->approver = User::factory()->create([ 'status' => 'active', 'user_type' => 'admin', 'email' => 'approver@example.com', ]); $this->approver->assignRole($role); // Standard Material $this->material = Material::create([ 'name' => 'Wizard Cement Bag', 'sku' => 'CEM-WIZ-101', 'unit' => 'bag', 'unit_cost' => 300.00, 'category' => 'cement', 'type' => 'raw', 'status' => 'active', ]); // Standard Labor $this->labor = Labor::create([ 'name' => 'Lead Foreman', 'category' => 'skilled', 'hourly_rate' => 150.00, 'status' => 'active', ]); // Standard Equipment $this->equipment = Equipment::create([ 'name' => '10-Wheeler Dump Truck', 'owner_name' => 'Company Owned', 'hourly_rate' => 1500.00, 'status' => 'active', ]); // Create Project (starts with current_wizard_step = 1) $this->project = Project::create([ 'name' => 'Wizard Test Project', 'code' => 'PRJ-WIZ-2026', 'status' => ProjectStatus::Planning, 'project_type' => 'standard', 'is_unprofitable' => false, 'contract_value' => 500000.00, 'current_wizard_step' => 1, ]); } public function test_can_access_wizard_allowed_steps(): void { // Assert can load step 1 $response = $this->actingAs($this->user)->get(route('projects.wizard', [$this->project->ulid, 'step' => 1])); $response->assertStatus(200); // Assert attempting to access step 3 when current_wizard_step is 1 defaults back to current step $response = $this->actingAs($this->user)->get(route('projects.wizard', [$this->project->ulid, 'step' => 3])); $response->assertStatus(200); // It should render step 1 (fallback) $this->assertEquals(1, $response->viewData('page')['props']['step']); } public function test_can_save_wizard_tasks_and_advance_step(): void { $response = $this->actingAs($this->user)->post(route('projects.wizard.tasks', $this->project->ulid), [ 'milestones' => [ [ 'name' => 'Substructure Phase', 'weight_percentage' => 40, ] ], 'tasks' => [ [ 'name' => 'Excavation Work', 'description' => 'Excavating the main foundation area', 'start_date' => '2026-06-01', 'end_date' => '2026-06-10', ] ] ]); $response->assertRedirect(route('projects.wizard', [$this->project->ulid, 'step' => 3])); // Assert milestones and tasks exist in database $this->assertDatabaseHas('project_milestones', [ 'project_id' => $this->project->id, 'name' => 'Substructure Phase', 'weight_percentage' => 40, ]); $this->assertDatabaseHas('tasks', [ 'project_id' => $this->project->id, 'name' => 'Excavation Work', 'description' => 'Excavating the main foundation area', ]); // Check project step advanced $this->project->refresh(); $this->assertEquals(3, $this->project->current_wizard_step); } public function test_can_save_wizard_estimates_and_advance_step(): void { // First set step to 3 $this->project->update(['current_wizard_step' => 3]); $response = $this->actingAs($this->user)->post(route('projects.wizard.estimates', $this->project->ulid), [ 'estimates' => [ [ 'material_ulid' => $this->material->ulid, 'estimated_qty' => 100, 'unit_cost' => 300.00, ] ] ]); $response->assertRedirect(route('projects.wizard', [$this->project->ulid, 'step' => 4])); $this->assertDatabaseHas('project_materials_estimates', [ 'project_id' => $this->project->id, 'material_id' => $this->material->id, 'estimated_qty' => 100, 'unit_cost' => 300.00, ]); $this->project->refresh(); $this->assertEquals(4, $this->project->current_wizard_step); } public function test_can_save_wizard_labor_and_advance_step(): void { $this->project->update(['current_wizard_step' => 4]); // Create a task to assign labor to $task = $this->project->tasks()->create([ 'name' => 'Concrete Pouring', 'status' => 'pending', 'labor_cost' => 0, 'estimated_hours' => 20, 'actual_hours' => 0, 'completion_percentage' => 0, 'sort_order' => 1, ]); $response = $this->actingAs($this->user)->post(route('projects.wizard.labor', $this->project->ulid), [ 'labor' => [ [ 'task_ulid' => $task->ulid, 'labor_ulid' => $this->labor->ulid, 'estimated_hours' => 40, ] ] ]); $response->assertRedirect(route('projects.wizard', [$this->project->ulid, 'step' => 5])); $this->assertDatabaseHas('task_labors', [ 'task_id' => $task->id, 'labor_id' => $this->labor->id, 'estimated_hours' => 40, ]); $this->project->refresh(); $this->assertEquals(5, $this->project->current_wizard_step); } public function test_can_save_wizard_equipment_and_advance_step(): void { $this->project->update(['current_wizard_step' => 5]); // Create a task to assign equipment to $task = $this->project->tasks()->create([ 'name' => 'Concrete Pouring', 'status' => 'pending', 'labor_cost' => 0, 'estimated_hours' => 20, 'actual_hours' => 0, 'completion_percentage' => 0, 'sort_order' => 1, ]); $response = $this->actingAs($this->user)->post(route('projects.wizard.equipment', $this->project->ulid), [ 'equipment' => [ [ 'task_ulid' => $task->ulid, 'equipment_ulid' => $this->equipment->ulid, 'estimated_hours' => 12, ] ] ]); $response->assertRedirect(route('projects.wizard', [$this->project->ulid, 'step' => 6])); $this->assertDatabaseHas('task_equipments', [ 'task_id' => $task->id, 'equipment_id' => $this->equipment->id, 'estimated_hours' => 12, ]); $this->project->refresh(); $this->assertEquals(6, $this->project->current_wizard_step); } public function test_can_submit_wizard_for_approval(): void { $this->project->update(['current_wizard_step' => 6]); $response = $this->actingAs($this->user)->post(route('projects.wizard.submit', $this->project->ulid), [ 'approver_ids' => [$this->approver->ulid], 'notes' => 'Setup is completed, please review and approve budget.', ]); $response->assertRedirect(route('projects.show', $this->project->ulid)); $this->project->refresh(); $this->assertEquals(7, $this->project->current_wizard_step); $this->assertDatabaseHas('approval_chains', [ 'approvable_type' => Project::class, 'approvable_id' => $this->project->id, 'status' => 'in_review', ]); } }