Podział na procesy indywidualne i grupowe
This commit is contained in:
parent
9134470c19
commit
9ea7b9d268
@ -173,13 +173,50 @@ class WorkflowEngine {
|
|||||||
$stmt_meetings->execute(['today' => $today]);
|
$stmt_meetings->execute(['today' => $today]);
|
||||||
$upcoming_meetings_flat = $stmt_meetings->fetchAll(PDO::FETCH_ASSOC);
|
$upcoming_meetings_flat = $stmt_meetings->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
$spotkania_cols = [];
|
|
||||||
|
$spotkania_cols = [];
|
||||||
|
$meeting_ids = [];
|
||||||
foreach ($upcoming_meetings_flat as $meeting) {
|
foreach ($upcoming_meetings_flat as $meeting) {
|
||||||
|
$meetingId = $this->getOrCreateMeeting($meeting['group_id'], $meeting['start_datetime']);
|
||||||
|
$meeting_ids[] = $meetingId;
|
||||||
$spotkania_cols[$meeting['group_id']]['group_id'] = $meeting['group_id'];
|
$spotkania_cols[$meeting['group_id']]['group_id'] = $meeting['group_id'];
|
||||||
$spotkania_cols[$meeting['group_id']]['group_name'] = $meeting['group_name'];
|
$spotkania_cols[$meeting['group_id']]['group_name'] = $meeting['group_name'];
|
||||||
$spotkania_cols[$meeting['group_id']]['meetings'][] = $meeting['start_datetime'];
|
$spotkania_cols[$meeting['group_id']]['meetings'][] = [
|
||||||
|
'datetime' => $meeting['start_datetime'],
|
||||||
|
'meeting_id' => $meetingId
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch process states for these meetings
|
||||||
|
$meeting_processes = [];
|
||||||
|
if (!empty($meeting_ids)) {
|
||||||
|
$placeholders = implode(',', array_fill(0, count($meeting_ids), '?'));
|
||||||
|
$stmt_mp = $this->pdo->prepare(
|
||||||
|
"SELECT pi.*, pd.code as process_code, pd.definition_json as pd_definition_json
|
||||||
|
FROM process_instances pi
|
||||||
|
JOIN process_definitions pd ON pi.process_definition_id = pd.id
|
||||||
|
WHERE pi.subject_type = 'meeting' AND pi.subject_id IN ($placeholders)"
|
||||||
|
);
|
||||||
|
$stmt_mp->execute($meeting_ids);
|
||||||
|
$mp_data = $stmt_mp->fetchAll(\PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
foreach ($mp_data as $instance) {
|
||||||
|
$def_id = $instance['process_definition_id'];
|
||||||
|
$node_id = $instance['current_node_id'];
|
||||||
|
$definition = !empty($instance['pd_definition_json']) ? json_decode($instance['pd_definition_json'], true) : ($definition_map[$def_id] ?? null);
|
||||||
|
|
||||||
|
if ($definition && isset($definition['nodes'][$node_id])) {
|
||||||
|
$node_info = $definition['nodes'][$node_id];
|
||||||
|
$instance['computed_status'] = $node_info['ui_hints']['status'] ?? $instance['current_status'];
|
||||||
|
} else {
|
||||||
|
$instance['computed_status'] = $instance['current_status'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$meeting_processes[$instance['subject_id']][$instance['process_code']] = $instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'people' => $people,
|
'people' => $people,
|
||||||
@ -188,7 +225,8 @@ class WorkflowEngine {
|
|||||||
'all_functions' => $all_functions,
|
'all_functions' => $all_functions,
|
||||||
'person_functions_map' => $person_functions_map,
|
'person_functions_map' => $person_functions_map,
|
||||||
'bni_groups' => $bni_groups,
|
'bni_groups' => $bni_groups,
|
||||||
'spotkania_cols' => $spotkania_cols, // Add this to the return array
|
'spotkania_cols' => $spotkania_cols,
|
||||||
|
'meeting_processes' => $meeting_processes, // Add this to the return array
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,9 +272,9 @@ class WorkflowEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$stmt_insert = $this->pdo->prepare(
|
$stmt_insert = $this->pdo->prepare(
|
||||||
"INSERT INTO process_instances (person_id, process_definition_id, current_node_id, current_status, last_activity_at, data_json) VALUES (?, ?, ?, 'in_progress', NOW(), ?)"
|
"INSERT INTO process_instances (person_id, subject_type, subject_id, process_definition_id, current_node_id, current_status, last_activity_at, data_json) VALUES (?, 'person', ?, ?, ?, 'in_progress', NOW(), ?)"
|
||||||
);
|
);
|
||||||
$stmt_insert->execute([$personId, $definition['id'], $startNodeId, $initialDataJson]);
|
$stmt_insert->execute([$personId, $personId, $definition['id'], $startNodeId, $initialDataJson]);
|
||||||
$instanceId = $this->pdo->lastInsertId();
|
$instanceId = $this->pdo->lastInsertId();
|
||||||
|
|
||||||
// 3. Create a system event for process start.
|
// 3. Create a system event for process start.
|
||||||
@ -556,9 +594,9 @@ class WorkflowEngine {
|
|||||||
$data_json = !empty($initial_data_map) ? json_encode($initial_data_map) : null;
|
$data_json = !empty($initial_data_map) ? json_encode($initial_data_map) : null;
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare(
|
$stmt = $this->pdo->prepare(
|
||||||
"INSERT INTO process_instances (person_id, process_definition_id, current_node_id, current_status, data_json, last_activity_at) VALUES (?, ?, ?, 'in_progress', ?, NOW())"
|
"INSERT INTO process_instances (person_id, subject_type, subject_id, process_definition_id, current_node_id, current_status, data_json, last_activity_at) VALUES (?, 'person', ?, ?, ?, 'in_progress', ?, NOW())"
|
||||||
);
|
);
|
||||||
$stmt->execute([$personId, $processDefinitionId, $start_node, $data_json]);
|
$stmt->execute([$personId, $personId, $processDefinitionId, $start_node, $data_json]);
|
||||||
$newInstanceId = $this->pdo->lastInsertId();
|
$newInstanceId = $this->pdo->lastInsertId();
|
||||||
|
|
||||||
$this->logEvent($newInstanceId, 'process_started', 'Process started', $start_node, ['context' => $context], $userId);
|
$this->logEvent($newInstanceId, 'process_started', 'Process started', $start_node, ['context' => $context], $userId);
|
||||||
@ -990,4 +1028,129 @@ class WorkflowEngine {
|
|||||||
|
|
||||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function startProcessForSubject(string $processCode, string $subjectType, int $subjectId, int $userId, string $mode='resume_or_create', ?string $cycleKey=null): int {
|
||||||
|
$inTransaction = $this->pdo->inTransaction();
|
||||||
|
if (!$inTransaction) { $this->pdo->beginTransaction(); }
|
||||||
|
try {
|
||||||
|
$stmt_def = $this->pdo->prepare("SELECT * FROM process_definitions WHERE code = ? AND is_active = 1");
|
||||||
|
$stmt_def->execute([$processCode]);
|
||||||
|
$definition = $stmt_def->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$definition) {
|
||||||
|
// If no active definition by code, try by ID if it's a checklist fallback
|
||||||
|
$stmt_def = $this->pdo->prepare("SELECT * FROM process_definitions WHERE id = ?");
|
||||||
|
$stmt_def->execute([$processCode]);
|
||||||
|
$definition = $stmt_def->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$definition) {
|
||||||
|
throw new WorkflowNotFoundException("Process definition '$processCode' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$definition_json = !empty($definition['definition_json']) ? json_decode($definition['definition_json'], true) : [];
|
||||||
|
if (empty($definition_json) || $definition_json['type'] !== 'checklist') {
|
||||||
|
throw new WorkflowNotAllowedException("Process definition '$processCode' not found or not a checklist.");
|
||||||
|
}
|
||||||
|
$startNodeId = null;
|
||||||
|
} else {
|
||||||
|
$definition_json = !empty($definition['definition_json']) ? json_decode($definition['definition_json'], true) : [];
|
||||||
|
if (empty($definition_json) || !isset($definition_json['start_node_id'])) {
|
||||||
|
throw new WorkflowRuleFailedException("Process definition is missing start_node_id.");
|
||||||
|
}
|
||||||
|
$startNodeId = $definition_json['start_node_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$initialDataJson = null;
|
||||||
|
if (isset($definition_json['initial_data'])) {
|
||||||
|
$initialDataJson = json_encode($definition_json['initial_data']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map person_id for backward compatibility
|
||||||
|
$personId = ($subjectType === 'person') ? $subjectId : 0;
|
||||||
|
|
||||||
|
$stmt_insert = $this->pdo->prepare(
|
||||||
|
"INSERT INTO process_instances (person_id, subject_type, subject_id, cycle_key, process_definition_id, current_node_id, current_status, last_activity_at, data_json) VALUES (?, ?, ?, ?, ?, ?, 'in_progress', NOW(), ?)"
|
||||||
|
);
|
||||||
|
$stmt_insert->execute([$personId, $subjectType, $subjectId, $cycleKey, $definition['id'], $startNodeId, $initialDataJson]);
|
||||||
|
$instanceId = $this->pdo->lastInsertId();
|
||||||
|
|
||||||
|
$this->addEvent($instanceId, 'system', 'Process started.', $startNodeId, [], $userId);
|
||||||
|
|
||||||
|
if (!$inTransaction) { $this->pdo->commit(); }
|
||||||
|
return (int)$instanceId;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
if (!$inTransaction) { $this->pdo->rollBack(); }
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrCreateInstanceForSubject(int $processDefinitionId, string $subjectType, int $subjectId, int $userId, string $mode='resume_or_create', ?string $cycleKey=null): array {
|
||||||
|
$stmt_def = $this->pdo->prepare("SELECT code, is_active FROM process_definitions WHERE id = ?");
|
||||||
|
$stmt_def->execute([$processDefinitionId]);
|
||||||
|
$definition = $stmt_def->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$definition) {
|
||||||
|
throw new WorkflowNotFoundException("Process definition #$processDefinitionId not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = $definition['code'];
|
||||||
|
$instance = null;
|
||||||
|
|
||||||
|
if ($mode !== 'create_new_run') {
|
||||||
|
$instance = $this->getActiveInstanceForSubject($code, $subjectType, $subjectId, $cycleKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$instance) {
|
||||||
|
if (empty($definition['is_active'])) {
|
||||||
|
throw new WorkflowNotAllowedException("Process is not active and cannot be started.");
|
||||||
|
}
|
||||||
|
$instanceId = $this->startProcessForSubject($code, $subjectType, $subjectId, $userId, $mode, $cycleKey);
|
||||||
|
$stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE id = ?");
|
||||||
|
$stmt->execute([$instanceId]);
|
||||||
|
$instance = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActiveInstanceForSubject(string $processCode, string $subjectType, int $subjectId, ?string $cycleKey=null): ?array {
|
||||||
|
$sql = "
|
||||||
|
SELECT pi.*
|
||||||
|
FROM process_instances pi
|
||||||
|
JOIN process_definitions pd ON pi.process_definition_id = pd.id
|
||||||
|
WHERE pi.subject_type = ? AND pi.subject_id = ? AND pd.code = ?
|
||||||
|
";
|
||||||
|
$params = [$subjectType, $subjectId, $processCode];
|
||||||
|
|
||||||
|
if ($cycleKey !== null) {
|
||||||
|
$sql .= " AND pi.cycle_key = ?";
|
||||||
|
$params[] = $cycleKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= " ORDER BY pi.last_activity_at DESC, pi.id DESC LIMIT 1";
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$instance = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
return $instance ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInstancesHistoryForSubject(string $processCode, string $subjectType, int $subjectId, int $limit=5): array {
|
||||||
|
$sql = "
|
||||||
|
SELECT pi.*
|
||||||
|
FROM process_instances pi
|
||||||
|
JOIN process_definitions pd ON pi.process_definition_id = pd.id
|
||||||
|
WHERE pi.subject_type = ? AND pi.subject_id = ? AND pd.code = ?
|
||||||
|
ORDER BY pi.last_activity_at DESC, pi.id DESC
|
||||||
|
LIMIT ?
|
||||||
|
";
|
||||||
|
$stmt = $this->pdo->prepare($sql);
|
||||||
|
$stmt->bindValue(1, $subjectType, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(2, $subjectId, PDO::PARAM_INT);
|
||||||
|
$stmt->bindValue(3, $processCode, PDO::PARAM_STR);
|
||||||
|
$stmt->bindValue(4, $limit, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
|
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -25,38 +25,51 @@ if (!$process_definition_id && $process_code) {
|
|||||||
$process_definition_id = $stmt->fetchColumn();
|
$process_definition_id = $stmt->fetchColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$person_id || !$process_definition_id) {
|
if (!$subject_id || !$process_definition_id) {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
echo json_encode(['error' => 'Missing person_id or process_id']);
|
echo json_encode(['error' => 'Missing subject_id or process_id']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$userId = $_SESSION['user_id'];
|
$userId = $_SESSION['user_id'];
|
||||||
$engine = new WorkflowEngine();
|
$engine = new WorkflowEngine();
|
||||||
|
|
||||||
// Fetch Person and Process Definition details first
|
// Fetch Subject and Process Definition details first
|
||||||
$stmt_person = $pdo->prepare("SELECT first_name, last_name FROM people WHERE id = ?");
|
$subjectName = 'Unknown';
|
||||||
$stmt_person->execute([$person_id]);
|
if ($subject_type === 'person') {
|
||||||
$person = $stmt_person->fetch();
|
$stmt_person = $pdo->prepare("SELECT first_name, last_name FROM people WHERE id = ?");
|
||||||
|
$stmt_person->execute([$subject_id]);
|
||||||
|
$person = $stmt_person->fetch();
|
||||||
|
if ($person) {
|
||||||
|
$subjectName = $person['first_name'] . ' ' . $person['last_name'];
|
||||||
|
}
|
||||||
|
} elseif ($subject_type === 'meeting') {
|
||||||
|
$stmt_meeting = $pdo->prepare("SELECT m.meeting_datetime, bg.name as group_name FROM meetings m JOIN bni_groups bg ON m.bni_group_id = bg.id WHERE m.id = ?");
|
||||||
|
$stmt_meeting->execute([$subject_id]);
|
||||||
|
$meeting = $stmt_meeting->fetch();
|
||||||
|
if ($meeting) {
|
||||||
|
$subjectName = $meeting['group_name'] . ' - ' . date('d.m.Y', strtotime($meeting['meeting_datetime']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$stmt_process = $pdo->prepare("SELECT * FROM process_definitions WHERE id = ?");
|
$stmt_process = $pdo->prepare("SELECT * FROM process_definitions WHERE id = ?");
|
||||||
$stmt_process->execute([$process_definition_id]);
|
$stmt_process->execute([$process_definition_id]);
|
||||||
$process = $stmt_process->fetch();
|
$process = $stmt_process->fetch();
|
||||||
|
|
||||||
if (!$person || !$process) {
|
if (!$process) {
|
||||||
http_response_code(404);
|
http_response_code(404);
|
||||||
echo "<p class='text-danger'>Could not find person or process.</p>";
|
echo "<p class='text-danger'>Could not find process.</p>";
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find an existing instance
|
// Try to find an existing instance
|
||||||
$instance = $engine->getInstanceByDefId($person_id, $process_definition_id);
|
$instance = $engine->getActiveInstanceForSubject($process['code'], $subject_type, $subject_id);
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!-- Title for the modal, to be grabbed by JS -->
|
<!-- Title for the modal, to be grabbed by JS -->
|
||||||
<div id="instance-modal-title" class="d-none">
|
<div id="instance-modal-title" class="d-none">
|
||||||
<?= htmlspecialchars($person['first_name']." ".$person['last_name']) ?> - <?= htmlspecialchars($process['name']) ?>
|
<?= htmlspecialchars($subjectName) ?> - <?= htmlspecialchars($process['name']) ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($instance): // INSTANCE EXISTS ?>
|
<?php if ($instance): // INSTANCE EXISTS ?>
|
||||||
@ -70,7 +83,7 @@ $instance = $engine->getInstanceByDefId($person_id, $process_definition_id);
|
|||||||
<strong><?= t('modal.process_completed', 'Proces zakończony.') ?></strong>
|
<strong><?= t('modal.process_completed', 'Proces zakończony.') ?></strong>
|
||||||
Obecny status: <?= htmlspecialchars($instance['current_status']) ?>.
|
Obecny status: <?= htmlspecialchars($instance['current_status']) ?>.
|
||||||
</div>
|
</div>
|
||||||
<button id="restartProcessBtn" class="btn btn-sm btn-primary" data-person-id="<?= $person_id ?>" data-process-code="<?= htmlspecialchars($process['code']) ?>" data-mode="create_new_run">
|
<button id="restartProcessBtn" class="btn btn-sm btn-primary" data-subject-type="<?= $subject_type ?>" data-subject-id="<?= $subject_id ?>" data-person-id="<?= $person_id ?>" data-process-code="<?= htmlspecialchars($process['code']) ?>" data-mode="create_new_run">
|
||||||
<?= t('modal.start_new_instance', 'Rozpocznij od nowa') ?>
|
<?= t('modal.start_new_instance', 'Rozpocznij od nowa') ?>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -266,14 +279,17 @@ $instance = $engine->getInstanceByDefId($person_id, $process_definition_id);
|
|||||||
|
|
||||||
<?php else: // NO INSTANCE EXISTS ?>
|
<?php else: // NO INSTANCE EXISTS ?>
|
||||||
<?php
|
<?php
|
||||||
$eligibility = $engine->checkEligibility($person_id, $process_definition_id);
|
$eligibility = ['is_eligible' => true, 'reasons' => []];
|
||||||
|
if ($subject_type === 'person') {
|
||||||
|
$eligibility = $engine->checkEligibility($subject_id, $process_definition_id);
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<?php if ($eligibility['is_eligible']): ?>
|
<?php if ($eligibility['is_eligible']): ?>
|
||||||
<h4><?= t('modal.process_not_started', 'Proces nie został uruchomiony') ?></h4>
|
<h4><?= t('modal.process_not_started', 'Proces nie został uruchomiony') ?></h4>
|
||||||
<p>This process has not been started for this person.</p>
|
<p>This process has not been started for this person.</p>
|
||||||
<button id="startProcessBtn" class="btn btn-primary" data-person-id="<?= $person_id ?>" data-process-id="<?= $process_definition_id ?>">
|
<button id="startProcessBtn" class="btn btn-primary" data-subject-type="<?= $subject_type ?>" data-subject-id="<?= $subject_id ?>" data-person-id="<?= $person_id ?>" data-process-id="<?= $process_definition_id ?>">
|
||||||
<?= t('modal.start_process', 'Uruchom proces') ?>
|
<?= t('modal.start_process', 'Uruchom proces') ?>
|
||||||
</button>
|
</button>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
|||||||
@ -19,6 +19,8 @@ if (!isset($_SESSION['user_id'])) {
|
|||||||
|
|
||||||
$userId = $_SESSION['user_id'];
|
$userId = $_SESSION['user_id'];
|
||||||
$personId = filter_input(INPUT_POST, 'person_id', FILTER_VALIDATE_INT);
|
$personId = filter_input(INPUT_POST, 'person_id', FILTER_VALIDATE_INT);
|
||||||
|
$subjectType = filter_input(INPUT_POST, 'subject_type', FILTER_SANITIZE_STRING) ?: 'person';
|
||||||
|
$subjectId = filter_input(INPUT_POST, 'subject_id', FILTER_VALIDATE_INT) ?: $personId;
|
||||||
$processDefinitionId = filter_input(INPUT_POST, 'process_id', FILTER_VALIDATE_INT);
|
$processDefinitionId = filter_input(INPUT_POST, 'process_id', FILTER_VALIDATE_INT);
|
||||||
$processCode = filter_input(INPUT_POST, 'process_code', FILTER_SANITIZE_STRING);
|
$processCode = filter_input(INPUT_POST, 'process_code', FILTER_SANITIZE_STRING);
|
||||||
$deleteExisting = filter_input(INPUT_POST, 'delete_existing');
|
$deleteExisting = filter_input(INPUT_POST, 'delete_existing');
|
||||||
@ -32,23 +34,32 @@ if (!$processDefinitionId && $processCode) {
|
|||||||
$processDefinitionId = $stmt->fetchColumn();
|
$processDefinitionId = $stmt->fetchColumn();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$personId || !$processDefinitionId) {
|
if (!$subjectId || !$processDefinitionId) {
|
||||||
throw new InvalidArgumentException('Invalid or missing person_id or process_id/process_code.');
|
throw new InvalidArgumentException('Invalid or missing subject_id or process_id/process_code.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$engine = new WorkflowEngine();
|
$engine = new WorkflowEngine();
|
||||||
|
|
||||||
|
|
||||||
|
$stmt_def = $pdo->prepare("SELECT code FROM process_definitions WHERE id = ?");
|
||||||
|
$stmt_def->execute([$processDefinitionId]);
|
||||||
|
$code = $stmt_def->fetchColumn() ?: $processCode;
|
||||||
|
|
||||||
if($deleteExisting === '1') {
|
if($deleteExisting === '1') {
|
||||||
$instance = $engine->getInstanceByDefId($personId, $processDefinitionId);
|
$instance = $engine->getActiveInstanceForSubject($code, $subjectType, $subjectId);
|
||||||
if ($instance) {
|
if ($instance) {
|
||||||
$engine->deleteInstance($instance['id']);
|
$engine->deleteInstance($instance['id']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($mode === 'create_new_run' || filter_input(INPUT_POST, 'force', FILTER_VALIDATE_INT) === 1) {
|
if ($mode === 'create_new_run' || filter_input(INPUT_POST, 'force', FILTER_VALIDATE_INT) === 1) {
|
||||||
$instance = $engine->createNewInstance($personId, $processDefinitionId, $userId);
|
// start new process
|
||||||
|
$instanceId = $engine->startProcessForSubject($code, $subjectType, $subjectId, $userId, 'create_new_run');
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM process_instances WHERE id = ?");
|
||||||
|
$stmt->execute([$instanceId]);
|
||||||
|
$instance = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
} else {
|
} else {
|
||||||
$instance = $engine->getOrCreateInstanceByDefId($personId, $processDefinitionId, $userId);
|
$instance = $engine->getOrCreateInstanceForSubject($processDefinitionId, $subjectType, $subjectId, $userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($instance) {
|
if ($instance) {
|
||||||
|
|||||||
66
db/migrations/037_add_process_subjects.php
Normal file
66
db/migrations/037_add_process_subjects.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function migrate_037($pdo) {
|
||||||
|
echo "Starting migration 037 (Process Subjects)...
|
||||||
|
";
|
||||||
|
|
||||||
|
// 1. process_instances: add subject_type, subject_id, cycle_key, bni_group_id
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM process_instances LIKE 'subject_type'");
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE process_instances ADD COLUMN subject_type VARCHAR(20) NOT NULL DEFAULT 'person' AFTER person_id");
|
||||||
|
echo "Added subject_type to process_instances.
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM process_instances LIKE 'subject_id'");
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE process_instances ADD COLUMN subject_id INT NOT NULL DEFAULT 0 AFTER subject_type");
|
||||||
|
echo "Added subject_id to process_instances.
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM process_instances LIKE 'cycle_key'");
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE process_instances ADD COLUMN cycle_key VARCHAR(64) NULL AFTER subject_id");
|
||||||
|
echo "Added cycle_key to process_instances.
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM process_instances LIKE 'bni_group_id'");
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE process_instances ADD COLUMN bni_group_id INT NULL AFTER cycle_key");
|
||||||
|
echo "Added bni_group_id to process_instances.
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Data migration
|
||||||
|
echo "Migrating existing data...
|
||||||
|
";
|
||||||
|
$pdo->exec("UPDATE process_instances SET subject_type = 'person', subject_id = person_id WHERE subject_id = 0");
|
||||||
|
|
||||||
|
// 3. Add indexes
|
||||||
|
$stmt = $pdo->query("SHOW INDEX FROM process_instances WHERE Key_name = 'idx_subject_active'");
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE process_instances ADD INDEX idx_subject_active (subject_type, subject_id, current_status)");
|
||||||
|
echo "Added index idx_subject_active.
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SHOW INDEX FROM process_instances WHERE Key_name = 'idx_subject_cycle'");
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE process_instances ADD INDEX idx_subject_cycle (subject_type, subject_id, cycle_key)");
|
||||||
|
echo "Added index idx_subject_cycle.
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SHOW INDEX FROM process_instances WHERE Key_name = 'idx_group'");
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE process_instances ADD INDEX idx_group (bni_group_id)");
|
||||||
|
echo "Added index idx_group.
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Migration 037 completed.
|
||||||
|
";
|
||||||
|
}
|
||||||
66
index.php
66
index.php
@ -120,7 +120,9 @@ $status_colors = [
|
|||||||
<?php
|
<?php
|
||||||
// Define process groups
|
// Define process groups
|
||||||
// Show all processes from the DB directly.
|
// Show all processes from the DB directly.
|
||||||
$inne_procesy_cols = $processes;
|
$inne_procesy_cols = array_filter($processes, function($p) {
|
||||||
|
return $p['code'] !== 'meeting_preparation';
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
@ -143,16 +145,25 @@ $status_colors = [
|
|||||||
<tr class="text-center">
|
<tr class="text-center">
|
||||||
<th rowspan="2" class="align-middle"><input type="checkbox" id="selectAll"></th>
|
<th rowspan="2" class="align-middle"><input type="checkbox" id="selectAll"></th>
|
||||||
<th rowspan="2" class="align-middle"><?= t('dashboard.person', 'Osoba') ?></th>
|
<th rowspan="2" class="align-middle"><?= t('dashboard.person', 'Osoba') ?></th>
|
||||||
|
<?php
|
||||||
|
$mp_def = null;
|
||||||
|
foreach ($definitions as $def) {
|
||||||
|
if ($def['code'] === 'meeting_preparation') {
|
||||||
|
$mp_def = $def;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
<?php if (!empty($spotkania_cols)): ?>
|
<?php if (!empty($spotkania_cols)): ?>
|
||||||
<th colspan="<?= count($spotkania_cols) ?>"><?= t('dashboard.meetings', 'Spotkania') ?></th>
|
<th colspan="<?= count($spotkania_cols) ?>"><?= t('dashboard.meetings', 'Spotkania') ?></th>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if (!empty($inne_procesy_cols)): ?>
|
<?php if (!empty($inne_procesy_cols)): ?>
|
||||||
<th colspan="<?= count($processes) ?>"><?= t('dashboard.other_processes', 'Inne procesy') ?></th>
|
<th colspan="<?= count($inne_procesy_cols) ?>"><?= t('dashboard.other_processes', 'Inne procesy') ?></th>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="text-center">
|
<tr class="text-center">
|
||||||
<?php foreach ($spotkania_cols as $col):
|
<?php foreach ($spotkania_cols as $col):
|
||||||
$isMeetingFilterActive = ($meetingFilterGroupId == $col['group_id'] && $meetingFilterDatetime == ($col['meetings'][0] ?? null));
|
$isMeetingFilterActive = ($meetingFilterGroupId == $col['group_id'] && $meetingFilterDatetime == ($col['meetings'][0]['datetime'] ?? null));
|
||||||
?>
|
?>
|
||||||
<th data-group-id="<?= $col['group_id'] ?>" data-meetings='<?= json_encode($col['meetings'] ?? []) ?>' class="<?= $isMeetingFilterActive ? 'active-filter' : '' ?>">
|
<th data-group-id="<?= $col['group_id'] ?>" data-meetings='<?= json_encode($col['meetings'] ?? []) ?>' class="<?= $isMeetingFilterActive ? 'active-filter' : '' ?>">
|
||||||
<a href="#" class="text-decoration-none text-dark meeting-filter-link">
|
<a href="#" class="text-decoration-none text-dark meeting-filter-link">
|
||||||
@ -161,13 +172,24 @@ $status_colors = [
|
|||||||
<div class="mx-2 text-center">
|
<div class="mx-2 text-center">
|
||||||
<?= htmlspecialchars($col['group_name']) ?><br>
|
<?= htmlspecialchars($col['group_name']) ?><br>
|
||||||
<small class="meeting-date"></small>
|
<small class="meeting-date"></small>
|
||||||
|
<?php if ($mp_def): ?>
|
||||||
|
<div class="mt-1 meeting-process-container" style="display:none;">
|
||||||
|
<span class="badge rounded-circle bg-secondary process-dot meeting-process-dot"
|
||||||
|
style="width: 15px; height: 15px; display: inline-block; cursor: pointer;"
|
||||||
|
data-bs-toggle="modal" data-bs-target="#instanceModal"
|
||||||
|
data-process-id="<?= $mp_def['id'] ?>"
|
||||||
|
data-subject-type="meeting"
|
||||||
|
data-subject-id=""
|
||||||
|
title="Przygotowanie spotkania"></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-outline-secondary meeting-nav-btn meeting-next-btn"><i class="bi bi-chevron-right"></i></button>
|
<button class="btn btn-sm btn-outline-secondary meeting-nav-btn meeting-next-btn"><i class="bi bi-chevron-right"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</th>
|
</th>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php foreach ($processes as $col): ?>
|
<?php foreach ($inne_procesy_cols as $col): ?>
|
||||||
<?php
|
<?php
|
||||||
$filterParams = $_GET;
|
$filterParams = $_GET;
|
||||||
$filterParams['active_process_id'] = $col['id'];
|
$filterParams['active_process_id'] = $col['id'];
|
||||||
@ -222,7 +244,7 @@ $status_colors = [
|
|||||||
|
|
||||||
// The meeting date will be determined by JS, we will add it to the data attribute
|
// The meeting date will be determined by JS, we will add it to the data attribute
|
||||||
// The initial meeting date is the first one in the list.
|
// The initial meeting date is the first one in the list.
|
||||||
$meeting_datetime = $col['meetings'][0] ?? '';
|
$meeting_datetime = $col['meetings'][0]['datetime'] ?? '';
|
||||||
|
|
||||||
$color = $status_colors[$status] ?? 'secondary';
|
$color = $status_colors[$status] ?? 'secondary';
|
||||||
?>
|
?>
|
||||||
@ -238,7 +260,7 @@ $status_colors = [
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
<?php // Inne Procesy Columns ?>
|
<?php // Inne Procesy Columns ?>
|
||||||
<?php foreach ($processes as $process):
|
<?php foreach ($inne_procesy_cols as $process):
|
||||||
$instance = $instances[$person['id']][$process['id']] ?? null;
|
$instance = $instances[$person['id']][$process['id']] ?? null;
|
||||||
$status = $instance ? $instance['computed_status'] : 'none';
|
$status = $instance ? $instance['computed_status'] : 'none';
|
||||||
$color = $status_colors[$status] ?? 'secondary';
|
$color = $status_colors[$status] ?? 'secondary';
|
||||||
@ -694,6 +716,10 @@ $status_colors = [
|
|||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
|
||||||
|
const meetingProcesses = <?= json_encode($meeting_processes ?? []) ?>;
|
||||||
|
const statusColors = <?= json_encode($status_colors ?? []) ?>;
|
||||||
|
|
||||||
|
|
||||||
const meetingModal = new bootstrap.Modal(document.getElementById('meetingAttendanceModal'));
|
const meetingModal = new bootstrap.Modal(document.getElementById('meetingAttendanceModal'));
|
||||||
const meetingForm = document.getElementById('meetingAttendanceForm');
|
const meetingForm = document.getElementById('meetingAttendanceForm');
|
||||||
const meetingPersonName = document.getElementById('meetingPersonName');
|
const meetingPersonName = document.getElementById('meetingPersonName');
|
||||||
@ -857,8 +883,26 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
|
|
||||||
function updateMeetingView() {
|
function updateMeetingView() {
|
||||||
if (meetings.length > 0) {
|
if (meetings.length > 0) {
|
||||||
const currentMeetingDate = meetings[currentIndex];
|
const currentMeetingObj = meetings[currentIndex];
|
||||||
|
const currentMeetingDate = typeof currentMeetingObj === 'object' ? currentMeetingObj.datetime : currentMeetingObj;
|
||||||
|
const currentMeetingId = typeof currentMeetingObj === 'object' ? currentMeetingObj.meeting_id : null;
|
||||||
headerCell.querySelector('.meeting-date').textContent = new Date(currentMeetingDate).toLocaleDateString('pl-PL', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
headerCell.querySelector('.meeting-date').textContent = new Date(currentMeetingDate).toLocaleDateString('pl-PL', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||||
|
|
||||||
|
const processContainer = headerCell.querySelector('.meeting-process-container');
|
||||||
|
if (processContainer && currentMeetingId) {
|
||||||
|
processContainer.style.display = 'block';
|
||||||
|
const dot = processContainer.querySelector('.meeting-process-dot');
|
||||||
|
if (dot) {
|
||||||
|
dot.dataset.subjectId = currentMeetingId;
|
||||||
|
const instance = meetingProcesses[currentMeetingId] && meetingProcesses[currentMeetingId]['meeting_preparation'];
|
||||||
|
const status = instance ? instance.computed_status : 'none';
|
||||||
|
const color = statusColors[status] || 'secondary';
|
||||||
|
dot.className = `badge rounded-circle bg-${color} process-dot meeting-process-dot`;
|
||||||
|
dot.dataset.processId = instance ? instance.id : '';
|
||||||
|
}
|
||||||
|
} else if (processContainer) {
|
||||||
|
processContainer.style.display = 'none';
|
||||||
|
}
|
||||||
fetchAndUpdateAllDotsForGroup(groupId, currentMeetingDate);
|
fetchAndUpdateAllDotsForGroup(groupId, currentMeetingDate);
|
||||||
} else {
|
} else {
|
||||||
headerCell.querySelector('.meeting-date').textContent = 'Brak';
|
headerCell.querySelector('.meeting-date').textContent = 'Brak';
|
||||||
@ -923,12 +967,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
var instanceModal = document.getElementById('instanceModal');
|
var instanceModal = document.getElementById('instanceModal');
|
||||||
instanceModal.addEventListener('show.bs.modal', function (event) {
|
instanceModal.addEventListener('show.bs.modal', function (event) {
|
||||||
var button = event.relatedTarget;
|
var button = event.relatedTarget;
|
||||||
var personId = button.dataset.personId;
|
var personId = button.dataset.personId || '';
|
||||||
var processId = button.dataset.processId;
|
var processId = button.dataset.processId;
|
||||||
|
var subjectType = button.dataset.subjectType || 'person';
|
||||||
|
var subjectId = button.dataset.subjectId || personId;
|
||||||
var modalBody = instanceModal.querySelector('.modal-body');
|
var modalBody = instanceModal.querySelector('.modal-body');
|
||||||
|
|
||||||
// Load content via AJAX
|
// Load content via AJAX
|
||||||
fetch(`_get_instance_details.php?person_id=${personId}&process_id=${processId}`)
|
fetch(`_get_instance_details.php?person_id=${personId}&subject_type=${subjectType}&subject_id=${subjectId}&process_id=${processId}`)
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(html => {
|
.then(html => {
|
||||||
modalBody.innerHTML = html;
|
modalBody.innerHTML = html;
|
||||||
@ -1459,6 +1505,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
if(button && button.dataset) {
|
if(button && button.dataset) {
|
||||||
if (button.dataset.personId) instanceModalElement.dataset.lastPersonId = button.dataset.personId;
|
if (button.dataset.personId) instanceModalElement.dataset.lastPersonId = button.dataset.personId;
|
||||||
if (button.dataset.processId) instanceModalElement.dataset.lastProcessId = button.dataset.processId;
|
if (button.dataset.processId) instanceModalElement.dataset.lastProcessId = button.dataset.processId;
|
||||||
|
if (button.dataset.subjectType) instanceModalElement.dataset.lastSubjectType = button.dataset.subjectType;
|
||||||
|
if (button.dataset.subjectId) instanceModalElement.dataset.lastSubjectId = button.dataset.subjectId;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,71 +0,0 @@
|
|||||||
import re
|
|
||||||
|
|
||||||
with open('WorkflowEngine.php', 'r') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
content = content.replace(
|
|
||||||
'SELECT id, name, definition_json, is_active FROM process_definitions WHERE is_active = 1 ORDER BY sort_order, name',
|
|
||||||
'SELECT id, code, name, definition_json, is_active FROM process_definitions WHERE is_active = 1 AND is_latest = 1 ORDER BY sort_order, name'
|
|
||||||
)
|
|
||||||
|
|
||||||
content = content.replace(
|
|
||||||
"'name' => $def['name'],",
|
|
||||||
"'code' => $def['code'],\n 'name' => $def['name'],"
|
|
||||||
)
|
|
||||||
|
|
||||||
content = content.replace(
|
|
||||||
'$stmt_instances = $this->pdo->prepare("SELECT * FROM process__instances WHERE person_id IN ($placeholders)");\n $stmt_instances->execute($person_ids);\n $instances_data = $stmt_instances->fetchAll(PDO::FETCH_ASSOC);',
|
|
||||||
"""
|
|
||||||
$stmt_instances = $this->pdo->prepare(
|
|
||||||
SELECT pi.*, pd.code as process_code, pd.id as definition_actual_id
|
|
||||||
FROM process_instances pi
|
|
||||||
JOIN process_definitions pd ON pi.process_definition_id = pd.id
|
|
||||||
WHERE pi.person_id IN ($placeholders)
|
|
||||||
ORDER BY pi.last_activity_at DESC, pi.id DESC
|
|
||||||
");
|
|
||||||
$stmt_instances->execute($person_ids);
|
|
||||||
$instances_data = $stmt_instances->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
// Map code to latest definition id
|
|
||||||
$codeToIdMap = [];
|
|
||||||
foreach ($definitions as $defId => $def) {
|
|
||||||
$codeToIdMap[$def['code']] = $defId;
|
|
||||||
}""")
|
|
||||||
|
|
||||||
content = content.replace(
|
|
||||||
"$instances[$instance['person_id']][$def_id] = $enriched_instance;",
|
|
||||||
"""
|
|
||||||
// Use process_code to map to the latest active column
|
|
||||||
$code = $instance['process_code'] ?? null;
|
|
||||||
if ($code && isset($codeToIdMap[$code])) {
|
|
||||||
$latestDefId = $codeToIdMap[$code];
|
|
||||||
// Only keep the most recently active instance per code (since ordered by last_activity_at DESC)
|
|
||||||
if (!isset($instances[$instance['person_id']][$latestDefId])) {
|
|
||||||
$instances[$instance['person_id']][$latestDefId] = $enriched_instance;
|
|
||||||
}
|
|
||||||
}""")
|
|
||||||
|
|
||||||
content = re.sub(
|
|
||||||
r"public function getInstanceByDefId\(int \$personId, int \$processDefinitionId\): \?array \{.*?return \$instance \?: null;\s*\}",
|
|
||||||
"""public function getInstanceByDefId(int $personId, int $processDefinitionId): ?array {
|
|
||||||
$stmt_code = $this->pdo->prepare("SELECT code FROM process_definitions WHERE id = ?");
|
|
||||||
$stmt_code->execute([$processDefinitionId]);
|
|
||||||
$code = $stmt_code->fetchColumn();
|
|
||||||
|
|
||||||
if (!$code) return null;
|
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare("\n SELECT pi.* \n FROM process_instances pi\n JOIN process_definitions pd ON pi.process_definition_id = pd.id\n WHERE pi.person_id = ? AND pd.code = ?\n ORDER BY pi.last_activity_at DESC, pi.id DESC\n LIMIT 1\n ");
|
|
||||||
$stmt->execute([$personId, $code]);
|
|
||||||
$instance = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
return $instance ?: null;
|
|
||||||
}""",
|
|
||||||
content, flags=re.DOTALL
|
|
||||||
)
|
|
||||||
|
|
||||||
# Wait, checkEligibility needs to use the NEW version definition ID?
|
|
||||||
# Actually checkEligibility uses $processDefinitionId which IS the new version's ID!
|
|
||||||
|
|
||||||
with open('WorkflowEngine.php', 'w') as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
print("Patched.")
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import re
|
|
||||||
|
|
||||||
with open('WorkflowEngine.php', 'r') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
new_method = """
|
|
||||||
public function createNewInstance(int $personId, int $processDefinitionId, int $userId, array $context = []): 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_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);
|
|
||||||
|
|
||||||
if (!$definition) {
|
|
||||||
throw new WorkflowNotFoundException("Process definition #$processDefinitionId not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($definition['is_active'])) {
|
|
||||||
throw new WorkflowNotAllowedException("Process is not active and cannot be started.");
|
|
||||||
}
|
|
||||||
|
|
||||||
$eligibility = $this->checkEligibility($personId, $processDefinitionId, $context);
|
|
||||||
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) : [];
|
|
||||||
$start_node = $definition_json['start_node_id'] ?? 'start';
|
|
||||||
$initial_data_map = $definition_json['initial_data'] ?? [];
|
|
||||||
$data_json = !empty($initial_data_map) ? json_encode($initial_data_map) : null;
|
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare(
|
|
||||||
"INSERT INTO process_instances (person_id, process_definition_id, current_node_id, current_status, data_json, last_activity_at) VALUES (?, ?, ?, 'in_progress', ?, NOW())"
|
|
||||||
);
|
|
||||||
$stmt->execute([$personId, $processDefinitionId, $start_node, $data_json]);
|
|
||||||
$newInstanceId = $this->pdo->lastInsertId();
|
|
||||||
|
|
||||||
$this->logEvent($newInstanceId, 'process_started', 'Process started', $start_node, ['context' => $context], $userId);
|
|
||||||
|
|
||||||
$stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE id = ?");
|
|
||||||
$stmt->execute([$newInstanceId]);
|
|
||||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
}
|
|
||||||
"
|
|
||||||
|
|
||||||
content = content.replace(
|
|
||||||
'public function getOrCreateInstanceByDefId(int $personId, int $processDefinitionId, int $userId, array $context = []): ?array {',
|
|
||||||
new_method + '\n public function getOrCreateInstanceByDefId(int $personId, int $processDefinitionId, int $userId, array $context = []): ?array {'
|
|
||||||
)
|
|
||||||
|
|
||||||
with open('WorkflowEngine.php', 'w') as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
print("Patched.")
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import re
|
|
||||||
|
|
||||||
with open('_get_instance_details.php', 'r', encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
pattern = r'<button id="startNewProcessBtn"([^>]+)>.*?</button>\s*</div>\s*<script>.*?</script>'
|
|
||||||
replacement = r'''<button id="restartProcessBtn" class="btn btn-sm btn-primary" data-person-id="<?= $person_id ?>" data-process-code="<?= htmlspecialchars($process['code']) ?>" data-mode="create_new_run">
|
|
||||||
<?= t('modal.start_new_instance', 'Rozpocznij od nowa') ?>
|
|
||||||
</button>
|
|
||||||
</div>'''
|
|
||||||
|
|
||||||
new_content, count = re.subn(pattern, replacement, content, flags=re.DOTALL)
|
|
||||||
|
|
||||||
with open('_get_instance_details.php', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(new_content)
|
|
||||||
|
|
||||||
print(f"Replaced {count} instances.")
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
import re
|
|
||||||
|
|
||||||
with open('_save_process_definition.php', 'r') as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
old_update_logic = """ if (empty($processId)) {
|
|
||||||
// Create new process
|
|
||||||
$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active) VALUES (?, ?, ?, ?, 1)';
|
|
||||||
$params = [$name, $code, $definition_json, $start_node];
|
|
||||||
$message = 'Process created successfully.';
|
|
||||||
} else {
|
|
||||||
// Update existing process
|
|
||||||
$is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : 0;
|
|
||||||
$sql = 'UPDATE process_definitions SET name = ?, definition_json = ?, start_node_id = ?, is_active = ? WHERE id = ?';
|
|
||||||
$params = [$name, $definition_json, $start_node, $is_active, $processId];
|
|
||||||
$message = 'Process updated successfully.';
|
|
||||||
}
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute($params);"""
|
|
||||||
|
|
||||||
new_update_logic = """ if (empty($processId)) {
|
|
||||||
// Create new process
|
|
||||||
$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, is_latest) VALUES (?, ?, ?, ?, 1, 1, 1)';
|
|
||||||
$params = [$name, $code, $definition_json, $start_node];
|
|
||||||
$message = 'Process created successfully.';
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute($params);
|
|
||||||
} else {
|
|
||||||
// "Update" existing process by creating a new version
|
|
||||||
$stmt_old = $pdo->prepare('SELECT code, version, sort_order, is_active FROM process_definitions WHERE id = ?');
|
|
||||||
$stmt_old->execute([$processId]);
|
|
||||||
$old = $stmt_old->fetch();
|
|
||||||
|
|
||||||
if ($old) {
|
|
||||||
$is_active = isset($_POST['is_active']) ? (int)$_POST['is_active'] : $old['is_active'];
|
|
||||||
$new_version = $old['version'] + 1;
|
|
||||||
$db_code = $old['code'];
|
|
||||||
|
|
||||||
// Mark all previous versions as not latest
|
|
||||||
$stmt_update = $pdo->prepare('UPDATE process_definitions SET is_latest = 0 WHERE code = ?');
|
|
||||||
$stmt_update->execute([$db_code]);
|
|
||||||
|
|
||||||
// Insert new version
|
|
||||||
$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, supersedes_definition_id, is_latest, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?)';
|
|
||||||
$params = [$name, $db_code, $definition_json, $start_node, $is_active, $new_version, $processId, $old['sort_order']];
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute($params);
|
|
||||||
$message = 'Process updated successfully (new version created).';
|
|
||||||
} else {
|
|
||||||
throw new WorkflowRuleFailedException('Process not found.');
|
|
||||||
}
|
|
||||||
}"""
|
|
||||||
|
|
||||||
content = content.replace(old_update_logic, new_update_logic)
|
|
||||||
|
|
||||||
with open('_save_process_definition.php', 'w') as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
print("Patched.")
|
|
||||||
Loading…
x
Reference in New Issue
Block a user