feat: Refactor task management to support recursive parent-child relationships and update database schema
This commit is contained in:
125
.vscode/ftp-kr.sync.cache.json
vendored
125
.vscode/ftp-kr.sync.cache.json
vendored
@@ -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": {
|
||||
|
||||
@@ -135,6 +135,7 @@ class MailToTaskImporter
|
||||
$client_id = $this -> resolveClientIdBySenderDomain( $sender );
|
||||
|
||||
$task_id = \factory\Tasks::task_save(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
self::TASK_USER_ID,
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
15
docs/migrations/2026-03-01-tasks-recursive-parent-id.sql
Normal file
15
docs/migrations/2026-03-01-tasks-recursive-parent-id.sql
Normal file
@@ -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;
|
||||
@@ -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();
|
||||
<script type="text/javascript" src="/libraries/ckeditor/ckeditor.js"></script>
|
||||
<script type="text/javascript" src="/libraries/ckeditor/adapters/jquery.js"></script>
|
||||
<script type="text/javascript">
|
||||
var parent_tasks_meta = <?= json_encode( $parent_tasks_meta );?>;
|
||||
|
||||
$(document).ready(function ()
|
||||
{
|
||||
var task_edit_root = $( '#task-edit-tabs' );
|
||||
@@ -548,6 +566,26 @@ echo $grid -> draw();
|
||||
}
|
||||
}
|
||||
|
||||
function syncProjectAndClientFromParentTask() {
|
||||
var parent_id = parseInt( $( '#parent_id' ).val(), 10 ) || 0;
|
||||
if ( !parent_id || !parent_tasks_meta || !parent_tasks_meta[ parent_id ] )
|
||||
return;
|
||||
|
||||
var parent_meta = parent_tasks_meta[ parent_id ];
|
||||
var project_id = parseInt( parent_meta.project_id, 10 ) || 0;
|
||||
var client_id = parseInt( parent_meta.client_id, 10 ) || 0;
|
||||
|
||||
if ( project_id > 0 && String( $( '#project_id' ).val() ) !== String( project_id ) )
|
||||
{
|
||||
$( '#project_id' ).val( project_id ).trigger( 'change' );
|
||||
}
|
||||
|
||||
if ( String( $( '#client_id' ).val() ) !== String( client_id ) )
|
||||
{
|
||||
$( '#client_id' ).val( client_id ).trigger( 'change' );
|
||||
}
|
||||
}
|
||||
|
||||
function refreshAttachmentsState() {
|
||||
var task_id = parseInt( $( '#id' ).val(), 10 ) || 0;
|
||||
var attachments_box = $( '.task-edit-attachments' );
|
||||
@@ -676,6 +714,10 @@ echo $grid -> draw();
|
||||
toggleRecursiveDetails();
|
||||
});
|
||||
|
||||
$( '#parent_id' ).on( 'change select2:select', function() {
|
||||
syncProjectAndClientFromParentTask();
|
||||
});
|
||||
|
||||
refreshAttachmentsState();
|
||||
var initial_task_id = parseInt( $( '#id' ).val(), 10 ) || 0;
|
||||
if ( initial_task_id > 0 )
|
||||
|
||||
Reference in New Issue
Block a user