- Implement ProjectController with CRUD, status transitions, estimate/forecast - Add ProjectService with state machine validation - Extract ProjectStatusService for reusable state machine logic - Add ProjectPolicy for role-based authorization - Create ProjectSeeder with test data - Implement frontend project management UI with modal forms - Add projectService API client - Complete all 9 incomplete unit tests (ProjectModelTest, ProjectForecastTest, ProjectPolicyTest) - Fix E2E test timing issues with loading state waits - Add Scribe API documentation annotations - Improve forecasted effort validation messages with detailed feedback Test Results: - Backend: 49 passed (182 assertions) - Frontend Unit: 32 passed - E2E: 134 passed (Chromium + Firefox) Phase 3 Refactor: - Extract ProjectStatusService for state machine - Optimize project list query with status joins - Improve forecasted effort validation messages Phase 4 Document: - Add Scribe annotations to ProjectController - Generate API documentation
62 lines
2.0 KiB
PHP
62 lines
2.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
/**
|
|
* Encapsulates the project lifecycle state machine.
|
|
*/
|
|
class ProjectStatusService
|
|
{
|
|
/**
|
|
* Valid status transitions for the project state machine.
|
|
* Key = from status, Value = array of valid target statuses
|
|
*/
|
|
protected array $statusTransitions = [
|
|
'Pre-sales' => ['SOW Approval'],
|
|
'SOW Approval' => ['Estimation', 'Pre-sales'],
|
|
'Estimation' => ['Estimate Approved', 'SOW Approval'],
|
|
'Estimate Approved' => ['Resource Allocation', 'Estimate Rework'],
|
|
'Resource Allocation' => ['Sprint 0', 'Estimate Approved'],
|
|
'Sprint 0' => ['In Progress', 'Resource Allocation'],
|
|
'In Progress' => ['UAT', 'Sprint 0', 'On Hold'],
|
|
'UAT' => ['Handover / Sign-off', 'In Progress', 'On Hold'],
|
|
'Handover / Sign-off' => ['Closed', 'UAT'],
|
|
'Estimate Rework' => ['Estimation'],
|
|
'On Hold' => ['In Progress', 'Cancelled'],
|
|
'Cancelled' => [],
|
|
'Closed' => [],
|
|
];
|
|
|
|
/**
|
|
* Return the valid target statuses for the provided current status.
|
|
*/
|
|
public function getValidTransitions(string $currentStatus): array
|
|
{
|
|
return $this->statusTransitions[$currentStatus] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Determine if a transition from the current status to the target is allowed.
|
|
*/
|
|
public function canTransition(string $currentStatus, string $targetStatus): bool
|
|
{
|
|
return in_array($targetStatus, $this->getValidTransitions($currentStatus), true);
|
|
}
|
|
|
|
/**
|
|
* Return statuses that do not allow further transitions.
|
|
*/
|
|
public function getTerminalStatuses(): array
|
|
{
|
|
return array_keys(array_filter($this->statusTransitions, static fn (array $targets): bool => $targets === []));
|
|
}
|
|
|
|
/**
|
|
* Determine if a status requires an approved estimate before entering.
|
|
*/
|
|
public function requiresEstimate(string $statusName): bool
|
|
{
|
|
return $statusName === 'Estimate Approved';
|
|
}
|
|
}
|