feat: complete GSB construction ERP implementation with dual-mode MR, capitalization cap engine, single PM rule, and system sign-off documentation
This commit is contained in:
@@ -605,8 +605,6 @@ class ProjectWizardFlowTest extends TestCase
|
||||
$this->assertFalse($project->personnel()->where('user_id', $user->id)->exists());
|
||||
}
|
||||
|
||||
// ─── Wizard Step Clamping ─────────────────────────────────────────────────
|
||||
|
||||
/** @test */
|
||||
public function cannot_skip_ahead_wizard_steps(): void
|
||||
{
|
||||
@@ -617,4 +615,166 @@ class ProjectWizardFlowTest extends TestCase
|
||||
->assertOk()
|
||||
->assertInertia(fn ($page) => $page->where('step', 2));
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function contractor_project_manager_appears_in_project_wizard_roster_and_can_be_assigned(): void
|
||||
{
|
||||
$cpmRole = Role::firstOrCreate(['name' => 'Contractor Project Manager', 'guard_name' => 'web']);
|
||||
$contractor = \Modules\ContractorManagement\Models\Contractor::create([
|
||||
'company_name' => 'Titan Prime Subcontractor Corp',
|
||||
'contact_person' => 'Juan Dela Cruz',
|
||||
'email' => 'juan@titanprime.test',
|
||||
'phone' => '09171234567',
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$contractorPM = User::factory()->create([
|
||||
'name' => 'Juan Contractor PM',
|
||||
'email' => 'juan.cpm@titanprime.test',
|
||||
'user_type' => 'contractor',
|
||||
'contractor_id' => $contractor->id,
|
||||
'status' => 'active',
|
||||
]);
|
||||
$contractorPM->assignRole($cpmRole);
|
||||
|
||||
// 1. Verify Contractor PM appears in projects.create employees list
|
||||
$this->actingAs($this->admin)
|
||||
->get(route('projects.create'))
|
||||
->assertOk()
|
||||
->assertInertia(fn ($page) =>
|
||||
$page->where('employees', fn ($emps) =>
|
||||
collect($emps)->contains('email', 'juan.cpm@titanprime.test')
|
||||
)
|
||||
);
|
||||
|
||||
// 2. Verify Contractor PM can be assigned as project PM on project store
|
||||
$storeResponse = $this->actingAs($this->admin)
|
||||
->post(route('projects.store'), [
|
||||
'name' => 'Titan Construction Tower A',
|
||||
'project_type' => 'standard',
|
||||
'pm_id' => $contractorPM->ulid,
|
||||
'contract_duration' => 120,
|
||||
]);
|
||||
|
||||
$storeResponse->assertRedirect();
|
||||
$newProject = Project::where('name', 'Titan Construction Tower A')->firstOrFail();
|
||||
$this->assertTrue($newProject->personnel()->where('user_id', $contractorPM->id)->wherePivot('role', 'pm')->exists());
|
||||
|
||||
// 3. Verify Contractor PM appears in Step 4 wizard roster and can be assigned as team member
|
||||
$wizardStep4Resp = $this->actingAs($this->admin)
|
||||
->get(route('projects.wizard', [$newProject, 'step' => 4]));
|
||||
|
||||
$wizardStep4Resp->assertOk()
|
||||
->assertInertia(fn ($page) =>
|
||||
$page->where('employees', fn ($emps) =>
|
||||
collect($emps)->contains('email', 'juan.cpm@titanprime.test')
|
||||
)
|
||||
);
|
||||
|
||||
// 4. Save Step 4 manpower with Contractor PM in team roster
|
||||
$saveLaborResp = $this->actingAs($this->admin)
|
||||
->post(route('projects.wizard.labor', $newProject), [
|
||||
'user_ulids' => [$contractorPM->ulid],
|
||||
'labor' => [],
|
||||
]);
|
||||
|
||||
$saveLaborResp->assertRedirect(route('projects.wizard', [$newProject, 'step' => 5]));
|
||||
$this->assertTrue($newProject->personnel()->where('user_id', $contractorPM->id)->exists());
|
||||
// Verify Contractor was automatically attached to project_contractor
|
||||
$this->assertTrue($newProject->contractors()->where('contractors.id', $contractor->id)->exists());
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function test_wizard_roster_auto_links_contractors_and_enforces_single_project_manager_with_multiple_site_operations(): void
|
||||
{
|
||||
$pmRole = Role::firstOrCreate(['name' => 'Project Manager', 'guard_name' => 'web']);
|
||||
$siteTechRole = Role::firstOrCreate(['name' => 'Site Technical', 'guard_name' => 'web']);
|
||||
$supervisorRole = Role::firstOrCreate(['name' => 'Construction Supervisor', 'guard_name' => 'web']);
|
||||
|
||||
$mainContractor = \Modules\ContractorManagement\Models\Contractor::create([
|
||||
'company_name' => 'Main Structural Corp',
|
||||
'contact_person' => 'Main Person',
|
||||
'email' => 'main@structural.test',
|
||||
'phone' => '09181111111',
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$subcontractor = \Modules\ContractorManagement\Models\Contractor::create([
|
||||
'company_name' => 'Subcon Electrical Services',
|
||||
'contact_person' => 'Sub Person',
|
||||
'email' => 'sub@electrical.test',
|
||||
'phone' => '09182222222',
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$firstPM = User::factory()->create([
|
||||
'name' => 'Alice First PM',
|
||||
'user_type' => 'contractor',
|
||||
'contractor_id' => $mainContractor->id,
|
||||
'status' => 'active',
|
||||
]);
|
||||
$firstPM->assignRole($pmRole);
|
||||
|
||||
$secondPM = User::factory()->create([
|
||||
'name' => 'Bob Second PM',
|
||||
'user_type' => 'employee',
|
||||
'status' => 'active',
|
||||
]);
|
||||
$secondPM->assignRole($pmRole);
|
||||
|
||||
$siteTechUser = User::factory()->create([
|
||||
'name' => 'Charlie Site Tech',
|
||||
'user_type' => 'contractor',
|
||||
'contractor_id' => $subcontractor->id,
|
||||
'status' => 'active',
|
||||
]);
|
||||
$siteTechUser->assignRole($siteTechRole);
|
||||
|
||||
$supervisorUser = User::factory()->create([
|
||||
'name' => 'David Supervisor',
|
||||
'user_type' => 'contractor',
|
||||
'contractor_id' => $subcontractor->id,
|
||||
'status' => 'active',
|
||||
]);
|
||||
$supervisorUser->assignRole($supervisorRole);
|
||||
|
||||
// 1. Create project with First PM
|
||||
$this->actingAs($this->admin)->post(route('projects.store'), [
|
||||
'name' => 'Skyline Heights Commercial Center',
|
||||
'project_type' => 'standard',
|
||||
'pm_id' => $firstPM->ulid,
|
||||
'contract_duration' => 180,
|
||||
]);
|
||||
|
||||
$project = Project::where('name', 'Skyline Heights Commercial Center')->firstOrFail();
|
||||
$this->assertEquals(1, $project->personnel()->wherePivot('role', 'pm')->count());
|
||||
$this->assertTrue($project->contractors()->where('contractors.id', $mainContractor->id)->exists());
|
||||
|
||||
// 2. Change PM in Wizard Step 1 details -> Strictly 1 PM remains
|
||||
$this->actingAs($this->admin)->post(route('projects.wizard.save-details', $project), [
|
||||
'name' => 'Skyline Heights Commercial Center',
|
||||
'project_type' => 'standard',
|
||||
'pm_id' => $secondPM->ulid,
|
||||
'contract_duration' => 180,
|
||||
]);
|
||||
|
||||
$project->refresh();
|
||||
$this->assertEquals(1, $project->personnel()->wherePivot('role', 'pm')->count());
|
||||
$this->assertEquals($secondPM->id, $project->personnel()->wherePivot('role', 'pm')->first()->id);
|
||||
|
||||
// 3. In Wizard Step 4, assign multiple site operations personnel from Subcontractor
|
||||
$this->actingAs($this->admin)->post(route('projects.wizard.labor', $project), [
|
||||
'user_ulids' => [$siteTechUser->ulid, $supervisorUser->ulid],
|
||||
'labor' => [],
|
||||
]);
|
||||
|
||||
$project->refresh();
|
||||
|
||||
// 4. Verify exactly 1 PM and multiple site operations
|
||||
$this->assertEquals(1, $project->personnel()->wherePivot('role', 'pm')->count());
|
||||
$this->assertEquals(2, $project->personnel()->wherePivot('role', 'member')->count());
|
||||
|
||||
// 5. Verify Subcontractor was automatically linked to the project in project_contractor
|
||||
$this->assertTrue($project->contractors()->where('contractors.id', $subcontractor->id)->exists());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user