From fc97a990fb1df22962fb9bc157612a41aa37c783 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Sun, 1 Mar 2026 11:39:28 +0100 Subject: [PATCH] feat: Refactor task management to support recursive parent-child relationships and update database schema --- .vscode/ftp-kr.sync.cache.json | 125 +++++++++++++----- autoload/Domain/Tasks/MailToTaskImporter.php | 1 + autoload/class.Cron.php | 4 +- autoload/controls/class.Tasks.php | 10 +- autoload/factory/class.Tasks.php | 27 ++-- .../2026-03-01-tasks-recursive-parent-id.sql | 15 +++ templates/tasks/task_edit.php | 44 +++++- 7 files changed, 179 insertions(+), 47 deletions(-) create mode 100644 docs/migrations/2026-03-01-tasks-recursive-parent-id.sql diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index 61f9d0f..b2d19c8 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -89,8 +89,8 @@ }, "UsersController.php": { "type": "-", - "size": 13159, - "lmtime": 1772131160725, + "size": 16981, + "lmtime": 1772277041282, "modified": false } }, @@ -115,20 +115,20 @@ }, "class.Site.php": { "type": "-", - "size": 1298, - "lmtime": 1771236164961, + "size": 1305, + "lmtime": 1772276662027, "modified": false }, "class.Tasks.php": { "type": "-", - "size": 25767, - "lmtime": 1771495209476, - "modified": true + "size": 26351, + "lmtime": 1772285310911, + "modified": false }, "class.Users.php": { "type": "-", - "size": 4974, - "lmtime": 1772141683315, + "size": 7131, + "lmtime": 1772276658356, "modified": false }, "class.Wiki.php": { @@ -184,8 +184,8 @@ "Users": { "PermissionRepository.php": { "type": "-", - "size": 1656, - "lmtime": 1772131139905, + "size": 2103, + "lmtime": 1772276648416, "modified": false }, "UserRepository.php": { @@ -230,13 +230,13 @@ "class.Projects.php": { "type": "-", "size": 27485, - "lmtime": 0, - "modified": true + "lmtime": 1772276304852, + "modified": false }, "class.Tasks.php": { "type": "-", - "size": 21700, - "lmtime": 1771237098140, + "size": 29649, + "lmtime": 1772285985430, "modified": false }, "class.Users.php": { @@ -319,8 +319,8 @@ }, "index.php": { "type": "-", - "size": 3935, - "lmtime": 1772141695415, + "size": 6268, + "lmtime": 1772276742587, "modified": false }, "layout": { @@ -343,7 +343,64 @@ "modified": false } }, - "libraries": {}, + "libraries": { + "Simple-Gant-master": { + "frappe-gantt.css": { + "type": "-", + "size": 6990, + "lmtime": 0, + "modified": false + }, + "frappe-gantt.js": { + "type": "-", + "size": 81951, + "lmtime": 1772285639705, + "modified": false + }, + "frappe-gantt.js.map": { + "type": "-", + "size": 101071, + "lmtime": 0, + "modified": false + }, + "frappe-gantt.min.js": { + "type": "-", + "size": 26395, + "lmtime": 0, + "modified": false + }, + "frappe-gantt.min.js.map": { + "type": "-", + "size": 324380, + "lmtime": 0, + "modified": false + }, + "index.html": { + "type": "-", + "size": 2131, + "lmtime": 0, + "modified": false + }, + "LICENSE": { + "type": "-", + "size": 1151, + "lmtime": 0, + "modified": false + }, + "names.txt": { + "type": "-", + "size": 361, + "lmtime": 0, + "modified": false + }, + "README.md": { + "type": "-", + "size": 32, + "lmtime": 0, + "modified": false + } + } + }, "logs.txt": { "type": "-", "size": 3048, @@ -565,26 +622,26 @@ }, "main_view.php": { "type": "-", - "size": 41808, - "lmtime": 1771336223833, - "modified": true + "size": 46340, + "lmtime": 1772285648542, + "modified": false }, "task_edit.php": { "type": "-", - "size": 32082, - "lmtime": 1771495910826, - "modified": true + "size": 32097, + "lmtime": 1772283180543, + "modified": false }, "task_popup.php": { "type": "-", - "size": 27993, - "lmtime": 1772111511936, + "size": 32627, + "lmtime": 1772282729802, "modified": false }, "task_single.php": { "type": "-", "size": 2893, - "lmtime": 0, + "lmtime": 1772276304856, "modified": false }, "work-time.php": { @@ -603,8 +660,14 @@ }, "main-view.php": { "type": "-", - "size": 3773, - "lmtime": 1772130816909, + "size": 6085, + "lmtime": 1772277033654, + "modified": false + }, + "permissions-popup.php": { + "type": "-", + "size": 1545, + "lmtime": 1772277004609, "modified": false }, "settings.php": { @@ -654,8 +717,8 @@ "Users": { "PermissionRepositoryTest.php": { "type": "-", - "size": 1373, - "lmtime": 1772131187402, + "size": 2111, + "lmtime": 1772276691784, "modified": false }, "UserRepositoryTest.php": { diff --git a/autoload/Domain/Tasks/MailToTaskImporter.php b/autoload/Domain/Tasks/MailToTaskImporter.php index ce491a2..cb7326f 100644 --- a/autoload/Domain/Tasks/MailToTaskImporter.php +++ b/autoload/Domain/Tasks/MailToTaskImporter.php @@ -135,6 +135,7 @@ class MailToTaskImporter $client_id = $this -> resolveClientIdBySenderDomain( $sender ); $task_id = \factory\Tasks::task_save( + null, null, null, self::TASK_USER_ID, diff --git a/autoload/class.Cron.php b/autoload/class.Cron.php index 533e3bc..4758384 100644 --- a/autoload/class.Cron.php +++ b/autoload/class.Cron.php @@ -7,7 +7,7 @@ class Cron global $mdb; $results = $mdb -> query( 'SELECT ' - . 't.*, ( SELECT COUNT(0) FROM tasks WHERE parent_id = t.id ) AS quantity ' + . 't.*, ( SELECT COUNT(0) FROM tasks WHERE recursive_parent_id = t.id ) AS quantity ' . 'FROM ' . 'tasks AS t ' . 'WHERE ' @@ -101,7 +101,7 @@ class Cron $row['show_in_calendar'] ? $show_in_calendar = 'on' : $show_in_calendar = 'off'; $new_task_id = \factory\Tasks::task_save( - null, $row['id'], $row['created_by'], $row['name'], $row['text'], $new_date_start, $new_date_end, $row['project_id'], $row['client_id'], $row['pay_rate'], $row['reminders_interval'], $row['recursively'] ? 'on' : 'off', $row['frequency'], $row['period'], $task_users, $row['date_end_month_day'], $row['date_start_month_day'], null, $row['status_change_mail'], true, $status, $show_in_calendar, $row['priority'], $row['recursive_last_date'] + null, null, $row['id'], $row['created_by'], $row['name'], $row['text'], $new_date_start, $new_date_end, $row['project_id'], $row['client_id'], $row['pay_rate'], $row['reminders_interval'], $row['recursively'] ? 'on' : 'off', $row['frequency'], $row['period'], $task_users, $row['date_end_month_day'], $row['date_start_month_day'], null, $row['status_change_mail'], true, $status, $show_in_calendar, $row['priority'], $row['recursive_last_date'] ); if ( $new_task_id ) { diff --git a/autoload/controls/class.Tasks.php b/autoload/controls/class.Tasks.php index 1ca0873..3c85b5e 100644 --- a/autoload/controls/class.Tasks.php +++ b/autoload/controls/class.Tasks.php @@ -494,9 +494,17 @@ class Tasks $values['priority'] = isset( $values['priority'] ) && $values['priority'] !== '' ? $values['priority'] : ( empty( $values['id'] ) ? 1 : 0 ); $values['recursive_last_date'] = isset( $values['recursive_last_date'] ) ? $values['recursive_last_date'] : null; $status = \Controllers\TasksController::resolveTaskStatusForSave( $values ); + $recursive_parent_id = null; + + if ( !empty( $values['id'] ) ) + { + $current_task = \factory\Tasks::task_details( (int)$values['id'] ); + if ( is_array( $current_task ) and isset( $current_task['recursive_parent_id'] ) and $current_task['recursive_parent_id'] !== '' ) + $recursive_parent_id = (int)$current_task['recursive_parent_id']; + } if ( $id = \factory\Tasks::task_save( - $values['id'], $values['parent_id'], $user['id'], $values['name'], $values['text'], $values['date_start'], $values['date_end'], $values['project_id'], $values['client_id'], $values['pay_rate'], $values['reminders_interval'], $values['recursively'], $values['frequency'], $values['period'], $values['users'], null, null, $values['send_email_notification'], $values['status_change_mail'], false, $status, $values['show_in_calendar'], $values['priority'], $values['recursive_last_date'] + $values['id'], $values['parent_id'], $recursive_parent_id, $user['id'], $values['name'], $values['text'], $values['date_start'], $values['date_end'], $values['project_id'], $values['client_id'], $values['pay_rate'], $values['reminders_interval'], $values['recursively'], $values['frequency'], $values['period'], $values['users'], null, null, $values['send_email_notification'], $values['status_change_mail'], false, $status, $values['show_in_calendar'], $values['priority'], $values['recursive_last_date'] ) ) { \factory\Tasks::clear_task_opened( $id ); diff --git a/autoload/factory/class.Tasks.php b/autoload/factory/class.Tasks.php index 748029e..dabfbff 100644 --- a/autoload/factory/class.Tasks.php +++ b/autoload/factory/class.Tasks.php @@ -53,16 +53,17 @@ class Tasks global $mdb; if ( $user_id != 1 ) - $sql = ' AND id IN (SELECT task_id FROM task_user WHERE user_id = ' . $user_id . ') '; + $sql = ' AND ( id IN (SELECT task_id FROM task_user WHERE user_id = ' . $user_id . ') OR created_by = ' . $user_id . ' ) '; else $sql = ''; - $result = $mdb -> query( 'SELECT name, id, project_id, client_id FROM tasks WHERE parent_id IS NULL AND status != 2 AND status != 3 AND status != 1 ' . $sql . ' ORDER BY date_start ASC, o ASC' ) -> fetchAll( \PDO::FETCH_ASSOC ); + $result = $mdb -> query( 'SELECT name, id, project_id, client_id FROM tasks WHERE deleted = 0 AND status != 2 ' . $sql . ' ORDER BY date_start IS NULL ASC, date_start ASC, o ASC, id DESC' ) -> fetchAll( \PDO::FETCH_ASSOC ); foreach ( $result as $row ) { $task['id'] = $row['id']; $task['name'] = htmlspecialchars( $row['name'] ); $task['project_id'] = $row['project_id']; + $task['client_id'] = $row['client_id'] ? (int)$row['client_id'] : 0; $task['client'] = $row['client_id'] ? \factory\Crm::get_client_name( (int)$row['client_id'] ) : null; $task['project'] = \factory\Projects::get_project_name( $row['project_id'] ); $tasks[] = $task; @@ -632,7 +633,7 @@ class Tasks } // przy zmianach pamiętać o zadaniach z CRON - static public function task_save( $task_id, $parent_id = null, $user_id, $name, $text, $date_start, $date_end, $project_id, $client_id, $pay_rate, $reminders_interval, $recursively, $frequency, $period, $users, $date_end_month_day = null, $date_start_month_day = null, $send_email_notification = false, $status_change_mail, $rescursive = false, $status = 0, $show_in_calendar, $priority = 0, $recursive_last_date = null ) + static public function task_save( $task_id, $parent_id = null, $recursive_parent_id = null, $user_id, $name, $text, $date_start, $date_end, $project_id, $client_id, $pay_rate, $reminders_interval, $recursively, $frequency, $period, $users, $date_end_month_day = null, $date_start_month_day = null, $send_email_notification = false, $status_change_mail, $rescursive = false, $status = 0, $show_in_calendar, $priority = 0, $recursive_last_date = null ) { global $mdb; @@ -643,6 +644,7 @@ class Tasks $mdb -> insert( 'tasks', [ 'created_by' => $user_id, 'parent_id' => $parent_id, + 'recursive_parent_id' => $recursive_parent_id, 'name' => $name, 'text' => $rescursive ? $text : htmlspecialchars( $text ), 'date_start' => $date_start != '' ? "$date_start" : null, @@ -689,6 +691,7 @@ class Tasks { $mdb -> update( 'tasks', [ 'parent_id' => $parent_id, + 'recursive_parent_id' => $recursive_parent_id, 'name' => $name, 'text' => htmlspecialchars( $text ), 'date_start' => $date_start != '' ? $date_start : null, @@ -741,29 +744,29 @@ class Tasks return true; } - static public function task_first_id( $parent_id ) { + static public function task_first_id( $recursive_parent_id ) { global $mdb; - if ( $task_id = $mdb -> get( 'tasks', 'parent_id', [ 'id' => $parent_id ] ) ) + if ( $task_id = $mdb -> get( 'tasks', 'recursive_parent_id', [ 'id' => $recursive_parent_id ] ) ) return self::task_first_id( $task_id ); else - return $parent_id; + return $recursive_parent_id; } static public function task_delete_all( $task_id ) { global $mdb; - $parent_id = $mdb -> get( 'tasks', 'parent_id', [ 'id' => $task_id ] ); + $recursive_parent_id = $mdb -> get( 'tasks', 'recursive_parent_id', [ 'id' => $task_id ] ); - if ( !$parent_id ) { + if ( !$recursive_parent_id ) { $children_id = self::task_delete_from_db( $task_id ); if ( $children_id ) self::task_delete_all( $children_id ); } - if ( $parent_id ) - self::task_delete_all( $parent_id ); + if ( $recursive_parent_id ) + self::task_delete_all( $recursive_parent_id ); else return false; } @@ -772,13 +775,13 @@ class Tasks { global $mdb; - $children = $mdb -> get( 'tasks', 'id', [ 'parent_id' => $task_id ] ); + $children = $mdb -> get( 'tasks', 'id', [ 'recursive_parent_id' => $task_id ] ); $attachments_repository = new \Domain\Tasks\TaskAttachmentRepository( $mdb ); $attachments_repository -> purgeByTaskId( $task_id ); $mdb -> delete( 'tasks', [ 'id' => $task_id ] ); - $mdb -> update( 'tasks', [ 'parent_id' => null ], [ 'parent_id' => $task_id ] ); + $mdb -> update( 'tasks', [ 'recursive_parent_id' => null ], [ 'recursive_parent_id' => $task_id ] ); return $children; } diff --git a/docs/migrations/2026-03-01-tasks-recursive-parent-id.sql b/docs/migrations/2026-03-01-tasks-recursive-parent-id.sql new file mode 100644 index 0000000..280b757 --- /dev/null +++ b/docs/migrations/2026-03-01-tasks-recursive-parent-id.sql @@ -0,0 +1,15 @@ +-- 2026-03-01: rozdzielenie relacji parent_id (hierarchia) i recursive_parent_id (rekurencja) + +ALTER TABLE tasks + ADD COLUMN recursive_parent_id INT NULL AFTER parent_id, + ADD INDEX idx_tasks_recursive_parent_id (recursive_parent_id); + +-- Przeniesienie historycznych powiazan rekurencyjnych do nowej kolumny. +UPDATE tasks +SET recursive_parent_id = parent_id +WHERE parent_id IS NOT NULL; + +-- parent_id pozostaje czyste i od teraz sluzy tylko do relacji nadrzedne/podrzedne. +UPDATE tasks +SET parent_id = NULL +WHERE parent_id IS NOT NULL; diff --git a/templates/tasks/task_edit.php b/templates/tasks/task_edit.php index 4f0492b..bb7dcb0 100644 --- a/templates/tasks/task_edit.php +++ b/templates/tasks/task_edit.php @@ -38,7 +38,23 @@ if ( is_array( $this -> clients ) ) $parent_tasks = [ 0 => '--- wybierz zadanie nadrzędne ---' ]; if ( is_array( $this -> parent_tasks ) ) foreach ( $this -> parent_tasks as $parent_task ) - $parent_tasks[ $parent_task[ 'id' ] ] = $parent_task[ 'name' ] . ( $parent_task['client'] ? ' (' . $parent_task['client'] . ')' : '' ); + $parent_tasks[ $parent_task[ 'id' ] ] = $parent_task[ 'name' ] . ( $parent_task['client'] ? ' - ' . $parent_task['client'] : '' ); + +$parent_tasks_meta = []; +if ( is_array( $this -> parent_tasks ) ) +{ + foreach ( $this -> parent_tasks as $parent_task ) + { + $parent_task_id = isset( $parent_task['id'] ) ? (int)$parent_task['id'] : 0; + if ( !$parent_task_id ) + continue; + + $parent_tasks_meta[ $parent_task_id ] = [ + 'project_id' => isset( $parent_task['project_id'] ) ? (int)$parent_task['project_id'] : 0, + 'client_id' => isset( $parent_task['client_id'] ) && $parent_task['client_id'] !== null ? (int)$parent_task['client_id'] : 0 + ]; + } +} ob_start(); ?> @@ -495,6 +511,8 @@ echo $grid -> draw();