From 6526dc1fd915a9915692ae3b7aeec42d70887b88 Mon Sep 17 00:00:00 2001
From: Tom Zhao <tom.zhao@dsv.su.se>
Date: Wed, 26 Mar 2025 12:57:18 +0100
Subject: [PATCH] 87: Add initial support of parent-child project

---
 .../se/su/dsv/scipro/project/Project.java     | 24 +++++++++++++++++++
 .../SplitOrRestartProjectServiceImpl.java     | 24 ++++++++++++-------
 .../v7__project_parent_phase2_review.sql      | 16 +++++++++++++
 .../admin/pages/AdminSplitProjectPage.java    |  5 ++++
 4 files changed, 60 insertions(+), 9 deletions(-)
 create mode 100644 core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql

diff --git a/core/src/main/java/se/su/dsv/scipro/project/Project.java b/core/src/main/java/se/su/dsv/scipro/project/Project.java
index 82f8400f9c..9a9e469738 100755
--- a/core/src/main/java/se/su/dsv/scipro/project/Project.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/Project.java
@@ -113,6 +113,14 @@ public class Project extends DomainObject {
     @Column(name = "daisy_identifier", unique = true)
     private Integer identifier;
 
+    @Basic
+    @Column(name = "parent_project_id")
+    private Long parentProjectId;
+
+    @Basic
+    @Column(name = "root_project_id")
+    private Long rootProjectId;
+
     // ----------------------------------------------------------------------------------
     // Embedded JPA-mapping
     // ----------------------------------------------------------------------------------
@@ -365,6 +373,22 @@ public class Project extends DomainObject {
         this.userNotes = userNotes;
     }
 
+    public Long getParentProjectId() {
+        return parentProjectId;
+    }
+
+    public void setParentProjectId(Long parentProjectId) {
+        this.parentProjectId = parentProjectId;
+    }
+
+    public Long getRootProjectId() {
+        return rootProjectId;
+    }
+
+    public void setRootProjectId(Long rootProjectId) {
+        this.rootProjectId = rootProjectId;
+    }
+
     // ----------------------------------------------------------------------------------
     // Methods Common To All Objects
     // ----------------------------------------------------------------------------------
diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java
index cd8fa7a87a..f0d5532afe 100644
--- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java
+++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java
@@ -1,6 +1,7 @@
 package se.su.dsv.scipro.project.split;
 
 import jakarta.inject.Inject;
+import jakarta.transaction.Transactional;
 import se.su.dsv.scipro.finalseminar.FinalSeminarService;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.project.ProjectService;
@@ -22,6 +23,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe
     }
 
     @Override
+    @Transactional
     public Pair<SplittableStatus, Project> getSplittableStatus(long projectId) {
         Project project = projectService.findOne(projectId);
         if (project == null)
@@ -43,6 +45,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe
     }
 
     @Override
+    @Transactional
     public void splitProject(long projectId) {
         Pair<SplittableStatus, Project> result = getSplittableStatus(projectId);
         if (result.getHead() != SplittableStatus.OK) {
@@ -52,35 +55,38 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe
 
         Project project = result.getTail();
 
+        // Todo: Get ev. Phase Two Approval
+
         for (User author : project.getProjectParticipants()) {
             Project childProject = new Project();
+
             childProject.setTitle(project.getTitle());
             childProject.setProjectType(project.getProjectType());
+            childProject.setProjectStatus(ProjectStatus.ACTIVE);
+            childProject.setResearchArea(project.getResearchArea());
 
             childProject.setStartDate(project.getStartDate());
             childProject.setExpectedEndDate(project.getExpectedEndDate());
 
-            // Copy supervisor
+            childProject.setHeadSupervisor(project.getHeadSupervisor());
             childProject.setProjectParticipants(List.of(author));
-            // Copy reviewer
-            // Copy cosupervisor
-            // Copy research area
+            childProject.setCoSupervisors(project.getCoSupervisors());
 
-            childProject.setProjectStatus(ProjectStatus.ACTIVE);
+            childProject.setReviewers(project.getReviewers());
 
-            // childProject.setParentProjectId
-            // childProject.setRootProjectId
+            childProject.setParentProjectId(project.getId());
+            childProject.setRootProjectId(project.getRootProjectId() != null ? project.getRootProjectId() :
+                    project.getId());
 
             childProject = projectService.save(childProject);
 
-            // Send event to eventBus to synchronize eventual Phase Two Approval
+            // Todo: Send event to eventBus to synchronize eventual Phase Two Approval
         }
 
         // Parent project will set as inactive
         project.setProjectStatus(ProjectStatus.INACTIVE);
         projectService.save(project);
 
-
         System.out.println("Hello, split project -> " + projectId);
     }
 }
diff --git a/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql b/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql
new file mode 100644
index 0000000000..2fa8341982
--- /dev/null
+++ b/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql
@@ -0,0 +1,16 @@
+
+alter table `project`
+    add column `parent_project_id` bigint(20) null default null;
+
+alter table `project`
+    add column `root_project_id` bigint(20) null default null;
+
+alter table `project`
+    add constraint fk_project_parent_project_id_project_id
+        foreign key (parent_project_id) references project(id)
+            on delete cascade on update cascade;
+
+alter table `project`
+    add constraint fk_project_root_project_id_project_id
+        foreign key (root_project_id) references project(id)
+            on delete cascade on update cascade;
diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java
index a91635ff5c..b3f5221637 100644
--- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java
+++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java
@@ -14,6 +14,7 @@ import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminProjectMan
 import se.su.dsv.scipro.data.DetachableServiceModel;
 import se.su.dsv.scipro.project.Project;
 import se.su.dsv.scipro.project.ProjectService;
+import se.su.dsv.scipro.project.split.SplitOrRestartProjectService;
 import se.su.dsv.scipro.security.auth.Authorization;
 import se.su.dsv.scipro.security.auth.roles.Roles;
 import se.su.dsv.scipro.system.User;
@@ -27,6 +28,9 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M
     @Inject
     private ProjectService projectService;
 
+    @Inject
+    private SplitOrRestartProjectService splitOrRestartProjectService;
+
     public AdminSplitProjectPage(PageParameters pp) {
         final long id = pp.get(PageParameterKeys.MAP.get(Project.class)).toLong(0);
         final Project project = projectService.findOne(id);
@@ -69,6 +73,7 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M
 
             System.out.println("Project ID: " + projectId);
 
+            splitOrRestartProjectService.splitProject(projectId);
 
             final PageParameters pp = new PageParameters();
             pp.set(PageParameterKeys.MAP.get(Project.class), projectId);