pdo = db(); } /** * Pobiera wszystkie dane niezbędne dla głównej macierzy pulpitu procesów. * * @return array Tablica zawierająca 'people', 'definitions' i zmapowaną tablicę 'instances'. */ public function getDashboardMatrix(): array { // Get all people (potential assignees) $stmt_people = $this->pdo->prepare("SELECT id, firstName, lastName, companyName, role, email, phone FROM people ORDER BY lastName, firstName"); $stmt_people->execute(); $people = $stmt_people->fetchAll(PDO::FETCH_ASSOC); // Fetch all process definitions $stmt_defs = $this->pdo->prepare("SELECT id, name FROM process_definitions ORDER BY name"); $stmt_defs->execute(); $process_definitions = $stmt_defs->fetchAll(PDO::FETCH_ASSOC); // Fetch instances $stmt_instances = $this->pdo->prepare("SELECT * FROM process_instances"); $stmt_instances->execute(); $instances_data = $stmt_instances->fetchAll(PDO::FETCH_ASSOC); $instances = []; foreach ($instances_data as $instance) { $instances[$instance['personId']][$instance['processDefinitionId']] = $instance; } return [ 'people' => $people, 'definitions' => $process_definitions, 'instances' => $instances, ]; } /** * Rozpoczyna nową instancję procesu dla danej osoby. * * @param string $processCode Unikalny kod definicji procesu. * @param int $personId ID osoby. * @param int $userId ID użytkownika inicjującego akcję. * @return int|null ID nowo utworzonej instancji lub null w przypadku niepowodzenia. */ public function startProcess(string $processCode, int $personId, int $userId): ?int { // 1. Znajdź aktywną definicję procesu po kodzie. // 2. Pobierz start_node_id z definicji. // 3. Utwórz nową instancję procesu ze statusem 'in_progress' i current_node_id. // 4. Utwórz zdarzenie systemowe dla rozpoczęcia procesu. // TODO: Implementacja logiki. return null; } /** * Pobiera pojedynczą instancję procesu, tworząc ją, jeśli nie istnieje. * * @param int $personId * @param int $processDefinitionId * @param int $userId Użytkownik inicjujący utworzenie, jeśli to nastąpi. * @return array|null Tablica asocjacyjna z danymi instancji. */ public function getOrCreateInstanceByDefId(int $personId, int $processDefinitionId, int $userId): ?array { $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE personId = ? AND processDefinitionId = ?"); $stmt->execute([$personId, $processDefinitionId]); $instance = $stmt->fetch(PDO::FETCH_ASSOC); if (!$instance) { // Fetch the process definition to get the initial status $stmt_def = $this->pdo->prepare("SELECT definition_json FROM process_definitions WHERE id = ?"); $stmt_def->execute([$processDefinitionId]); $definition_raw = $stmt_def->fetchColumn(); $definition = $definition_raw ? json_decode($definition_raw, true) : null; $initial_status = $definition['initial_status'] ?? 'none'; $stmt_insert = $this->pdo->prepare("INSERT INTO process_instances (personId, processDefinitionId, current_status) VALUES (?, ?, ?)"); $stmt_insert->execute([$personId, $processDefinitionId, $initial_status]); $instanceId = $this->pdo->lastInsertId(); // Utwórz zdarzenie systemowe dla utworzenia instancji $this->addEvent($instanceId, 'system', 'Instancja utworzona.', null, [], $userId); // Pobierz ponownie nowo utworzoną instancję $stmt->execute([$personId, $processDefinitionId]); $instance = $stmt->fetch(PDO::FETCH_ASSOC); } return $instance; } /** * Dodaje nowe zdarzenie do historii instancji. * To jest wewnętrzna metoda pomocnicza. */ private function addEvent(int $instanceId, string $eventType, string $message, ?string $nodeId, array $payload, int $userId): void { $stmt = $this->pdo->prepare( "INSERT INTO process_events (processInstanceId, event_type, message, node_id, payload_json, createdById) VALUES (?, ?, ?, ?, ?, ?)" ); $stmt->execute([$instanceId, $eventType, $message, $nodeId, json_encode($payload), $userId]); } /** * Pobiera historię zdarzeń dla danej instancji. * * @param int $instanceId * @return array */ public function getEvents(int $instanceId): array { $stmt_events = $this->pdo->prepare("SELECT pe.*, p.email as user_email, p.firstName, p.lastName FROM process_events pe JOIN people p ON pe.createdById = p.id WHERE pe.processInstanceId = ? ORDER BY pe.createdAt DESC"); $stmt_events->execute([$instanceId]); return $stmt_events->fetchAll(PDO::FETCH_ASSOC); } /** * Pobiera listę dostępnych przejść z bieżącego węzła instancji. * * @param int $instanceId * @return array Lista dostępnych przejść. */ public function getAvailableTransitions(int $instanceId): array { $stmt = $this->pdo->prepare( 'SELECT pi.current_status, pd.definition_json ' . 'FROM process_instances pi ' . 'JOIN process_definitions pd ON pi.processDefinitionId = pd.id ' . 'WHERE pi.id = ?' ); $stmt->execute([$instanceId]); $result = $stmt->fetch(PDO::FETCH_ASSOC); if (!$result || empty($result['definition_json'])) { return []; } $definition = json_decode($result['definition_json'], true); if (!$definition || !isset($definition['transitions'])) { return []; } $current_status = $result['current_status']; $allowed_transitions = $definition['transitions'][$current_status] ?? []; $transitions = []; foreach ($allowed_transitions as $target_status) { $transitions[] = [ 'id' => 'transition_' . str_replace(' ', '_', strtolower($target_status)), // e.g., transition_in_progress 'name' => 'Oznacz jako ' . $target_status, // e.g., Oznacz jako In Progress 'target_status' => $target_status ]; } return $transitions; } /** * Stosuje przejście do instancji procesu. To jest główna metoda do postępu w przepływie pracy. * * @param int $instanceId ID instancji procesu. * @param string $transitionId ID przejścia do zastosowania (z definition_json). * @param array $inputPayload Dane zebrane od użytkownika dla tego kroku. * @param int $userId ID użytkownika wykonującego akcję. * @return bool True w przypadku sukcesu, false w przypadku porażki. */ public function applyTransition(int $instanceId, string $transitionId, array $inputPayload, int $userId): bool { $this->pdo->beginTransaction(); try { // 1. Pobierz instancję i dostępne przejścia $stmt = $this->pdo->prepare("SELECT * FROM process_instances WHERE id = ?"); $stmt->execute([$instanceId]); $instance = $stmt->fetch(PDO::FETCH_ASSOC); if (!$instance) { // Instancja nie znaleziona $this->pdo->rollBack(); return false; } $availableTransitions = $this->getAvailableTransitions($instanceId); // 2. Sprawdź, czy przejście jest dozwolone $selectedTransition = null; if ($transitionId === 'note') { // Specjalny przypadek dodawania notatki $selectedTransition = ['id' => 'note', 'name' => 'Dodano notatkę', 'target_status' => $instance['current_status']]; } else { foreach ($availableTransitions as $trans) { if ($trans['id'] === $transitionId) { $selectedTransition = $trans; break; } } } if (!$selectedTransition) { // Nieprawidłowe lub niedozwolone przejście $this->pdo->rollBack(); return false; } // 3. Utwórz zdarzenie $eventType = ($transitionId === 'note') ? 'note' : 'transition_applied'; $message = $inputPayload['message'] ?? $selectedTransition['name']; $this->addEvent($instanceId, $eventType, $message, null, $inputPayload, $userId); // 4. Zaktualizuj instancję $stmt_update = $this->pdo->prepare( "UPDATE process_instances SET current_status = ?, lastActivityAt = CURRENT_TIMESTAMP WHERE id = ?" ); $stmt_update->execute([$selectedTransition['target_status'], $instanceId]); $this->pdo->commit(); return true; } catch (Exception $e) { $this->pdo->rollBack(); error_log("Błąd w applyTransition: " . $e->getMessage()); return false; } } /** * Masowa operacja stosowania tego samego przejścia do wielu osób dla danego procesu. * * @param string $processCode * @param array $personIds * @param string $transitionId * @param array $inputPayload * @param int $userId * @return array Podsumowanie wyników (np. ['success' => count, 'failed' => count]). */ public function bulkApplyTransition(string $processCode, array $personIds, string $transitionId, array $inputPayload, int $userId): array { // 1. Upewnij się, że instancje istnieją dla wszystkich osób (użyj ensureInstances). // 2. Przejdź przez pętlę personIds i wywołaj applyTransition dla każdej z nich. // TODO: Implementacja logiki. return ['success' => 0, 'failed' => 0]; } /** * Zapewnia, że instancje procesów istnieją dla danego zestawu osób i kodów procesów. * Jeśli instancja brakuje, zostanie utworzona. * * @param array $personIds * @param array $processCodes * @param int $userId * @return array Podsumowanie utworzonych instancji. */ public function ensureInstances(array $personIds, array $processCodes, int $userId): array { // TODO: Implementacja logiki do tworzenia brakujących instancji. return ['created' => 0]; } }