From 3a34acd6cd3724a5c47f96224deac7a5f4ad17b5 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Mon, 6 Jul 2026 15:12:13 +0200 Subject: [PATCH 01/13] FIX: Difficulty to process some messages --- .../ExternalMessageFacade.java | 9 ++++++++ .../src/main/resources/strings.properties | 2 +- .../main/resources/strings_ar-SA.properties | 2 +- .../main/resources/strings_cs-CZ.properties | 2 +- .../main/resources/strings_de-CH.properties | 2 +- .../main/resources/strings_de-DE.properties | 2 +- .../main/resources/strings_en-AF.properties | 2 +- .../main/resources/strings_en-GH.properties | 2 +- .../main/resources/strings_en-GM.properties | 2 +- .../main/resources/strings_en-KE.properties | 2 +- .../main/resources/strings_en-LR.properties | 2 +- .../main/resources/strings_en-NG.properties | 2 +- .../main/resources/strings_es-BO.properties | 2 +- .../main/resources/strings_fa-AF.properties | 2 +- .../main/resources/strings_fi-FI.properties | 2 +- .../main/resources/strings_fr-CH.properties | 2 +- .../main/resources/strings_fr-FR.properties | 2 +- .../main/resources/strings_fr-TN.properties | 2 +- .../main/resources/strings_it-CH.properties | 2 +- .../main/resources/strings_it-IT.properties | 2 +- .../main/resources/strings_ka-GE.properties | 2 +- .../main/resources/strings_pl-PL.properties | 2 +- .../main/resources/strings_ps-AF.properties | 2 +- .../main/resources/strings_pt-BR.properties | 2 +- .../main/resources/strings_pt-CV.properties | 2 +- .../main/resources/strings_ru-RU.properties | 2 +- .../main/resources/strings_sr-CS.properties | 2 +- .../main/resources/strings_sw-TZ.properties | 2 +- .../main/resources/strings_ur-PK.properties | 2 +- .../main/resources/strings_zh-CN.properties | 2 +- .../ExternalMessageFacadeEjb.java | 22 ++++++++++++------ .../AutomaticSurveyResponseProcessor.java | 12 ++++++++-- .../sormas/backend/patch/DataPatcherImpl.java | 6 +---- .../ExternalMessageController.java | 14 ++++++++--- .../SurveyResponseDetailsWindow.java | 23 ++++++++++++++++--- 35 files changed, 95 insertions(+), 49 deletions(-) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageFacade.java index f06be399ca6..b3e88ee2ce7 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageFacade.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageFacade.java @@ -87,6 +87,15 @@ default List saveAndProcessSurveyResponses() { ExternalMessageDto getForSurveillanceReport(SurveillanceReportReferenceDto surveillanceReport); + /** + * Re-submits a survey response. + * + * @param uuid + * UUID of the external message (must be of type SURVEY_RESPONSE) + * @return updated ExternalMessageDto after reprocessing + */ + ExternalMessageDto reAttemptSurveyProcessing(String uuid); + /** * Re-submits a survey response with a corrected patch dictionary after previous processing failures. * diff --git a/sormas-api/src/main/resources/strings.properties b/sormas-api/src/main/resources/strings.properties index f9eecd5320d..dfd9f3b4dfb 100644 --- a/sormas-api/src/main/resources/strings.properties +++ b/sormas-api/src/main/resources/strings.properties @@ -2096,7 +2096,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_ar-SA.properties b/sormas-api/src/main/resources/strings_ar-SA.properties index e66ac526392..cffdd1836f2 100644 --- a/sormas-api/src/main/resources/strings_ar-SA.properties +++ b/sormas-api/src/main/resources/strings_ar-SA.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_cs-CZ.properties b/sormas-api/src/main/resources/strings_cs-CZ.properties index 508402702e6..6132c39153b 100644 --- a/sormas-api/src/main/resources/strings_cs-CZ.properties +++ b/sormas-api/src/main/resources/strings_cs-CZ.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_de-CH.properties b/sormas-api/src/main/resources/strings_de-CH.properties index bf769252d06..d36ee009ed9 100644 --- a/sormas-api/src/main/resources/strings_de-CH.properties +++ b/sormas-api/src/main/resources/strings_de-CH.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_de-DE.properties b/sormas-api/src/main/resources/strings_de-DE.properties index e7ff782bf37..d3055de2c66 100644 --- a/sormas-api/src/main/resources/strings_de-DE.properties +++ b/sormas-api/src/main/resources/strings_de-DE.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_en-AF.properties b/sormas-api/src/main/resources/strings_en-AF.properties index e1bdb0f2cfc..d1f31c28cbc 100644 --- a/sormas-api/src/main/resources/strings_en-AF.properties +++ b/sormas-api/src/main/resources/strings_en-AF.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_en-GH.properties b/sormas-api/src/main/resources/strings_en-GH.properties index 3364a5a0167..c1388d08a78 100644 --- a/sormas-api/src/main/resources/strings_en-GH.properties +++ b/sormas-api/src/main/resources/strings_en-GH.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_en-GM.properties b/sormas-api/src/main/resources/strings_en-GM.properties index f76d60d0791..024f2556465 100644 --- a/sormas-api/src/main/resources/strings_en-GM.properties +++ b/sormas-api/src/main/resources/strings_en-GM.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_en-KE.properties b/sormas-api/src/main/resources/strings_en-KE.properties index 34e28924c8f..c4ef262e4a3 100644 --- a/sormas-api/src/main/resources/strings_en-KE.properties +++ b/sormas-api/src/main/resources/strings_en-KE.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_en-LR.properties b/sormas-api/src/main/resources/strings_en-LR.properties index f76d60d0791..024f2556465 100644 --- a/sormas-api/src/main/resources/strings_en-LR.properties +++ b/sormas-api/src/main/resources/strings_en-LR.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_en-NG.properties b/sormas-api/src/main/resources/strings_en-NG.properties index 9f5ef764206..1d9527cb7ba 100644 --- a/sormas-api/src/main/resources/strings_en-NG.properties +++ b/sormas-api/src/main/resources/strings_en-NG.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_es-BO.properties b/sormas-api/src/main/resources/strings_es-BO.properties index e3450b74415..d1549ec31ff 100644 --- a/sormas-api/src/main/resources/strings_es-BO.properties +++ b/sormas-api/src/main/resources/strings_es-BO.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_fa-AF.properties b/sormas-api/src/main/resources/strings_fa-AF.properties index ffca4e8ef2a..261f01a20cc 100644 --- a/sormas-api/src/main/resources/strings_fa-AF.properties +++ b/sormas-api/src/main/resources/strings_fa-AF.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_fi-FI.properties b/sormas-api/src/main/resources/strings_fi-FI.properties index 84d3e7268d7..9a627e94e45 100644 --- a/sormas-api/src/main/resources/strings_fi-FI.properties +++ b/sormas-api/src/main/resources/strings_fi-FI.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_fr-CH.properties b/sormas-api/src/main/resources/strings_fr-CH.properties index 587a610f5e3..45e53dc51b7 100644 --- a/sormas-api/src/main/resources/strings_fr-CH.properties +++ b/sormas-api/src/main/resources/strings_fr-CH.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_fr-FR.properties b/sormas-api/src/main/resources/strings_fr-FR.properties index 5f3ef79ab66..3eb1e0d82aa 100644 --- a/sormas-api/src/main/resources/strings_fr-FR.properties +++ b/sormas-api/src/main/resources/strings_fr-FR.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_fr-TN.properties b/sormas-api/src/main/resources/strings_fr-TN.properties index bb1d14b1aee..6f311982291 100644 --- a/sormas-api/src/main/resources/strings_fr-TN.properties +++ b/sormas-api/src/main/resources/strings_fr-TN.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_it-CH.properties b/sormas-api/src/main/resources/strings_it-CH.properties index 39f13764d62..8bc6d07b335 100644 --- a/sormas-api/src/main/resources/strings_it-CH.properties +++ b/sormas-api/src/main/resources/strings_it-CH.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_it-IT.properties b/sormas-api/src/main/resources/strings_it-IT.properties index 6654edba704..76050208928 100644 --- a/sormas-api/src/main/resources/strings_it-IT.properties +++ b/sormas-api/src/main/resources/strings_it-IT.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_ka-GE.properties b/sormas-api/src/main/resources/strings_ka-GE.properties index 7eb86d844f5..aed47e4bcae 100644 --- a/sormas-api/src/main/resources/strings_ka-GE.properties +++ b/sormas-api/src/main/resources/strings_ka-GE.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_pl-PL.properties b/sormas-api/src/main/resources/strings_pl-PL.properties index 9f8c44484f4..653c8cdead2 100644 --- a/sormas-api/src/main/resources/strings_pl-PL.properties +++ b/sormas-api/src/main/resources/strings_pl-PL.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_ps-AF.properties b/sormas-api/src/main/resources/strings_ps-AF.properties index a63dd9b73cc..df6aa6a20cf 100644 --- a/sormas-api/src/main/resources/strings_ps-AF.properties +++ b/sormas-api/src/main/resources/strings_ps-AF.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_pt-BR.properties b/sormas-api/src/main/resources/strings_pt-BR.properties index 4b5087d67ab..1a97375b149 100644 --- a/sormas-api/src/main/resources/strings_pt-BR.properties +++ b/sormas-api/src/main/resources/strings_pt-BR.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_pt-CV.properties b/sormas-api/src/main/resources/strings_pt-CV.properties index de5920a3e70..0750dc40a19 100644 --- a/sormas-api/src/main/resources/strings_pt-CV.properties +++ b/sormas-api/src/main/resources/strings_pt-CV.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_ru-RU.properties b/sormas-api/src/main/resources/strings_ru-RU.properties index fe1a46a8f3d..1adce04f2d5 100644 --- a/sormas-api/src/main/resources/strings_ru-RU.properties +++ b/sormas-api/src/main/resources/strings_ru-RU.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_sr-CS.properties b/sormas-api/src/main/resources/strings_sr-CS.properties index 369652d2ae4..ff75118548f 100644 --- a/sormas-api/src/main/resources/strings_sr-CS.properties +++ b/sormas-api/src/main/resources/strings_sr-CS.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_sw-TZ.properties b/sormas-api/src/main/resources/strings_sw-TZ.properties index 7eb86d844f5..aed47e4bcae 100644 --- a/sormas-api/src/main/resources/strings_sw-TZ.properties +++ b/sormas-api/src/main/resources/strings_sw-TZ.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_ur-PK.properties b/sormas-api/src/main/resources/strings_ur-PK.properties index 48ec95b07b8..bb6c9694c47 100644 --- a/sormas-api/src/main/resources/strings_ur-PK.properties +++ b/sormas-api/src/main/resources/strings_ur-PK.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-api/src/main/resources/strings_zh-CN.properties b/sormas-api/src/main/resources/strings_zh-CN.properties index 9244eaa6899..8b13bdf5619 100644 --- a/sormas-api/src/main/resources/strings_zh-CN.properties +++ b/sormas-api/src/main/resources/strings_zh-CN.properties @@ -2095,7 +2095,7 @@ headingSurveyResponseDetails=Response Details headingSurveyResponseFailures=Validation Issues headingSurveyResponseCorrectAndReprocess=Correct & Reprocess messageSurveyResponseAllFieldsApplied=All fields were successfully applied -messageSurveyResponseNotYetProcessed=This response has not been processed yet +messageSurveyResponseNotYetProcessed=There was an issue when building the survey request, please fetch the surveys again or wait for the next automatic fetch messageSurveyResponseReprocessed=The response has been successfully reprocessed messageSurveyResponseReprocessedFailure=There was a failure when reprocessing the response, please try to edit it again to check what caused the issue. diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java index df8c3358292..ea0a928aa38 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nullable; import javax.annotation.security.PermitAll; import javax.ejb.EJB; import javax.ejb.LocalBean; @@ -50,7 +51,6 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.Selection; -import javax.transaction.Transactional; import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -330,9 +330,6 @@ private ExternalMessageDto saveWithFallback(ExternalMessageDto dto) { @Override @RightsAllowed(UserRight._EXTERNAL_MESSAGE_SURVEY_RESPONSE_PROCESS) - @Transactional(value = Transactional.TxType.REQUIRES_NEW, - rollbackOn = { - Exception.class }) public List saveAndProcessSurveyResponses(Date since) { if (since == null) { @@ -402,7 +399,10 @@ public List saveAndProcessSurveyResponses(Date since) { } catch (ExecutionException e) { logger.error("Could not process survey responses with UUID [{}]", extractUuids(filteredSurveyResponses), e); } finally { - savedDtos = filteredSurveyResponses.stream().map(this::save).collect(toList()); + savedDtos = filteredSurveyResponses.stream() + .peek(externalMessageDto -> externalMessageDto.setChangeDate(new Date())) + .map(this::save) + .collect(toList()); } if (logger.isTraceEnabled()) { @@ -1044,7 +1044,14 @@ public ExternalMessageDto getForSurveillanceReport(SurveillanceReportReferenceDt @Override @RightsAllowed(UserRight._EXTERNAL_MESSAGE_SURVEY_RESPONSE_PROCESS) - public ExternalMessageDto overwriteSurveyResponse(String uuid, PatchDictionary correctedDictionary) { + public ExternalMessageDto reAttemptSurveyProcessing(String uuid) { + logger.debug("reAttemptSurveyProcessing: [{}]", uuid); + return overwriteSurveyResponse(uuid, null); + } + + @Override + @RightsAllowed(UserRight._EXTERNAL_MESSAGE_SURVEY_RESPONSE_PROCESS) + public ExternalMessageDto overwriteSurveyResponse(String uuid, @Nullable PatchDictionary correctedDictionary) { logger.debug("overwriteSurveyResponse: [{}],[{}]", uuid, correctedDictionary); ExternalMessageDto externalMessage = getByUuid(uuid); ExternalMessageSurveyResponseRequest latestRequest = externalMessage.getSurveyResponseData().getLatest().getRequest(); @@ -1063,13 +1070,14 @@ public ExternalMessageDto overwriteSurveyResponse(String uuid, PatchDictionary c .setAllowFallbackValues(latestRequest.isAllowFallbackValues()) .setSkipIfAlreadyProcessed(latestRequest.isSkipIfAlreadyProcessed()) .setPatchedInCaseOfFailures(latestRequest.isPatchedInCaseOfFailures()) - .setPatchDictionary(correctedDictionary) + .setPatchDictionary(correctedDictionary != null ? correctedDictionary : latestRequest.getPatchDictionary()) .setExcludedPatchDictionary(latestRequest.getExcludedPatchDictionary()); logger.debug("Request after transformation: [{}]", correctedRequest); ExternalMessageSurveyResponseWrapper updatedWrapper = new ExternalMessageSurveyResponseWrapper().setRequest(correctedRequest); externalMessage.getSurveyResponseData().setUpdated(updatedWrapper); + externalMessage.setChangeDate(new Date()); ExternalMessageDto result; try { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java index 588ad2cb0bd..d9ec0a09c3e 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; @@ -11,7 +12,8 @@ import javax.ejb.EJB; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; -import javax.transaction.Transactional; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; import org.apache.commons.collections4.CollectionUtils; import org.jetbrains.annotations.NotNull; @@ -33,6 +35,7 @@ import de.symeda.sormas.backend.survey.SurveyFacadeEjb; import de.symeda.sormas.backend.survey.SurveyTokenFacadeEjb; import de.symeda.sormas.backend.util.CollectorUtils; +import de.symeda.sormas.backend.util.ModelConstants; /** * Performs the coordinating for patch operations out of Survey-responses. @@ -51,6 +54,9 @@ public class AutomaticSurveyResponseProcessor { @EJB private SurveyTokenFacadeEjb.SurveyTokenFacadeEjbLocal surveyTokenFacade; + @PersistenceContext(unitName = ModelConstants.PERSISTENCE_UNIT_NAME) + private EntityManager em; + public AutomaticSurveyResponseProcessor() { } @@ -63,7 +69,6 @@ public AutomaticSurveyResponseProcessor( this.surveyTokenFacade = surveyTokenFacade; } - @Transactional(Transactional.TxType.REQUIRES_NEW) public List processSurveyResponses(List externalMessages) throws InterruptedException, ExecutionException { @@ -120,6 +125,7 @@ public List processSurveyResponses(List processSurveyResponses(List patchingTuples = computePatchingTuples(request); - logger.trace("Computed patchingTuples: [{}]", patchingTuples); + logger.debug("Computed patchingTuples: [{}]", patchingTuples); Predicate customizableFieldsPredicate = patchResult -> patchResult.getField().getField().contains(PatchFieldHelper.CUSTOM_PREFIX); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java index a18982f824d..e877e7c7db4 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java @@ -168,9 +168,17 @@ public void processSurveyResponse(String surveyResponseMessageUuid) { ? externalMessage.getSurveyResponseData().getLatest().getResult() : null; + // when button click: if (result == null) { - Notification.show(I18nProperties.getString(Strings.messageSurveyResponseNotYetProcessed), Notification.Type.HUMANIZED_MESSAGE); - return; + try { + result = FacadeProvider.getExternalMessageFacade() + .reAttemptSurveyProcessing(surveyResponseMessageUuid) + .getSurveyResponseData() + .getLatest() + .getResult(); + } catch (RuntimeException e) { + Notification.show(I18nProperties.getString(Strings.messageSurveyResponseNotYetProcessed), Notification.Type.HUMANIZED_MESSAGE); + } } if (result.getPatchResponse() != null && result.getPatchResponse().hasFailures()) { @@ -607,4 +615,4 @@ private Consumer> bulkOperationCallback( } }; } -} \ No newline at end of file +} diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java index 1adbf3ad37a..e48b1dffea7 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java @@ -21,14 +21,24 @@ import com.vaadin.server.ExternalResource; import com.vaadin.server.Sizeable; -import com.vaadin.ui.*; +import com.vaadin.ui.Button; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Label; +import com.vaadin.ui.Link; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.Window; import com.vaadin.ui.themes.ValoTheme; import de.symeda.sormas.api.DiseaseHelper; import de.symeda.sormas.api.FacadeProvider; import de.symeda.sormas.api.externalmessage.ExternalMessageDto; import de.symeda.sormas.api.externalmessage.ExternalMessageStatus; -import de.symeda.sormas.api.externalmessage.survey.*; +import de.symeda.sormas.api.externalmessage.survey.ExternalMessageSurveyResponseRequest; +import de.symeda.sormas.api.externalmessage.survey.ExternalMessageSurveyResponseResult; +import de.symeda.sormas.api.externalmessage.survey.ExternalMessageSurveyResponseWrapper; +import de.symeda.sormas.api.externalmessage.survey.PatchDictionary; +import de.symeda.sormas.api.externalmessage.survey.PatchField; import de.symeda.sormas.api.i18n.Captions; import de.symeda.sormas.api.i18n.I18nProperties; import de.symeda.sormas.api.i18n.Strings; @@ -165,6 +175,13 @@ public SurveyResponseDetailsWindow(ExternalMessageDto externalMessage, Runnable layout.addComponent(dictionaryGrid); } + if (result == null) { + try { + result = FacadeProvider.getExternalMessageFacade().reAttemptSurveyProcessing(uuid).getSurveyResponseData().getLatest().getResult(); + } catch (RuntimeException e) { + } + } + // --- Processing Result section --- if (result != null) { Label resultHeading = new Label(I18nProperties.getCaption(Captions.surveyResponseProcessingResult)); @@ -186,7 +203,7 @@ public SurveyResponseDetailsWindow(ExternalMessageDto externalMessage, Runnable I18nProperties.getCaption(Captions.surveyResponseApplied), patchResponse.isApplied() ? I18nProperties.getCaption(Captions.actionYes) : I18nProperties.getCaption(Captions.actionNo)); - if (patchResponse.hasFailures()) { + if (patchResponse.hasFailures() || externalMessage.getStatus() == ExternalMessageStatus.UNPROCESSED) { Label failuresHeading = new Label(I18nProperties.getString(Strings.headingSurveyResponseFailures)); CssStyles.style(failuresHeading, CssStyles.H4); layout.addComponent(failuresHeading); From f6c1db4adfd5b16dc687d4a007a88ac8ed512e6a Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Mon, 6 Jul 2026 17:58:18 +0200 Subject: [PATCH 02/13] Better transactional handling --- .../AutomaticSurveyResponseProcessor.java | 6 +++-- .../backend/patch/BusinessDtoFacade.java | 27 ++++++++++++++----- .../sormas/backend/patch/DataPatcherImpl.java | 4 ++- .../ExternalMessageController.java | 9 ++++++- .../externalmessage/ExternalMessageGrid.java | 2 +- 5 files changed, 37 insertions(+), 11 deletions(-) diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java index d9ec0a09c3e..21b2cbce10c 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java @@ -14,6 +14,7 @@ import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; import org.apache.commons.collections4.CollectionUtils; import org.jetbrains.annotations.NotNull; @@ -69,6 +70,9 @@ public AutomaticSurveyResponseProcessor( this.surveyTokenFacade = surveyTokenFacade; } + @Transactional(value = Transactional.TxType.REQUIRES_NEW, + rollbackOn = { + Exception.class }) public List processSurveyResponses(List externalMessages) throws InterruptedException, ExecutionException { @@ -155,8 +159,6 @@ public List processSurveyResponses(List, LeafAttacher> leafAttacherRegistry = new LinkedHashMap<>(); + private final Map, LeafAttacher> leafAttacherDictionary = new LinkedHashMap<>(); @PostConstruct private void init() { @@ -190,7 +199,7 @@ private void registerLeafAttacherOperations() { } private void registerLeafAttacher(Class leafClass, LeafAttacher attacher) { - leafAttacherRegistry.put(leafClass, attacher); + leafAttacherDictionary.put(leafClass, attacher); } private CaseDataDto requireCaseData(List> dtosInProgress) { @@ -303,7 +312,15 @@ private T saveDirectEntity(@NotNull EntityDto entityDto) { public void save(@NotNull List> entityDtosByKey) { List> dtosInProgress = new ArrayList<>(entityDtosByKey); - leafAttacherRegistry.forEach((leafClass, attacher) -> { + List> orderedRootEntities = entityDtosByKey.stream() + .filter(tuple -> leafAttacherDictionary.keySet().stream().noneMatch(leafClass -> leafClass.isInstance(tuple.getSecond()))) + .sorted(Comparator.comparing(Tuple::getSecond, new EntityDtoTypeComparator())) + .collect(Collectors.toList()); + + orderedRootEntities.stream().map(Tuple::getSecond).forEach(this::saveDirectEntity); + + // once it's done, leaf entities can be saved. + leafAttacherDictionary.forEach((leafClass, attacher) -> { List> leaves = dtosInProgress.stream().filter(t -> leafClass.isInstance(t.getSecond())).collect(Collectors.toList()); @@ -312,8 +329,6 @@ public void save(@NotNull List> entityDtosByKey) { attacher.attachLeaf(leafTuple.getSecond(), leafTuple.getFirst(), dtosInProgress); }); }); - - dtosInProgress.stream().map(Tuple::getSecond).forEach(this::saveDirectEntity); } @FunctionalInterface diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java index a080cfd3e93..d2883fb8ea8 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java @@ -16,6 +16,7 @@ import javax.ejb.EJB; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; +import javax.transaction.Transactional; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; @@ -110,6 +111,7 @@ public DataPatcherImpl( this.configFacade = configFacade; } + @Transactional(rollbackOn = Exception.class) @Override public DataPatchResponse patch(CaseDataPatchRequest request) { logger.debug("patch: [{}]", request); @@ -243,7 +245,7 @@ private PlainSinglePatchResult produceSinglePatchResult( private void saveDTOsIfAppropriate(Map, AttachedEntityWrapper> entityCache) { List> toSave = entityCache.entrySet() .stream() - .map(entry -> Tuple. of(entry.getKey().getSecond(), entry.getValue().getEntityDto())) + .map(entry -> Tuple.of(entry.getKey().getSecond(), entry.getValue().getEntityDto())) .collect(Collectors.toList()); if (toSave.isEmpty()) { diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java index e877e7c7db4..aeb7b4efca9 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java @@ -160,7 +160,7 @@ public void showExternalMessage(String messageUuid, boolean withActions, Runnabl form.setValue(newDto); } - public void processSurveyResponse(String surveyResponseMessageUuid) { + public void processSurveyResponse(String surveyResponseMessageUuid, Runnable successRunnable) { ExternalMessageDto externalMessage = FacadeProvider.getExternalMessageFacade().getByUuid(surveyResponseMessageUuid); de.symeda.sormas.api.externalmessage.survey.ExternalMessageSurveyResponseResult result = @@ -181,6 +181,11 @@ public void processSurveyResponse(String surveyResponseMessageUuid) { } } + if (result == null) { + Notification.show(I18nProperties.getString(Strings.messageSurveyResponseNotYetProcessed), Notification.Type.HUMANIZED_MESSAGE); + return; + } + if (result.getPatchResponse() != null && result.getPatchResponse().hasFailures()) { de.symeda.sormas.api.patch.partial_retrieval.DisplayablePartialRetrievalResponse displayData; try { @@ -197,6 +202,8 @@ public void processSurveyResponse(String surveyResponseMessageUuid) { UI.getCurrent().addWindow(editor); } else { Notification.show(I18nProperties.getString(Strings.messageSurveyResponseAllFieldsApplied), Notification.Type.HUMANIZED_MESSAGE); + + successRunnable.run(); } } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageGrid.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageGrid.java index 741049ecb6b..fa5a570c1b2 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageGrid.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageGrid.java @@ -226,7 +226,7 @@ private Component buildProcessComponent(ExternalMessageIndexDto indexDto) { } else if (ExternalMessageType.PHYSICIANS_REPORT == indexDto.getType()) { ControllerProvider.getExternalMessageController().processDoctorDeclarationMessage(indexDto.getUuid()); } else if (ExternalMessageType.SURVEY_RESPONSE == indexDto.getType()) { - ControllerProvider.getExternalMessageController().processSurveyResponse(indexDto.getUuid()); + ControllerProvider.getExternalMessageController().processSurveyResponse(indexDto.getUuid(), this::reload); } }, ValoTheme.BUTTON_PRIMARY); } else { From feaa6064dc109abb10cdfdd742d4a1048a13f5c1 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Mon, 6 Jul 2026 18:00:06 +0200 Subject: [PATCH 03/13] missing file --- .../patch/EntityDtoTypeComparator.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java new file mode 100644 index 00000000000..a43a0aedfe1 --- /dev/null +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java @@ -0,0 +1,32 @@ +package de.symeda.sormas.backend.patch; + +import java.util.Comparator; +import java.util.Map; + +import de.symeda.sormas.api.EntityDto; +import de.symeda.sormas.api.caze.CaseDataDto; +import de.symeda.sormas.api.epidata.EpiDataDto; +import de.symeda.sormas.api.hospitalization.HospitalizationDto; +import de.symeda.sormas.api.immunization.ImmunizationDto; +import de.symeda.sormas.api.person.PersonDto; + +/** + * Orders {@link AttachedEntityWrapper}s so that {@link CaseDataDto} comes first, {@link PersonDto} second, + * and any other DTO type last. + */ +public class EntityDtoTypeComparator implements Comparator { + + private static final Map, Integer> WEIGHT_BY_CLASS = + Map.of(CaseDataDto.class, 1, PersonDto.class, 2, EpiDataDto.class, 3, ImmunizationDto.class, 4, HospitalizationDto.class, 5); + + private static final int DEFAULT_WEIGHT = WEIGHT_BY_CLASS.size() + 1; + + @Override + public int compare(EntityDto first, EntityDto second) { + return Integer.compare(weightOf(first), weightOf(second)); + } + + private static int weightOf(EntityDto entityDto) { + return WEIGHT_BY_CLASS.getOrDefault(entityDto.getClass(), DEFAULT_WEIGHT); + } +} From f76704c50c3984eb7ed86db40893437154147668 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Tue, 7 Jul 2026 08:21:09 +0200 Subject: [PATCH 04/13] FIX: not all processed --- .../ExternalMessageFacadeEjb.java | 5 ++- .../AutomaticSurveyResponseProcessor.java | 33 +++++++++++-------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java index ea0a928aa38..411f668fd6a 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java @@ -390,7 +390,10 @@ public List saveAndProcessSurveyResponses(Date since) { processingResults.forEach(wrapper -> { ProcessingResultStatus result = wrapper.getResultStatus(); if (result == null || result.isCanceled()) { - logger.error("Processing of surveyResponse with UUID {} has been canceled", wrapper.getExternalMessage().getUuid()); + logger.error( + "Processing of surveyResponse with UUID {} has been canceled, result: [{}]", + wrapper.getExternalMessage().getUuid(), + wrapper); } }); } catch (InterruptedException e) { diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java index 21b2cbce10c..df29fa52508 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java @@ -35,7 +35,6 @@ import de.symeda.sormas.api.utils.dataprocessing.ProcessingResultStatus; import de.symeda.sormas.backend.survey.SurveyFacadeEjb; import de.symeda.sormas.backend.survey.SurveyTokenFacadeEjb; -import de.symeda.sormas.backend.util.CollectorUtils; import de.symeda.sormas.backend.util.ModelConstants; /** @@ -81,17 +80,18 @@ public List processSurveyResponses(List tokenByExternalSurveyIdDictionary = - externalMessages.stream().map(ExternalMessageDto::getSurveyResponseData).map(responseData -> { - ExternalMessageSurveyResponseRequest request = responseData.getLatest().getRequest(); - return new Tuple<>(request.getExternalSurveyId(), request.getToken()); - }).collect(CollectorUtils.toOrderedNullSafeMap(Tuple::getFirst, Tuple::getSecond)); + Map> tokensByExternalIds = externalMessages.stream().map(ExternalMessageDto::getSurveyResponseData).map(responseData -> { + ExternalMessageSurveyResponseRequest request = responseData.getLatest().getRequest(); + return new Tuple<>(request.getExternalSurveyId(), request.getToken()); + }).collect(Collectors.groupingBy(Tuple::getFirst, Collectors.mapping(Tuple::getSecond, Collectors.toList()))); - List externalSurveyIds = new ArrayList<>(tokenByExternalSurveyIdDictionary.keySet()); + logger.debug("tokensByExternalIds: [{}]", tokensByExternalIds); + + List externalSurveyIds = new ArrayList<>(tokensByExternalIds.keySet()); List> tokenBySurveyReferenceTuples = surveyFacade.getByExternalIds(externalSurveyIds) .stream() - .map(survey -> new Tuple<>(survey.toReference(), tokenByExternalSurveyIdDictionary.get(survey.getExternalId()))) + .flatMap(survey -> tokensByExternalIds.get(survey.getExternalId()).stream().map(token -> new Tuple<>(survey.toReference(), token))) .collect(Collectors.toList()); List surveyTokens = surveyTokenFacade.getBySurveyReferenceTokenTuples(tokenBySurveyReferenceTuples); @@ -103,15 +103,15 @@ public List processSurveyResponses(List surveyTokens) { logger.trace("tryProcessExternalMessage: [{}], [{}]", externalMessage, surveyTokens); - SurveyResponseProcessingResult surveyResponseProcessingResult = new SurveyResponseProcessingResult().setExternalMessage(externalMessage); + SurveyResponseProcessingResult surveyResponseProcessingResult = + new SurveyResponseProcessingResult().setExternalMessage(externalMessage).setResultStatus(ProcessingResultStatus.DONE); ExternalMessageSurveyResponseWrapper latestResponseWrapper = externalMessage.getSurveyResponseData().getLatest(); ExternalMessageSurveyResponseRequest request = latestResponseWrapper.getRequest(); + String externalMessageUuid = externalMessage.getUuid(); if (latestResponseWrapper.getResult() != null && request.isSkipIfAlreadyProcessed()) { - logger.info( - "Skipping survey response for external message [{}]: already processed and skipIfAlreadyProcessed=true", - externalMessage.getUuid()); + logger.info("Skipping survey response for external message [{}]: already processed and skipIfAlreadyProcessed=true", externalMessageUuid); return surveyResponseProcessingResult.setResultStatus(ProcessingResultStatus.CANCELED); } @@ -120,7 +120,10 @@ public List processSurveyResponses(List tokenCandidate.getToken().equals(requestToken)).findAny(); if (surveyToken.isEmpty()) { - logger.error("Token could not be found within available survey token DTOs: [{}]. Survey response processing is cancelled.", requestToken); + logger.error( + "Token could not be found within available survey token DTOs: [{}]. Survey response processing for: [{}] is cancelled.", + requestToken, + externalMessageUuid); return surveyResponseProcessingResult.setResultStatus(ProcessingResultStatus.CANCELED); } @@ -142,6 +145,8 @@ public List processSurveyResponses(List processSurveyResponses(List Date: Tue, 7 Jul 2026 09:22:00 +0200 Subject: [PATCH 05/13] broken test --- .../java/de/symeda/sormas/backend/ArchitectureTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/ArchitectureTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/ArchitectureTest.java index b0fffdf2d20..25ebf7f70e6 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/ArchitectureTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/ArchitectureTest.java @@ -1,6 +1,9 @@ package de.symeda.sormas.backend; -import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; import java.util.Arrays; import java.util.Collections; @@ -291,6 +294,7 @@ public void testExternalMessageFacadeEjbAuthorization(JavaClasses classes) { "bulkAssignExternalMessages", "delete", "overwriteSurveyResponse", + "reAttemptSurveyProcessing", "saveAndProcessSurveyResponses"), classes); } From d4e50960f29061142ca4467dbe4216b1dedcb831 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Tue, 7 Jul 2026 09:24:37 +0200 Subject: [PATCH 06/13] missing error-message --- .../surveyresponse/SurveyResponseDetailsWindow.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java index e48b1dffea7..713833e45a2 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java @@ -25,6 +25,7 @@ import com.vaadin.ui.Grid; import com.vaadin.ui.Label; import com.vaadin.ui.Link; +import com.vaadin.ui.Notification; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; @@ -179,6 +180,7 @@ public SurveyResponseDetailsWindow(ExternalMessageDto externalMessage, Runnable try { result = FacadeProvider.getExternalMessageFacade().reAttemptSurveyProcessing(uuid).getSurveyResponseData().getLatest().getResult(); } catch (RuntimeException e) { + Notification.show(I18nProperties.getString(Strings.messageSurveyResponseNotYetProcessed), Notification.Type.HUMANIZED_MESSAGE); } } From 032061d31855e8378c3b7253aa27b64d7ce5a826 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Tue, 7 Jul 2026 09:41:30 +0200 Subject: [PATCH 07/13] missing error-message --- .../survey/AutomaticSurveyResponseProcessor.java | 6 ------ .../de/symeda/sormas/backend/patch/BusinessDtoFacade.java | 3 ++- .../de/symeda/sormas/backend/patch/DataPatcherImpl.java | 2 +- .../sormas/backend/patch/EntityDtoTypeComparator.java | 5 +++-- .../ui/externalmessage/ExternalMessageController.java | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java index df29fa52508..03c594c0990 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java @@ -12,8 +12,6 @@ import javax.ejb.EJB; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; import javax.transaction.Transactional; import org.apache.commons.collections4.CollectionUtils; @@ -35,7 +33,6 @@ import de.symeda.sormas.api.utils.dataprocessing.ProcessingResultStatus; import de.symeda.sormas.backend.survey.SurveyFacadeEjb; import de.symeda.sormas.backend.survey.SurveyTokenFacadeEjb; -import de.symeda.sormas.backend.util.ModelConstants; /** * Performs the coordinating for patch operations out of Survey-responses. @@ -54,9 +51,6 @@ public class AutomaticSurveyResponseProcessor { @EJB private SurveyTokenFacadeEjb.SurveyTokenFacadeEjbLocal surveyTokenFacade; - @PersistenceContext(unitName = ModelConstants.PERSISTENCE_UNIT_NAME) - private EntityManager em; - public AutomaticSurveyResponseProcessor() { } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java index 9399f463ef0..5b2014d60d0 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java @@ -312,6 +312,7 @@ private T saveDirectEntity(@NotNull EntityDto entityDto) { public void save(@NotNull List> entityDtosByKey) { List> dtosInProgress = new ArrayList<>(entityDtosByKey); + // ordering is important: root entities must be saved first to avoid OutdatedEntityException as leaf entities seem to update the case. List> orderedRootEntities = entityDtosByKey.stream() .filter(tuple -> leafAttacherDictionary.keySet().stream().noneMatch(leafClass -> leafClass.isInstance(tuple.getSecond()))) .sorted(Comparator.comparing(Tuple::getSecond, new EntityDtoTypeComparator())) @@ -319,7 +320,7 @@ public void save(@NotNull List> entityDtosByKey) { orderedRootEntities.stream().map(Tuple::getSecond).forEach(this::saveDirectEntity); - // once it's done, leaf entities can be saved. + // once it's done, leaf entities can be saved, as updating root entities will not break leafAttacherDictionary.forEach((leafClass, attacher) -> { List> leaves = dtosInProgress.stream().filter(t -> leafClass.isInstance(t.getSecond())).collect(Collectors.toList()); diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java index d2883fb8ea8..243b808d755 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/DataPatcherImpl.java @@ -125,7 +125,7 @@ public DataPatchResponse patch(CaseDataPatchRequest request) { List patchingTuples = computePatchingTuples(request); - logger.debug("Computed patchingTuples: [{}]", patchingTuples); + logger.trace("Computed patchingTuples: [{}]", patchingTuples); Predicate customizableFieldsPredicate = patchResult -> patchResult.getField().getField().contains(PatchFieldHelper.CUSTOM_PREFIX); diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java index a43a0aedfe1..c80087869b2 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java @@ -11,8 +11,9 @@ import de.symeda.sormas.api.person.PersonDto; /** - * Orders {@link AttachedEntityWrapper}s so that {@link CaseDataDto} comes first, {@link PersonDto} second, - * and any other DTO type last. + * Orders {@link EntityDto}s so that root entities come first. + * Needed to properly store the "root-est" entity is persisted first, to avoid {@link de.symeda.sormas.api.utils.OutdatedEntityException} in + * case a leaf modifies the parent. */ public class EntityDtoTypeComparator implements Comparator { diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java index aeb7b4efca9..da8205326fc 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java @@ -168,7 +168,7 @@ public void processSurveyResponse(String surveyResponseMessageUuid, Runnable suc ? externalMessage.getSurveyResponseData().getLatest().getResult() : null; - // when button click: + // In case of failure during operation, attempt to process the message again. if (result == null) { try { result = FacadeProvider.getExternalMessageFacade() From fa99181a58967e82937e5b285e2482566fbfcfe8 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Tue, 7 Jul 2026 10:00:30 +0200 Subject: [PATCH 08/13] fix test --- .../src/main/java/de/symeda/sormas/api/audit/Constants.java | 1 + .../sormas/api/externalmessage/ExternalMessageFacade.java | 2 +- .../backend/externalmessage/ExternalMessageFacadeEjb.java | 2 +- .../sormas/ui/externalmessage/ExternalMessageController.java | 2 +- .../surveyresponse/SurveyResponseDetailsWindow.java | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/audit/Constants.java b/sormas-api/src/main/java/de/symeda/sormas/api/audit/Constants.java index 62fa8c57d7c..7d868d760c3 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/audit/Constants.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/audit/Constants.java @@ -62,6 +62,7 @@ public class Constants { "cancel", "end", "accept", + "execute", "sync", "request", "revoke", diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageFacade.java b/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageFacade.java index b3e88ee2ce7..eac3ddef95b 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageFacade.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageFacade.java @@ -94,7 +94,7 @@ default List saveAndProcessSurveyResponses() { * UUID of the external message (must be of type SURVEY_RESPONSE) * @return updated ExternalMessageDto after reprocessing */ - ExternalMessageDto reAttemptSurveyProcessing(String uuid); + ExternalMessageDto executeSurveyProcessing(String uuid); /** * Re-submits a survey response with a corrected patch dictionary after previous processing failures. diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java index 411f668fd6a..0bcdeac7b83 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java @@ -1047,7 +1047,7 @@ public ExternalMessageDto getForSurveillanceReport(SurveillanceReportReferenceDt @Override @RightsAllowed(UserRight._EXTERNAL_MESSAGE_SURVEY_RESPONSE_PROCESS) - public ExternalMessageDto reAttemptSurveyProcessing(String uuid) { + public ExternalMessageDto executeSurveyProcessing(String uuid) { logger.debug("reAttemptSurveyProcessing: [{}]", uuid); return overwriteSurveyResponse(uuid, null); } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java index da8205326fc..1befde860f0 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/ExternalMessageController.java @@ -172,7 +172,7 @@ public void processSurveyResponse(String surveyResponseMessageUuid, Runnable suc if (result == null) { try { result = FacadeProvider.getExternalMessageFacade() - .reAttemptSurveyProcessing(surveyResponseMessageUuid) + .executeSurveyProcessing(surveyResponseMessageUuid) .getSurveyResponseData() .getLatest() .getResult(); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java index 713833e45a2..e2253c7d2c5 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java @@ -178,7 +178,7 @@ public SurveyResponseDetailsWindow(ExternalMessageDto externalMessage, Runnable if (result == null) { try { - result = FacadeProvider.getExternalMessageFacade().reAttemptSurveyProcessing(uuid).getSurveyResponseData().getLatest().getResult(); + result = FacadeProvider.getExternalMessageFacade().executeSurveyProcessing(uuid).getSurveyResponseData().getLatest().getResult(); } catch (RuntimeException e) { Notification.show(I18nProperties.getString(Strings.messageSurveyResponseNotYetProcessed), Notification.Type.HUMANIZED_MESSAGE); } From 2bf96c2a40c40d88a0ace642a4d4dfefc5d5ec18 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:12:28 +0200 Subject: [PATCH 09/13] missing symptoms in UI. TODO: add missing translations --- .../java/de/symeda/sormas/ui/symptoms/SymptomsForm.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java index 1b51d206a6b..e3b801b24ed 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java @@ -526,6 +526,12 @@ public String getFormattedHtmlMessage() { URINARY_RETENTION, FEVER, BLOATING, + LOSS_OF_APPETITE, + FLATULENCE, + SMELLY_BURPS, + COUGHING_ATTACKS, + COUGHING_AT_NIGHT, + ABDOMINAL_CRAMPS, REOCCURRENCE, WEIGHT_LOSS, WEIGHT_LOSS_AMOUNT, From 2d8a7aee7b9a99c1dbd7df4d7c06efc72ab38a3c Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Wed, 8 Jul 2026 07:37:23 +0200 Subject: [PATCH 10/13] added test that triggers exception --- .../backend/patch/BusinessDtoFacade.java | 23 +++- .../sormas/patch/DataPatcherImplTest.java | 113 ++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java index 5b2014d60d0..41253b317bb 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java @@ -310,10 +310,15 @@ private T saveDirectEntity(@NotNull EntityDto entityDto) { } public void save(@NotNull List> entityDtosByKey) { + // Be careful about "leaf-entities" that can change the case data: + // Person ; Immunization and VaccinationDto + /* List> dtosInProgress = new ArrayList<>(entityDtosByKey); + //entityDtosByKey.stream() .filter(dto -> dto.) + // ordering is important: root entities must be saved first to avoid OutdatedEntityException as leaf entities seem to update the case. - List> orderedRootEntities = entityDtosByKey.stream() + List> orderedRootEntities = entityDtosByKey.stream() .filter(tuple -> leafAttacherDictionary.keySet().stream().noneMatch(leafClass -> leafClass.isInstance(tuple.getSecond()))) .sorted(Comparator.comparing(Tuple::getSecond, new EntityDtoTypeComparator())) .collect(Collectors.toList()); @@ -330,6 +335,22 @@ public void save(@NotNull List> entityDtosByKey) { attacher.attachLeaf(leafTuple.getSecond(), leafTuple.getFirst(), dtosInProgress); }); }); + */ + List> dtosInProgress = new ArrayList<>(entityDtosByKey); + + leafAttacherDictionary.forEach((leafClass, attacher) -> { + List> leaves = + dtosInProgress.stream().filter(t -> leafClass.isInstance(t.getSecond())).collect(Collectors.toList()); + + leaves.forEach(leafTuple -> { + dtosInProgress.remove(leafTuple); + attacher.attachLeaf(leafTuple.getSecond(), leafTuple.getFirst(), dtosInProgress); + }); + }); + + dtosInProgress.stream().map(Tuple::getSecond).forEach(this::saveDirectEntity); + + } @FunctionalInterface diff --git a/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java b/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java index c60457d0e8c..307930774a9 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java @@ -11,6 +11,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import de.symeda.sormas.api.contact.FollowUpStatus; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Tag; @@ -25,6 +26,7 @@ import de.symeda.sormas.api.activityascase.ActivityAsCaseType; import de.symeda.sormas.api.caze.CaseDataDto; import de.symeda.sormas.api.caze.Vaccine; +import de.symeda.sormas.api.caze.VaccinationStatus; import de.symeda.sormas.api.customizableenum.CustomizableEnumTranslation; import de.symeda.sormas.api.customizableenum.CustomizableEnumType; import de.symeda.sormas.api.customizablefield.CustomizableFieldContext; @@ -43,6 +45,7 @@ import de.symeda.sormas.api.hospitalization.PreviousHospitalizationDto; import de.symeda.sormas.api.immunization.ImmunizationDto; import de.symeda.sormas.api.immunization.ImmunizationStatus; +import de.symeda.sormas.api.immunization.MeansOfImmunization; import de.symeda.sormas.api.infrastructure.country.CountryDto; import de.symeda.sormas.api.infrastructure.country.CountryReferenceDto; import de.symeda.sormas.api.infrastructure.facility.FacilityDto; @@ -1014,6 +1017,116 @@ void patch_exposure() { () -> Assertions.assertEquals("market visit", exposures.get(0).getDescription())); } + @Test + void patch_multipleEntities_caseDataExposureHospitalizationSymptoms() { + // This modifies many entities are triggers case update. + // Might require changing outdated range to reproduce every time + // PREPARE + Disease disease = Disease.DENGUE; + CaseDataDto originalCase = creator.createUnclassifiedCase(disease); + + // EXECUTE + DataPatchResponse response = victim().patch( + new CaseDataPatchRequest().setCaseUuid(originalCase.getUuid()) + .setReplacementStrategy(DataReplacementStrategy.ALWAYS) + .setPatchDictionary( + Map.ofEntries( + // CaseData + Map.entry("CaseData.quarantineChangeComment", "some comment"), + Map.entry(toFieldName(CaseDataDto.I18N_PREFIX, CaseDataDto.FOLLOW_UP_STATUS), FollowUpStatus.FOLLOW_UP), + + // Exposures + Map.entry(toFieldName(ExposureDto.I18N_PREFIX, ExposureDto.EXPOSURE_TYPE), "WORK"), + Map.entry(toFieldName(ExposureDto.I18N_PREFIX, ExposureDto.DESCRIPTION), "market visit"), + + // EpiData + Map.entry("EpiData.exposureDetailsKnown", "YES"), + Map.entry("EpiData.contactWithSourceCaseKnown", "NO"), + + // Hospitalization + Map.entry(toFieldName(HospitalizationDto.I18N_PREFIX, HospitalizationDto.ADMITTED_TO_HEALTH_FACILITY), "YES"), + Map.entry(toFieldName(HospitalizationDto.I18N_PREFIX, HospitalizationDto.ADMISSION_DATE), "2024-05-10"), + + // Previous hospitalization + Map.entry(toFieldName(PreviousHospitalizationDto.I18N_PREFIX, PreviousHospitalizationDto.ADMITTED_TO_HEALTH_FACILITY), "YES"), + Map.entry(toFieldName(PreviousHospitalizationDto.I18N_PREFIX, PreviousHospitalizationDto.ADMISSION_DATE), "2024-03-15"), + Map.entry(toFieldName(PreviousHospitalizationDto.I18N_PREFIX, PreviousHospitalizationDto.ICU_LENGTH_OF_STAY), "7"), + + // Immunization — deliberately made ACQUIRED + valid across the case reportDate so that + // saving the Immunization/Vaccination (their own facades, not CaseFacade) forces + // CaseService#updateDeterminedVaccinationStatuses to recompute and persist the Case's vaccinationStatus. + Map.entry(toFieldName(ImmunizationDto.I18N_PREFIX, ImmunizationDto.IMMUNIZATION_STATUS), "ACQUIRED"), + Map.entry(toFieldName(ImmunizationDto.I18N_PREFIX, ImmunizationDto.MEANS_OF_IMMUNIZATION), "VACCINATION"), + Map.entry(toFieldName(ImmunizationDto.I18N_PREFIX, ImmunizationDto.VALID_FROM), "2020-01-01"), + Map.entry(toFieldName(ImmunizationDto.I18N_PREFIX, ImmunizationDto.VALID_UNTIL), "2035-01-01"), + + // Vaccination + Map.entry(toFieldName(VaccinationDto.I18N_PREFIX, VaccinationDto.VACCINE_NAME), "COMIRNATY"), + Map.entry(toFieldName(VaccinationDto.I18N_PREFIX, VaccinationDto.VACCINATION_DATE), "2024-05-01"), + Map.entry(toFieldName(VaccinationDto.I18N_PREFIX, VaccinationDto.VACCINE_DOSE), "1"), + + // Person — saving Person cascades into CaseFacade#onCaseChanged for every case of that person + Map.entry("Person.sex", "MALE"), + + // Symptoms + Map.entry("Symptoms.cough", "YES")))); + + // CHECK + CaseDataDto actualCase = getCaseFacade().getByUuid(originalCase.getUuid()); + List exposures = actualCase.getEpiData().getExposures(); + List previousHospitalizations = actualCase.getHospitalization().getPreviousHospitalizations(); + List immunizations = getImmunizationFacade().getByPersonUuids(List.of(originalCase.getPerson().getUuid())); + PersonDto actualPerson = getPersonFacade().getByUuid(originalCase.getPerson().getUuid()); + + Assertions.assertAll( + () -> Assertions.assertTrue(response.getFailures().isEmpty(), "Failures: " + response.getFailures()), + () -> Assertions.assertTrue(response.isApplied()), + + // CaseData + () -> Assertions.assertEquals("some comment", actualCase.getQuarantineChangeComment()), + + // Exposures + () -> Assertions.assertEquals(1, exposures.size()), + () -> Assertions.assertEquals(ExposureType.WORK, exposures.get(0).getExposureType()), + () -> Assertions.assertEquals("market visit", exposures.get(0).getDescription()), + + // EpiData + () -> Assertions.assertEquals(YesNoUnknown.YES, actualCase.getEpiData().getExposureDetailsKnown()), + () -> Assertions.assertEquals(YesNoUnknown.NO, actualCase.getEpiData().getContactWithSourceCaseKnown()), + + // Hospitalization + () -> Assertions.assertEquals(YesNoUnknown.YES, actualCase.getHospitalization().getAdmittedToHealthFacility()), + () -> Assertions.assertEquals( + Date.from(LocalDate.parse("2024-05-10").atStartOfDay(ZoneId.systemDefault()).toInstant()), + actualCase.getHospitalization().getAdmissionDate()), + + // Previous hospitalization + () -> Assertions.assertEquals(1, previousHospitalizations.size()), + () -> Assertions.assertEquals(YesNoUnknown.YES, previousHospitalizations.get(0).getAdmittedToHealthFacility()), + () -> Assertions.assertEquals( + Date.from(LocalDate.parse("2024-03-15").atStartOfDay(ZoneId.systemDefault()).toInstant()), + previousHospitalizations.get(0).getAdmissionDate()), + () -> Assertions.assertEquals(7, previousHospitalizations.get(0).getIcuLengthOfStay()), + + // Immunization / Vaccination + () -> Assertions.assertEquals(1, immunizations.size()), + () -> Assertions.assertEquals(ImmunizationStatus.ACQUIRED, immunizations.get(0).getImmunizationStatus()), + () -> Assertions.assertEquals(MeansOfImmunization.VACCINATION, immunizations.get(0).getMeansOfImmunization()), + () -> Assertions.assertEquals(1, immunizations.get(0).getVaccinations().size()), + () -> Assertions.assertEquals(Vaccine.COMIRNATY, immunizations.get(0).getVaccinations().get(0).getVaccineName()), + () -> Assertions.assertEquals("1", immunizations.get(0).getVaccinations().get(0).getVaccineDose()), + + // Case's vaccinationStatus is not set directly by the patch — it is only recomputed as a side effect + // of ImmunizationFacade/VaccinationFacade#save() calling CaseService#updateDeterminedVaccinationStatuses + () -> Assertions.assertEquals(VaccinationStatus.UNVACCINATED, actualCase.getVaccinationStatus()), + + // Person — proves saving Person cascades an update into the case as well + () -> Assertions.assertEquals(Sex.MALE, actualPerson.getSex()), + + // Symptoms + () -> Assertions.assertEquals(SymptomState.YES, actualCase.getSymptoms().getCough())); + } + @Test void patch_grouped_twoExposures() { // PREPARE From 7ca953dd7ce9cc5fa76cbbb68925e1acc1fb52d9 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Wed, 8 Jul 2026 08:21:55 +0200 Subject: [PATCH 11/13] Added unit to make sure that symptoms of other diseases are blocked --- .../sormas/api/symptoms/SymptomsDto.java | 12 +++++++++ .../src/main/resources/captions.properties | 5 ++++ .../sormas/patch/DataPatcherImplTest.java | 25 +++++++++++++++++-- .../sormas/ui/symptoms/SymptomsForm.java | 1 - 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java index ce45e48142f..2c371db997e 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java @@ -2963,27 +2963,39 @@ public static SymptomsDto build() { CRYPTOSPORIDIOSIS, GIARDIASIS, RESPIRATORY_SYNCYTIAL_VIRUS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) private SymptomState lossOfAppetite; @Diseases({ GIARDIASIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) private SymptomState flatulence; @Diseases({ GIARDIASIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) private SymptomState smellyBurps; @Diseases({ PERTUSSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) private SymptomState coughingAttacks; @Diseases({ PERTUSSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) private SymptomState coughingAtNight; @Diseases({ CRYPTOSPORIDIOSIS, GIARDIASIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) private SymptomState abdominalCramps; private DiagnosisType diagnosis; diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties index e858e57b46e..395d706a362 100644 --- a/sormas-api/src/main/resources/captions.properties +++ b/sormas-api/src/main/resources/captions.properties @@ -3144,6 +3144,11 @@ Symptoms.tenesmus=Tenesmus Symptoms.haemolyticUremicSyndrome=Haemolytic uremic syndrome Symptoms.bloodyDiarrhea=Bloody diarrhea Symptoms.wateryDiarrhea=Watery diarrhoea +Symptoms.lossOfAppetite=Loss of appetite +Symptoms.flatulence=Flatulence +Symptoms.coughingAttacks=Coughing attacks +Symptoms.coughingAtNight=Coughing at night +Symptoms.abdominalCramps=Abdominal cramps titleComplications=Complications titleComplications.DENG=Warning signs titleNoComplications=No complications diff --git a/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java b/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java index 307930774a9..f16612b8db4 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java @@ -11,7 +11,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import de.symeda.sormas.api.contact.FollowUpStatus; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Tag; @@ -25,8 +24,9 @@ import de.symeda.sormas.api.activityascase.ActivityAsCaseDto; import de.symeda.sormas.api.activityascase.ActivityAsCaseType; import de.symeda.sormas.api.caze.CaseDataDto; -import de.symeda.sormas.api.caze.Vaccine; import de.symeda.sormas.api.caze.VaccinationStatus; +import de.symeda.sormas.api.caze.Vaccine; +import de.symeda.sormas.api.contact.FollowUpStatus; import de.symeda.sormas.api.customizableenum.CustomizableEnumTranslation; import de.symeda.sormas.api.customizableenum.CustomizableEnumType; import de.symeda.sormas.api.customizablefield.CustomizableFieldContext; @@ -482,6 +482,27 @@ void patch_notSupportedForCountry() { () -> Assertions.assertEquals(expectedFailures, response.getFailures())); } + @Test + void patch_notSupportedForDisease_symptoms_offset() { + // PREPARE + CaseDataDto originalCase = creator.createUnclassifiedCase(Disease.GIARDIASIS); + + Map patchDictionary = Map.of("Symptoms.offsetDate", new java.util.Date()); + + // EXECUTE + DataPatchResponse response = + victim().patch(new CaseDataPatchRequest().setCaseUuid(originalCase.getUuid()).setPatchDictionary(patchDictionary)); + + // CHECK + Map expectedFailures = + buildDictionaryOfFailureType(patchDictionary, DataPatchFailureCause.UNSUPPORTED_FIELD_FOR_DISEASE_OR_COUNTRY_OR_FEATURE); + + Assertions.assertAll( + () -> Assertions.assertTrue(response.getValidPatchDictionary().isEmpty(), "Nothing should have been patched, should be empty"), + // FAILURES + () -> Assertions.assertEquals(expectedFailures, response.getFailures())); + } + @Test void patch_notSupportedFeature() { // PREPARE diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java index e3b801b24ed..8c18973bb6a 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java @@ -528,7 +528,6 @@ public String getFormattedHtmlMessage() { BLOATING, LOSS_OF_APPETITE, FLATULENCE, - SMELLY_BURPS, COUGHING_ATTACKS, COUGHING_AT_NIGHT, ABDOMINAL_CRAMPS, From e2d80a101c1445ab90bab1fcb696fc10012ebbe2 Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Wed, 8 Jul 2026 10:44:50 +0200 Subject: [PATCH 12/13] =?UTF-8?q?=F0=9F=90=9B:=20FIXED:=20ordered=20save?= =?UTF-8?q?=20of=20caseData=20to=20match?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/caze/CaseListCriteriaBuilder.java | 4 +- .../backend/patch/BusinessDtoFacade.java | 51 ++++++++++--------- .../patch/EntityDtoTypeComparator.java | 33 ------------ .../backend/patch/PropertyAccessor.java | 10 ++-- .../backend/patch/PropertyAccessorTest.java | 16 ++---- .../sormas/patch/DataPatcherImplTest.java | 6 +-- 6 files changed, 42 insertions(+), 78 deletions(-) delete mode 100644 sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseListCriteriaBuilder.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseListCriteriaBuilder.java index ed52a00cc50..ba946054e90 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseListCriteriaBuilder.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseListCriteriaBuilder.java @@ -264,6 +264,8 @@ private List> getIndexOrders(SortProperty sortProperty, Root switch (sortProperty.propertyName) { case CaseIndexDto.PERSON_UUID: return Collections.singletonList(joins.getPerson().get(Person.UUID)); + case CaseIndexDto.DISEASE_VARIANT: + return Collections.singletonList(caze.get(Case.DISEASE_VARIANT_VALUE)); case CaseIndexDto.ID: case CaseIndexDto.UUID: case CaseIndexDto.EPID_NUMBER: @@ -279,8 +281,6 @@ private List> getIndexOrders(SortProperty sortProperty, Root case CaseIndexDto.FOLLOW_UP_STATUS: case CaseIndexDto.FOLLOW_UP_UNTIL: case CaseIndexDto.VACCINATION_STATUS: - case CaseIndexDto.DISEASE_VARIANT: - return Collections.singletonList(caze.get(Case.DISEASE_VARIANT_VALUE)); case CaseIndexDto.EXTERNAL_ID: case CaseIndexDto.EXTERNAL_TOKEN: case CaseIndexDto.INTERNAL_TOKEN: diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java index 41253b317bb..bfa67cffc11 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/BusinessDtoFacade.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -11,6 +10,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -67,6 +67,12 @@ public class BusinessDtoFacade { */ private final Map, LeafAttacher> leafAttacherDictionary = new LinkedHashMap<>(); + /** + * Attached directly to case data. + * Will be saved in one shot with the case. + */ + private final Map, LeafAttacher> caseDataLeafAttacherDictionary = new LinkedHashMap<>(); + @PostConstruct private void init() { registerDirectSaveOperations(); @@ -187,13 +193,15 @@ private void registerLeafAttacherOperations() { } immunization.getVaccinations().add((VaccinationDto) leaf); }); - registerLeafAttacher(ExposureDto.class, (leaf, groupIndex, list) -> { + + // directly linked to the case + registerCaseDataLeafAttacher(ExposureDto.class, (leaf, groupIndex, list) -> { requireCaseData(list).getEpiData().getExposures().add((ExposureDto) leaf); }); - registerLeafAttacher(ActivityAsCaseDto.class, (leaf, groupIndex, list) -> { + registerCaseDataLeafAttacher(ActivityAsCaseDto.class, (leaf, groupIndex, list) -> { requireCaseData(list).getEpiData().getActivitiesAsCase().add((ActivityAsCaseDto) leaf); }); - registerLeafAttacher(PreviousHospitalizationDto.class, (leaf, groupIndex, list) -> { + registerCaseDataLeafAttacher(PreviousHospitalizationDto.class, (leaf, groupIndex, list) -> { requireCaseData(list).getHospitalization().getPreviousHospitalizations().add((PreviousHospitalizationDto) leaf); }); } @@ -202,6 +210,10 @@ private void registerLeafAttacher(Class leafClass, Leaf leafAttacherDictionary.put(leafClass, attacher); } + private void registerCaseDataLeafAttacher(Class leafClass, LeafAttacher attacher) { + caseDataLeafAttacherDictionary.put(leafClass, attacher); + } + private CaseDataDto requireCaseData(List> dtosInProgress) { return dtosInProgress.stream() .map(Tuple::getSecond) @@ -310,23 +322,13 @@ private T saveDirectEntity(@NotNull EntityDto entityDto) { } public void save(@NotNull List> entityDtosByKey) { - // Be careful about "leaf-entities" that can change the case data: - // Person ; Immunization and VaccinationDto - /* - List> dtosInProgress = new ArrayList<>(entityDtosByKey); - - //entityDtosByKey.stream() .filter(dto -> dto.) + Predicate> allButCaseDataDto = tuple -> tuple.getSecond() instanceof CaseDataDto; + Optional caseDataOpt = entityDtosByKey.stream().filter(allButCaseDataDto).map(Tuple::getSecond).findAny(); - // ordering is important: root entities must be saved first to avoid OutdatedEntityException as leaf entities seem to update the case. - List> orderedRootEntities = entityDtosByKey.stream() - .filter(tuple -> leafAttacherDictionary.keySet().stream().noneMatch(leafClass -> leafClass.isInstance(tuple.getSecond()))) - .sorted(Comparator.comparing(Tuple::getSecond, new EntityDtoTypeComparator())) - .collect(Collectors.toList()); - - orderedRootEntities.stream().map(Tuple::getSecond).forEach(this::saveDirectEntity); + List> dtosInProgress = new ArrayList<>(entityDtosByKey); - // once it's done, leaf entities can be saved, as updating root entities will not break - leafAttacherDictionary.forEach((leafClass, attacher) -> { + // must be attached to case data before being stored, otherwise those entities are lost. + caseDataLeafAttacherDictionary.forEach((leafClass, attacher) -> { List> leaves = dtosInProgress.stream().filter(t -> leafClass.isInstance(t.getSecond())).collect(Collectors.toList()); @@ -335,12 +337,13 @@ public void save(@NotNull List> entityDtosByKey) { attacher.attachLeaf(leafTuple.getSecond(), leafTuple.getFirst(), dtosInProgress); }); }); - */ - List> dtosInProgress = new ArrayList<>(entityDtosByKey); + + // case data must be stored up-front because "logically-attached" entities might update it again: immunization etc. + caseDataOpt.ifPresent(this::saveDirectEntity); leafAttacherDictionary.forEach((leafClass, attacher) -> { List> leaves = - dtosInProgress.stream().filter(t -> leafClass.isInstance(t.getSecond())).collect(Collectors.toList()); + dtosInProgress.stream().filter(t -> leafClass.isInstance(t.getSecond())).collect(Collectors.toList()); leaves.forEach(leafTuple -> { dtosInProgress.remove(leafTuple); @@ -348,9 +351,7 @@ public void save(@NotNull List> entityDtosByKey) { }); }); - dtosInProgress.stream().map(Tuple::getSecond).forEach(this::saveDirectEntity); - - + dtosInProgress.stream().filter(Predicate.not(allButCaseDataDto)).map(Tuple::getSecond).forEach(this::saveDirectEntity); } @FunctionalInterface diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java deleted file mode 100644 index c80087869b2..00000000000 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/EntityDtoTypeComparator.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.symeda.sormas.backend.patch; - -import java.util.Comparator; -import java.util.Map; - -import de.symeda.sormas.api.EntityDto; -import de.symeda.sormas.api.caze.CaseDataDto; -import de.symeda.sormas.api.epidata.EpiDataDto; -import de.symeda.sormas.api.hospitalization.HospitalizationDto; -import de.symeda.sormas.api.immunization.ImmunizationDto; -import de.symeda.sormas.api.person.PersonDto; - -/** - * Orders {@link EntityDto}s so that root entities come first. - * Needed to properly store the "root-est" entity is persisted first, to avoid {@link de.symeda.sormas.api.utils.OutdatedEntityException} in - * case a leaf modifies the parent. - */ -public class EntityDtoTypeComparator implements Comparator { - - private static final Map, Integer> WEIGHT_BY_CLASS = - Map.of(CaseDataDto.class, 1, PersonDto.class, 2, EpiDataDto.class, 3, ImmunizationDto.class, 4, HospitalizationDto.class, 5); - - private static final int DEFAULT_WEIGHT = WEIGHT_BY_CLASS.size() + 1; - - @Override - public int compare(EntityDto first, EntityDto second) { - return Integer.compare(weightOf(first), weightOf(second)); - } - - private static int weightOf(EntityDto entityDto) { - return WEIGHT_BY_CLASS.getOrDefault(entityDto.getClass(), DEFAULT_WEIGHT); - } -} diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/PropertyAccessor.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/PropertyAccessor.java index 7457126ca15..3372ff8eead 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/PropertyAccessor.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/patch/PropertyAccessor.java @@ -39,7 +39,7 @@ public static Tuple, Object>, PropertyAccessFailure> getNestedPro return new Tuple<>(null, PropertyAccessFailure.INVALID_INPUT); } - boolean notNestedPath = fieldName.indexOf(PATH_SEPARATOR) == fieldName.lastIndexOf(PATH_SEPARATOR); + boolean notNestedPath = fieldName.indexOf(PATH_SEPARATOR) == -1; if (notNestedPath) { return getPropertyTypeAndValue(bean, fieldName, fieldVisibilityCheckers); @@ -47,7 +47,8 @@ public static Tuple, Object>, PropertyAccessFailure> getNestedPro String leafPath = fieldName.substring(fieldName.lastIndexOf(PATH_SEPARATOR) + 1); - return getNestedProperty(bean, fieldName).map(leafParent -> getPropertyTypeAndValue(leafParent, leafPath, fieldVisibilityCheckers)) + return getNestedProperty(bean, fieldName.substring(0, fieldName.lastIndexOf(PATH_SEPARATOR))) + .map(leafParent -> getPropertyTypeAndValue(leafParent, leafPath, fieldVisibilityCheckers)) .orElseGet(() -> new Tuple<>(null, PropertyAccessFailure.FIELD_DOES_NOT_EXIST)); } @@ -59,7 +60,7 @@ public static Tuple, PropertyAccessFailure> getNestedPropertyType( return INVALID_INPUT; } - boolean notNestedPath = fieldName.indexOf(PATH_SEPARATOR) == fieldName.lastIndexOf(PATH_SEPARATOR); + boolean notNestedPath = fieldName.indexOf(PATH_SEPARATOR) == -1; if (notNestedPath) { return getPropertyType(bean, fieldName, fieldVisibilityCheckers); @@ -67,7 +68,8 @@ public static Tuple, PropertyAccessFailure> getNestedPropertyType( String leafPath = fieldName.substring(fieldName.lastIndexOf(PATH_SEPARATOR) + 1); - return getNestedProperty(bean, fieldName).map(leafParent -> getPropertyType(leafParent, leafPath, fieldVisibilityCheckers)) + return getNestedProperty(bean, fieldName.substring(0, fieldName.lastIndexOf(PATH_SEPARATOR))) + .map(leafParent -> getPropertyType(leafParent, leafPath, fieldVisibilityCheckers)) .orElse(FIELD_DOES_NOT_EXIST); } diff --git a/sormas-backend/src/test/java/de/symeda/sormas/backend/patch/PropertyAccessorTest.java b/sormas-backend/src/test/java/de/symeda/sormas/backend/patch/PropertyAccessorTest.java index 76514d4f25c..e919ae3c8c5 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/backend/patch/PropertyAccessorTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/backend/patch/PropertyAccessorTest.java @@ -141,12 +141,10 @@ void getNestedPropertyType_invisibleField_returnsUnsupportedField() { AddressBean address = new AddressBean(); PersonBean person = new PersonBean(); person.setAddress(address); - FieldVisibilityCheckers hideAll = - FieldVisibilityCheckers.withCheckers((FieldVisibilityCheckers.FieldNameBaseChecker) (type, id) -> false); + FieldVisibilityCheckers hideAll = FieldVisibilityCheckers.withCheckers((FieldVisibilityCheckers.FieldNameBaseChecker) (type, id) -> false); // EXECUTE - Tuple, PropertyAccessFailure> result = - PropertyAccessor.getNestedPropertyType(person, "address.street", hideAll); + Tuple, PropertyAccessFailure> result = PropertyAccessor.getNestedPropertyType(person, "address.street", hideAll); // CHECK assertEquals(PropertyAccessFailure.UNSUPPORTED_FIELD_FOR_DISEASE_OR_COUNTRY_OR_FEATURE, result.getSecond()); @@ -183,9 +181,7 @@ void getNestedPropertyAndType_twoSegmentPath_returnsTypeAndValue() { @Test void getNestedPropertyAndType_threeSegmentPath_returnsTypeAndValue() { // PREPARE - AddressBean level4 = new AddressBean(); AddressBean level3 = new AddressBean(); - level3.setAddress(level4); AddressBean level2 = new AddressBean(); level2.setAddress(level3); AddressBean level1 = new AddressBean(); @@ -200,7 +196,7 @@ void getNestedPropertyAndType_threeSegmentPath_returnsTypeAndValue() { // CHECK assertNull(result.getSecond()); assertEquals(AddressBean.class, result.getFirst().getFirst()); - assertEquals(level4, result.getFirst().getSecond()); + assertEquals(level3, result.getFirst().getSecond()); } @Test @@ -254,12 +250,10 @@ void getPropertyTypeAndValue_nonExistentField_returnsFieldDoesNotExist() { void getPropertyTypeAndValue_invisibleField_returnsUnsupportedField() { // PREPARE AddressBean address = new AddressBean(); - FieldVisibilityCheckers hideAll = - FieldVisibilityCheckers.withCheckers((FieldVisibilityCheckers.FieldNameBaseChecker) (type, id) -> false); + FieldVisibilityCheckers hideAll = FieldVisibilityCheckers.withCheckers((FieldVisibilityCheckers.FieldNameBaseChecker) (type, id) -> false); // EXECUTE - Tuple, Object>, PropertyAccessFailure> result = - PropertyAccessor.getPropertyTypeAndValue(address, "street", hideAll); + Tuple, Object>, PropertyAccessFailure> result = PropertyAccessor.getPropertyTypeAndValue(address, "street", hideAll); // CHECK assertEquals(PropertyAccessFailure.UNSUPPORTED_FIELD_FOR_DISEASE_OR_COUNTRY_OR_FEATURE, result.getSecond()); diff --git a/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java b/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java index f16612b8db4..598da825382 100644 --- a/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java +++ b/sormas-backend/src/test/java/de/symeda/sormas/patch/DataPatcherImplTest.java @@ -487,7 +487,7 @@ void patch_notSupportedForDisease_symptoms_offset() { // PREPARE CaseDataDto originalCase = creator.createUnclassifiedCase(Disease.GIARDIASIS); - Map patchDictionary = Map.of("Symptoms.offsetDate", new java.util.Date()); + Map patchDictionary = Map.of("CaseData.symptoms.offsetDate", new java.util.Date()); // EXECUTE DataPatchResponse response = @@ -1043,7 +1043,7 @@ void patch_multipleEntities_caseDataExposureHospitalizationSymptoms() { // This modifies many entities are triggers case update. // Might require changing outdated range to reproduce every time // PREPARE - Disease disease = Disease.DENGUE; + Disease disease = Disease.MEASLES; CaseDataDto originalCase = creator.createUnclassifiedCase(disease); // EXECUTE @@ -1139,7 +1139,7 @@ void patch_multipleEntities_caseDataExposureHospitalizationSymptoms() { // Case's vaccinationStatus is not set directly by the patch — it is only recomputed as a side effect // of ImmunizationFacade/VaccinationFacade#save() calling CaseService#updateDeterminedVaccinationStatuses - () -> Assertions.assertEquals(VaccinationStatus.UNVACCINATED, actualCase.getVaccinationStatus()), + () -> Assertions.assertEquals(VaccinationStatus.VACCINATED, actualCase.getVaccinationStatus()), // Person — proves saving Person cascades an update into the case as well () -> Assertions.assertEquals(Sex.MALE, actualPerson.getSex()), From 65aeedbcedba0ca0273e8cf7576fd91a847d0a1c Mon Sep 17 00:00:00 2001 From: Pa-Touche <47572440+Pa-Touche@users.noreply.github.com> Date: Wed, 8 Jul 2026 15:19:39 +0200 Subject: [PATCH 13/13] fixed broken tests - resolved PR comments --- .../sormas/api/symptoms/SymptomsDto.java | 96 ------------------- .../src/main/resources/captions.properties | 5 - .../ExternalMessageFacadeEjb.java | 10 +- .../AutomaticSurveyResponseProcessor.java | 7 +- .../backend/symptoms/SymptomsFacadeEjb.java | 13 --- .../SurveyResponseDetailsWindow.java | 3 +- .../sormas/ui/symptoms/SymptomsForm.java | 5 - 7 files changed, 16 insertions(+), 123 deletions(-) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java index 2c371db997e..6785155bf75 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/symptoms/SymptomsDto.java @@ -346,14 +346,6 @@ public class SymptomsDto extends PseudonymizableDto { public static final String WEIGHT_LOSS = "weightLoss"; public static final String WEIGHT_LOSS_AMOUNT = "weightLossAmount"; public static final String BLOATING = "bloating"; - - public static final String LOSS_OF_APPETITE = "lossOfAppetite"; - public static final String FLATULENCE = "flatulence"; - public static final String SMELLY_BURPS = "smellyBurps"; - public static final String COUGHING_ATTACKS = "coughingAttacks"; - public static final String COUGHING_AT_NIGHT = "coughingAtNight"; - public static final String ABDOMINAL_CRAMPS = "abdominalCramps"; - public static final String REOCCURRENCE = "reoccurrence"; public static final String OVERNIGHT_STAY_REQUIRED = "overnightStayRequired"; public static final String SYMPTOM_CURRENT_STATUS = "symptomCurrentStatus"; @@ -2958,46 +2950,6 @@ public static SymptomsDto build() { GIARDIASIS }) @SymptomGrouping(SymptomGroup.GASTROINTESTINAL) private SymptomState bloating; - - @Diseases({ - CRYPTOSPORIDIOSIS, - GIARDIASIS, - RESPIRATORY_SYNCYTIAL_VIRUS }) - @HideForCountriesExcept(countries = { - CountryHelper.COUNTRY_CODE_LUXEMBOURG }) - private SymptomState lossOfAppetite; - - @Diseases({ - GIARDIASIS }) - @HideForCountriesExcept(countries = { - CountryHelper.COUNTRY_CODE_LUXEMBOURG }) - private SymptomState flatulence; - - @Diseases({ - GIARDIASIS }) - @HideForCountriesExcept(countries = { - CountryHelper.COUNTRY_CODE_LUXEMBOURG }) - private SymptomState smellyBurps; - - @Diseases({ - PERTUSSIS }) - @HideForCountriesExcept(countries = { - CountryHelper.COUNTRY_CODE_LUXEMBOURG }) - private SymptomState coughingAttacks; - - @Diseases({ - PERTUSSIS }) - @HideForCountriesExcept(countries = { - CountryHelper.COUNTRY_CODE_LUXEMBOURG }) - private SymptomState coughingAtNight; - - @Diseases({ - CRYPTOSPORIDIOSIS, - GIARDIASIS }) - @HideForCountriesExcept(countries = { - CountryHelper.COUNTRY_CODE_LUXEMBOURG }) - private SymptomState abdominalCramps; - private DiagnosisType diagnosis; private InfectionSite majorSite; private String otherMajorSiteDetails; @@ -5364,54 +5316,6 @@ public void setEyeIrritation(SymptomState eyeIrritation) { this.eyeIrritation = eyeIrritation; } - public SymptomState getCoughingAtNight() { - return coughingAtNight; - } - - public void setCoughingAtNight(SymptomState coughingAtNight) { - this.coughingAtNight = coughingAtNight; - } - - public SymptomState getLossOfAppetite() { - return lossOfAppetite; - } - - public void setLossOfAppetite(SymptomState lossOfAppetite) { - this.lossOfAppetite = lossOfAppetite; - } - - public SymptomState getFlatulence() { - return flatulence; - } - - public void setFlatulence(SymptomState flatulence) { - this.flatulence = flatulence; - } - - public SymptomState getSmellyBurps() { - return smellyBurps; - } - - public void setSmellyBurps(SymptomState smellyBurps) { - this.smellyBurps = smellyBurps; - } - - public SymptomState getCoughingAttacks() { - return coughingAttacks; - } - - public void setCoughingAttacks(SymptomState coughingAttacks) { - this.coughingAttacks = coughingAttacks; - } - - public SymptomState getAbdominalCramps() { - return abdominalCramps; - } - - public void setAbdominalCramps(SymptomState abdominalCramps) { - this.abdominalCramps = abdominalCramps; - } - public SymptomState getTenesmus() { return tenesmus; } diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties index 395d706a362..e858e57b46e 100644 --- a/sormas-api/src/main/resources/captions.properties +++ b/sormas-api/src/main/resources/captions.properties @@ -3144,11 +3144,6 @@ Symptoms.tenesmus=Tenesmus Symptoms.haemolyticUremicSyndrome=Haemolytic uremic syndrome Symptoms.bloodyDiarrhea=Bloody diarrhea Symptoms.wateryDiarrhea=Watery diarrhoea -Symptoms.lossOfAppetite=Loss of appetite -Symptoms.flatulence=Flatulence -Symptoms.coughingAttacks=Coughing attacks -Symptoms.coughingAtNight=Coughing at night -Symptoms.abdominalCramps=Abdominal cramps titleComplications=Complications titleComplications.DENG=Warning signs titleNoComplications=No complications diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java index 0bcdeac7b83..e25854bc6da 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java @@ -1057,7 +1057,13 @@ public ExternalMessageDto executeSurveyProcessing(String uuid) { public ExternalMessageDto overwriteSurveyResponse(String uuid, @Nullable PatchDictionary correctedDictionary) { logger.debug("overwriteSurveyResponse: [{}],[{}]", uuid, correctedDictionary); ExternalMessageDto externalMessage = getByUuid(uuid); - ExternalMessageSurveyResponseRequest latestRequest = externalMessage.getSurveyResponseData().getLatest().getRequest(); + ExternalSurveyResponseData surveyResponseData = externalMessage.getSurveyResponseData(); + + if (surveyResponseData == null) { + throw new IllegalStateException(String.format("Missing surveyResponseData for external message: [%s]", uuid)); + } + + ExternalMessageSurveyResponseRequest latestRequest = surveyResponseData.getLatest().getRequest(); logger.info("On reprocessing replacement strategy is set to ALWAYS to allow override values, enable debug to see request"); logger.debug("Request before transformation: [{}]", latestRequest); @@ -1079,7 +1085,7 @@ public ExternalMessageDto overwriteSurveyResponse(String uuid, @Nullable PatchDi logger.debug("Request after transformation: [{}]", correctedRequest); ExternalMessageSurveyResponseWrapper updatedWrapper = new ExternalMessageSurveyResponseWrapper().setRequest(correctedRequest); - externalMessage.getSurveyResponseData().setUpdated(updatedWrapper); + surveyResponseData.setUpdated(updatedWrapper); externalMessage.setChangeDate(new Date()); ExternalMessageDto result; diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java index 03c594c0990..1a404d22d68 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/survey/AutomaticSurveyResponseProcessor.java @@ -5,6 +5,7 @@ import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; @@ -85,7 +86,11 @@ public List processSurveyResponses(List> tokenBySurveyReferenceTuples = surveyFacade.getByExternalIds(externalSurveyIds) .stream() - .flatMap(survey -> tokensByExternalIds.get(survey.getExternalId()).stream().map(token -> new Tuple<>(survey.toReference(), token))) + .flatMap( + survey -> tokensByExternalIds.get(survey.getExternalId()) + .stream() + .filter(Objects::nonNull) + .map(token -> new Tuple<>(survey.toReference(), token))) .collect(Collectors.toList()); List surveyTokens = surveyTokenFacade.getBySurveyReferenceTokenTuples(tokenBySurveyReferenceTuples); diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/symptoms/SymptomsFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/symptoms/SymptomsFacadeEjb.java index 43b777b05f6..63eafeafa2b 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/symptoms/SymptomsFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/symptoms/SymptomsFacadeEjb.java @@ -248,13 +248,6 @@ public Symptoms fillOrBuildEntity(SymptomsDto source, Symptoms target, boolean c target.setWeightLossAmount(source.getWeightLossAmount()); target.setBloating(source.getBloating()); target.setOvernightStayRequired(source.getOvernightStayRequired()); - target.setLossOfAppetite(source.getLossOfAppetite()); - target.setFlatulence(source.getFlatulence()); - target.setSmellyBurps(source.getSmellyBurps()); - target.setCoughingAttacks(source.getCoughingAttacks()); - target.setCoughingAtNight(source.getCoughingAtNight()); - target.setAbdominalCramps(source.getAbdominalCramps()); - target.setClammySkin(source.getClammySkin()); target.setColdSkin(source.getColdSkin()); target.setEncephalitis(source.getEncephalitis()); @@ -548,12 +541,6 @@ public static SymptomsDto toSymptomsDto(Symptoms symptoms) { target.setConstipation(source.getConstipation()); target.setDysuria(source.getDysuria()); target.setEyeIrritation(source.getEyeIrritation()); - target.setLossOfAppetite(source.getLossOfAppetite()); - target.setFlatulence(source.getFlatulence()); - target.setSmellyBurps(source.getSmellyBurps()); - target.setCoughingAttacks(source.getCoughingAttacks()); - target.setCoughingAtNight(source.getCoughingAtNight()); - target.setAbdominalCramps(source.getAbdominalCramps()); target.setTenesmus(source.getTenesmus()); target.setHaemolyticUremicSyndrome(source.getHaemolyticUremicSyndrome()); target.setBloodyDiarrhea(source.getBloodyDiarrhea()); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java index e2253c7d2c5..ac725fcb8e1 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/surveyresponse/SurveyResponseDetailsWindow.java @@ -178,7 +178,8 @@ public SurveyResponseDetailsWindow(ExternalMessageDto externalMessage, Runnable if (result == null) { try { - result = FacadeProvider.getExternalMessageFacade().executeSurveyProcessing(uuid).getSurveyResponseData().getLatest().getResult(); + externalMessage = FacadeProvider.getExternalMessageFacade().executeSurveyProcessing(uuid); + result = externalMessage.getSurveyResponseData().getLatest().getResult(); } catch (RuntimeException e) { Notification.show(I18nProperties.getString(Strings.messageSurveyResponseNotYetProcessed), Notification.Type.HUMANIZED_MESSAGE); } diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java index 8c18973bb6a..1b51d206a6b 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/symptoms/SymptomsForm.java @@ -526,11 +526,6 @@ public String getFormattedHtmlMessage() { URINARY_RETENTION, FEVER, BLOATING, - LOSS_OF_APPETITE, - FLATULENCE, - COUGHING_ATTACKS, - COUGHING_AT_NIGHT, - ABDOMINAL_CRAMPS, REOCCURRENCE, WEIGHT_LOSS, WEIGHT_LOSS_AMOUNT,