diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java
index c47d7446ce9..fadd698da1f 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java
@@ -1397,6 +1397,7 @@ public interface Strings {
String messagePersonListAddedAsEventPerticipants = "messagePersonListAddedAsEventPerticipants";
String messagePersonMergedAddressDescription = "messagePersonMergedAddressDescription";
String messagePersonMergeNoEventParticipantRights = "messagePersonMergeNoEventParticipantRights";
+ String messagePersonNationalHealthIdInvalid = "messagePersonNationalHealthIdInvalid";
String messagePersonSaved = "messagePersonSaved";
String messagePersonSavedClassificationChanged = "messagePersonSavedClassificationChanged";
String messagePickEventParticipantsIncompleteSelection = "messagePickEventParticipantsIncompleteSelection";
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Validations.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Validations.java
index 5fd5571e404..29d9dfc8231 100644
--- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Validations.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Validations.java
@@ -144,6 +144,7 @@ public interface Validations {
String importUnexpectedError = "importUnexpectedError";
String importWrongDataTypeError = "importWrongDataTypeError";
String infrastructureDataLocked = "infrastructureDataLocked";
+ String invalidNationalHealthId = "invalidNationalHealthId";
String investigationStatusUnclassifiedCase = "investigationStatusUnclassifiedCase";
String jurisdictionChangeUserAssignment = "jurisdictionChangeUserAssignment";
String latitudeBetween = "latitudeBetween";
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/CheckDigitLuhn.java b/sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/CheckDigitLuhn.java
similarity index 95%
rename from sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/CheckDigitLuhn.java
rename to sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/CheckDigitLuhn.java
index 698b79ce1b3..a9ba98dde08 100644
--- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/CheckDigitLuhn.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/CheckDigitLuhn.java
@@ -1,6 +1,6 @@
/*
* SORMAS® - Surveillance Outbreak Response Management & Analysis System
- * Copyright © 2016-2023 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
+ * Copyright © 2016-2024 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package de.symeda.sormas.backend.externalemail.luxembourg;
+package de.symeda.sormas.api.utils.luxembourg;
/**
* Apply Luhn algorithm to compute check digit
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/CheckDigitVerhoeff.java b/sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/CheckDigitVerhoeff.java
similarity index 98%
rename from sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/CheckDigitVerhoeff.java
rename to sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/CheckDigitVerhoeff.java
index c34cc70d58d..88620dcd123 100644
--- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/CheckDigitVerhoeff.java
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/CheckDigitVerhoeff.java
@@ -1,6 +1,6 @@
/*
* SORMAS® - Surveillance Outbreak Response Management & Analysis System
- * Copyright © 2016-2023 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
+ * Copyright © 2016-2024 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-package de.symeda.sormas.backend.externalemail.luxembourg;
+package de.symeda.sormas.api.utils.luxembourg;
/**
* Apply Verhoeff algorithm to compute check digit
diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/LuxembourgNationalHealthIdValidator.java b/sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/LuxembourgNationalHealthIdValidator.java
new file mode 100644
index 00000000000..a4d6fb87cf5
--- /dev/null
+++ b/sormas-api/src/main/java/de/symeda/sormas/api/utils/luxembourg/LuxembourgNationalHealthIdValidator.java
@@ -0,0 +1,67 @@
+/*
+ * SORMAS® - Surveillance Outbreak Response Management & Analysis System
+ * Copyright © 2016-2024 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.symeda.sormas.api.utils.luxembourg;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class LuxembourgNationalHealthIdValidator {
+
+ /**
+ * - AAAA = année de naissance
+ * - MM = mois de naissance
+ * - JJ = jour de naissance
+ * - XXX = numéro aléatoire unique par date de naissance
+ * - C1 = numéro de contrôle calculé sur AAAAMMJJXXX suivant l’algorithme LUHN 10
+ * - C2 = numéro de contrôle calculé sur AAAAMMJJXXX suivant l’algorithme VERHOEFF
+ */
+ private static final Pattern NATIONAL_HEALTH_ID_PATTERN = Pattern.compile("(\\d{4})(\\d{2})(\\d{2})(\\d{3})(\\d)(\\d)");
+
+ public static boolean isValid(String nationalHealthId, Integer birthdateYYYY, Integer birthdateMM, Integer birthdateDD) {
+ if (nationalHealthId == null) {
+ return false;
+ }
+
+ Matcher patternMatcher = NATIONAL_HEALTH_ID_PATTERN.matcher(nationalHealthId);
+ if (!patternMatcher.matches()) {
+ return false;
+ }
+
+ String yyyy = patternMatcher.group(1);
+ String mm = patternMatcher.group(2);
+ String dd = patternMatcher.group(3);
+ String xxx = patternMatcher.group(4);
+ String c1 = patternMatcher.group(5);
+ String c2 = patternMatcher.group(6);
+
+ if (isNullOrEquals(birthdateYYYY, Integer.parseInt(yyyy))
+ && isNullOrEquals(birthdateMM, Integer.parseInt(mm))
+ && isNullOrEquals(birthdateDD, Integer.parseInt(dd))) {
+ String iNumber = yyyy + mm + dd + xxx;
+
+ if (CheckDigitLuhn.checkDigit(iNumber + c1) && CheckDigitVerhoeff.checkDigit(iNumber + c2)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static boolean isNullOrEquals(Integer personBirthdateFieldValue, int yyyy) {
+ return personBirthdateFieldValue == null || personBirthdateFieldValue == yyyy;
+ }
+
+}
diff --git a/sormas-api/src/main/resources/strings.properties b/sormas-api/src/main/resources/strings.properties
index 8496db6f8aa..4c5d537912f 100644
--- a/sormas-api/src/main/resources/strings.properties
+++ b/sormas-api/src/main/resources/strings.properties
@@ -1498,6 +1498,7 @@ messageExternalEmailNoAttachments=No attachments
messageCustomizableEnumValueSaved = Customizable enum value saved
messageExternalEmailAttachmentPassword=Please use this password to open the documents sent to you via email from SORMAS: %s
messageExternalEmailAttachmentNotAvailableInfo=Attaching documents is disabled because encryption would not be possible. To encrypt documents, the person needs to have either a national health ID specified, or a primary mobile phone number set with SMS sending set up on this system.
+messagePersonNationalHealthIdInvalid=The entered national health ID does not seem to be correct
# Notifications
notificationCaseClassificationChanged = The classification of case %s has changed to %s.
notificationCaseInvestigationDone = The investigation of case %s has been done.
diff --git a/sormas-api/src/main/resources/validations.properties b/sormas-api/src/main/resources/validations.properties
index 5e92dd5e38c..2f55e0e2a24 100644
--- a/sormas-api/src/main/resources/validations.properties
+++ b/sormas-api/src/main/resources/validations.properties
@@ -291,4 +291,5 @@ customizableEnumValueAllowedCharacters = Value may only consist of uppercase let
customizableEnumValueEmptyTranslations = Please select languages and enter captions for all translations in the list.
customizableEnumValueDuplicateLanguage = Please only add one translation per language.
customizableEnumValueDuplicateValue = The value %s already exists for data type %s. Enum values have to be unique.
-attachedDocumentNotRelatedToEntity=The attached document is not related to the entity.
\ No newline at end of file
+attachedDocumentNotRelatedToEntity=The attached document is not related to the entity.
+invalidNationalHealthId=This value does not seem to be a correct national health ID
\ No newline at end of file
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjb.java
index aba01f432fe..3bd6cfe6825 100644
--- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjb.java
+++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjb.java
@@ -60,6 +60,7 @@
import de.symeda.sormas.api.travelentry.TravelEntryReferenceDto;
import de.symeda.sormas.api.utils.DataHelper.Pair;
import de.symeda.sormas.api.utils.ValidationRuntimeException;
+import de.symeda.sormas.api.utils.luxembourg.LuxembourgNationalHealthIdValidator;
import de.symeda.sormas.backend.caze.CaseService;
import de.symeda.sormas.backend.common.ConfigFacadeEjb.ConfigFacadeEjbLocal;
import de.symeda.sormas.backend.common.messaging.EmailService;
@@ -75,7 +76,6 @@
import de.symeda.sormas.backend.document.DocumentService;
import de.symeda.sormas.backend.document.DocumentStorageService;
import de.symeda.sormas.backend.event.EventParticipantService;
-import de.symeda.sormas.backend.externalemail.luxembourg.NationalHealthIdValidator;
import de.symeda.sormas.backend.manualmessagelog.ManualMessageLog;
import de.symeda.sormas.backend.manualmessagelog.ManualMessageLogService;
import de.symeda.sormas.backend.person.Person;
@@ -151,8 +151,8 @@ public void sendEmail(@Valid ExternalEmailOptionsDto options) throws DocumentTem
User currentUser = userService.getCurrentUser();
DocumentTemplateEntities documentEntities = templateEntitiesBuilder.resolveEntities(
- new RootEntities().addReference(options.getRootEntityType(), options.getRootEntityReference())
- .addEntity(RootEntityType.ROOT_USER, currentUser));
+ new RootEntities().addReference(options.getRootEntityType(), options.getRootEntityReference())
+ .addEntity(RootEntityType.ROOT_USER, currentUser));
PersonReferenceDto personRef = (PersonReferenceDto) documentEntities.getEntity(RootEntityType.ROOT_PERSON);
Person person = personService.getByReferenceDto(personRef);
@@ -176,7 +176,7 @@ public void sendEmail(@Valid ExternalEmailOptionsDto options) throws DocumentTem
}
String generatedText =
- documentTemplateFacade.generateDocumentTxtFromEntities(options.getDocumentWorkflow(), options.getTemplateName(), documentEntities, null);
+ documentTemplateFacade.generateDocumentTxtFromEntities(options.getDocumentWorkflow(), options.getTemplateName(), documentEntities, null);
EmailTemplateTexts emailTexts = splitTemplateContent(generatedText);
try {
@@ -184,10 +184,10 @@ public void sendEmail(@Valid ExternalEmailOptionsDto options) throws DocumentTem
if (passwordType == PasswordType.RANDOM) {
messagingService.sendManualMessage(
- person,
- null,
- String.format(I18nProperties.getString(Strings.messageExternalEmailAttachmentPassword), password),
- MessageType.SMS);
+ person,
+ null,
+ String.format(I18nProperties.getString(Strings.messageExternalEmailAttachmentPassword), password),
+ MessageType.SMS);
}
} catch (MessagingException | NotificationDeliveryFailedException e) {
logger.error("Error sending email", e);
@@ -201,9 +201,9 @@ public void sendEmail(@Valid ExternalEmailOptionsDto options) throws DocumentTem
private static void validateAttachedDocuments(List sormasDocuments, ExternalEmailOptionsDto options) {
DocumentRelatedEntityType documentRelatedEntityType = DOCUMENT_WORKFLOW_DOCUMENT_RELATION_MAPPING.get(options.getDocumentWorkflow());
if (sormasDocuments.stream()
- .anyMatch(
- d -> d.getRelatedEntityType() != documentRelatedEntityType
- && !Objects.equals(d.getRelatedEntityUuid(), options.getRootEntityReference().getUuid()))) {
+ .anyMatch(
+ d -> d.getRelatedEntityType() != documentRelatedEntityType
+ && !Objects.equals(d.getRelatedEntityUuid(), options.getRootEntityReference().getUuid()))) {
throw new ValidationRuntimeException(I18nProperties.getValidationError(Validations.attachedDocumentNotRelatedToEntity));
}
}
@@ -238,30 +238,30 @@ private Pair getPassword(Person person) throws ExternalEma
PasswordType passwordType = getApplicablePasswordType(person);
switch (passwordType) {
- case HEALTH_ID:
- return new Pair<>(person.getNationalHealthId(), passwordType);
- case RANDOM:
- return new Pair<>(generateRandomPassword(), passwordType);
- case NONE:
- default:
- throw new ExternalEmailException(I18nProperties.getString(Strings.errorExternalEmailAttachmentCannotEncrypt));
+ case HEALTH_ID:
+ return new Pair<>(person.getNationalHealthId(), passwordType);
+ case RANDOM:
+ return new Pair<>(generateRandomPassword(), passwordType);
+ case NONE:
+ default:
+ throw new ExternalEmailException(I18nProperties.getString(Strings.errorExternalEmailAttachmentCannotEncrypt));
}
}
private static String generateRandomPassword() {
return new RandomStringGenerator.Builder().withinRange(
- new char[]{
- 'a',
- 'z'},
- new char[]{
- 'A',
- 'Z'},
- new char[]{
- '2',
- '9'})
- .filteredBy(codePoint -> !"lIO".contains(String.valueOf((char) codePoint)))
- .build()
- .generate(ATTACHMENT_PASSWORD_LENGTH);
+ new char[] {
+ 'a',
+ 'z' },
+ new char[] {
+ 'A',
+ 'Z' },
+ new char[] {
+ '2',
+ '9' })
+ .filteredBy(codePoint -> !"lIO".contains(String.valueOf((char) codePoint)))
+ .build()
+ .generate(ATTACHMENT_PASSWORD_LENGTH);
}
private static void validateOptions(ExternalEmailOptionsDto options) {
@@ -272,10 +272,10 @@ private static void validateOptions(ExternalEmailOptionsDto options) {
}
private ManualMessageLog createMessageLog(
- ExternalEmailOptionsDto options,
- PersonReferenceDto personRef,
- User currentUser,
- List attachedDocuments) {
+ ExternalEmailOptionsDto options,
+ PersonReferenceDto personRef,
+ User currentUser,
+ List attachedDocuments) {
ManualMessageLog log = new ManualMessageLog();
log.setMessageType(MessageType.EMAIL);
@@ -290,10 +290,10 @@ private ManualMessageLog createMessageLog(
log.setCaze(caseService.getByReferenceDto(getRootEntityReference(options, RootEntityType.ROOT_CASE, CaseReferenceDto.class)));
log.setContact(contactService.getByReferenceDto(getRootEntityReference(options, RootEntityType.ROOT_CONTACT, ContactReferenceDto.class)));
log.setEventParticipant(
- eventParticipantService
- .getByReferenceDto(getRootEntityReference(options, RootEntityType.ROOT_EVENT_PARTICIPANT, EventParticipantReferenceDto.class)));
+ eventParticipantService
+ .getByReferenceDto(getRootEntityReference(options, RootEntityType.ROOT_EVENT_PARTICIPANT, EventParticipantReferenceDto.class)));
log.setTravelEntry(
- travelEntryService.getByReferenceDto(getRootEntityReference(options, RootEntityType.ROOT_TRAVEL_ENTRY, TravelEntryReferenceDto.class)));
+ travelEntryService.getByReferenceDto(getRootEntityReference(options, RootEntityType.ROOT_TRAVEL_ENTRY, TravelEntryReferenceDto.class)));
return log;
}
@@ -312,7 +312,7 @@ private static boolean isValidLuxembourgNationalHealthId(String nationalHealthId
return false;
}
- return NationalHealthIdValidator.isValid(nationalHealthId, person);
+ return LuxembourgNationalHealthIdValidator.isValid(nationalHealthId, person.getBirthdateYYYY(), person.getBirthdateMM(), person.getBirthdateDD());
}
@Stateless
diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/NationalHealthIdValidator.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/NationalHealthIdValidator.java
deleted file mode 100644
index cbb3e995f4e..00000000000
--- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalemail/luxembourg/NationalHealthIdValidator.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SORMAS® - Surveillance Outbreak Response Management & Analysis System
- * Copyright © 2016-2023 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package de.symeda.sormas.backend.externalemail.luxembourg;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import de.symeda.sormas.backend.person.Person;
-
-public class NationalHealthIdValidator {
-
- /**
- * - AAAA = année de naissance
- * - MM = mois de naissance
- * - JJ = jour de naissance
- * - XXX = numéro aléatoire unique par date de naissance
- * - C1 = numéro de contrôle calculé sur AAAAMMJJXXX suivant l’algorithme LUHN 10
- * - C2 = numéro de contrôle calculé sur AAAAMMJJXXX suivant l’algorithme VERHOEFF
- */
- private static final Pattern NATIONAL_HEALTH_ID_PATTERN = Pattern.compile("(\\d{4})(\\d{2})(\\d{2})(\\d{3})(\\d)(\\d)");
-
- public static boolean isValid(String nationalHealthId, Person person) {
- if (nationalHealthId == null) {
- return false;
- }
-
- Matcher patternMatcher = NATIONAL_HEALTH_ID_PATTERN.matcher(nationalHealthId);
- if (!patternMatcher.matches()) {
- return false;
- }
-
- String yyyy = patternMatcher.group(1);
- String mm = patternMatcher.group(2);
- String dd = patternMatcher.group(3);
- String xxx = patternMatcher.group(4);
- String c1 = patternMatcher.group(5);
- String c2 = patternMatcher.group(6);
-
- if (isNullOrEquals(person.getBirthdateYYYY(), Integer.parseInt(yyyy))
- && isNullOrEquals(person.getBirthdateMM(), Integer.parseInt(mm))
- && isNullOrEquals(person.getBirthdateDD(), Integer.parseInt(dd))) {
- String iNumber = yyyy + mm + dd + xxx;
-
- if (CheckDigitLuhn.checkDigit(iNumber + c1) && CheckDigitVerhoeff.checkDigit(iNumber + c2)) {
- return true;
- }
- }
-
- return false;
- }
-
- private static boolean isNullOrEquals(Integer personBirthdateFieldValue, int yyyy) {
- return personBirthdateFieldValue == null || personBirthdateFieldValue == yyyy;
- }
-
-}
diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjbTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjbTest.java
index f5f60e6eb76..bf07b5b8738 100644
--- a/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjbTest.java
+++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/ExternalEmailFacadeEjbTest.java
@@ -16,7 +16,7 @@
package de.symeda.sormas.backend.externalemail;
import static de.symeda.sormas.backend.docgeneration.TemplateTestUtil.updateLineSeparatorsBasedOnOS;
-import static de.symeda.sormas.backend.externalemail.luxembourg.NationalHealthIdValidatorTest.VALID_LU_NATIONAL_HEALTH_ID;
+import static de.symeda.sormas.backend.util.luxembourg.LuxembourgNationalHealthIdValidatorTest.VALID_LU_NATIONAL_HEALTH_ID;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasItem;
diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/luxembourg/NationalHealthIdValidatorTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/luxembourg/NationalHealthIdValidatorTest.java
deleted file mode 100644
index cf4d55517c7..00000000000
--- a/sormas-backend/src/test/java/de/symeda/sormas/backend/externalemail/luxembourg/NationalHealthIdValidatorTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * SORMAS® - Surveillance Outbreak Response Management & Analysis System
- * Copyright © 2016-2023 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package de.symeda.sormas.backend.externalemail.luxembourg;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-import org.jetbrains.annotations.NotNull;
-import org.junit.jupiter.api.Test;
-
-import de.symeda.sormas.backend.person.Person;
-
-public class NationalHealthIdValidatorTest {
-
- public static final String VALID_LU_NATIONAL_HEALTH_ID = "1980010145728";
-
- @Test
- public void testIsValid() {
- assertThat(NationalHealthIdValidator.isValid("19800101", createPerson(1980, 1, 1)), is(false));
- assertThat(NationalHealthIdValidator.isValid("1980Jan0145728", createPerson(1980, 1, 1)), is(false));
- assertThat(NationalHealthIdValidator.isValid("1980Ja0145728", createPerson(1980, 1, 1)), is(false));
-
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(1980, 1, 1)), is(true));
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(null, 1, 1)), is(true));
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(1980, 1, null)), is(true));
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(1980, null, 1)), is(true));
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(1980, null, null)), is(true));
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(null, null, null)), is(true));
-
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(1981, 1, 1)), is(false));
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(1980, 2, 1)), is(false));
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(1980, 1, 2)), is(false));
- assertThat(NationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, createPerson(1981, 1, null)), is(false));
-
- assertThat(NationalHealthIdValidator.isValid("1980010145628", createPerson(1980, 1, 1)), is(false));
- assertThat(NationalHealthIdValidator.isValid("1980010145718", createPerson(1980, 1, 1)), is(false));
- assertThat(NationalHealthIdValidator.isValid("1980010145723", createPerson(1980, 1, 1)), is(false));
- assertThat(NationalHealthIdValidator.isValid("1980010345728", createPerson(1980, 1, null)), is(false));
- }
-
- @NotNull
- private static Person createPerson(Integer birthdateYYYY, Integer birthdateMM, Integer birthdateDD) {
- Person person = new Person();
- person.setBirthdateYYYY(birthdateYYYY);
- person.setBirthdateMM(birthdateMM);
- person.setBirthdateDD(birthdateDD);
-
- return person;
- }
-}
diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/util/luxembourg/LuxembourgNationalHealthIdValidatorTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/util/luxembourg/LuxembourgNationalHealthIdValidatorTest.java
new file mode 100644
index 00000000000..9955063e320
--- /dev/null
+++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/util/luxembourg/LuxembourgNationalHealthIdValidatorTest.java
@@ -0,0 +1,55 @@
+/*
+ * SORMAS® - Surveillance Outbreak Response Management & Analysis System
+ * Copyright © 2016-2024 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.symeda.sormas.backend.util.luxembourg;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.jupiter.api.Test;
+
+import de.symeda.sormas.api.utils.luxembourg.LuxembourgNationalHealthIdValidator;
+
+public class LuxembourgNationalHealthIdValidatorTest {
+
+ public static final String VALID_LU_NATIONAL_HEALTH_ID = "1980010145728";
+
+ @Test
+ public void testIsValid() {
+ assertThat(LuxembourgNationalHealthIdValidator.isValid("19800101", 1980, 1, 1), is(false));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid("1980Jan0145728", 1980, 1, 1), is(false));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid("1980Ja0145728", 1980, 1, 1), is(false));
+
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, 1980, 1, 1), is(true));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, null, 1, 1), is(true));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, 1980, 1, null), is(true));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, 1980, null, 1), is(true));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, 1980, null, null), is(true));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, null, null, null), is(true));
+
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, 1981, 1, 1), is(false));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, 1980, 2, 1), is(false));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, 1980, 1, 2), is(false));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid(VALID_LU_NATIONAL_HEALTH_ID, 1981, 1, null), is(false));
+
+ assertThat(LuxembourgNationalHealthIdValidator.isValid("1980010145628", 1980, 1, 1), is(false));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid("1980010145718", 1980, 1, 1), is(false));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid("1980010145723", 1980, 1, 1), is(false));
+ assertThat(LuxembourgNationalHealthIdValidator.isValid("1980010345728", 1980, 1, null), is(false));
+ }
+}
diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java
index 744498cc7b7..035088d6a8f 100644
--- a/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java
+++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java
@@ -37,6 +37,7 @@
import org.apache.commons.lang3.StringUtils;
+import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.ui.CustomLayout;
import com.vaadin.ui.Label;
import com.vaadin.v7.data.Item;
@@ -49,6 +50,7 @@
import com.vaadin.v7.ui.TextArea;
import com.vaadin.v7.ui.TextField;
+import de.symeda.sormas.api.CountryHelper;
import de.symeda.sormas.api.Disease;
import de.symeda.sormas.api.FacadeProvider;
import de.symeda.sormas.api.caze.CaseDataDto;
@@ -79,6 +81,7 @@
import de.symeda.sormas.api.utils.fieldaccess.UiFieldAccessCheckers;
import de.symeda.sormas.api.utils.fieldvisibility.FieldVisibilityCheckers;
import de.symeda.sormas.api.utils.fieldvisibility.checkers.CountryFieldVisibilityChecker;
+import de.symeda.sormas.api.utils.luxembourg.LuxembourgNationalHealthIdValidator;
import de.symeda.sormas.ui.ControllerProvider;
import de.symeda.sormas.ui.UserProvider;
import de.symeda.sormas.ui.location.LocationEditForm;
@@ -103,6 +106,7 @@ public class PersonEditForm extends AbstractEditForm {
private static final String ADDRESSES_HEADER = "addressesHeader";
private static final String CONTACT_INFORMATION_HEADER = "contactInformationHeader";
private static final String EXTERNAL_TOKEN_WARNING_LOC = "externalTokenWarningLoc";
+ private static final String NATIONAL_HEALTH_ID_WARNING_LABEL = "nationalHealthIdWarningLoc";
private static final String GENERAL_COMMENT_LOC = "generalCommentLoc";
//@formatter:off
private static final String HTML_LAYOUT =
@@ -134,6 +138,7 @@ public class PersonEditForm extends AbstractEditForm {
oneOfTwoCol(PersonDto.BURIAL_PLACE_DESCRIPTION)
) +
fluidRowLocs(PersonDto.PASSPORT_NUMBER, PersonDto.NATIONAL_HEALTH_ID) +
+ fluidRowLocs("", NATIONAL_HEALTH_ID_WARNING_LABEL) +
fluidRowLocs(PersonDto.EXTERNAL_ID, PersonDto.EXTERNAL_TOKEN) +
fluidRowLocs(PersonDto.INTERNAL_TOKEN, EXTERNAL_TOKEN_WARNING_LOC) +
@@ -366,7 +371,13 @@ protected void addFields() {
addInfrastructureField(PersonDto.BIRTH_COUNTRY).addItems(countries);
addInfrastructureField(PersonDto.CITIZENSHIP).addItems(countries);
- addFields(PersonDto.PASSPORT_NUMBER, PersonDto.NATIONAL_HEALTH_ID);
+ addField(PersonDto.PASSPORT_NUMBER);
+
+ TextField nationalHealthIdField = addField(PersonDto.NATIONAL_HEALTH_ID);
+ Label nationalHealthIdWarningLabel = new Label(I18nProperties.getString(Strings.messagePersonNationalHealthIdInvalid));
+ nationalHealthIdWarningLabel.addStyleNames(VSPACE_3, LABEL_WHITE_SPACE_NORMAL);
+ getContent().addComponent(nationalHealthIdWarningLabel, NATIONAL_HEALTH_ID_WARNING_LABEL);
+
Field externalId = addField(PersonDto.EXTERNAL_ID);
if (FacadeProvider.getExternalSurveillanceToolFacade().isFeatureEnabled()) {
externalId.setEnabled(false);
@@ -566,13 +577,24 @@ protected void addFields() {
burialDate.setValidationVisible(!burialDate.isValid());
});
- addValueChangeListener((e) -> {
+ addValueChangeListener(e -> {
ValidationUtils.initComponentErrorValidator(
externalTokenField,
getValue().getExternalToken(),
Validations.duplicateExternalToken,
externalTokenWarningLabel,
- (externalToken) -> FacadeProvider.getPersonFacade().doesExternalTokenExist(externalToken, getValue().getUuid()));
+ externalToken -> FacadeProvider.getPersonFacade().doesExternalTokenExist(externalToken, getValue().getUuid()));
+
+ if (FacadeProvider.getConfigFacade().isConfiguredCountry(CountryHelper.COUNTRY_CODE_LUXEMBOURG)) {
+ ValidationUtils.initComponentErrorValidator(
+ nationalHealthIdField,
+ getValue().getNationalHealthId(),
+ Validations.invalidNationalHealthId,
+ nationalHealthIdWarningLabel,
+ nationalHealthId -> !LuxembourgNationalHealthIdValidator
+ .isValid(nationalHealthId, (Integer)birthDateYear.getValue(), (Integer)birthDateMonth.getValue(), (Integer)birthDateDay.getValue()),
+ ErrorLevel.WARNING);
+ }
personContactDetailsField.setThisPerson((PersonDto) e.getProperty().getValue());
});
diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/ValidationUtils.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/ValidationUtils.java
index fd9a40c07bc..8a98ab668f8 100644
--- a/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/ValidationUtils.java
+++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/ValidationUtils.java
@@ -20,7 +20,9 @@
import org.apache.commons.lang3.StringUtils;
import com.vaadin.server.UserError;
+import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.ui.Label;
+import com.vaadin.v7.data.Property;
import com.vaadin.v7.ui.TextField;
import de.symeda.sormas.api.i18n.I18nProperties;
@@ -36,13 +38,31 @@ public static void initComponentErrorValidator(
String validationMessageTag,
Label warningLabel,
Function exists) {
+ initComponentErrorValidator(field, initialFieldValue, validationMessageTag, warningLabel, exists, ErrorLevel.ERROR);
+ }
+
+ public static void initComponentErrorValidator(
+ TextField field,
+ String initialFieldValue,
+ String validationMessageTag,
+ Label warningLabel,
+ Function isInvlaid,
+ ErrorLevel errorLevel) {
if (field == null) {
return;
}
+ // already initialized
+ if (field.getData() != null) {
+ // remove old listener
+ if (Property.ValueChangeListener.class.isAssignableFrom(field.getData().getClass())) {
+ field.removeValueChangeListener((Property.ValueChangeListener) field.getData());
+ }
+ }
+
Function validateExternalToken = (String value) -> {
- if (field.isVisible() && !StringUtils.isEmpty(value) && exists.apply(value)) {
- field.setComponentError(new UserError(I18nProperties.getValidationError(validationMessageTag)));
+ if (field.isVisible() && !StringUtils.isEmpty(value) && isInvlaid.apply(value)) {
+ field.setComponentError(new UserError(I18nProperties.getValidationError(validationMessageTag), null, errorLevel));
warningLabel.setVisible(true);
} else {
field.setComponentError(null);
@@ -53,6 +73,8 @@ public static void initComponentErrorValidator(
};
validateExternalToken.apply(initialFieldValue);
- field.addValueChangeListener(f -> validateExternalToken.apply((String) f.getProperty().getValue()));
+ Property.ValueChangeListener valueChangeListener = f -> validateExternalToken.apply((String) f.getProperty().getValue());
+ field.addValueChangeListener(valueChangeListener);
+ field.setData(valueChangeListener);
}
}