Podział na typy procesów
This commit is contained in:
parent
45d975adbf
commit
708233f9b4
@ -57,7 +57,7 @@ class WorkflowEngine {
|
||||
$people = $stmt_people->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// 4. Fetch all process definitions with their JSON
|
||||
$stmt_defs = $this->pdo->prepare("SELECT id, code, name, definition_json, is_active FROM process_definitions WHERE is_active = 1 AND is_latest = 1 ORDER BY sort_order, name");
|
||||
$stmt_defs = $this->pdo->prepare("SELECT id, code, name, definition_json, is_active FROM process_definitions WHERE is_active = 1 AND is_latest = 1 AND subject_scope = 'person' ORDER BY sort_order, name");
|
||||
$stmt_defs->execute();
|
||||
$process_definitions_raw = $stmt_defs->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@ -1155,4 +1155,103 @@ $spotkania_cols = [];
|
||||
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getOrCreateInstanceBySubject(string $subjectType, int $subjectId, string $processCode, int $userId, string $mode='resume_or_create'): array {
|
||||
if (!in_array($subjectType, ['person', 'meeting', 'bni_group', 'organization'])) {
|
||||
throw new \InvalidArgumentException("Invalid subjectType.");
|
||||
}
|
||||
if ($subjectId <= 0) {
|
||||
throw new \InvalidArgumentException("subjectId must be > 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,15 @@ if ($subject_type === 'person') {
|
||||
if ($meeting) {
|
||||
$subjectName = $meeting['group_name'] . ' - ' . date('d.m.Y', strtotime($meeting['meeting_datetime']));
|
||||
}
|
||||
} elseif ($subject_type === 'bni_group') {
|
||||
$stmt_group = $pdo->prepare("SELECT name FROM bni_groups WHERE id = ?");
|
||||
$stmt_group->execute([$subject_id]);
|
||||
$group = $stmt_group->fetch();
|
||||
if ($group) {
|
||||
$subjectName = 'Grupa ' . $group['name'];
|
||||
}
|
||||
} elseif ($subject_type === 'organization') {
|
||||
$subjectName = 'Główna Organizacja';
|
||||
}
|
||||
|
||||
$stmt_process = $pdo->prepare("SELECT * FROM process_definitions WHERE id = ?");
|
||||
@ -302,7 +311,7 @@ $instance = $engine->getActiveInstanceForSubject($process['code'], $subject_type
|
||||
<div class="text-center">
|
||||
<?php if ($eligibility['is_eligible']): ?>
|
||||
<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 subject.</p>
|
||||
<button id="startProcessBtn" class="btn btn-primary" data-subject-type="<?= $subject_type ?>" data-subject-id="<?= $subject_id ?>" data-process-id="<?= $process_definition_id ?>" data-process-code="<?= $process_code ?>" data-cycle-key="<?= $cycle_key ?>">
|
||||
<?= t('modal.start_process', 'Uruchom proces') ?>
|
||||
</button>
|
||||
|
||||
@ -19,51 +19,35 @@ 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);
|
||||
|
||||
$subjectType = filter_input(INPUT_POST, 'subject_type', FILTER_SANITIZE_STRING);
|
||||
$subjectId = filter_input(INPUT_POST, 'subject_id', FILTER_VALIDATE_INT);
|
||||
$processCode = filter_input(INPUT_POST, 'process_code', FILTER_SANITIZE_STRING);
|
||||
$deleteExisting = filter_input(INPUT_POST, 'delete_existing');
|
||||
$mode = filter_input(INPUT_POST, 'mode', FILTER_SANITIZE_STRING);
|
||||
$mode = filter_input(INPUT_POST, 'mode', FILTER_SANITIZE_STRING) ?: 'resume_or_create';
|
||||
|
||||
if ($personId && !$subjectType) {
|
||||
$subjectType = 'person';
|
||||
$subjectId = $personId;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
if (!$processDefinitionId && $processCode) {
|
||||
$stmt = $pdo->prepare("SELECT id FROM process_definitions WHERE code = ? AND is_latest = 1 LIMIT 1");
|
||||
$stmt->execute([$processCode]);
|
||||
$processDefinitionId = $stmt->fetchColumn();
|
||||
if ($processDefinitionId && !$processCode) {
|
||||
$stmt = $pdo->prepare("SELECT code FROM process_definitions WHERE id = ?");
|
||||
$stmt->execute([$processDefinitionId]);
|
||||
$processCode = $stmt->fetchColumn();
|
||||
}
|
||||
|
||||
if (!$subjectId || !$processDefinitionId) {
|
||||
throw new InvalidArgumentException('Invalid or missing subject_id or process_id/process_code.');
|
||||
if (!$subjectId || !$subjectType || !$processCode) {
|
||||
throw new InvalidArgumentException('Invalid or missing subject_type, subject_id, or process_code.');
|
||||
}
|
||||
|
||||
$engine = new WorkflowEngine();
|
||||
$instance = $engine->getOrCreateInstanceBySubject($subjectType, $subjectId, $processCode, $userId, $mode);
|
||||
|
||||
|
||||
$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->getActiveInstanceForSubject($code, $subjectType, $subjectId, $cycleKey);
|
||||
if ($instance) {
|
||||
$engine->deleteInstance($instance['id']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($mode === 'create_new_run' || filter_input(INPUT_POST, 'force', FILTER_VALIDATE_INT) === 1) {
|
||||
// start new process
|
||||
$instanceId = $engine->startProcessForSubject($code, $subjectType, $subjectId, $userId, 'create_new_run', $cycleKey);
|
||||
$stmt = $pdo->prepare("SELECT * FROM process_instances WHERE id = ?");
|
||||
$stmt->execute([$instanceId]);
|
||||
$instance = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$instance = $engine->getOrCreateInstanceForSubject($processDefinitionId, $subjectType, $subjectId, $userId);
|
||||
}
|
||||
|
||||
if ($instance) {
|
||||
echo json_encode(['success' => true, 'message' => 'Process initialized successfully.', 'instance_id' => $instance['id']]);
|
||||
if ($instance && isset($instance['instance_id'])) {
|
||||
echo json_encode(['success' => true, 'message' => 'Process initialized successfully.', 'instance_id' => $instance['instance_id']]);
|
||||
} else {
|
||||
throw new Exception("Failed to initialize process for an unknown reason.");
|
||||
}
|
||||
@ -93,6 +93,7 @@ try {
|
||||
$processId = $_POST['process_id'] ?? null;
|
||||
$name = $_POST['name'] ?? '';
|
||||
$definition_json = $_POST['definition_json'] ?? '';
|
||||
$subject_scope = $_POST['subject_scope'] ?? 'person';
|
||||
|
||||
validate_definition_json($definition_json);
|
||||
|
||||
@ -108,14 +109,14 @@ try {
|
||||
|
||||
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];
|
||||
$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, is_latest, subject_scope) VALUES (?, ?, ?, ?, 1, 1, 1, ?)';
|
||||
$params = [$name, $code, $definition_json, $start_node, $subject_scope];
|
||||
$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 = $pdo->prepare('SELECT code, version, sort_order, is_active, subject_scope FROM process_definitions WHERE id = ?');
|
||||
$stmt_old->execute([$processId]);
|
||||
$old = $stmt_old->fetch();
|
||||
|
||||
@ -129,8 +130,8 @@ try {
|
||||
$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']];
|
||||
$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, supersedes_definition_id, is_latest, sort_order, subject_scope) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?)';
|
||||
$params = [$name, $db_code, $definition_json, $start_node, $is_active, $new_version, $processId, $old['sort_order'], $subject_scope];
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$message = 'Process updated successfully (new version created).';
|
||||
|
||||
18
_sidebar.php
18
_sidebar.php
@ -7,6 +7,24 @@
|
||||
<i class="bi bi-kanban"></i>
|
||||
<span class="nav-link-text"><?= t('menu.process_dashboard', 'Pulpit procesów') ?></span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= ($current_page == 'meetings_processes.php') ? 'active' : '' ?>" href="meetings_processes.php">
|
||||
<i class="bi bi-camera-video"></i>
|
||||
<span class="nav-link-text">Procesy spotkań</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= ($current_page == 'groups_processes.php') ? 'active' : '' ?>" href="groups_processes.php">
|
||||
<i class="bi bi-collection"></i>
|
||||
<span class="nav-link-text">Procesy grup BNI</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= ($current_page == 'organization_processes.php') ? 'active' : '' ?>" href="organization_processes.php">
|
||||
<i class="bi bi-building"></i>
|
||||
<span class="nav-link-text">Procesy organizacji</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= ($current_page == 'calendar.php') ? 'active' : '' ?>" href="calendar.php">
|
||||
|
||||
@ -132,11 +132,13 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (isEdit) {
|
||||
const processId = button.dataset.processId;
|
||||
const processName = button.dataset.processName;
|
||||
const subjectScope = button.dataset.subjectScope || 'person';
|
||||
const processDefinition = button.dataset.processDefinition;
|
||||
|
||||
modalTitle.textContent = 'Edit Process';
|
||||
processIdInput.value = processId;
|
||||
processNameInput.value = processName;
|
||||
if(document.getElementById('subjectScope')) document.getElementById('subjectScope').value = subjectScope;
|
||||
|
||||
try {
|
||||
definition = JSON.parse(processDefinition || '{}');
|
||||
|
||||
@ -2,4 +2,4 @@
|
||||
# https://curl.se/docs/http-cookies.html
|
||||
# This file was generated by libcurl! Edit at your own risk.
|
||||
|
||||
127.0.0.1 FALSE / FALSE 0 PHPSESSID np7q4kbboaogr1lb2gg3753mdu
|
||||
localhost FALSE / FALSE 0 PHPSESSID nupuqse7tg6gsrulg298ru8sn8
|
||||
|
||||
42
db/migrations/038_add_subject_scope_and_run_number.php
Normal file
42
db/migrations/038_add_subject_scope_and_run_number.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
function migrate_038($pdo) {
|
||||
echo "Starting migration 038 (Subject Scope and Run Number)...
|
||||
";
|
||||
|
||||
$stmt = $pdo->query("SHOW COLUMNS FROM process_definitions LIKE 'subject_scope'");
|
||||
if (!$stmt->fetch()) {
|
||||
$pdo->exec("ALTER TABLE process_definitions ADD COLUMN subject_scope VARCHAR(32) NOT NULL DEFAULT 'person' AFTER name");
|
||||
echo "Added subject_scope to process_definitions.
|
||||
";
|
||||
}
|
||||
|
||||
$stmt = $pdo->query("SHOW COLUMNS FROM process_instances LIKE 'run_number'");
|
||||
if (!$stmt->fetch()) {
|
||||
$pdo->exec("ALTER TABLE process_instances ADD COLUMN run_number INT NOT NULL DEFAULT 1 AFTER subject_id");
|
||||
echo "Added run_number to process_instances.
|
||||
";
|
||||
}
|
||||
|
||||
$defJson = json_encode([
|
||||
'start_node_id' => 'node_start',
|
||||
'nodes' => [
|
||||
['id' => 'node_start', 'name' => 'Planowanie', 'type' => 'task', 'ui_hints' => ['color' => 'blue', 'status' => 'not_started']],
|
||||
['id' => 'node_end', 'name' => 'Zakończone', 'type' => 'end', 'ui_hints' => ['color' => 'green', 'status' => 'completed']]
|
||||
],
|
||||
'transitions' => [
|
||||
['from' => 'node_start', 'to' => 'node_end', 'name' => 'Oznacz jako gotowe', 'trigger' => 'manual']
|
||||
]
|
||||
]);
|
||||
|
||||
$stmt = $pdo->prepare("SELECT id FROM process_definitions WHERE code = 'meeting_preparation_meeting'");
|
||||
$stmt->execute();
|
||||
if (!$stmt->fetch()) {
|
||||
$stmtIns = $pdo->prepare("INSERT INTO process_definitions (code, name, version, is_active, subject_scope, definition_json) VALUES ('meeting_preparation_meeting', 'Przygotowanie spotkania (spotkanie)', 1, 1, 'meeting', :json)");
|
||||
$stmtIns->execute(['json' => $defJson]);
|
||||
echo "Inserted meeting_preparation_meeting definition.
|
||||
";
|
||||
}
|
||||
|
||||
echo "Migration 038 completed.
|
||||
";
|
||||
}
|
||||
114
groups_processes.php
Normal file
114
groups_processes.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
require_once 'WorkflowEngine.php';
|
||||
require_once 'lib/i18n.php';
|
||||
|
||||
session_start();
|
||||
|
||||
$engine = new WorkflowEngine();
|
||||
$pdo = db();
|
||||
|
||||
$stmt_groups = $pdo->prepare("SELECT * FROM bni_groups ORDER BY name");
|
||||
$stmt_groups->execute();
|
||||
$groups = $stmt_groups->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
$stmt_defs = $pdo->prepare("SELECT * FROM process_definitions WHERE is_active = 1 AND is_latest = 1 AND subject_scope = 'bni_group' ORDER BY sort_order, name");
|
||||
$stmt_defs->execute();
|
||||
$definitions = $stmt_defs->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
?>
|
||||
<?php include '_header.php'; ?>
|
||||
<?php include '_navbar.php'; ?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<?php include '_sidebar.php'; ?>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Procesy Grup BNI</h1>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive mt-3">
|
||||
<table class="table table-striped table-hover table-bordered align-middle matrix-table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Grupa BNI</th>
|
||||
<?php foreach ($definitions as $def): ?>
|
||||
<th class="text-center"><?= htmlspecialchars($def['name']) ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($groups)): ?>
|
||||
<tr><td colspan="<?= count($definitions) + 1 ?>" class="text-center">Brak grup.</td></tr>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= htmlspecialchars($group['name']) ?></strong>
|
||||
</td>
|
||||
<?php foreach ($definitions as $def): ?>
|
||||
<?php
|
||||
$instance = $engine->getActiveInstanceForSubject($def['code'], 'bni_group', $group['id']);
|
||||
$cellColor = '';
|
||||
$cellText = 'Brak';
|
||||
if ($instance) {
|
||||
$cellText = $instance['current_status'];
|
||||
if (in_array($instance['current_status'], ['completed','positive'])) {
|
||||
$cellColor = 'background-color: #d1e7dd;';
|
||||
} else if ($instance['current_status'] == 'in_progress') {
|
||||
$cellColor = 'background-color: #fff3cd;';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<td class="text-center p-2" style="<?= $cellColor ?>">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary w-100"
|
||||
onclick="openInstanceModal('bni_group', <?= $group['id'] ?>, '<?= htmlspecialchars($def['code']) ?>')">
|
||||
<?= htmlspecialchars($cellText) ?>
|
||||
</button>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openInstanceModal(subjectType, subjectId, processCode) {
|
||||
const modal = new bootstrap.Modal(document.getElementById('instanceModal'));
|
||||
const modalBody = document.getElementById('instanceModalBody');
|
||||
const modalTitle = document.getElementById('instanceModalLabel');
|
||||
|
||||
modalBody.innerHTML = '<div class="text-center"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div></div>';
|
||||
modalTitle.textContent = 'Ładowanie...';
|
||||
modal.show();
|
||||
|
||||
fetch(`_get_instance_details.php?subject_type=${subjectType}&subject_id=${subjectId}&process_code=${processCode}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.text();
|
||||
})
|
||||
.then(html => {
|
||||
modalBody.innerHTML = html;
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = html;
|
||||
const titleEl = tempDiv.querySelector('#instance-modal-title');
|
||||
if (titleEl) {
|
||||
modalTitle.textContent = titleEl.textContent;
|
||||
} else {
|
||||
modalTitle.textContent = 'Szczegóły procesu';
|
||||
}
|
||||
bindModalEvents();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching details:', error);
|
||||
modalBody.innerHTML = '<div class="alert alert-danger">Błąd podczas pobierania szczegółów.</div>';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include '_footer.php'; ?>
|
||||
127
meetings_processes.php
Normal file
127
meetings_processes.php
Normal file
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
require_once 'WorkflowEngine.php';
|
||||
require_once 'lib/i18n.php';
|
||||
|
||||
session_start();
|
||||
|
||||
$engine = new WorkflowEngine();
|
||||
$pdo = db();
|
||||
|
||||
// Fetch future meetings
|
||||
$today = date('Y-m-d H:i:s');
|
||||
$stmt_meetings = $pdo->prepare("
|
||||
SELECT m.id as meeting_id, m.meeting_datetime, bg.name as group_name
|
||||
FROM meetings m
|
||||
JOIN bni_groups bg ON m.bni_group_id = bg.id
|
||||
WHERE m.meeting_datetime >= :today
|
||||
ORDER BY m.meeting_datetime ASC
|
||||
");
|
||||
$stmt_meetings->execute(['today' => $today]);
|
||||
$meetings = $stmt_meetings->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
// Fetch meeting process definitions
|
||||
$stmt_defs = $pdo->prepare("SELECT * FROM process_definitions WHERE is_active = 1 AND is_latest = 1 AND subject_scope = 'meeting' ORDER BY sort_order, name");
|
||||
$stmt_defs->execute();
|
||||
$definitions = $stmt_defs->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
?>
|
||||
<?php include '_header.php'; ?>
|
||||
<?php include '_navbar.php'; ?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<?php include '_sidebar.php'; ?>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Procesy spotkań</h1>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive mt-3">
|
||||
<table class="table table-striped table-hover table-bordered align-middle matrix-table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Spotkanie</th>
|
||||
<?php foreach ($definitions as $def): ?>
|
||||
<th class="text-center"><?= htmlspecialchars($def['name']) ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($meetings)): ?>
|
||||
<tr><td colspan="<?= count($definitions) + 1 ?>" class="text-center">Brak przyszłych spotkań w bazie.</td></tr>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($meetings as $meeting): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= htmlspecialchars($meeting['group_name']) ?></strong><br>
|
||||
<small class="text-muted"><?= date('d.m.Y H:i', strtotime($meeting['meeting_datetime'])) ?></small>
|
||||
</td>
|
||||
<?php foreach ($definitions as $def): ?>
|
||||
<?php
|
||||
// Let's get instance state
|
||||
$instance = $engine->getActiveInstanceForSubject($def['code'], 'meeting', $meeting['meeting_id']);
|
||||
$cellColor = '';
|
||||
$cellText = 'Brak';
|
||||
if ($instance) {
|
||||
// For simplicity just map status or display button
|
||||
$cellText = $instance['current_status'];
|
||||
if (in_array($instance['current_status'], ['completed','positive'])) {
|
||||
$cellColor = 'background-color: #d1e7dd;';
|
||||
} else if ($instance['current_status'] == 'in_progress') {
|
||||
$cellColor = 'background-color: #fff3cd;';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<td class="text-center p-2" style="<?= $cellColor ?>">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary w-100"
|
||||
onclick="openInstanceModal('meeting', <?= $meeting['meeting_id'] ?>, '<?= htmlspecialchars($def['code']) ?>')">
|
||||
<?= htmlspecialchars($cellText) ?>
|
||||
</button>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openInstanceModal(subjectType, subjectId, processCode) {
|
||||
const modal = new bootstrap.Modal(document.getElementById('instanceModal'));
|
||||
const modalBody = document.getElementById('instanceModalBody');
|
||||
const modalTitle = document.getElementById('instanceModalLabel');
|
||||
|
||||
modalBody.innerHTML = '<div class="text-center"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div></div>';
|
||||
modalTitle.textContent = 'Ładowanie...';
|
||||
modal.show();
|
||||
|
||||
fetch(`_get_instance_details.php?subject_type=${subjectType}&subject_id=${subjectId}&process_code=${processCode}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.text();
|
||||
})
|
||||
.then(html => {
|
||||
modalBody.innerHTML = html;
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = html;
|
||||
const titleEl = tempDiv.querySelector('#instance-modal-title');
|
||||
if (titleEl) {
|
||||
modalTitle.textContent = titleEl.textContent;
|
||||
} else {
|
||||
modalTitle.textContent = 'Szczegóły procesu';
|
||||
}
|
||||
bindModalEvents();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching details:', error);
|
||||
modalBody.innerHTML = '<div class="alert alert-danger">Błąd podczas pobierania szczegółów.</div>';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include '_footer.php'; ?>
|
||||
106
organization_processes.php
Normal file
106
organization_processes.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
require_once 'WorkflowEngine.php';
|
||||
require_once 'lib/i18n.php';
|
||||
|
||||
session_start();
|
||||
|
||||
$engine = new WorkflowEngine();
|
||||
$pdo = db();
|
||||
|
||||
$stmt_defs = $pdo->prepare("SELECT * FROM process_definitions WHERE is_active = 1 AND is_latest = 1 AND subject_scope = 'organization' ORDER BY sort_order, name");
|
||||
$stmt_defs->execute();
|
||||
$definitions = $stmt_defs->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
?>
|
||||
<?php include '_header.php'; ?>
|
||||
<?php include '_navbar.php'; ?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<?php include '_sidebar.php'; ?>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
|
||||
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||
<h1 class="h2">Procesy Organizacji</h1>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive mt-3">
|
||||
<table class="table table-striped table-hover table-bordered align-middle matrix-table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Organizacja</th>
|
||||
<?php foreach ($definitions as $def): ?>
|
||||
<th class="text-center"><?= htmlspecialchars($def['name']) ?></th>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>Główna Organizacja</strong>
|
||||
</td>
|
||||
<?php foreach ($definitions as $def): ?>
|
||||
<?php
|
||||
// We use subject_id = 1 for the whole organization
|
||||
$instance = $engine->getActiveInstanceForSubject($def['code'], 'organization', 1);
|
||||
$cellColor = '';
|
||||
$cellText = 'Brak';
|
||||
if ($instance) {
|
||||
$cellText = $instance['current_status'];
|
||||
if (in_array($instance['current_status'], ['completed','positive'])) {
|
||||
$cellColor = 'background-color: #d1e7dd;';
|
||||
} else if ($instance['current_status'] == 'in_progress') {
|
||||
$cellColor = 'background-color: #fff3cd;';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<td class="text-center p-2" style="<?= $cellColor ?>">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary w-100"
|
||||
onclick="openInstanceModal('organization', 1, '<?= htmlspecialchars($def['code']) ?>')">
|
||||
<?= htmlspecialchars($cellText) ?>
|
||||
</button>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openInstanceModal(subjectType, subjectId, processCode) {
|
||||
const modal = new bootstrap.Modal(document.getElementById('instanceModal'));
|
||||
const modalBody = document.getElementById('instanceModalBody');
|
||||
const modalTitle = document.getElementById('instanceModalLabel');
|
||||
|
||||
modalBody.innerHTML = '<div class="text-center"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div></div>';
|
||||
modalTitle.textContent = 'Ładowanie...';
|
||||
modal.show();
|
||||
|
||||
fetch(`_get_instance_details.php?subject_type=${subjectType}&subject_id=${subjectId}&process_code=${processCode}`)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.text();
|
||||
})
|
||||
.then(html => {
|
||||
modalBody.innerHTML = html;
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = html;
|
||||
const titleEl = tempDiv.querySelector('#instance-modal-title');
|
||||
if (titleEl) {
|
||||
modalTitle.textContent = titleEl.textContent;
|
||||
} else {
|
||||
modalTitle.textContent = 'Szczegóły procesu';
|
||||
}
|
||||
bindModalEvents();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching details:', error);
|
||||
modalBody.innerHTML = '<div class="alert alert-danger">Błąd podczas pobierania szczegółów.</div>';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php include '_footer.php'; ?>
|
||||
23
patch_form.py
Normal file
23
patch_form.py
Normal file
@ -0,0 +1,23 @@
|
||||
import re
|
||||
with open('process_definitions.php', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
new_field = """ <div class="mb-3">
|
||||
<label for="subjectScope" class="form-label">Zakres procesu (Subject Scope)</label>
|
||||
<select class="form-select" id="subjectScope" name="subject_scope" required>
|
||||
<option value="person">Osoba (Person)</option>
|
||||
<option value="meeting">Spotkanie (Meeting)</option>
|
||||
<option value="bni_group">Grupa BNI (BNI Group)</option>
|
||||
<option value="organization">Organizacja (Organization)</option>
|
||||
</select>
|
||||
</div>
|
||||
"""
|
||||
|
||||
content = content.replace(
|
||||
'<div class="mb-3">\n <label for="processName" class="form-label">Process Name</label>',
|
||||
new_field + ' <div class="mb-3">\n <label for="processName" class="form-label">Process Name</label>'
|
||||
)
|
||||
|
||||
with open('process_definitions.php', 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
22
patch_js.py
Normal file
22
patch_js.py
Normal file
@ -0,0 +1,22 @@
|
||||
import re
|
||||
with open('assets/js/process_definitions.js', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
content = content.replace(
|
||||
'const processName = button.dataset.processName;',
|
||||
'const processName = button.dataset.processName;\n const subjectScope = button.dataset.subjectScope || \'person\';'
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
'processNameInput.value = processName;',
|
||||
'processNameInput.value = processName;\n if(document.getElementById(\'subjectScope\')) document.getElementById(\'subjectScope\').value = subjectScope;'
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
'processNameInput.value = \'";',
|
||||
'processNameInput.value = \'";\n if(document.getElementById(\'subjectScope\')) document.getElementById(\'subjectScope\').value = \'person\';'
|
||||
)
|
||||
|
||||
with open('assets/js/process_definitions.js', 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
@ -1,63 +1,38 @@
|
||||
import re
|
||||
import sys
|
||||
|
||||
with open('_get_instance_details.php', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
old_block = " <?php foreach ($currentNode['ui_hints']['form_schema'] as $field): ?>
|
||||
<div class="mb-3">
|
||||
<label for="<?= $field['name'] ?>" class="form-label"><?= $field['label'] ?></label>
|
||||
<?php if ($field['type'] === 'textarea'): ?>
|
||||
<textarea id="<?= $field['name'] ?>" name="<?= $field['name'] ?>" class="form-control"></textarea>
|
||||
<?php elseif ($field['type'] === 'select'): ?>
|
||||
<select id="<?= $field['name'] ?>" name="<?= $field['name'] ?>" class="form-select">
|
||||
<?php foreach ($field['options'] as $option): ?>
|
||||
<option value="<?= $option['value'] ?>"><?= $option['label'] ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php else: ?>
|
||||
<input type="<?= $field['type'] ?>" id="<?= $field['name'] ?>" name="<?= $field['name'] ?>" class="form-control" value="<?= ($field['default'] ?? '') === 'now' ? date('Y-m-d\TH:i') : '' ?>">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>"
|
||||
patch = """} 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']));
|
||||
}
|
||||
} elseif ($subject_type === 'bni_group') {
|
||||
$stmt_group = $pdo->prepare("SELECT name FROM bni_groups WHERE id = ?");
|
||||
$stmt_group->execute([$subject_id]);
|
||||
$group = $stmt_group->fetch();
|
||||
if ($group) {
|
||||
$subjectName = 'Grupa ' . $group['name'];
|
||||
}
|
||||
} elseif ($subject_type === 'organization') {
|
||||
$subjectName = 'Główna Organizacja';
|
||||
}"""
|
||||
|
||||
new_block = " <?php foreach ($currentNode['ui_hints']['form_schema'] as $field):
|
||||
$fieldName = $field['name'];
|
||||
$currentValue = $instanceData[$fieldName] ?? null;
|
||||
?>
|
||||
<?php if ($field['type'] === 'checkbox'): ?>
|
||||
<div class="form-check mb-3">
|
||||
<input type="hidden" name="<?= $fieldName ?>" value="0">
|
||||
<input type="checkbox" id="<?= $fieldName ?>" name="<?= $fieldName ?>" class="form-check-input" value="1" <?= (!empty($currentValue) || (!isset($currentValue) && !empty($field['default']))) ? 'checked' : '' ?>>
|
||||
<label for="<?= $fieldName ?>" class="form-check-label"><?= $field['label'] ?></label>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="mb-3">
|
||||
<label for="<?= $fieldName ?>" class="form-label"><?= $field['label'] ?></label>
|
||||
<?php if ($field['type'] === 'textarea'): ?>
|
||||
<textarea id="<?= $fieldName ?>" name="<?= $fieldName ?>" class="form-control"><?= htmlspecialchars((string)($currentValue ?? '')) ?></textarea>
|
||||
<?php elseif ($field['type'] === 'select'): ?>
|
||||
<select id="<?= $fieldName ?>" name="<?= $fieldName ?>" class="form-select">
|
||||
<?php foreach ($field['options'] as $option):
|
||||
$selected = ($currentValue !== null && (string)$currentValue === (string)$option['value']) ? 'selected' : '';
|
||||
?>
|
||||
<option value="<?= $option['value'] ?>" <?= $selected ?>><?= $option['label'] ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php else: ?>
|
||||
<?php
|
||||
$defaultVal = ($field['default'] ?? '') === 'now' ? date('Y-m-d\\TH:i') : ($field['default'] ?? '');
|
||||
$valToUse = $currentValue ?? $defaultVal;
|
||||
?>
|
||||
<input type="<?= $field['type'] ?>" id="<?= $fieldName ?>" name="<?= $fieldName ?>" class="form-control" value="<?= htmlspecialchars((string)$valToUse) ?>">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>"
|
||||
old_code = """} 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']));
|
||||
}
|
||||
}"""
|
||||
|
||||
if old_block in content:
|
||||
content = content.replace(old_block, new_block)
|
||||
with open('_get_instance_details.php', 'w') as f:
|
||||
f.write(content)
|
||||
print("Patched successfully")
|
||||
else:
|
||||
print("Old block not found!")
|
||||
content = content.replace(old_code, patch)
|
||||
|
||||
with open('_get_instance_details.php', 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
print("Patched modal")
|
||||
43
patch_save_def.php
Normal file
43
patch_save_def.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
$content = file_get_contents('_save_process_definition.php');
|
||||
|
||||
// We need to add subject_scope extraction and insert/update
|
||||
$content = str_replace(
|
||||
"$definition_json = $_POST['definition_json'] ?? '';",
|
||||
"$definition_json = $_POST['definition_json'] ?? '';\n $subject_scope = $_POST['subject_scope'] ?? 'person';",
|
||||
$content
|
||||
);
|
||||
|
||||
$content = str_replace(
|
||||
"$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, is_latest) VALUES (?, ?, ?, ?, 1, 1, 1)';",
|
||||
"$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, is_latest, subject_scope) VALUES (?, ?, ?, ?, 1, 1, 1, ?)';",
|
||||
$content
|
||||
);
|
||||
|
||||
$content = str_replace(
|
||||
"$params = [ $name, $code, $definition_json, $start_node ];",
|
||||
"$params = [ $name, $code, $definition_json, $start_node, $subject_scope ];",
|
||||
$content
|
||||
);
|
||||
|
||||
$content = str_replace(
|
||||
"$stmt_old = $pdo->prepare('SELECT code, version, sort_order, is_active FROM process_definitions WHERE id = ?');",
|
||||
"$stmt_old = $pdo->prepare('SELECT code, version, sort_order, is_active, subject_scope FROM process_definitions WHERE id = ?');",
|
||||
$content
|
||||
);
|
||||
|
||||
$content = str_replace(
|
||||
"$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, supersedes_definition_id, is_latest, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?)';",
|
||||
"$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, supersedes_definition_id, is_latest, sort_order, subject_scope) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?)';",
|
||||
$content
|
||||
);
|
||||
|
||||
$content = str_replace(
|
||||
"$params = [ $name, $db_code, $definition_json, $start_node, $is_active, $new_version, $processId, $old['sort_order'] ];",
|
||||
"$params = [ $name, $db_code, $definition_json, $start_node, $is_active, $new_version, $processId, $old['sort_order'], $subject_scope ];",
|
||||
$content
|
||||
);
|
||||
|
||||
file_put_contents('_save_process_definition.php', $content);
|
||||
echo "Patched _save_process_definition.php\n";
|
||||
|
||||
39
patch_save_def.py
Normal file
39
patch_save_def.py
Normal file
@ -0,0 +1,39 @@
|
||||
import sys
|
||||
|
||||
with open('_save_process_definition.php', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
content = content.replace(
|
||||
"$definition_json = $_POST['definition_json'] ?? '';",
|
||||
"$definition_json = $_POST['definition_json'] ?? '';\n $subject_scope = $_POST['subject_scope'] ?? 'person';"
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
"$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, is_latest) VALUES (?, ?, ?, ?, 1, 1, 1)';",
|
||||
"$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, is_latest, subject_scope) VALUES (?, ?, ?, ?, 1, 1, 1, ?)';"
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
"$params = [$name, $code, $definition_json, $start_node];",
|
||||
"$params = [$name, $code, $definition_json, $start_node, $subject_scope];"
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
"$stmt_old = $pdo->prepare('SELECT code, version, sort_order, is_active FROM process_definitions WHERE id = ?');",
|
||||
"$stmt_old = $pdo->prepare('SELECT code, version, sort_order, is_active, subject_scope FROM process_definitions WHERE id = ?');"
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
"$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, supersedes_definition_id, is_latest, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?)';",
|
||||
"$sql = 'INSERT INTO process_definitions (name, code, definition_json, start_node_id, is_active, version, supersedes_definition_id, is_latest, sort_order, subject_scope) VALUES (?, ?, ?, ?, ?, ?, ?, 1, ?, ?)';"
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
"$params = [$name, $db_code, $definition_json, $start_node, $is_active, $new_version, $processId, $old['sort_order']];",
|
||||
"$params = [$name, $db_code, $definition_json, $start_node, $is_active, $new_version, $processId, $old['sort_order'], $subject_scope];"
|
||||
)
|
||||
|
||||
with open('_save_process_definition.php', 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
print("Patched")
|
||||
31
patch_sidebar.php
Normal file
31
patch_sidebar.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
$content = file_get_contents('_sidebar.php');
|
||||
|
||||
$newLinks = <<<HTML
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= (\$current_page == 'meetings_processes.php') ? 'active' : '' ?>" href="meetings_processes.php">
|
||||
<i class="bi bi-camera-video"></i>
|
||||
<span class="nav-link-text">Procesy spotkań</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= (\$current_page == 'groups_processes.php') ? 'active' : '' ?>" href="groups_processes.php">
|
||||
<i class="bi bi-collection"></i>
|
||||
<span class="nav-link-text">Procesy grup BNI</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?= (\$current_page == 'organization_processes.php') ? 'active' : '' ?>" href="organization_processes.php">
|
||||
<i class="bi bi-building"></i>
|
||||
<span class="nav-link-text">Procesy organizacji</span>
|
||||
</a>
|
||||
</li>
|
||||
HTML;
|
||||
|
||||
$search = '<li class="nav-item">
|
||||
<a class="nav-link <?= ($current_page == \'calendar.php\') ? \'active\' : \'\' ?>" href="calendar.php">';
|
||||
|
||||
$content = str_replace($search, $newLinks . "\n " . $search, $content);
|
||||
file_put_contents('_sidebar.php', $content);
|
||||
echo "Patched _sidebar.php\n";
|
||||
|
||||
121
patch_we.php
Normal file
121
patch_we.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
$content = file_get_contents('WorkflowEngine.php');
|
||||
if (strpos($content, 'function getOrCreateInstanceBySubject') !== false) {
|
||||
echo "Method already exists.\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$newMethod = <<<'EOD'
|
||||
|
||||
public function getOrCreateInstanceBySubject(string $subjectType, int $subjectId, string $processCode, int $userId, string $mode='resume_or_create'): array {
|
||||
if (!in_array($subjectType, ['person', 'meeting', 'bni_group', 'organization'])) {
|
||||
throw new \InvalidArgumentException("Invalid subjectType.");
|
||||
}
|
||||
if ($subjectId <= 0) {
|
||||
throw new \InvalidArgumentException("subjectId must be > 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";
|
||||
}
|
||||
|
||||
|
||||
109
patch_we.py
Normal file
109
patch_we.py
Normal file
@ -0,0 +1,109 @@
|
||||
import re
|
||||
|
||||
with open('WorkflowEngine.php', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
new_method = """
|
||||
public function getOrCreateInstanceBySubject(string $subjectType, int $subjectId, string $processCode, int $userId, string $mode='resume_or_create'): array {
|
||||
if (!in_array($subjectType, ['person', 'meeting', 'bni_group', 'organization'])) {
|
||||
throw new InvalidArgumentException("Invalid subjectType.");
|
||||
}
|
||||
if ($subjectId <= 0) {
|
||||
throw new InvalidArgumentException("subjectId must be > 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) \n 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 = ?"
|
||||
);
|
||||
$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 \n (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)\n 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;
|
||||
}
|
||||
}
|
||||
"
|
||||
|
||||
if "function getOrCreateInstanceBySubject" not in content:
|
||||
last_brace_idx = content.rfind('}')
|
||||
content = content[:last_brace_idx] + new_method + "\n}\n"
|
||||
with open('WorkflowEngine.php', 'w') as f:
|
||||
f.write(content)
|
||||
print("Method added.")
|
||||
else:
|
||||
print("Method already exists.")
|
||||
@ -41,7 +41,7 @@ $processes = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
<button class="btn btn-sm btn-secondary edit-process-btn"
|
||||
data-bs-toggle="modal" data-bs-target="#createProcessModal"
|
||||
data-process-id="<?= $process['id'] ?>"
|
||||
data-process-name="<?= htmlspecialchars($process['name']) ?>"
|
||||
data-process-name="<?= htmlspecialchars($process['name']) ?>" data-subject-scope="<?= htmlspecialchars($process['subject_scope'] ?? 'person') ?>"
|
||||
data-process-definition='<?= htmlspecialchars($process['definition_json'] ?? '') ?>'>
|
||||
Edit
|
||||
</button>
|
||||
@ -67,6 +67,15 @@ $processes = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
<div class="modal-body">
|
||||
<form id="createProcessForm" action="_save_process_definition.php" method="post">
|
||||
<input type="hidden" name="process_id" id="processId">
|
||||
<div class="mb-3">
|
||||
<label for="subjectScope" class="form-label">Zakres procesu (Subject Scope)</label>
|
||||
<select class="form-select" id="subjectScope" name="subject_scope" required>
|
||||
<option value="person">Osoba (Person)</option>
|
||||
<option value="meeting">Spotkanie (Meeting)</option>
|
||||
<option value="bni_group">Grupa BNI (BNI Group)</option>
|
||||
<option value="organization">Organizacja (Organization)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="processName" class="form-label">Process Name</label>
|
||||
<input type="text" class="form-control" id="processName" name="name" required>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user