188 lines
9.3 KiB
PHP
188 lines
9.3 KiB
PHP
public function getDashboardMatrix(?string $searchTerm = null, ?int $groupId = null, ?int $activeProcessDefinitionId = null, ?int $meetingFilterGroupId = null, ?string $meetingFilterDatetime = null): array {
|
|
// 1. Base query for people
|
|
$sql_people = "SELECT p.*, bg.name as bni_group_name FROM people p LEFT JOIN bni_groups bg ON p.bni_group_id = bg.id";
|
|
$params = [];
|
|
$where_clauses = [];
|
|
|
|
// 2. Add filter conditions
|
|
if ($searchTerm) {
|
|
$where_clauses[] = "(p.first_name LIKE :search OR p.last_name LIKE :search OR p.company_name LIKE :search OR p.email LIKE :search)";
|
|
$params[':search'] = '%' . $searchTerm . '%';
|
|
}
|
|
|
|
if ($groupId) {
|
|
$where_clauses[] = "p.bni_group_id = :group_id";
|
|
$params[':group_id'] = $groupId;
|
|
}
|
|
|
|
if ($activeProcessDefinitionId) {
|
|
$terminal_statuses = ['positive', 'negative', 'completed', 'error', 'inactive'];
|
|
$in_clause = implode(',', array_map([$this->pdo, 'quote'], $terminal_statuses));
|
|
|
|
$sql_people .= " INNER JOIN process_instances pi ON p.id = pi.person_id";
|
|
$where_clauses[] = "pi.process_definition_id = :active_process_id AND (pi.current_status IS NOT NULL AND pi.current_status NOT IN ($in_clause))";
|
|
$params[':active_process_id'] = $activeProcessDefinitionId;
|
|
}
|
|
|
|
if ($meetingFilterGroupId && $meetingFilterDatetime) {
|
|
$meetingId = $this->getOrCreateMeeting($meetingFilterGroupId, $meetingFilterDatetime);
|
|
$sql_people .= " INNER JOIN meeting_attendance ma ON p.id = ma.person_id";
|
|
$where_clauses[] = "ma.meeting_id = :meeting_id";
|
|
$where_clauses[] = "ma.attendance_status IN ('present', 'absent', 'substitute')";
|
|
$params[':meeting_id'] = $meetingId;
|
|
}
|
|
|
|
if (!empty($where_clauses)) {
|
|
$sql_people .= " WHERE " . implode(" AND ", $where_clauses);
|
|
}
|
|
|
|
$sql_people .= " ORDER BY p.last_name, p.first_name";
|
|
|
|
// 3. Execute query to get filtered people
|
|
$stmt_people = $this->pdo->prepare($sql_people);
|
|
$stmt_people->execute($params);
|
|
$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->execute();
|
|
$process_definitions_raw = $stmt_defs->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$definitions = [];
|
|
$definition_map = [];
|
|
foreach ($process_definitions_raw as $def) {
|
|
$definitions[$def['id']] = [
|
|
'id' => $def['id'],
|
|
'code' => $def['code'],
|
|
'name' => $def['name'],
|
|
'is_active' => $def['is_active']
|
|
];
|
|
$definition_map[$def['id']] = !empty($def['definition_json']) ? json_decode($def['definition_json'], true) : null;
|
|
}
|
|
|
|
// 5. Fetch instances ONLY for the filtered people
|
|
$instances = [];
|
|
$person_ids = array_column($people, 'id');
|
|
if (!empty($person_ids)) {
|
|
$placeholders = implode(',', array_fill(0, count($person_ids), '?'));
|
|
$stmt_instances = $this->pdo->prepare("SELECT * FROM process_instances WHERE person_id IN ($placeholders)");
|
|
$stmt_instances->execute($person_ids);
|
|
$instances_data = $stmt_instances->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
foreach ($instances_data as $instance) {
|
|
$enriched_instance = $instance;
|
|
$def_id = $instance['process_definition_id'];
|
|
$node_id = $instance['current_node_id'];
|
|
|
|
$definition = $definition_map[$def_id] ?? null;
|
|
|
|
if ($definition && isset($definition['type']) && $definition['type'] === 'checklist') {
|
|
$tasks = $definition['tasks'] ?? [];
|
|
$instanceData = $instance['data_json'] ? json_decode($instance['data_json'], true) : [];
|
|
$totalTasks = count($tasks);
|
|
$completedTasks = 0;
|
|
if(is_array($instanceData)) {
|
|
foreach ($tasks as $task) {
|
|
if (!empty($instanceData[$task['code']])) {
|
|
$completedTasks++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($totalTasks > 0 && $completedTasks === $totalTasks) {
|
|
$status = 'completed';
|
|
} elseif ($completedTasks > 0) {
|
|
$status = 'in_progress';
|
|
} else {
|
|
$status = 'inactive';
|
|
}
|
|
$enriched_instance['computed_status'] = $status;
|
|
$enriched_instance['computed_reason'] = "$completedTasks/$totalTasks completed";
|
|
$enriched_instance['computed_next_step'] = '';
|
|
} else if ($definition && isset($definition['nodes'][$node_id])) {
|
|
$node_info = $definition['nodes'][$node_id];
|
|
$enriched_instance['computed_status'] = $node_info['ui_hints']['status'] ?? $instance['current_status'];
|
|
$enriched_instance['computed_reason'] = $node_info['ui_hints']['reason'] ?? $instance['current_reason'];
|
|
$enriched_instance['computed_next_step'] = $node_info['ui_hints']['next_step'] ?? $instance['suggested_next_step'];
|
|
} else {
|
|
$enriched_instance['computed_status'] = $instance['current_status'];
|
|
$enriched_instance['computed_reason'] = $instance['current_reason'];
|
|
$enriched_instance['computed_next_step'] = $instance['suggested_next_step'];
|
|
}
|
|
|
|
$instances[$instance['person_id']][$def_id] = $enriched_instance;
|
|
}
|
|
}
|
|
|
|
// 6. Fetch ancillary data
|
|
$stmt_functions = $this->pdo->query("SELECT * FROM functions ORDER BY display_order");
|
|
$all_functions = $stmt_functions->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$stmt_person_functions = $this->pdo->query("SELECT user_id, function_id FROM user_functions");
|
|
$person_functions_map = [];
|
|
while ($row = $stmt_person_functions->fetch(PDO::FETCH_ASSOC)) {
|
|
$person_functions_map[$row['user_id']][] = $row['function_id'];
|
|
}
|
|
|
|
$stmt_bni_groups = $this->pdo->query("SELECT * FROM bni_groups ORDER BY name");
|
|
$bni_groups = $stmt_bni_groups->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// 7. Fetch Spotkania columns (upcoming meetings)
|
|
$today = date('Y-m-d H:i:s');
|
|
$stmt_meetings = $this->pdo->prepare("
|
|
WITH RankedMeetings AS (
|
|
SELECT
|
|
bg.id as group_id,
|
|
bg.name as group_name,
|
|
ce.start_datetime,
|
|
ROW_NUMBER() OVER(PARTITION BY bg.id ORDER BY ce.start_datetime) as rn
|
|
FROM bni_groups bg
|
|
JOIN calendar_event_groups ceg ON bg.id = ceg.bni_group_id
|
|
JOIN calendar_events ce ON ceg.calendar_event_id = ce.id
|
|
WHERE ce.start_datetime >= :today
|
|
)
|
|
SELECT group_id, group_name, start_datetime
|
|
FROM RankedMeetings
|
|
WHERE rn <= 3
|
|
ORDER BY group_id, start_datetime;
|
|
");
|
|
$stmt_meetings->execute(['today' => $today]);
|
|
$upcoming_meetings_flat = $stmt_meetings->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$spotkania_cols = [];
|
|
foreach ($upcoming_meetings_flat as $meeting) {
|
|
$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'];
|
|
}
|
|
|
|
|
|
return [
|
|
'people' => $people,
|
|
'definitions' => array_values($definitions),
|
|
'instances' => $instances,
|
|
'all_functions' => $all_functions,
|
|
'person_functions_map' => $person_functions_map,
|
|
'bni_groups' => $bni_groups,
|
|
'spotkania_cols' => $spotkania_cols, // Add this to the return array
|
|
];
|
|
}
|
|
|
|
public function startProcess(string $processCode, int $personId, int $userId): int {
|
|
$inTransaction = $this->pdo->inTransaction();
|
|
if (!$inTransaction) { $this->pdo->beginTransaction(); }
|
|
try {
|
|
// 1. Find active process definition by code.
|
|
$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 process definition is found, check if there is a definition for a checklist
|
|
$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 with code or id '$processCode' not found.");
|