Files
GSB-Construction/Modules/ProjectManagement/tests/Feature/ProjectEditLockTest.php
Christopher Boyles 64d4b331a8
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
Additional Changes
2026-06-02 22:04:03 +08:00

202 lines
6.7 KiB
PHP

<?php
namespace Modules\ProjectManagement\Tests\Feature;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\ProjectManagement\Models\Project;
use Modules\ProjectManagement\Models\ProjectMilestone;
use Modules\ProjectManagement\Models\Task;
use Modules\ProjectManagement\Enums\ProjectStatus;
use Modules\MasterData\Models\Material;
use Modules\Labors\Models\Labor;
use Modules\Equipments\Models\Equipment;
use Spatie\Permission\Models\Role;
use Spatie\Permission\Models\Permission;
use Tests\TestCase;
class ProjectEditLockTest extends TestCase
{
use RefreshDatabase;
protected User $user;
protected Project $unapprovedProject;
protected Project $approvedProject;
protected Material $material;
protected Labor $labor;
protected Equipment $equipment;
protected function setUp(): void
{
parent::setUp();
// Seed roles & permissions
$role = Role::firstOrCreate(['name' => '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' => 'Test Cement Bag',
'sku' => 'CEM-TST-101',
'unit' => 'bag',
'unit_cost' => 300.00,
'category' => 'cement',
'type' => 'raw',
'status' => 'active',
]);
// Standard Labor
$this->labor = Labor::create([
'name' => 'Foreman',
'category' => 'skilled',
'hourly_rate' => 150.00,
'status' => 'active',
]);
// Standard Equipment
$this->equipment = Equipment::create([
'name' => 'Dump Truck',
'owner_name' => 'Company Owned',
'hourly_rate' => 1500.00,
'status' => 'active',
]);
// Create Unapproved Project (step < 8)
$this->unapprovedProject = Project::create([
'name' => 'Draft Project',
'code' => 'PRJ-DFT-2026',
'status' => ProjectStatus::Planning,
'project_type' => 'standard',
'is_unprofitable' => false,
'contract_value' => 500000.00,
'current_wizard_step' => 2,
]);
// Create Approved Project (step >= 8)
$this->approvedProject = Project::create([
'name' => 'Approved Project',
'code' => 'PRJ-APP-2026',
'status' => ProjectStatus::InProgress,
'project_type' => 'standard',
'is_unprofitable' => false,
'contract_value' => 1000000.00,
'current_wizard_step' => 8,
]);
}
public function test_unapproved_project_can_be_updated_details(): void
{
$response = $this->actingAs($this->user)->put(route('projects.update', $this->unapprovedProject->ulid), [
'name' => 'Updated Draft Project',
'project_type' => 'standard',
]);
$response->assertRedirect(route('projects.show', $this->unapprovedProject->ulid));
$this->unapprovedProject->refresh();
$this->assertEquals('Updated Draft Project', $this->unapprovedProject->name);
}
public function test_approved_project_updates_blocked(): void
{
$response = $this->actingAs($this->user)->put(route('projects.update', $this->approvedProject->ulid), [
'name' => 'Updated Approved Project',
'project_type' => 'standard',
]);
$response->assertRedirect();
$response->assertSessionHas('error', 'Cannot update details: This project has been approved and locked.');
$this->approvedProject->refresh();
$this->assertEquals('Approved Project', $this->approvedProject->name);
}
public function test_approved_project_cannot_modify_tasks(): void
{
$response = $this->actingAs($this->user)->post(route('projects.wizard.tasks', $this->approvedProject->ulid), [
'tasks' => [
[
'name' => 'Attempted Task',
]
]
]);
$response->assertRedirect();
$response->assertSessionHas('error', 'Cannot modify tasks: This project has been approved and locked.');
}
public function test_approved_project_cannot_modify_estimates(): void
{
$response = $this->actingAs($this->user)->post(route('projects.wizard.estimates', $this->approvedProject->ulid), [
'estimates' => [
[
'material_ulid' => $this->material->ulid,
'estimated_qty' => 100,
'unit_cost' => 300.00,
]
]
]);
$response->assertRedirect();
$response->assertSessionHas('error', 'Cannot modify estimates: This project has been approved and locked.');
}
public function test_approved_project_cannot_modify_labor(): void
{
$task = $this->approvedProject->tasks()->create([
'name' => 'Mock Task',
'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->approvedProject->ulid), [
'labor' => [
[
'task_ulid' => $task->ulid,
'labor_ulid' => $this->labor->ulid,
'estimated_hours' => 40,
]
]
]);
$response->assertRedirect();
$response->assertSessionHas('error', 'Cannot modify labor allocations: This project has been approved and locked.');
}
public function test_approved_project_cannot_modify_equipment(): void
{
$task = $this->approvedProject->tasks()->create([
'name' => 'Mock Task',
'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->approvedProject->ulid), [
'equipment' => [
[
'task_ulid' => $task->ulid,
'equipment_ulid' => $this->equipment->ulid,
'estimated_hours' => 12,
]
]
]);
$response->assertRedirect();
$response->assertSessionHas('error', 'Cannot modify equipment allocations: This project has been approved and locked.');
}
}