0."); } if (empty($processCode)) { throw new \InvalidArgumentException("processCode cannot be empty."); } $inTransaction = $this->pdo->inTransaction(); if (!$inTransaction) { $this->pdo->beginTransaction(); } try { $sql = "SELECT * FROM process_definitions WHERE code = ? AND is_active = 1 AND subject_scope = ? ORDER BY version DESC, id DESC LIMIT 1"; $stmt = $this->pdo->prepare($sql); $stmt->execute([$processCode, $subjectType]); $definition = $stmt->fetch(\PDO::FETCH_ASSOC); if (!$definition) { $stmt = $this->pdo->prepare("SELECT * FROM process_definitions WHERE code = ? AND is_active = 1 ORDER BY version DESC, id DESC LIMIT 1"); $stmt->execute([$processCode]); $definition = $stmt->fetch(\PDO::FETCH_ASSOC); if (!$definition) { throw new WorkflowNotFoundException("No active process definition found for code '$processCode'"); } } $defId = $definition['id']; $defJson = json_decode($definition['definition_json'], true); $startNodeId = $defJson['start_node_id'] ?? null; if (!$startNodeId) { throw new \Exception("Process definition missing start_node_id"); } $instance = null; if ($mode === 'resume_or_create') { $stmt = $this->pdo->prepare( "SELECT pi.* FROM process_instances pi\n JOIN process_definitions pd ON pi.process_definition_id = pd.id\n WHERE pi.subject_type = ? AND pi.subject_id = ? AND pd.code = ?\n ORDER BY pi.id DESC LIMIT 1" ); $stmt->execute([$subjectType, $subjectId, $processCode]); $instance = $stmt->fetch(\PDO::FETCH_ASSOC); } if (!$instance) { $stmt = $this->pdo->prepare( "SELECT MAX(pi.run_number) 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 = ?" ); $stmt->execute([$subjectType, $subjectId, $processCode]); $maxRun = (int)$stmt->fetchColumn(); $runNumber = $maxRun + 1; $personIdVal = ($subjectType === 'person') ? $subjectId : null; $status = $defJson['nodes'][$startNodeId]['ui_hints']['status'] ?? 'in_progress'; $reason = $defJson['nodes'][$startNodeId]['ui_hints']['reason'] ?? ''; $nextStep = $defJson['nodes'][$startNodeId]['ui_hints']['next_step'] ?? ''; $stmt = $this->pdo->prepare( "INSERT INTO process_instances (subject_type, subject_id, person_id, process_definition_id, current_node_id, current_status, current_reason, suggested_next_step, run_number, last_activity_at, data_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), '{}')" ); $stmt->execute([ $subjectType, $subjectId, $personIdVal, $defId, $startNodeId, $status, $reason, $nextStep, $runNumber ]); $instanceId = $this->pdo->lastInsertId(); $this->addEvent($instanceId, 'system', 'Process started.', $startNodeId, [], $userId); $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE id = ?"); $stmt->execute([$instanceId]); $instance = $stmt->fetch(\PDO::FETCH_ASSOC); } if (!$inTransaction) { $this->pdo->commit(); } return [ 'instance_id' => $instance['id'], 'process_definition_id' => $instance['process_definition_id'], 'process_code' => $processCode, 'subject_type' => $instance['subject_type'], 'subject_id' => $instance['subject_id'], 'person_id' => $instance['person_id'], 'current_node_id' => $instance['current_node_id'], 'current_status' => $instance['current_status'], 'last_activity_at' => $instance['last_activity_at'], 'data_json' => $instance['data_json'] ]; } catch (\Exception $e) { if (!$inTransaction) { $this->pdo->rollBack(); } throw $e; } } EOD; $lastBracePos = strrpos($content, '}'); if ($lastBracePos !== false) { $content = substr($content, 0, $lastBracePos) . $newMethod . "\n} "; file_put_contents('WorkflowEngine.php', $content); echo "Method added.\n"; } else { echo "Failed to find closing brace.\n"; }