diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b89de935a0b6d885d4759f9a78f222be2efb082..fe85b44fecd0cfd3eac153bf483e8a37db3d385c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## Unreleased
+
+### Added
+- Email notifications for approved/rejected raise requests. @toberhuber
+
+### Changed
+
+### Fixed
+
## [2.1.6]
### Added
diff --git a/src/main/java/nl/tudelft/tam/service/RaiseRequestService.java b/src/main/java/nl/tudelft/tam/service/RaiseRequestService.java
index a9323f8e253ac71c0282fea2b7b44f6323c9d26a..c90d1485699a9bd5b508dca6c69c3ab0f4319b6e 100644
--- a/src/main/java/nl/tudelft/tam/service/RaiseRequestService.java
+++ b/src/main/java/nl/tudelft/tam/service/RaiseRequestService.java
@@ -30,10 +30,12 @@ import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
+import nl.tudelft.labracore.api.dto.PersonDetailsDTO;
import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
import nl.tudelft.labracore.api.dto.ProgramSummaryDTO;
import nl.tudelft.labracore.lib.security.user.Person;
import nl.tudelft.librador.dto.view.View;
+import nl.tudelft.tam.dto.create.EmailCreateDTO;
import nl.tudelft.tam.dto.create.RaiseRequestCreateDTO;
import nl.tudelft.tam.dto.view.details.RaiseRequestDetailsDTO;
import nl.tudelft.tam.dto.view.summary.RaiseRequestSummaryDTO;
@@ -60,6 +62,9 @@ public class RaiseRequestService {
@Autowired
private PersonService personService;
+ @Autowired
+ private EmailService emailService;
+
@Lazy
@Autowired
private ProgramService programService;
@@ -155,9 +160,16 @@ public class RaiseRequestService {
*/
public void approve(Long id, Person approvedBy) {
RaiseRequest raiseRequest = raiseRequestRepository.findByIdOrThrow(id);
+
+ String oldPayscale = raiseRequest.getRequestedBy().getPayScale().toString();
+ String newPayscale = raiseRequest.getPayScale().toString();
+
raiseRequest.getRequestedBy().setPayScale(raiseRequest.getPayScale());
raiseRequest.setApprovedBy(profileService.findByIdOrThrow(approvedBy.getId()));
raiseRequest.setStatus(Status.ACCEPTED);
+
+ // send notification of approval to the student
+ sendRaiseRequestApprovedNotification(raiseRequest.getRequestedBy().getId(), oldPayscale, newPayscale);
}
/**
@@ -170,6 +182,11 @@ public class RaiseRequestService {
RaiseRequest raiseRequest = raiseRequestRepository.findByIdOrThrow(id);
raiseRequest.setApprovedBy(profileService.findByIdOrThrow(approvedBy.getId()));
raiseRequest.setStatus(Status.REJECTED_BY_TEACHER);
+
+ // send notification of rejection to the student
+ sendRaiseRequestApprovedNotification(raiseRequest.getRequestedBy().getId(),
+ raiseRequest.getRequestedBy().getPayScale().toString(),
+ raiseRequest.getPayScale().toString());
}
/**
@@ -306,4 +323,56 @@ public class RaiseRequestService {
return conflicts;
}
+
+ // region notifications
+
+ /**
+ * Send a raise request approved notification
+ *
+ * @param personId The ID of the person to send the notification to
+ * @param oldPayscale The previous payscale
+ * @param newPayscale The new payscale
+ */
+ public void sendRaiseRequestApprovedNotification(Long personId, String oldPayscale, String newPayscale) {
+ Profile profile = profileService.findByIdOrCreate(personId);
+ PersonDetailsDTO person = personService.getPersonById(personId);
+
+ EmailCreateDTO email = EmailCreateDTO.builder()
+ .subject("Raise Request Approved!")
+ .template("html/raise_request_approved")
+ .contextVariables(
+ Map.of("name", person.getDisplayName(), "oldPayscale", oldPayscale,
+ "newPayscale", newPayscale))
+ .build();
+
+ if (profile.getEmailNotifications()) {
+ emailService.sendEmail(List.of(person.getEmail()), email);
+ }
+ }
+
+ /**
+ * Send a raise request rejected notification
+ *
+ * @param personId The ID of the person to send the notification to
+ * @param oldPayscale The previous payscale
+ * @param newPayscale The new payscale
+ */
+ public void sendRaiseRequestRejectedNotification(Long personId, String oldPayscale, String newPayscale) {
+ Profile profile = profileService.findByIdOrCreate(personId);
+ PersonDetailsDTO person = personService.getPersonById(personId);
+
+ EmailCreateDTO email = EmailCreateDTO.builder()
+ .subject("Raise Request Rejected!")
+ .template("html/raise_request_rejected")
+ .contextVariables(
+ Map.of("name", person.getDisplayName(), "oldPayscale", oldPayscale,
+ "newPayscale", newPayscale))
+ .build();
+
+ if (profile.getEmailNotifications()) {
+ emailService.sendEmail(List.of(person.getEmail()), email);
+ }
+ }
+
+ // end region
}
diff --git a/src/main/resources/email/html/raise_request_approved.html b/src/main/resources/email/html/raise_request_approved.html
new file mode 100644
index 0000000000000000000000000000000000000000..581c31181ba806f42e4a3f4fb80c31aecd8f61bf
--- /dev/null
+++ b/src/main/resources/email/html/raise_request_approved.html
@@ -0,0 +1,57 @@
+<!--
+
+ TAM
+ Copyright (C) 2021 - Delft University of Technology
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!doctype html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" style="font-family: sans-serif">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ </head>
+
+ <body>
+ <div>
+ <div style="width: 100%; background-color: #00a8db">
+ <a th:href="#{general.app_url}" style="text-decoration: none">
+ <span style="color: white; font-size: 40px">TAM</span>
+ </a>
+ </div>
+
+ <div>
+ <div style="padding: 0.5em 0">
+ <p th:text="#{email.greeting(${name})}"></p>
+ <p
+ th:text="#{email.raiseRequests.approved.message(${oldPayscale}, ${newPayscale})}"></p>
+ <p th:text="#{email.closing}"></p>
+ <p th:text="#{email.closing.person}"></p>
+ </div>
+
+ <div>
+ <p style="color: #707070">
+ <span th:text="#{email.notificationPreferences}"></span>
+ <a
+ style="color: #00a6d6; text-decoration: none"
+ target="_blank"
+ th:href="|#{general.app_url}/profile|"
+ th:text="#{email.notificationPreferences.here}"></a>
+ </p>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/main/resources/email/html/raise_request_rejected.html b/src/main/resources/email/html/raise_request_rejected.html
new file mode 100644
index 0000000000000000000000000000000000000000..b2bf04fb011700b8e77fd4a8b9dc8e122009a80a
--- /dev/null
+++ b/src/main/resources/email/html/raise_request_rejected.html
@@ -0,0 +1,57 @@
+<!--
+
+ TAM
+ Copyright (C) 2021 - Delft University of Technology
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+-->
+<!doctype html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" style="font-family: sans-serif">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ </head>
+
+ <body>
+ <div>
+ <div style="width: 100%; background-color: #00a8db">
+ <a th:href="#{general.app_url}" style="text-decoration: none">
+ <span style="color: white; font-size: 40px">TAM</span>
+ </a>
+ </div>
+
+ <div>
+ <div style="padding: 0.5em 0">
+ <p th:text="#{email.greeting(${name})}"></p>
+ <p
+ th:text="#{email.raiseRequests.rejected.message(${oldPayscale}, ${newPayscale})}"></p>
+ <p th:text="#{email.closing}"></p>
+ <p th:text="#{email.closing.person}"></p>
+ </div>
+
+ <div>
+ <p style="color: #707070">
+ <span th:text="#{email.notificationPreferences}"></span>
+ <a
+ style="color: #00a6d6; text-decoration: none"
+ target="_blank"
+ th:href="|#{general.app_url}/profile|"
+ th:text="#{email.notificationPreferences.here}"></a>
+ </p>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index 4a7999996efe92e9693e0f64a310b970a87a45c0..3c525a75212da670b8d30d84b96f7dc44839ab84 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -446,5 +446,7 @@ email.extraWork.unhandled.title = Unhandled Contract Requests
email.extraWork.unhandled.message = There are currently {0} unhandled contract requests for the following courses:
email.extraWork.unhandled.callToAction = Please review these contract requests as soon as possible.
email.raiseRequests.pending.message = There are currently pending raise requests. Please review these as soon as possible.
+email.raiseRequests.approved.message = Your raise request from {0} to {1} has been approved!
+email.raiseRequests.rejected.message = Your raise request from {0} to {1} has been rejected. Please contact your TA coordinator for more details.
search.people = Search people
\ No newline at end of file
diff --git a/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceMockTest.java b/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceMockTest.java
index 617ec48e482f557a6cadfc2cac1217bdb0a05c28..64e9a195d1b3178fd46a55e973c0676b50662699 100644
--- a/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceMockTest.java
+++ b/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceMockTest.java
@@ -37,6 +37,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import application.test.TestTAMApplication;
+import nl.tudelft.labracore.api.dto.PersonDetailsDTO;
import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
import nl.tudelft.labracore.api.dto.ProgramSummaryDTO;
import nl.tudelft.labracore.lib.security.user.Person;
@@ -71,6 +72,9 @@ class RaiseRequestServiceMockTest {
@MockBean
ProfileService profileService;
+ @MockBean
+ EmailService emailService;
+
@Autowired
ModelMapper mapper;
@@ -109,6 +113,8 @@ class RaiseRequestServiceMockTest {
void approve() {
when(raiseRequestRepository.findByIdOrThrow(anyLong())).thenReturn(request);
when(profileService.findByIdOrThrow(anyLong())).thenReturn(teacher);
+ when(profileService.findByIdOrCreate(anyLong())).thenReturn(student);
+ when(personService.getPersonById(anyLong())).thenReturn(new PersonDetailsDTO().displayName("Test").email("test@test.com"));
assertThat(student.getPayScale()).isEqualTo(PS_FROM);
assertThat(request.getStatus()).isEqualTo(Status.SUBMITTED);
@@ -126,6 +132,8 @@ class RaiseRequestServiceMockTest {
void reject() {
when(raiseRequestRepository.findByIdOrThrow(anyLong())).thenReturn(request);
when(profileService.findByIdOrThrow(anyLong())).thenReturn(teacher);
+ when(profileService.findByIdOrCreate(anyLong())).thenReturn(student);
+ when(personService.getPersonById(anyLong())).thenReturn(new PersonDetailsDTO().displayName("Test").email("test@test.com"));
assertThat(student.getPayScale()).isEqualTo(PS_FROM);
assertThat(request.getStatus()).isEqualTo(Status.SUBMITTED);