Pierwsze procesy

This commit is contained in:
Flatlogic Bot 2026-01-10 21:10:13 +00:00
parent 3b1a26adc9
commit 4674e7458b
4 changed files with 123 additions and 62 deletions

View File

@ -405,12 +405,19 @@ class WorkflowEngine {
}
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,7 +425,14 @@ 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) : [];

View File

@ -1,29 +1,44 @@
<?php
session_start();
require_once 'WorkflowEngine.php';
require_once 'lib/ErrorHandler.php';
require_once 'lib/WorkflowExceptions.php';
register_error_handler();
require_once 'db/config.php';
require_once 'WorkflowEngine.php';
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
header('Content-Type: application/json');
if (!isset($_SESSION['user_id'])) {
throw new WorkflowNotAllowedException('Authentication required.');
http_response_code(401);
echo json_encode(['error' => ['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();

View File

@ -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,6 +714,22 @@ 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);
@ -718,14 +740,50 @@ document.addEventListener('DOMContentLoaded', function () {
})
.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;
});
});
}
// Reload modal content after successful submission
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;
}
})
.catch(error => {
console.error('Error submitting request:', error);
modalBody.innerHTML = `<div class="alert alert-danger">Wystąpił błąd sieciowy.</div>`;
let errorMessage = `<div class="alert alert-danger">`;
errorMessage += `<strong>Error:</strong> ${error.message}`;
if (error.correlation_id) {
errorMessage += `<br><small class="text-muted">Correlation ID: ${error.correlation_id}</small>`;
}
errorMessage += `</div>`;
modalBody.innerHTML = errorMessage;
});
}

View File

@ -1,44 +1,18 @@
<?php
class WorkflowException extends Exception {
protected $httpCode;
protected $details;
class WorkflowNotFoundException extends Exception {}
class WorkflowNotAllowedException extends Exception {}
class WorkflowRuleFailedException extends Exception {}
public function __construct($message = "", $code = 0, $httpCode = 500, $details = [], Throwable $previous = null) {
class WorkflowEligibilityException extends Exception {
private $reasons;
public function __construct($message = "", $reasons = [], $code = 0, Throwable $previous = null) {
parent::__construct($message, $code, $previous);
$this->httpCode = $httpCode;
$this->details = $details;
$this->reasons = $reasons;
}
public function getHttpCode() {
return $this->httpCode;
}
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);
public function getReasons() {
return $this->reasons;
}
}