Files
GSB-Construction/Modules/ProjectManagement/tests/Feature/TaskActivityAndCostTest.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

225 lines
7.2 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\Task;
use Modules\ProjectManagement\Models\TaskActivity;
use Modules\ProjectManagement\Enums\ProjectStatus;
use Modules\ProjectManagement\Enums\TaskStatus;
use Modules\ProjectManagement\Enums\DelayReason;
use Modules\MasterData\Models\Material;
use Spatie\Permission\Models\Role;
use Tests\TestCase;
class TaskActivityAndCostTest extends TestCase
{
use RefreshDatabase;
protected User $user;
protected Project $project;
protected Material $material;
protected function setUp(): void
{
parent::setUp();
$role = Role::firstOrCreate(['name' => 'Super Admin']);
$this->user = User::factory()->create([
'status' => 'active',
'user_type' => 'admin',
]);
$this->user->assignRole($role);
$this->project = Project::create([
'name' => 'Test Project',
'code' => 'PRJ-2026-TEST',
'status' => ProjectStatus::Planning,
'project_type' => 'standard',
'is_unprofitable' => false,
'contract_value' => 500000.00,
]);
$this->material = Material::create([
'name' => 'Steel Rebar',
'sku' => 'STL-REB-001',
'unit' => 'pcs',
'unit_cost' => 150.00,
'category' => 'steel',
'type' => 'raw',
'status' => 'active',
]);
}
public function test_task_creation_activity_log(): void
{
$this->actingAs($this->user);
$task = $this->project->tasks()->create([
'name' => 'Task One',
'status' => TaskStatus::Pending,
'labor_cost' => 1000.00,
'equipment_cost' => 500.00,
'estimated_hours' => 10,
'sort_order' => 0,
]);
$this->assertDatabaseHas('task_activities', [
'task_id' => $task->id,
'user_id' => $this->user->id,
'description' => 'created the task.',
'type' => 'creation',
]);
}
public function test_task_updating_fields_activity_log(): void
{
$this->actingAs($this->user);
$task = $this->project->tasks()->create([
'name' => 'Initial Name',
'status' => TaskStatus::Pending,
'labor_cost' => 1000.00,
'equipment_cost' => 500.00,
'estimated_hours' => 10,
'sort_order' => 0,
]);
// Clean out creation log so we only check update log
$task->activities()->delete();
$task->update([
'name' => 'Updated Name',
'estimated_hours' => 15,
'status' => TaskStatus::InProgress,
]);
$this->assertDatabaseHas('task_activities', [
'task_id' => $task->id,
'user_id' => $this->user->id,
'type' => 'update',
]);
$activity = $task->activities()->first();
$this->assertStringContainsString("changed name from 'Initial Name' to 'Updated Name'", $activity->description);
$this->assertStringContainsString("changed estimated hours from 10.00 to 15", $activity->description);
$this->assertStringContainsString("changed status from 'Pending' to 'in_progress'", $activity->description);
}
public function test_task_cost_fallback_calculations(): void
{
$this->actingAs($this->user);
$task = $this->project->tasks()->create([
'name' => 'Task Cost Test',
'status' => TaskStatus::Pending,
'labor_cost' => 1200.00,
'equipment_cost' => 800.00,
'estimated_hours' => 10,
'sort_order' => 0,
]);
// Prior to completions, actual costs should be 0 because no resource records exist
$this->assertEquals(0.00, $task->actual_labor_cost);
$this->assertEquals(0.00, $task->actual_equipment_cost);
// Estimated costs should fall back to the columns
$this->assertEquals(1200.00, $task->estimated_labor_cost);
$this->assertEquals(800.00, $task->estimated_equipment_cost);
// When task is completed, actual costs should fall back to columns (labor_cost and equipment_cost)
$task->update(['status' => TaskStatus::Completed]);
$this->assertEquals(1200.00, $task->actual_labor_cost);
$this->assertEquals(800.00, $task->actual_equipment_cost);
$this->assertEquals(2000.00, $task->total_cost);
}
public function test_task_material_actions_logged(): void
{
$this->actingAs($this->user);
$task = $this->project->tasks()->create([
'name' => 'Material Task',
'status' => TaskStatus::Pending,
'sort_order' => 0,
]);
// Add task material
$tm = $task->taskMaterials()->create([
'material_id' => $this->material->id,
'planned_qty' => 10.00,
'actual_qty' => 0.00,
'unit_cost' => $this->material->unit_cost,
]);
$this->assertDatabaseHas('task_activities', [
'task_id' => $task->id,
'user_id' => $this->user->id,
'description' => "added material 'Steel Rebar' (Planned Qty: 10.00).",
'type' => 'material_add',
]);
// Update task material
$tm->update(['actual_qty' => 5.00]);
$this->assertDatabaseHas('task_activities', [
'task_id' => $task->id,
'user_id' => $this->user->id,
'description' => "updated actual quantity for 'Steel Rebar' to 5.00.",
'type' => 'material_update',
]);
// Delete task material
$tm->delete();
$this->assertDatabaseHas('task_activities', [
'task_id' => $task->id,
'user_id' => $this->user->id,
'description' => "removed material 'Steel Rebar'.",
'type' => 'material_remove',
]);
}
public function test_task_delay_actions_logged(): void
{
$this->actingAs($this->user);
$task = $this->project->tasks()->create([
'name' => 'Delay Task',
'status' => TaskStatus::Pending,
'sort_order' => 0,
]);
// Log delay
$td = $task->delays()->create([
'reason_type' => DelayReason::Weather,
'delay_date' => now(),
'lost_hours' => 8.00,
'reported_by' => $this->user->id,
]);
$this->assertDatabaseHas('task_activities', [
'task_id' => $task->id,
'user_id' => $this->user->id,
'type' => 'delay_add',
]);
// Update delay
$td->update(['lost_hours' => 12.00]);
$this->assertDatabaseHas('task_activities', [
'task_id' => $task->id,
'user_id' => $this->user->id,
'description' => 'updated delay: changed delay hours to 12.00h.',
'type' => 'delay_update',
]);
// Delete delay
$td->delete();
$this->assertDatabaseHas('task_activities', [
'task_id' => $task->id,
'user_id' => $this->user->id,
'type' => 'delay_remove',
]);
}
}