diff --git a/backend/.idea/workspace.xml b/backend/.idea/workspace.xml
index 1a297ba..93c0480 100644
--- a/backend/.idea/workspace.xml
+++ b/backend/.idea/workspace.xml
@@ -4,8 +4,16 @@
-
-
+
+
+
+
+
+
+
+
+
+
@@ -102,7 +110,7 @@
-
+
@@ -208,7 +216,15 @@
1698306889808
-
+
+
+ 1698500028161
+
+
+
+ 1698500028161
+
+
@@ -230,7 +246,8 @@
-
+
+
diff --git a/backend/src/main/java/core/api/controller/TaskController.java b/backend/src/main/java/core/api/controller/TaskController.java
index 093102b..7ec2b13 100644
--- a/backend/src/main/java/core/api/controller/TaskController.java
+++ b/backend/src/main/java/core/api/controller/TaskController.java
@@ -1,6 +1,7 @@
package core.api.controller;
import core.api.models.auth.SimpleStatusResponse;
+import core.api.models.timemanager.taskSchedule.ScheduleInfo;
import core.api.models.timemanager.tasks.TaskEntityInfo;
import core.api.models.timemanager.tasks.TaskFieldInfo;
import core.entities.timemanager.Task;
@@ -110,4 +111,19 @@ public class TaskController {
return ResponseEntity.ok(new TaskEntityInfo(taskPermissionResult.getResult()));
}
+
+ @PostMapping("/tasks/{taskID}/finish")
+ public ResponseEntity> finishTask(@PathVariable long taskID) {
+ PermissionResult taskPermissionResult = taskService.getTaskPermissions(taskID, SecurityContextHolder.getContext().getAuthentication().getName());
+ if (!taskPermissionResult.isHasPermissions()) {
+ return ResponseEntity.status(403).body(new SimpleStatusResponse("failed"));
+ }
+
+ if (taskPermissionResult.getExitCode() == ServiceExitCode.MISSING_ENTITY) {
+ return ResponseEntity.status(404).body(new SimpleStatusResponse("failed"));
+ }
+
+ taskService.finishTask(taskPermissionResult.getResult());
+ return ResponseEntity.ok(new SimpleStatusResponse("success"));
+ }
}
diff --git a/backend/src/main/java/core/entities/timemanager/Task.java b/backend/src/main/java/core/entities/timemanager/Task.java
index 90fa77c..92b45e7 100644
--- a/backend/src/main/java/core/entities/timemanager/Task.java
+++ b/backend/src/main/java/core/entities/timemanager/Task.java
@@ -2,6 +2,8 @@ package core.entities.timemanager;
import javax.persistence.*;
import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -27,7 +29,7 @@ public class Task {
private boolean finished;
@OneToMany(mappedBy = "task", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
- private Set basicTaskSchedules;
+ private List basicTaskSchedules;
private int workTime;
@@ -128,11 +130,11 @@ public class Task {
this.taskID = taskID;
}
- public Set getBasicTaskSchedules() {
+ public List getBasicTaskSchedules() {
return basicTaskSchedules;
}
- public void setBasicTaskSchedules(Set basicTaskSchedules) {
+ public void setBasicTaskSchedules(List basicTaskSchedules) {
this.basicTaskSchedules = basicTaskSchedules;
}
diff --git a/backend/src/main/java/core/services/TaskService.java b/backend/src/main/java/core/services/TaskService.java
index 4f2f9d4..7a31773 100644
--- a/backend/src/main/java/core/services/TaskService.java
+++ b/backend/src/main/java/core/services/TaskService.java
@@ -1,6 +1,7 @@
package core.services;
import core.api.models.timemanager.tasks.TaskFieldInfo;
+import core.entities.timemanager.BasicTaskSchedule;
import core.entities.timemanager.Task;
import core.entities.timemanager.Taskgroup;
import core.repositories.timemanager.BasicTaskScheduleRepository;
@@ -9,8 +10,8 @@ import core.repositories.timemanager.TaskgroupRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import java.util.Collection;
-import java.util.Optional;
+import java.time.LocalDateTime;
+import java.util.*;
@Service
public class TaskService {
@@ -75,4 +76,26 @@ public class TaskService {
public void clearTasks(Taskgroup taskgroup) {
taskRepository.deleteAllByTaskgroup(taskgroup);
}
+
+ public void finishTask(Task task) {
+ //Mark task as finished and remove all unstarted schedules as well as finish all started schedules
+ task.finish();
+ taskRepository.save(task);
+
+ List removedBasicTaskSchedules = new LinkedList<>();
+ for(BasicTaskSchedule basicTaskSchedule : task.getBasicTaskSchedules()) {
+ if(basicTaskSchedule.getStartTime() == null) {
+ removedBasicTaskSchedules.add(basicTaskSchedule);
+ } else if(basicTaskSchedule.getFinishedTime() == null) {
+ ServiceResult> result = taskScheduleService.stopSchedule(basicTaskSchedule, true);
+ System.out.println(result);
+ }
+ }
+
+ for(BasicTaskSchedule deletedTaskSchedule: removedBasicTaskSchedules) {
+ task.getBasicTaskSchedules().remove(deletedTaskSchedule);
+ taskRepository.save(task);
+ taskScheduleService.deleteBasicSchedule(deletedTaskSchedule);
+ }
+ }
}
diff --git a/frontend/src/api/api/task.service.ts b/frontend/src/api/api/task.service.ts
index 9a8fb4f..69a7362 100644
--- a/frontend/src/api/api/task.service.ts
+++ b/frontend/src/api/api/task.service.ts
@@ -147,6 +147,66 @@ export class TaskService {
);
}
+ /**
+ * finishs task
+ * finish tasks
+ * @param taskID internal id of task
+ * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
+ * @param reportProgress flag to report request and response progress.
+ */
+ public tasksTaskIDFinishPost(taskID: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable;
+ public tasksTaskIDFinishPost(taskID: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>;
+ public tasksTaskIDFinishPost(taskID: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable>;
+ public tasksTaskIDFinishPost(taskID: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext}): Observable {
+ if (taskID === null || taskID === undefined) {
+ throw new Error('Required parameter taskID was null or undefined when calling tasksTaskIDFinishPost.');
+ }
+
+ let localVarHeaders = this.defaultHeaders;
+
+ let localVarCredential: string | undefined;
+ // authentication (API_TOKEN) required
+ localVarCredential = this.configuration.lookupCredential('API_TOKEN');
+ if (localVarCredential) {
+ localVarHeaders = localVarHeaders.set('Authorization', 'Bearer ' + localVarCredential);
+ }
+
+ let localVarHttpHeaderAcceptSelected: string | undefined = options && options.httpHeaderAccept;
+ if (localVarHttpHeaderAcceptSelected === undefined) {
+ // to determine the Accept header
+ const httpHeaderAccepts: string[] = [
+ 'application/json'
+ ];
+ localVarHttpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
+ }
+ if (localVarHttpHeaderAcceptSelected !== undefined) {
+ localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected);
+ }
+
+ let localVarHttpContext: HttpContext | undefined = options && options.context;
+ if (localVarHttpContext === undefined) {
+ localVarHttpContext = new HttpContext();
+ }
+
+
+ let responseType_: 'text' | 'json' = 'json';
+ if(localVarHttpHeaderAcceptSelected && localVarHttpHeaderAcceptSelected.startsWith('text')) {
+ responseType_ = 'text';
+ }
+
+ return this.httpClient.post(`${this.configuration.basePath}/tasks/${encodeURIComponent(String(taskID))}/finish`,
+ null,
+ {
+ context: localVarHttpContext,
+ responseType: responseType_,
+ withCredentials: this.configuration.withCredentials,
+ headers: localVarHeaders,
+ observe: observe,
+ reportProgress: reportProgress
+ }
+ );
+ }
+
/**
* gets taskdetails
* loads information about a given task
diff --git a/frontend/src/app/dashboard/task-overview/task-overview.component.html b/frontend/src/app/dashboard/task-overview/task-overview.component.html
index 57d622a..56aa9df 100644
--- a/frontend/src/app/dashboard/task-overview/task-overview.component.html
+++ b/frontend/src/app/dashboard/task-overview/task-overview.component.html
@@ -9,6 +9,6 @@
-
+
diff --git a/frontend/src/app/dashboard/task-overview/task-overview.component.ts b/frontend/src/app/dashboard/task-overview/task-overview.component.ts
index 163e102..3c0e173 100644
--- a/frontend/src/app/dashboard/task-overview/task-overview.component.ts
+++ b/frontend/src/app/dashboard/task-overview/task-overview.component.ts
@@ -1,5 +1,5 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
-import {BasicScheduleEntityInfo, ScheduleInfo, ScheduleService, TaskOverviewInfo} from "../../../api";
+import {BasicScheduleEntityInfo, ScheduleInfo, ScheduleService, TaskOverviewInfo, TaskService} from "../../../api";
import {MatSnackBar} from "@angular/material/snack-bar";
@Component({
@@ -13,7 +13,8 @@ export class TaskOverviewComponent {
@Output('onStartNow') startNowEmitter: EventEmitter = new EventEmitter();
constructor(private scheduleService: ScheduleService,
- private snackbar: MatSnackBar) {
+ private snackbar: MatSnackBar,
+ private taskService: TaskService) {
}
startTaskNow(task: TaskOverviewInfo) {
@@ -36,6 +37,19 @@ export class TaskOverviewComponent {
}
finishTask(task: TaskOverviewInfo) {
-
+ this.taskService.tasksTaskIDFinishPost(task.taskID).subscribe({
+ next: resp => {
+
+ },
+ error: err => {
+ if(err.status == 403) {
+ this.snackbar.open("No permission", "", {duration: 2000});
+ } else if(err.status == 404) {
+ this.snackbar.open("Task not found", "", {duration: 2000});
+ } else {
+ this.snackbar.open("Unexpected error", "", {duration: 2000});
+ }
+ }
+ })
}
}
diff --git a/openapi.yaml b/openapi.yaml
index 4116bf7..c7a16a9 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -1171,6 +1171,44 @@ paths:
schema:
type: object
$ref: "#/components/schemas/SimpleStatusResponse"
+ /tasks/{taskID}/finish:
+ post:
+ security:
+ - API_TOKEN: []
+ tags:
+ - task
+ summary: finishs task
+ description: finish tasks
+ parameters:
+ - name: taskID
+ in: path
+ description: internal id of task
+ required: true
+ schema:
+ type: number
+ example: 1
+ responses:
+ 200:
+ description: Anfrage erfolgreich
+ content:
+ 'application/json':
+ schema:
+ type: object
+ $ref: "#/components/schemas/SimpleStatusResponse"
+ 403:
+ description: No permission
+ content:
+ 'application/json':
+ schema:
+ type: object
+ $ref: "#/components/schemas/SimpleStatusResponse"
+ 404:
+ description: Taskgroup does not exist
+ content:
+ 'application/json':
+ schema:
+ type: object
+ $ref: "#/components/schemas/SimpleStatusResponse"
/schedules:
get:
security: