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();