diff --git a/WorkflowEngine.php b/WorkflowEngine.php index 16f74e5..256de23 100644 --- a/WorkflowEngine.php +++ b/WorkflowEngine.php @@ -404,13 +404,20 @@ class WorkflowEngine { $stmt->execute([$instanceId, $eventType, $message, $nodeId, json_encode($payload), $userId]); } - public function getOrCreateInstanceByDefId(int $personId, int $processDefinitionId, int $userId): ?array { + public function getOrCreateInstanceByDefId(int $personId, int $processDefinitionId, int $userId): ?array { + if (!is_int($processDefinitionId) || $processDefinitionId <= 0) { + throw new InvalidArgumentException("processDefinitionId must be a positive integer."); + } + if (!is_int($personId) || $personId <= 0) { + throw new InvalidArgumentException("personId must be a positive integer."); + } + $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE `person_id` = ? AND `process_definition_id` = ?"); $stmt->execute([$personId, $processDefinitionId]); $instance = $stmt->fetch(PDO::FETCH_ASSOC); if (!$instance) { - $stmt_def = $this->pdo->prepare("SELECT definition_json, code FROM process_definitions WHERE id = ?"); + $stmt_def = $this->pdo->prepare("SELECT definition_json, code, is_active FROM process_definitions WHERE id = ?"); $stmt_def->execute([$processDefinitionId]); $definition = $stmt_def->fetch(PDO::FETCH_ASSOC); @@ -418,17 +425,24 @@ class WorkflowEngine { throw new WorkflowNotFoundException("Process definition #$processDefinitionId not found."); } - $this->checkEligibility($personId, $definition); + if (empty($definition['is_active'])) { + throw new WorkflowNotAllowedException("Process is not active and cannot be started."); + } + + $eligibility = $this->checkEligibility($personId, $processDefinitionId); + if (!$eligibility['is_eligible']) { + throw new WorkflowEligibilityException("Person is not eligible to start this process.", $eligibility['reasons']); + } $definition_json = !empty($definition['definition_json']) ? json_decode($definition['definition_json'], true) : []; - $processCode = ($definition_json && isset($definition_json['type']) && $definition_json['type'] === 'checklist') - ? (string) $processDefinitionId + $processCode = ($definition_json && isset($definition_json['type']) && $definition_json['type'] === 'checklist') + ? (string) $processDefinitionId : $definition['code']; - if($processCode) { + if ($processCode) { $instanceId = $this->startProcess($processCode, $personId, $userId); - if($instanceId) { + if ($instanceId) { $stmt->execute([$personId, $processDefinitionId]); $instance = $stmt->fetch(PDO::FETCH_ASSOC); } diff --git a/_init_single_instance.php b/_init_single_instance.php index 60b5c26..9141324 100644 --- a/_init_single_instance.php +++ b/_init_single_instance.php @@ -1,29 +1,44 @@ ['message' => 'Authentication required.']]); + exit; } -$userId = $_SESSION['user_id']; -$personId = $_GET['personId'] ?? null; -$processDefinitionId = $_GET['processId'] ?? null; +$userId = $_SESSION['user_id']; +$personId = filter_input(INPUT_POST, 'person_id', FILTER_VALIDATE_INT); +$processDefinitionId = filter_input(INPUT_POST, 'process_id', FILTER_VALIDATE_INT); if (!$personId || !$processDefinitionId) { - throw new WorkflowRuleFailedException('Missing parameters for process initialization.'); + // InvalidArgumentException will be caught by the handler and result in a 400 Bad Request + throw new InvalidArgumentException('Invalid or missing person_id or process_id.'); } $engine = new WorkflowEngine(); + +// The getOrCreateInstanceByDefId method is now responsible for all checks: +// 1. Validating the process definition exists. +// 2. Checking if the process is active. +// 3. Checking if the person is eligible. +// 4. Creating the instance if it doesn't exist. +// It will throw specific exceptions (WorkflowNotFoundException, WorkflowNotAllowedException, WorkflowEligibilityException) which our ErrorHandler will turn into 404, 409, and 422 responses. $instance = $engine->getOrCreateInstanceByDefId($personId, $processDefinitionId, $userId); if ($instance) { - $_SESSION['success_message'] = "Process initialized successfully."; + echo json_encode(['success' => true, 'message' => 'Process initialized successfully.', 'instance_id' => $instance['id']]); } else { - $_SESSION['error_message'] = "Failed to initialize process."; + // This case should not be reached if the engine works as expected, as failures should throw exceptions. + throw new Exception("Failed to initialize process for an unknown reason."); } - -header('Location: index.php'); -exit(); diff --git a/index.php b/index.php index 6fc68cb..5223afc 100644 --- a/index.php +++ b/index.php @@ -607,6 +607,12 @@ document.addEventListener('DOMContentLoaded', function () { handleAddNote(noteBtn); return; } + + const startBtn = event.target.closest('#startProcessBtn'); + if (startBtn) { + handleStartProcess(startBtn); + return; + } }); instanceModal.addEventListener('change', function(event) { @@ -708,24 +714,76 @@ document.addEventListener('DOMContentLoaded', function () { submitRequestAndReloadModal('_apply_transition.php', formData); } + function handleStartProcess(button) { + const personId = button.dataset.personId; + const processId = button.dataset.processId; + + if (!personId || !processId) { + alert('Missing data for starting process. Please close the modal and try again.'); + return; + } + + const formData = new FormData(); + formData.append('person_id', personId); + formData.append('process_id', processId); + + submitRequestAndReloadModal('_init_single_instance.php', formData); + } + function submitRequestAndReloadModal(url, formData) { const modalBody = instanceModal.querySelector('.modal-body'); showLoading(modalBody); - + fetch(url, { method: 'POST', body: formData }) .then(response => { if (!response.ok) { - throw new Error('Network response was not ok'); + // If response is not OK, it's an error. Clone the response to read it twice. + const clone = response.clone(); + return response.json() + .then(json => { + // We have a JSON error body, throw a custom error with its details + const error = new Error(json.error?.message || 'An unkown error occurred.'); + error.correlation_id = json.correlation_id; + error.response = response; // Attach full response + throw error; + }) + .catch(() => { + // If JSON parsing fails, fall back to the text body + return clone.text().then(text => { + const error = new Error(text || 'Network response was not ok and could not parse error body.'); + error.response = response; + throw error; + }); + }); + } + return response.json(); // On success, just parse the JSON + }) + .then(data => { + if (data.success) { + // Reload modal content for the same person/process after successful submission + fetchAndRenderModalContent(currentPersonId, currentProcessId); + } else { + // Handle cases where the server returns 200 OK but with success: false + const error = new Error(data.message || 'An unknown error occurred.'); + if (data.correlation_id) { + error.correlation_id = data.correlation_id; + } + throw error; } - // Reload modal content after successful submission - fetchAndRenderModalContent(currentPersonId, currentProcessId); }) .catch(error => { console.error('Error submitting request:', error); - modalBody.innerHTML = `
Wystąpił błąd sieciowy.
`; + + let errorMessage = `
`; + errorMessage += `Error: ${error.message}`; + if (error.correlation_id) { + errorMessage += `
Correlation ID: ${error.correlation_id}`; + } + errorMessage += `
`; + modalBody.innerHTML = errorMessage; }); } diff --git a/lib/WorkflowExceptions.php b/lib/WorkflowExceptions.php index 1d3a5af..f17534b 100644 --- a/lib/WorkflowExceptions.php +++ b/lib/WorkflowExceptions.php @@ -1,44 +1,18 @@ httpCode = $httpCode; - $this->details = $details; + $this->reasons = $reasons; } - public function getHttpCode() { - return $this->httpCode; + public function getReasons() { + return $this->reasons; } - - public function getDetails() { - return $this->details; - } -} - -class WorkflowNotFoundException extends WorkflowException { - public function __construct($message = "Not Found", $details = [], Throwable $previous = null) { - parent::__construct($message, 404, 404, $details, $previous); - } -} - -class WorkflowNotAllowedException extends WorkflowException { - public function __construct($message = "Bad Request", $details = [], Throwable $previous = null) { - parent::__construct($message, 400, 400, $details, $previous); - } -} - -class WorkflowRuleFailedException extends WorkflowException { - public function __construct($message = "Unprocessable Entity", $details = [], Throwable $previous = null) { - parent::__construct($message, 422, 422, $details, $previous); - } -} - -class WorkflowConflictException extends WorkflowException { - public function __construct($message = "Conflict", $details = [], Throwable $previous = null) { - parent::__construct($message, 409, 409, $details, $previous); - } -} +} \ No newline at end of file