diff --git a/WorkflowEngine.php b/WorkflowEngine.php index a1c5e48..712b0c2 100644 --- a/WorkflowEngine.php +++ b/WorkflowEngine.php @@ -173,13 +173,50 @@ class WorkflowEngine { $stmt_meetings->execute(['today' => $today]); $upcoming_meetings_flat = $stmt_meetings->fetchAll(PDO::FETCH_ASSOC); - $spotkania_cols = []; + +$spotkania_cols = []; + $meeting_ids = []; 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_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 [ 'people' => $people, @@ -188,7 +225,8 @@ class WorkflowEngine { 'all_functions' => $all_functions, 'person_functions_map' => $person_functions_map, '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( - "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(); // 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; $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(); $this->logEvent($newInstanceId, 'process_started', 'Process started', $start_node, ['context' => $context], $userId); @@ -990,4 +1028,129 @@ class WorkflowEngine { return $stmt->fetchAll(PDO::FETCH_ASSOC); } -} \ No newline at end of file + + 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); + } +} diff --git a/_get_instance_details.php b/_get_instance_details.php index cf8d738..38c3305 100644 --- a/_get_instance_details.php +++ b/_get_instance_details.php @@ -25,38 +25,51 @@ if (!$process_definition_id && $process_code) { $process_definition_id = $stmt->fetchColumn(); } -if (!$person_id || !$process_definition_id) { +if (!$subject_id || !$process_definition_id) { 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; } $userId = $_SESSION['user_id']; $engine = new WorkflowEngine(); -// Fetch Person and Process Definition details first -$stmt_person = $pdo->prepare("SELECT first_name, last_name FROM people WHERE id = ?"); -$stmt_person->execute([$person_id]); -$person = $stmt_person->fetch(); +// Fetch Subject and Process Definition details first +$subjectName = 'Unknown'; +if ($subject_type === 'person') { + $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->execute([$process_definition_id]); $process = $stmt_process->fetch(); -if (!$person || !$process) { +if (!$process) { http_response_code(404); - echo "

Could not find person or process.

"; + echo "

Could not find process.

"; exit; } // Try to find an existing instance -$instance = $engine->getInstanceByDefId($person_id, $process_definition_id); +$instance = $engine->getActiveInstanceForSubject($process['code'], $subject_type, $subject_id); ?>
- - + -
@@ -70,7 +83,7 @@ $instance = $engine->getInstanceByDefId($person_id, $process_definition_id); Obecny status: . - @@ -266,14 +279,17 @@ $instance = $engine->getInstanceByDefId($person_id, $process_definition_id); checkEligibility($person_id, $process_definition_id); + $eligibility = ['is_eligible' => true, 'reasons' => []]; + if ($subject_type === 'person') { + $eligibility = $engine->checkEligibility($subject_id, $process_definition_id); + } ?>

This process has not been started for this person.

- diff --git a/_init_single_instance.php b/_init_single_instance.php index ba9e914..09083dc 100644 --- a/_init_single_instance.php +++ b/_init_single_instance.php @@ -19,6 +19,8 @@ if (!isset($_SESSION['user_id'])) { $userId = $_SESSION['user_id']; $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); $processCode = filter_input(INPUT_POST, 'process_code', FILTER_SANITIZE_STRING); $deleteExisting = filter_input(INPUT_POST, 'delete_existing'); @@ -32,23 +34,32 @@ if (!$processDefinitionId && $processCode) { $processDefinitionId = $stmt->fetchColumn(); } -if (!$personId || !$processDefinitionId) { - throw new InvalidArgumentException('Invalid or missing person_id or process_id/process_code.'); +if (!$subjectId || !$processDefinitionId) { + throw new InvalidArgumentException('Invalid or missing subject_id or process_id/process_code.'); } $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') { - $instance = $engine->getInstanceByDefId($personId, $processDefinitionId); + $instance = $engine->getActiveInstanceForSubject($code, $subjectType, $subjectId); if ($instance) { $engine->deleteInstance($instance['id']); } } 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 { - $instance = $engine->getOrCreateInstanceByDefId($personId, $processDefinitionId, $userId); + $instance = $engine->getOrCreateInstanceForSubject($processDefinitionId, $subjectType, $subjectId, $userId); } if ($instance) { diff --git a/db/migrations/037_add_process_subjects.php b/db/migrations/037_add_process_subjects.php new file mode 100644 index 0000000..8873887 --- /dev/null +++ b/db/migrations/037_add_process_subjects.php @@ -0,0 +1,66 @@ +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. +"; +} \ No newline at end of file diff --git a/index.php b/index.php index 5500967..a279a9e 100644 --- a/index.php +++ b/index.php @@ -120,7 +120,9 @@ $status_colors = [ @@ -143,16 +145,25 @@ $status_colors = [ + - + ' class=""> @@ -161,13 +172,24 @@ $status_colors = [

+ + +
- + @@ -238,7 +260,7 @@ $status_colors = [ - document.addEventListener('DOMContentLoaded', function () { + const meetingProcesses = ; + const statusColors = ; + + const meetingModal = new bootstrap.Modal(document.getElementById('meetingAttendanceModal')); const meetingForm = document.getElementById('meetingAttendanceForm'); const meetingPersonName = document.getElementById('meetingPersonName'); @@ -857,8 +883,26 @@ document.addEventListener('DOMContentLoaded', function () { function updateMeetingView() { 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' }); + + 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); } else { headerCell.querySelector('.meeting-date').textContent = 'Brak'; @@ -923,12 +967,14 @@ document.addEventListener('DOMContentLoaded', function () { var instanceModal = document.getElementById('instanceModal'); instanceModal.addEventListener('show.bs.modal', function (event) { var button = event.relatedTarget; - var personId = button.dataset.personId; + var personId = button.dataset.personId || ''; var processId = button.dataset.processId; + var subjectType = button.dataset.subjectType || 'person'; + var subjectId = button.dataset.subjectId || personId; var modalBody = instanceModal.querySelector('.modal-body'); // 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(html => { modalBody.innerHTML = html; @@ -1459,6 +1505,8 @@ document.addEventListener('DOMContentLoaded', function () { if(button && button.dataset) { if (button.dataset.personId) instanceModalElement.dataset.lastPersonId = button.dataset.personId; 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; } }); } diff --git a/patch_engine.py b/patch_engine.py deleted file mode 100644 index 5fe00bd..0000000 --- a/patch_engine.py +++ /dev/null @@ -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.") \ No newline at end of file diff --git a/patch_init.py b/patch_init.py deleted file mode 100644 index f66e74a..0000000 --- a/patch_init.py +++ /dev/null @@ -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.") diff --git a/patch_modal.py b/patch_modal.py deleted file mode 100644 index b6cfaab..0000000 --- a/patch_modal.py +++ /dev/null @@ -1,17 +0,0 @@ -import re - -with open('_get_instance_details.php', 'r', encoding='utf-8') as f: - content = f.read() - -pattern = r'\s*\s*' -replacement = r''' - ''' - -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.") \ No newline at end of file diff --git a/patch_save.py b/patch_save.py deleted file mode 100644 index 4a7bf65..0000000 --- a/patch_save.py +++ /dev/null @@ -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.")