'Super Admin', 'guard_name' => 'web']); Permission::firstOrCreate(['name' => 'projects.access', 'guard_name' => 'web']); Permission::firstOrCreate(['name' => 'materials-catalog.access', 'guard_name' => 'web']); Permission::firstOrCreate(['name' => 'inventory.access', 'guard_name' => 'web']); $this->admin = User::factory()->create([ 'status' => 'active', 'user_type' => 'admin', ]); $this->admin->assignRole($role); $this->cement = Material::create([ 'name' => 'Portland Cement Type 1', 'sku' => 'CEM-POR-001', 'unit' => 'bag', 'unit_cost' => 300.00, 'category' => 'cement', 'type' => 'raw', 'status' => 'active', ]); $this->sealant = Material::create([ 'name' => 'Polyurethane Sealant', 'sku' => 'SLN-PLY-002', 'unit' => 'gallon', 'unit_cost' => 1000.00, 'category' => 'chemicals', 'type' => 'raw', 'status' => 'active', ]); $this->project = Project::create([ 'name' => 'Highrise Horizon Tower', 'code' => 'PRJ-HRZ-2026', 'status' => ProjectStatus::InProgress, 'project_type' => 'standard', 'is_unprofitable' => false, 'contract_value' => 5000000.00, 'total_capitalization' => 0.00, 'current_wizard_step' => 8, ]); // Add 100 bags of cement to baseline estimation $this->project->materialsEstimates()->create([ 'material_id' => $this->cement->id, 'estimated_qty' => 100, 'unit_cost' => 300.00, ]); $this->warehouse = Warehouse::create([ 'name' => 'Horizon Project Site Warehouse', 'code' => 'WH-HRZ-001', 'project_id' => $this->project->id, 'is_active' => true, ]); } /** @test */ public function test_estimated_materials_populate_remaining_quantities_and_lock_when_exhausted(): void { // 1. Initial MR Create check -> 100 bags remaining, hasRemainingEstimates is true $initialCreateResp = $this->actingAs($this->admin)->get(route('requisitions.create', [ 'project_ulid' => $this->project->ulid, ])); $initialCreateResp->assertOk(); $this->assertTrue($initialCreateResp->viewData('page')['props']['hasRemainingEstimates']); $prefilled = $initialCreateResp->viewData('page')['props']['prefilledItems']; $this->assertCount(1, $prefilled); $this->assertEquals(100, $prefilled[0]['remaining_qty']); // 2. Request all 100 bags in first MR $storeMR1Resp = $this->actingAs($this->admin)->post(route('requisitions.store'), [ 'project_ulid' => $this->project->ulid, 'requisition_type' => 'estimated', 'notes' => 'First batch: all 100 bags of cement.', 'items' => [ [ 'material_ulid' => $this->cement->ulid, 'quantity' => 100, 'unit_cost' => 300.00, 'is_unestimated' => false, ], ], ]); $storeMR1Resp->assertRedirect(); $mr1 = MaterialRequisition::where('project_id', $this->project->id)->firstOrFail(); $this->assertEquals('estimated', $mr1->requisition_type); $this->assertFalse($mr1->items->first()->is_unestimated); // 3. Second MR Create check -> All estimated materials requested -> hasRemainingEstimates is false (LOCKED) $secondCreateResp = $this->actingAs($this->admin)->get(route('requisitions.create', [ 'project_ulid' => $this->project->ulid, ])); $secondCreateResp->assertOk(); $this->assertFalse($secondCreateResp->viewData('page')['props']['hasRemainingEstimates']); $this->assertEmpty($secondCreateResp->viewData('page')['props']['prefilledItems']); } /** @test */ public function test_unestimated_materials_prices_are_added_to_project_total_capitalization(): void { $this->project->refresh(); $this->assertEquals(0.00, (float) $this->project->total_capitalization); // 1. Create MR with unestimated materials (15 gallons of sealant @ 1,000 PHP = 15,000 PHP) $unestimatedMRResp = $this->actingAs($this->admin)->post(route('requisitions.store'), [ 'project_ulid' => $this->project->ulid, 'requisition_type' => 'unestimated', 'notes' => 'Urgent missed sealant required for joint waterproofing.', 'items' => [ [ 'material_ulid' => $this->sealant->ulid, 'quantity' => 15, 'unit_cost' => 1000.00, 'is_unestimated' => true, ], ], ]); $unestimatedMRResp->assertRedirect(); $mr = MaterialRequisition::where('requisition_type', 'unestimated')->firstOrFail(); $this->assertTrue($mr->items->first()->is_unestimated); $this->assertEquals(15000.00, (float) $mr->total_cost); // 2. Verify that the project total_capitalization was automatically increased by 15,000 PHP $this->project->refresh(); $this->assertEquals(15000.00, (float) $this->project->total_capitalization); // 3. Verify that Project Show page reflects the updated capitalization $showResp = $this->actingAs($this->admin)->get(route('projects.show', $this->project)); $showResp->assertOk(); $this->assertEquals(15000.00, (float) $showResp->viewData('page')['props']['project']['total_capitalization']); } }