From 6771f4b31fa270b6c2efd8267dedd78a23688211 Mon Sep 17 00:00:00 2001 From: Harold Date: Wed, 29 Apr 2026 16:26:59 +0300 Subject: [PATCH 1/8] Register SALMONELLOSIS disease + base configuration --- .../java/de/symeda/sormas/api/Disease.java | 1 + sormas-api/src/main/resources/enum.properties | 4 +- .../src/main/resources/sql/sormas_schema.sql | 49 ++++++++++++------- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/Disease.java b/sormas-api/src/main/java/de/symeda/sormas/api/Disease.java index b0a2949ab8d..6cedee61d39 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/Disease.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/Disease.java @@ -90,6 +90,7 @@ public enum Disease INVASIVE_MENINGOCOCCAL_INFECTION(true, true, true, false, true, 7, false, false, false, true, 1, 10), GIARDIASIS(true, true, true, false, true, 14, false, false, false, true, 7, 21), CRYPTOSPORIDIOSIS(true, true, true, false, true, 14, false, false, false, true, 2, 12), + SALMONELLOSIS(true, true, true, false, true, 14, false, false, false, true, 1, 3), OTHER(true, true, true, false, true, 21, false, false, false, false, 0, 0), UNDEFINED(true, true, true, false, true, 0, false, false, false, false, 0, 0); diff --git a/sormas-api/src/main/resources/enum.properties b/sormas-api/src/main/resources/enum.properties index fcde5ef6726..acf95642795 100644 --- a/sormas-api/src/main/resources/enum.properties +++ b/sormas-api/src/main/resources/enum.properties @@ -578,6 +578,7 @@ Disease.POST_IMMUNIZATION_ADVERSE_EVENTS_SEVERE = Post-immunization adverse even Disease.FHA = FHA (Functional Hypothalamic Amenorrhea) Disease.GIARDIASIS=Giardiasis Disease.CRYPTOSPORIDIOSIS=Cryptosporidiosis +Disease.SALMONELLOSIS=Salmonellosis Disease.Short.AFP = AFP Disease.Short.CHOLERA = Cholera Disease.Short.CONGENITAL_RUBELLA = CRS @@ -644,6 +645,7 @@ Disease.Short.INVASIVE_PNEUMOCOCCAL_INFECTION = IPI Disease.Short.INVASIVE_MENINGOCOCCAL_INFECTION = IMI Disease.Short.GIARDIASIS=Giardia Disease.Short.CRYPTOSPORIDIOSIS=Cryptosporidiosis +Disease.Short.SALMONELLOSIS=Salmonellosis DiseaseTransmissionMode.HUMAN_TO_HUMAN = Primarily via human to human DiseaseTransmissionMode.ANIMAL = Primarily via animal @@ -3091,4 +3093,4 @@ AnimalCategory.WILD=Wild # FomiteTransmissionLocation FomiteTransmissionLocation.INSIDE_HOME=Inside home -FomiteTransmissionLocation.OUTSIDE=Outside \ No newline at end of file +FomiteTransmissionLocation.OUTSIDE=Outside diff --git a/sormas-backend/src/main/resources/sql/sormas_schema.sql b/sormas-backend/src/main/resources/sql/sormas_schema.sql index 9c1251e9dbf..379fe86b9d4 100644 --- a/sormas-backend/src/main/resources/sql/sormas_schema.sql +++ b/sormas-backend/src/main/resources/sql/sormas_schema.sql @@ -15513,11 +15513,11 @@ ALTER TABLE testreport ADD COLUMN IF NOT EXISTS serotypetext varchar(255); UPDATE testreport SET serotypetext = serotype, serotype = 'OTHER' WHERE serotype IS NOT null and serotypetext is null; DO $$ BEGIN - IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport' AND column_name = 'genotyperesult') + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport' AND column_name = 'genotyperesult') AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport' AND column_name = 'genotype') THEN ALTER TABLE testreport RENAME COLUMN genotyperesult TO genotype; END IF; - IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport' AND column_name = 'genotyperesulttext') + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport' AND column_name = 'genotyperesulttext') AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport' AND column_name = 'genotypetext') THEN ALTER TABLE testreport RENAME COLUMN genotyperesulttext TO genotypetext; END IF; @@ -15527,11 +15527,11 @@ ALTER TABLE testreport_history ADD COLUMN IF NOT EXISTS serotypetext varchar(255 UPDATE testreport_history SET serotypetext = serotype, serotype = 'OTHER' WHERE serotype IS NOT null and serotypetext is null; DO $$ BEGIN - IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport_history' AND column_name = 'genotyperesult') + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport_history' AND column_name = 'genotyperesult') AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport_history' AND column_name = 'genotype') THEN ALTER TABLE testreport_history RENAME COLUMN genotyperesult TO genotype; END IF; - IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport_history' AND column_name = 'genotyperesulttext') + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport_history' AND column_name = 'genotyperesulttext') AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'testreport_history' AND column_name = 'genotypetext') THEN ALTER TABLE testreport_history RENAME COLUMN genotyperesulttext TO genotypetext; END IF; @@ -15598,20 +15598,20 @@ CREATE TABLE IF NOT EXISTS customizablefieldmetadata ( change_user_id bigint, sys_period tstzrange NOT NULL, - + PRIMARY KEY (id), UNIQUE(name, contextClass) ); -CREATE INDEX idx_customizablefieldmetadata_uuid +CREATE INDEX idx_customizablefieldmetadata_uuid ON customizablefieldmetadata (uuid); -CREATE INDEX idx_customizablefieldmetadata_contextClass +CREATE INDEX idx_customizablefieldmetadata_contextClass ON customizablefieldmetadata (contextClass); -CREATE INDEX idx_customizablefieldmetadata_uiGroup +CREATE INDEX idx_customizablefieldmetadata_uiGroup ON customizablefieldmetadata (uiGroup); -CREATE INDEX idx_customizablefieldmetadata_active +CREATE INDEX idx_customizablefieldmetadata_active ON customizablefieldmetadata (active); -CREATE INDEX idx_customizablefieldmetadata_deleted +CREATE INDEX idx_customizablefieldmetadata_deleted ON customizablefieldmetadata (deleted); ALTER TABLE customizablefieldmetadata OWNER TO sormas_user; @@ -15662,20 +15662,20 @@ CREATE TABLE IF NOT EXISTS customizablefieldvalue ( change_user_id bigint, sys_period tstzrange NOT NULL, - + PRIMARY KEY (id), UNIQUE(customizablefieldmetadata_id, entityUuid, contextClass) ); -CREATE INDEX idx_customizablefieldvalue_uuid +CREATE INDEX idx_customizablefieldvalue_uuid ON customizablefieldvalue (uuid); -CREATE INDEX idx_customizablefieldvalue_entityUuid +CREATE INDEX idx_customizablefieldvalue_entityUuid ON customizablefieldvalue (entityUuid); -CREATE INDEX idx_customizablefieldvalue_contextEntity +CREATE INDEX idx_customizablefieldvalue_contextEntity ON customizablefieldvalue (contextClass, entityUuid); -CREATE INDEX idx_customizablefieldvalue_fieldMetadata +CREATE INDEX idx_customizablefieldvalue_fieldMetadata ON customizablefieldvalue (customizablefieldmetadata_id); -CREATE INDEX idx_customizablefieldvalue_deleted +CREATE INDEX idx_customizablefieldvalue_deleted ON customizablefieldvalue (deleted); ALTER TABLE customizablefieldvalue ADD CONSTRAINT fk_change_user_id FOREIGN KEY (change_user_id) REFERENCES users (id); @@ -15822,4 +15822,19 @@ ALTER TABLE externalmessage_history ADD COLUMN healthcareprofessional varchar(25 ALTER TABLE externalmessage_history ADD COLUMN modeoftransmission varchar(255); ALTER TABLE externalmessage_history ADD column modeoftransmissiontype varchar(255); INSERT INTO schema_version (version_number, comment) VALUES (624, '#13838, #13837, #13835, #13836 - Malaria and Dengue Doctors declaration and lab messages changes'); --- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. *** \ No newline at end of file + +-- 2026-04-27 Salmonellosis disease configuration (Luxembourg) #13915 +-- The diseaseconfiguration row for SALMONELLOSIS is created by createMissingDiseaseConfigurations() at startup, +-- which runs after this migration. Insert a row up-front so the UPDATE applies on the first deployment. +INSERT INTO diseaseconfiguration (id, uuid, changedate, creationdate, disease) +SELECT nextval('entity_seq'), + upper(substring(CAST(CAST(md5(CAST(random() AS text) || CAST(clock_timestamp() AS text)) AS uuid) AS text), 3, 29)), + now(), now(), 'SALMONELLOSIS' +WHERE NOT EXISTS (SELECT 1 FROM diseaseconfiguration WHERE disease = 'SALMONELLOSIS'); + +UPDATE diseaseconfiguration + SET exposurecategories = 'FOOD_BORNE,WATER_BORNE,ANIMAL_CONTACT,DIRECT_CONTACT' + WHERE disease = 'SALMONELLOSIS'; + +INSERT INTO schema_version (version_number, comment) VALUES (625, '#13915 - Salmonellosis disease configuration'); +-- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. *** From 769720bdbe77a2729efaec9842fec72585210c1b Mon Sep 17 00:00:00 2001 From: Harold Date: Wed, 29 Apr 2026 17:24:27 +0300 Subject: [PATCH 2/8] Enhance EpiData, EpiDataForm HospitalizationDto, HospitalizationForm, PersonDto, PersonEditForm & schema to support SAL disease --- .../symeda/sormas/api/epidata/EpiDataDto.java | 64 ++++++++++++++++++- .../hospitalization/HospitalizationDto.java | 3 +- .../de/symeda/sormas/api/i18n/Captions.java | 46 +++++++++---- .../symeda/sormas/api/person/PersonDto.java | 12 ++-- .../src/main/resources/captions.properties | 6 +- .../sormas/backend/epidata/EpiData.java | 43 +++++++++++++ .../backend/epidata/EpiDataFacadeEjb.java | 8 +++ .../src/main/resources/sql/sormas_schema.sql | 13 ++++ .../symeda/sormas/ui/epidata/EpiDataForm.java | 7 ++ .../hospitalization/HospitalizationForm.java | 4 +- .../sormas/ui/person/PersonEditForm.java | 10 ++- 11 files changed, 190 insertions(+), 26 deletions(-) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java index 85c2e5ef928..5169eb4bb36 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java @@ -18,6 +18,7 @@ package de.symeda.sormas.api.epidata; import java.util.ArrayList; +import java.util.Date; import java.util.List; import javax.validation.Valid; @@ -73,6 +74,10 @@ public class EpiDataDto extends PseudonymizableDto { public static final String HEALTHCARE_PROFESSIONAL = "healthcareProfessional"; public static final String PLACE_OF_INFECTION = "placeOfInfection"; public static final String RESIDENCE_AT_ONSET = "residenceAtOnset"; + public static final String EXPOSURE_INVESTIGATION_FROM_DATE = "exposureInvestigationFromDate"; + public static final String EXPOSURE_INVESTIGATION_TO_DATE = "exposureInvestigationToDate"; + public static final String ACTIVITY_AS_CASE_FROM_DATE = "activityAsCaseFromDate"; + public static final String ACTIVITY_AS_CASE_TO_DATE = "activityAsCaseToDate"; private YesNoUnknown exposureDetailsKnown; private YesNoUnknown activityAsCaseDetailsKnown; @@ -84,7 +89,8 @@ public class EpiDataDto extends PseudonymizableDto { private CaseImportedStatus caseImportedStatus; @Diseases({ - Disease.GIARDIASIS }) + Disease.GIARDIASIS, + Disease.SALMONELLOSIS }) private YesNoUnknown importedCase; @HideForCountriesExcept(countries = { @@ -138,7 +144,8 @@ public class EpiDataDto extends PseudonymizableDto { private String infectionSourceText; @Diseases({ - Disease.GIARDIASIS }) + Disease.GIARDIASIS, + Disease.SALMONELLOSIS }) private CountryReferenceDto country; @Valid @@ -172,6 +179,27 @@ public class EpiDataDto extends PseudonymizableDto { @Size(max = 255, message = Validations.textTooLong) private String residenceAtOnset; + @Diseases({ + Disease.SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + private Date exposureInvestigationFromDate; + @Diseases({ + Disease.SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + private Date exposureInvestigationToDate; + @Diseases({ + Disease.SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + private Date activityAsCaseFromDate; + @Diseases({ + Disease.SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + private Date activityAsCaseToDate; + public YesNoUnknown getExposureDetailsKnown() { return exposureDetailsKnown; } @@ -365,6 +393,38 @@ public void setResidenceAtOnset(String residenceAtOnset) { this.residenceAtOnset = residenceAtOnset; } + public Date getExposureInvestigationFromDate() { + return exposureInvestigationFromDate; + } + + public void setExposureInvestigationFromDate(Date exposureInvestigationFromDate) { + this.exposureInvestigationFromDate = exposureInvestigationFromDate; + } + + public Date getExposureInvestigationToDate() { + return exposureInvestigationToDate; + } + + public void setExposureInvestigationToDate(Date exposureInvestigationToDate) { + this.exposureInvestigationToDate = exposureInvestigationToDate; + } + + public Date getActivityAsCaseFromDate() { + return activityAsCaseFromDate; + } + + public void setActivityAsCaseFromDate(Date activityAsCaseFromDate) { + this.activityAsCaseFromDate = activityAsCaseFromDate; + } + + public Date getActivityAsCaseToDate() { + return activityAsCaseToDate; + } + + public void setActivityAsCaseToDate(Date activityAsCaseToDate) { + this.activityAsCaseToDate = activityAsCaseToDate; + } + @Override public EpiDataDto clone() throws CloneNotSupportedException { EpiDataDto clone = (EpiDataDto) super.clone(); diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/hospitalization/HospitalizationDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/hospitalization/HospitalizationDto.java index f1d7d1aff13..fa8b6c81409 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/hospitalization/HospitalizationDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/hospitalization/HospitalizationDto.java @@ -99,7 +99,8 @@ public class HospitalizationDto extends EntityDto { @Diseases({ Disease.RESPIRATORY_SYNCYTIAL_VIRUS, Disease.GIARDIASIS, - Disease.CRYPTOSPORIDIOSIS }) + Disease.CRYPTOSPORIDIOSIS, + Disease.SALMONELLOSIS }) private YesNoUnknown stillHospitalized; @Diseases({ Disease.RESPIRATORY_SYNCYTIAL_VIRUS, diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java index a143a8dc941..29e4766c21a 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java @@ -336,9 +336,11 @@ public interface Captions { String AefiInvestigation_aefiClassificationDetails = "AefiInvestigation.aefiClassificationDetails"; String AefiInvestigation_aefiClassificationSubType = "AefiInvestigation.aefiClassificationSubType"; String AefiInvestigation_allCasesInClusterReceivedVaccineFromSameVial = "AefiInvestigation.allCasesInClusterReceivedVaccineFromSameVial"; - String AefiInvestigation_allCasesInClusterReceivedVaccineFromSameVialDetails = "AefiInvestigation.allCasesInClusterReceivedVaccineFromSameVialDetails"; + String AefiInvestigation_allCasesInClusterReceivedVaccineFromSameVialDetails = + "AefiInvestigation.allCasesInClusterReceivedVaccineFromSameVialDetails"; String AefiInvestigation_anyOtherItemInRefrigerator = "AefiInvestigation.anyOtherItemInRefrigerator"; - String AefiInvestigation_anyStorageTemperatureDeviationOutsideTwoToEightDegrees = "AefiInvestigation.anyStorageTemperatureDeviationOutsideTwoToEightDegrees"; + String AefiInvestigation_anyStorageTemperatureDeviationOutsideTwoToEightDegrees = + "AefiInvestigation.anyStorageTemperatureDeviationOutsideTwoToEightDegrees"; String AefiInvestigation_autopsyDate = "AefiInvestigation.autopsyDate"; String AefiInvestigation_autopsyDone = "AefiInvestigation.autopsyDone"; String AefiInvestigation_autopsyPlannedDateTime = "AefiInvestigation.autopsyPlannedDateTime"; @@ -374,7 +376,8 @@ public interface Captions { String AefiInvestigation_errorPrescribingVaccine = "AefiInvestigation.errorPrescribingVaccine"; String AefiInvestigation_errorPrescribingVaccineDetails = "AefiInvestigation.errorPrescribingVaccineDetails"; String AefiInvestigation_eventIsAStressResponseRelatedToImmunization = "AefiInvestigation.eventIsAStressResponseRelatedToImmunization"; - String AefiInvestigation_eventIsAStressResponseRelatedToImmunizationDetails = "AefiInvestigation.eventIsAStressResponseRelatedToImmunizationDetails"; + String AefiInvestigation_eventIsAStressResponseRelatedToImmunizationDetails = + "AefiInvestigation.eventIsAStressResponseRelatedToImmunizationDetails"; String AefiInvestigation_externalId = "AefiInvestigation.externalId"; String AefiInvestigation_familyHistoryOfDiseaseOrAllergy = "AefiInvestigation.familyHistoryOfDiseaseOrAllergy"; String AefiInvestigation_familyHistoryOfDiseaseOrAllergyDetails = "AefiInvestigation.familyHistoryOfDiseaseOrAllergyDetails"; @@ -382,8 +385,10 @@ public interface Captions { String AefiInvestigation_formCompletionDate = "AefiInvestigation.formCompletionDate"; String AefiInvestigation_historyOfAllergyToVaccineDrugOrFood = "AefiInvestigation.historyOfAllergyToVaccineDrugOrFood"; String AefiInvestigation_historyOfAllergyToVaccineDrugOrFoodDetails = "AefiInvestigation.historyOfAllergyToVaccineDrugOrFoodDetails"; - String AefiInvestigation_historyOfHospitalizationInLastThirtyDaysWithCause = "AefiInvestigation.historyOfHospitalizationInLastThirtyDaysWithCause"; - String AefiInvestigation_historyOfHospitalizationInLastThirtyDaysWithCauseDetails = "AefiInvestigation.historyOfHospitalizationInLastThirtyDaysWithCauseDetails"; + String AefiInvestigation_historyOfHospitalizationInLastThirtyDaysWithCause = + "AefiInvestigation.historyOfHospitalizationInLastThirtyDaysWithCause"; + String AefiInvestigation_historyOfHospitalizationInLastThirtyDaysWithCauseDetails = + "AefiInvestigation.historyOfHospitalizationInLastThirtyDaysWithCauseDetails"; String AefiInvestigation_hospitalizationDate = "AefiInvestigation.hospitalizationDate"; String AefiInvestigation_injectionTechniqueAdditionalDetails = "AefiInvestigation.injectionTechniqueAdditionalDetails"; String AefiInvestigation_investigationCaseId = "AefiInvestigation.investigationCaseId"; @@ -395,13 +400,17 @@ public interface Captions { String AefiInvestigation_keySymptomDateTime = "AefiInvestigation.keySymptomDateTime"; String AefiInvestigation_lastTrainingReceivedByVaccinatorDate = "AefiInvestigation.lastTrainingReceivedByVaccinatorDate"; String AefiInvestigation_nonTouchTechniqueFollowed = "AefiInvestigation.nonTouchTechniqueFollowed"; - String AefiInvestigation_numberImmunizedConcernedVaccineSameBatchNumberLocationDetails = "AefiInvestigation.numberImmunizedConcernedVaccineSameBatchNumberLocationDetails"; - String AefiInvestigation_numberImmunizedConcernedVaccineSameBatchNumberOtherLocations = "AefiInvestigation.numberImmunizedConcernedVaccineSameBatchNumberOtherLocations"; + String AefiInvestigation_numberImmunizedConcernedVaccineSameBatchNumberLocationDetails = + "AefiInvestigation.numberImmunizedConcernedVaccineSameBatchNumberLocationDetails"; + String AefiInvestigation_numberImmunizedConcernedVaccineSameBatchNumberOtherLocations = + "AefiInvestigation.numberImmunizedConcernedVaccineSameBatchNumberOtherLocations"; String AefiInvestigation_numberImmunizedFromConcernedVaccineVial = "AefiInvestigation.numberImmunizedFromConcernedVaccineVial"; String AefiInvestigation_numberImmunizedWithConcernedVaccineInSameSession = "AefiInvestigation.numberImmunizedWithConcernedVaccineInSameSession"; - String AefiInvestigation_numberOfAefiReportedFromVaccineDistributionCenterLastThirtyDays = "AefiInvestigation.numberOfAefiReportedFromVaccineDistributionCenterLastThirtyDays"; + String AefiInvestigation_numberOfAefiReportedFromVaccineDistributionCenterLastThirtyDays = + "AefiInvestigation.numberOfAefiReportedFromVaccineDistributionCenterLastThirtyDays"; String AefiInvestigation_numberOfCasesDetectedInCluster = "AefiInvestigation.numberOfCasesDetectedInCluster"; - String AefiInvestigation_numberOfSimilarEventsReportedSamePeriodAndLocality = "AefiInvestigation.numberOfSimilarEventsReportedSamePeriodAndLocality"; + String AefiInvestigation_numberOfSimilarEventsReportedSamePeriodAndLocality = + "AefiInvestigation.numberOfSimilarEventsReportedSamePeriodAndLocality"; String AefiInvestigation_numberOfThoseAffectedNotVaccinated = "AefiInvestigation.numberOfThoseAffectedNotVaccinated"; String AefiInvestigation_numberOfThoseAffectedVaccinated = "AefiInvestigation.numberOfThoseAffectedVaccinated"; String AefiInvestigation_numberOfThoseAffectedVaccinatedUnknown = "AefiInvestigation.numberOfThoseAffectedVaccinatedUnknown"; @@ -422,7 +431,8 @@ public interface Captions { String AefiInvestigation_placeOfVaccination = "AefiInvestigation.placeOfVaccination"; String AefiInvestigation_placeOfVaccinationDetails = "AefiInvestigation.placeOfVaccinationDetails"; String AefiInvestigation_preExistingIllnessThirtyDaysOrCongenitalDisorder = "AefiInvestigation.preExistingIllnessThirtyDaysOrCongenitalDisorder"; - String AefiInvestigation_preExistingIllnessThirtyDaysOrCongenitalDisorderDetails = "AefiInvestigation.preExistingIllnessThirtyDaysOrCongenitalDisorderDetails"; + String AefiInvestigation_preExistingIllnessThirtyDaysOrCongenitalDisorderDetails = + "AefiInvestigation.preExistingIllnessThirtyDaysOrCongenitalDisorderDetails"; String AefiInvestigation_provisionalOrFinalDiagnosis = "AefiInvestigation.provisionalOrFinalDiagnosis"; String AefiInvestigation_reconstitutionAdditionalDetails = "AefiInvestigation.reconstitutionAdditionalDetails"; String AefiInvestigation_reportDate = "AefiInvestigation.reportDate"; @@ -442,14 +452,17 @@ public interface Captions { String AefiInvestigation_responsibleRegion = "AefiInvestigation.responsibleRegion"; String AefiInvestigation_sameReconstitutionSyringeForEachVaccination = "AefiInvestigation.sameReconstitutionSyringeForEachVaccination"; String AefiInvestigation_sameReconstitutionSyringeForEachVaccineVial = "AefiInvestigation.sameReconstitutionSyringeForEachVaccineVial"; - String AefiInvestigation_sameReconstitutionSyringeUsedForMultipleVialsOfSameVaccine = "AefiInvestigation.sameReconstitutionSyringeUsedForMultipleVialsOfSameVaccine"; - String AefiInvestigation_sameReconstitutionSyringeUsedForReconstitutingDifferentVaccines = "AefiInvestigation.sameReconstitutionSyringeUsedForReconstitutingDifferentVaccines"; + String AefiInvestigation_sameReconstitutionSyringeUsedForMultipleVialsOfSameVaccine = + "AefiInvestigation.sameReconstitutionSyringeUsedForMultipleVialsOfSameVaccine"; + String AefiInvestigation_sameReconstitutionSyringeUsedForReconstitutingDifferentVaccines = + "AefiInvestigation.sameReconstitutionSyringeUsedForReconstitutingDifferentVaccines"; String AefiInvestigation_seriousAefiInfoSource = "AefiInvestigation.seriousAefiInfoSource"; String AefiInvestigation_seriousAefiInfoSourceDetails = "AefiInvestigation.seriousAefiInfoSourceDetails"; String AefiInvestigation_seriousAefiVerbalAutopsyInfoSourceDetails = "AefiInvestigation.seriousAefiVerbalAutopsyInfoSourceDetails"; String AefiInvestigation_signsAndSymptomsFromTimeOfVaccination = "AefiInvestigation.signsAndSymptomsFromTimeOfVaccination"; String AefiInvestigation_similarEventsReportedSamePeriodAndLocality = "AefiInvestigation.similarEventsReportedSamePeriodAndLocality"; - String AefiInvestigation_similarEventsReportedSamePeriodAndLocalityDetails = "AefiInvestigation.similarEventsReportedSamePeriodAndLocalityDetails"; + String AefiInvestigation_similarEventsReportedSamePeriodAndLocalityDetails = + "AefiInvestigation.similarEventsReportedSamePeriodAndLocalityDetails"; String AefiInvestigation_statusOnDateOfInvestigation = "AefiInvestigation.statusOnDateOfInvestigation"; String AefiInvestigation_storageTemperatureMonitoringAdditionalDetails = "AefiInvestigation.storageTemperatureMonitoringAdditionalDetails"; String AefiInvestigation_syringesUsedAdditionalDetails = "AefiInvestigation.syringesUsedAdditionalDetails"; @@ -468,7 +481,8 @@ public interface Captions { String AefiInvestigation_vaccinationFacilityDetails = "AefiInvestigation.vaccinationFacilityDetails"; String AefiInvestigation_vaccineAdministeredIncorrectly = "AefiInvestigation.vaccineAdministeredIncorrectly"; String AefiInvestigation_vaccineAdministeredIncorrectlyDetails = "AefiInvestigation.vaccineAdministeredIncorrectlyDetails"; - String AefiInvestigation_vaccineCarrierReturnedFromSiteOnSameDateAsVaccination = "AefiInvestigation.vaccineCarrierReturnedFromSiteOnSameDateAsVaccination"; + String AefiInvestigation_vaccineCarrierReturnedFromSiteOnSameDateAsVaccination = + "AefiInvestigation.vaccineCarrierReturnedFromSiteOnSameDateAsVaccination"; String AefiInvestigation_vaccineCarrierSentToSiteOnSameDateAsVaccination = "AefiInvestigation.vaccineCarrierSentToSiteOnSameDateAsVaccination"; String AefiInvestigation_vaccineCarrierType = "AefiInvestigation.vaccineCarrierType"; String AefiInvestigation_vaccineCarrierTypeDetails = "AefiInvestigation.vaccineCarrierTypeDetails"; @@ -1611,6 +1625,8 @@ public interface Captions { String EpiData = "EpiData"; String EpiData_activitiesAsCase = "EpiData.activitiesAsCase"; String EpiData_activityAsCaseDetailsKnown = "EpiData.activityAsCaseDetailsKnown"; + String EpiData_activityAsCaseFromDate = "EpiData.activityAsCaseFromDate"; + String EpiData_activityAsCaseToDate = "EpiData.activityAsCaseToDate"; String EpiData_airportWorker = "EpiData.airportWorker"; String EpiData_areaInfectedAnimals = "EpiData.areaInfectedAnimals"; String EpiData_caseImportedStatus = "EpiData.caseImportedStatus"; @@ -1620,6 +1636,8 @@ public interface Captions { String EpiData_contactWithSourceCaseKnown = "EpiData.contactWithSourceCaseKnown"; String EpiData_country = "EpiData.country"; String EpiData_exposureDetailsKnown = "EpiData.exposureDetailsKnown"; + String EpiData_exposureInvestigationFromDate = "EpiData.exposureInvestigationFromDate"; + String EpiData_exposureInvestigationToDate = "EpiData.exposureInvestigationToDate"; String EpiData_exposures = "EpiData.exposures"; String EpiData_healthcareProfessional = "EpiData.healthcareProfessional"; String EpiData_highTransmissionRiskArea = "EpiData.highTransmissionRiskArea"; diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/person/PersonDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/person/PersonDto.java index 98d4f45ed31..ab15d4170ef 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/person/PersonDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/person/PersonDto.java @@ -442,12 +442,14 @@ public class PersonDto extends PseudonymizableDto implements IsPerson { @SensitiveData @Diseases(value = { Disease.GIARDIASIS, - Disease.CRYPTOSPORIDIOSIS }) + Disease.CRYPTOSPORIDIOSIS, + Disease.SALMONELLOSIS }) private WorkPlace workPlace; @SensitiveData @Diseases(value = { Disease.GIARDIASIS, - Disease.CRYPTOSPORIDIOSIS }) + Disease.CRYPTOSPORIDIOSIS, + Disease.SALMONELLOSIS }) @DependantOn(WORK_PLACE) private String workPlaceText; @@ -731,7 +733,7 @@ public void setAdditionalPhone(String phone) { } /** - * + * * @return the PRIMARY email address. Email addresses set with the {@link #setEmailAddress(String)} method automatically become primary. * An email address entered in the personEditForm is not, and thus does not become primary email address unless the user * specifically sets it. @@ -742,7 +744,7 @@ public String getEmailAddress() { } /** - * + * * @param onlyPrimary * if true, the return value is same as in {@link #getEmailAddress()}. Otherwise, this method tries to return the only email * address for this person, no matter if primary or not. Results in an SeveralNonPrimaryContactDetailsException when there @@ -780,7 +782,7 @@ public List getAllEmailAddresses() { } /** - * + * * @param email * is automatically set as primary email address, removing the primary status from another email address if necessary. */ diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties index e00f16a8c9c..4e17787ebfa 100644 --- a/sormas-api/src/main/resources/captions.properties +++ b/sormas-api/src/main/resources/captions.properties @@ -1165,6 +1165,10 @@ EpiData.airportWorker=Working at an airport EpiData.healthcareProfessional=Working as a healthcare professional EpiData.placeOfInfection=Probable place of infection EpiData.residenceAtOnset=Residence at the time of symptom onset +EpiData.exposureInvestigationFromDate=Exposure investigation from +EpiData.exposureInvestigationToDate=Exposure investigation to +EpiData.activityAsCaseFromDate=Activity as case from +EpiData.activityAsCaseToDate=Activity as case to #Therapy Therapy.directlyObservedTreatment = Directly observed treatment @@ -3809,4 +3813,4 @@ epipulseDownloadColumnCaption=Download epipulseNewExport=New export epipulseActiveExports=Active Epipulse exports epipulseArchivedExports=Archived Epipulse exports -epipulseAllExports=All Epipulse exports \ No newline at end of file +epipulseAllExports=All Epipulse exports diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiData.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiData.java index 968878b6c60..424ac2af71d 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiData.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiData.java @@ -29,6 +29,8 @@ import javax.persistence.FetchType; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import de.symeda.sormas.api.epidata.CaseImportedStatus; import de.symeda.sormas.api.epidata.ClusterType; @@ -86,6 +88,11 @@ public class EpiData extends AbstractDomainObject { private String placeOfInfection; private String residenceAtOnset; + private Date exposureInvestigationFromDate; + private Date exposureInvestigationToDate; + private Date activityAsCaseFromDate; + private Date activityAsCaseToDate; + @Enumerated(EnumType.STRING) public YesNoUnknown getExposureDetailsKnown() { return exposureDetailsKnown; @@ -301,4 +308,40 @@ public void setResidenceAtOnset(String residenceAtOnset) { this.residenceAtOnset = residenceAtOnset; } + @Temporal(TemporalType.TIMESTAMP) + public Date getExposureInvestigationFromDate() { + return exposureInvestigationFromDate; + } + + public void setExposureInvestigationFromDate(Date exposureInvestigationFromDate) { + this.exposureInvestigationFromDate = exposureInvestigationFromDate; + } + + @Temporal(TemporalType.TIMESTAMP) + public Date getExposureInvestigationToDate() { + return exposureInvestigationToDate; + } + + public void setExposureInvestigationToDate(Date exposureInvestigationToDate) { + this.exposureInvestigationToDate = exposureInvestigationToDate; + } + + @Temporal(TemporalType.TIMESTAMP) + public Date getActivityAsCaseFromDate() { + return activityAsCaseFromDate; + } + + public void setActivityAsCaseFromDate(Date activityAsCaseFromDate) { + this.activityAsCaseFromDate = activityAsCaseFromDate; + } + + @Temporal(TemporalType.TIMESTAMP) + public Date getActivityAsCaseToDate() { + return activityAsCaseToDate; + } + + public void setActivityAsCaseToDate(Date activityAsCaseToDate) { + this.activityAsCaseToDate = activityAsCaseToDate; + } + } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiDataFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiDataFacadeEjb.java index fe4e2cf0439..1676e86a075 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiDataFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiDataFacadeEjb.java @@ -135,6 +135,10 @@ public EpiData fillOrBuildEntity(EpiDataDto source, EpiData target, boolean chec target.setHealthcareProfessional(source.getHealthcareProfessional()); target.setPlaceOfInfection(source.getPlaceOfInfection()); target.setResidenceAtOnset(source.getResidenceAtOnset()); + target.setExposureInvestigationFromDate(source.getExposureInvestigationFromDate()); + target.setExposureInvestigationToDate(source.getExposureInvestigationToDate()); + target.setActivityAsCaseFromDate(source.getActivityAsCaseFromDate()); + target.setActivityAsCaseToDate(source.getActivityAsCaseToDate()); return target; } @@ -325,6 +329,10 @@ public static EpiDataDto toDto(EpiData epiData) { target.setHealthcareProfessional(source.getHealthcareProfessional()); target.setPlaceOfInfection(source.getPlaceOfInfection()); target.setResidenceAtOnset(source.getResidenceAtOnset()); + target.setExposureInvestigationFromDate(source.getExposureInvestigationFromDate()); + target.setExposureInvestigationToDate(source.getExposureInvestigationToDate()); + target.setActivityAsCaseFromDate(source.getActivityAsCaseFromDate()); + target.setActivityAsCaseToDate(source.getActivityAsCaseToDate()); return target; } diff --git a/sormas-backend/src/main/resources/sql/sormas_schema.sql b/sormas-backend/src/main/resources/sql/sormas_schema.sql index 379fe86b9d4..c934347f95b 100644 --- a/sormas-backend/src/main/resources/sql/sormas_schema.sql +++ b/sormas-backend/src/main/resources/sql/sormas_schema.sql @@ -15837,4 +15837,17 @@ UPDATE diseaseconfiguration WHERE disease = 'SALMONELLOSIS'; INSERT INTO schema_version (version_number, comment) VALUES (625, '#13915 - Salmonellosis disease configuration'); + +-- 2026-04-29 Salmonellosis EpiData investigation/activity windows #13915 +ALTER TABLE epidata ADD COLUMN IF NOT EXISTS exposureinvestigationfromdate timestamp; +ALTER TABLE epidata ADD COLUMN IF NOT EXISTS exposureinvestigationtodate timestamp; +ALTER TABLE epidata ADD COLUMN IF NOT EXISTS activityascasefromdate timestamp; +ALTER TABLE epidata ADD COLUMN IF NOT EXISTS activityascasetodate timestamp; + +ALTER TABLE epidata_history ADD COLUMN IF NOT EXISTS exposureinvestigationfromdate timestamp; +ALTER TABLE epidata_history ADD COLUMN IF NOT EXISTS exposureinvestigationtodate timestamp; +ALTER TABLE epidata_history ADD COLUMN IF NOT EXISTS activityascasefromdate timestamp; +ALTER TABLE epidata_history ADD COLUMN IF NOT EXISTS activityascasetodate timestamp; + +INSERT INTO schema_version (version_number, comment) VALUES (626, '#13915 - Salmonellosis EpiData investigation/activity windows'); -- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. *** diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java index 2c4d1f6d963..29cd4650bdc 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java @@ -115,6 +115,7 @@ public class EpiDataForm extends AbstractEditForm { fluidRowLocs("EXP_DATES_LAYOUT") + loc(LOC_EXPOSURE_INVESTIGATION_HEADING) + fluidRowLocs(6,EpiDataDto.CASE_IMPORTED_STATUS,6,"") + + fluidRowLocs(6, EpiDataDto.EXPOSURE_INVESTIGATION_FROM_DATE, 6, EpiDataDto.EXPOSURE_INVESTIGATION_TO_DATE) + loc(LOC_EXP_PERIOD_HEADING) + loc(EpiDataDto.EXPOSURE_DETAILS_KNOWN) + loc(EpiDataDto.EXPOSURES) + @@ -126,6 +127,7 @@ public class EpiDataForm extends AbstractEditForm { fluidRowLocs(EpiDataDto.INFECTION_SOURCE, EpiDataDto.INFECTION_SOURCE_TEXT) + fluidRowLocs(EpiDataDto.PLACE_OF_INFECTION, EpiDataDto.RESIDENCE_AT_ONSET) + loc(LOC_ACTIVITY_AS_CASE_INVESTIGATION_HEADING) + + fluidRowLocs(6, EpiDataDto.ACTIVITY_AS_CASE_FROM_DATE, 6, EpiDataDto.ACTIVITY_AS_CASE_TO_DATE) + loc(EpiDataDto.ACTIVITY_AS_CASE_DETAILS_KNOWN)+ loc(EpiDataDto.ACTIVITIES_AS_CASE) + loc(LOC_CUSTOMIZABLE_FIELDS_ACTIVITY_AS_CASE) + @@ -251,6 +253,11 @@ protected void addFields() { ComboBox country = addInfrastructureField(EpiDataDto.COUNTRY); country.addItems(countries); + addField(EpiDataDto.EXPOSURE_INVESTIGATION_FROM_DATE, DateField.class); + addField(EpiDataDto.EXPOSURE_INVESTIGATION_TO_DATE, DateField.class); + addField(EpiDataDto.ACTIVITY_AS_CASE_FROM_DATE, DateField.class); + addField(EpiDataDto.ACTIVITY_AS_CASE_TO_DATE, DateField.class); + includeExposureDates(symptomOnsetDate, disease); addField(EpiDataDto.AIRPORT_WORKER, NullableOptionGroup.class); addField(EpiDataDto.HEALTHCARE_PROFESSIONAL, NullableOptionGroup.class); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java index 2d6981a5019..3224b2ab12e 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java @@ -173,7 +173,7 @@ protected void addFields() { final NullableOptionGroup oxygenPrescribedField = addField(HospitalizationDto.OXYGEN_PRESCRIBED, NullableOptionGroup.class); final NullableOptionGroup stillHospitalizedField = addField(HospitalizationDto.STILL_HOSPITALIZED, NullableOptionGroup.class); - if (!List.of(Disease.RESPIRATORY_SYNCYTIAL_VIRUS, Disease.GIARDIASIS, Disease.CRYPTOSPORIDIOSIS).contains(caze.getDisease())) { + if (!List.of(Disease.RESPIRATORY_SYNCYTIAL_VIRUS, Disease.GIARDIASIS, Disease.CRYPTOSPORIDIOSIS, Disease.SALMONELLOSIS).contains(caze.getDisease())) { icuLengthOfStayField.setVisible(false); oxygenPrescribedField.setVisible(false); stillHospitalizedField.setVisible(false); @@ -328,7 +328,7 @@ public String getFormattedHtmlMessage() { dischargeDateField.addValueChangeListener(event -> admissionDateField.markAsDirty()); // re-evaluate admission date for consistent validation of all fields // RSV specific logic - if (List.of(Disease.RESPIRATORY_SYNCYTIAL_VIRUS, Disease.CRYPTOSPORIDIOSIS, Disease.GIARDIASIS).contains(caze.getDisease())) { + if (List.of(Disease.RESPIRATORY_SYNCYTIAL_VIRUS, Disease.CRYPTOSPORIDIOSIS, Disease.GIARDIASIS, Disease.SALMONELLOSIS).contains(caze.getDisease())) { intensiveCareUnitStart.addValidator( new DateComparisonValidator( intensiveCareUnitStart, diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java index cef3923347e..1a92dbf7d76 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java @@ -523,7 +523,15 @@ protected void addFields() { FieldHelper.setVisibleWhen(getFieldGroup(), PersonDto.EDUCATION_DETAILS, PersonDto.EDUCATION_TYPE, Arrays.asList(EducationType.OTHER), true); - FieldHelper.setVisibleWhen(getFieldGroup(), PersonDto.WORK_PLACE_TEXT, PersonDto.WORK_PLACE, WorkPlace.OTHER, true); + // Lu Salmonellosis renders the place-of-work text field unconditionally and hides the WorkPlace enum; + // other diseases keep the existing dependent-visibility binding. + boolean isLuxSalmonellosis = disease == Disease.SALMONELLOSIS && isConfiguredServer(CountryHelper.COUNTRY_CODE_LUXEMBOURG); + if (isLuxSalmonellosis) { + setVisible(false, PersonDto.WORK_PLACE); + getField(PersonDto.WORK_PLACE_TEXT).setVisible(true); + } else { + FieldHelper.setVisibleWhen(getFieldGroup(), PersonDto.WORK_PLACE_TEXT, PersonDto.WORK_PLACE, WorkPlace.OTHER, true); + } // Set initial visibilities From 6f24690099c3100c170e63f73d5ec49147e23215 Mon Sep 17 00:00:00 2001 From: Harold Date: Thu, 30 Apr 2026 06:47:27 +0300 Subject: [PATCH 3/8] Adds Salmonellosis fields support to the Symptoms & Exposure flow --- .../sormas/api/exposure/EatingOutVenue.java | 39 +++++++++++ .../sormas/api/exposure/ExposureDto.java | 47 ++++++++++++++ .../api/exposure/ExposureSubSetting.java | 1 + .../de/symeda/sormas/api/i18n/Captions.java | 48 ++++++-------- .../sormas/api/symptoms/SymptomsDto.java | 65 ++++++++++++++++++- .../src/main/resources/captions.properties | 6 ++ sormas-api/src/main/resources/enum.properties | 21 ++++++ .../backend/epidata/EpiDataFacadeEjb.java | 6 ++ .../sormas/backend/exposure/Exposure.java | 39 +++++++++++ .../sormas/backend/symptoms/Symptoms.java | 30 +++++++++ .../backend/symptoms/SymptomsFacadeEjb.java | 6 ++ .../src/main/resources/sql/sormas_schema.sql | 42 +++++++++++- .../sormas/ui/exposure/ExposureForm.java | 42 +++++++++++- .../sormas/ui/symptoms/SymptomsForm.java | 14 +++- 14 files changed, 372 insertions(+), 34 deletions(-) create mode 100644 sormas-api/src/main/java/de/symeda/sormas/api/exposure/EatingOutVenue.java diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/exposure/EatingOutVenue.java b/sormas-api/src/main/java/de/symeda/sormas/api/exposure/EatingOutVenue.java new file mode 100644 index 00000000000..a517f57641a --- /dev/null +++ b/sormas-api/src/main/java/de/symeda/sormas/api/exposure/EatingOutVenue.java @@ -0,0 +1,39 @@ +/* + * SORMAS® - Surveillance Outbreak Response Management & Analysis System + * Copyright © 2016-2026 Helmholtz-Zentrum für Infektionsforschung GmbH (HZI) + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ + +package de.symeda.sormas.api.exposure; + +import de.symeda.sormas.api.i18n.I18nProperties; + +public enum EatingOutVenue { + + CANTEENS, + FOOD_SERVED_ON_BOARD, + GAS_STATIONS, + STREET_VENDORS, + FOOD_COURTS, + CAFES_OR_TEA_ROOMS, + SANDWICH_SHOPS_BAKERIES_DELICATESSENS, + CAFETERIAS_OR_BARS, + KEBAB_FALAFEL_SNACK_SHOPS, + BURGER_RESTAURANTS, + FAST_FOOD_SNACK_BARS, + PIZZERIAS, + ASIAN_RESTAURANTS, + HOTELS, + TAKEAWAY_FOOD, + BUFFET_STYLE_RESTAURANTS, + FOOD_AT_EVENTS, + OTHER; + + @Override + public String toString() { + return I18nProperties.getEnumCaption(this); + } +} diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/exposure/ExposureDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/exposure/ExposureDto.java index dc5ce0a47d9..59c0c1ce0a7 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/exposure/ExposureDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/exposure/ExposureDto.java @@ -22,6 +22,7 @@ import javax.validation.Valid; import javax.validation.constraints.Size; +import de.symeda.sormas.api.CountryHelper; import de.symeda.sormas.api.Disease; import de.symeda.sormas.api.contact.ContactReferenceDto; import de.symeda.sormas.api.epidata.AnimalCondition; @@ -139,6 +140,9 @@ public class ExposureDto extends PseudonymizableDto { public static final String PROPHYLAXIS_ADHERENCE_DETAILS = "prophylaxisAdherenceDetails"; public static final String TRAVEL_PURPOSE = "travelPurpose"; public static final String TRAVEL_PURPOSE_DETAILS = "travelPurposeDetails"; + public static final String EATING_OUT_VENUES = "eatingOutVenues"; + public static final String EATING_OUT_VENUE_OTHER = "eatingOutVenueOther"; + public static final String SHOPPING_FOR_FOOD_DETAILS = "shoppingForFoodDetails"; @SensitiveData private UserReferenceDto reportingUser; @@ -455,6 +459,24 @@ public class ExposureDto extends PseudonymizableDto { @Size(max = FieldConstraints.CHARACTER_LIMIT_TEXT, message = Validations.textTooLong) private String travelPurposeDetails; + @Diseases({ + Disease.SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + private Set eatingOutVenues; + @Diseases({ + Disease.SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + @Size(max = FieldConstraints.CHARACTER_LIMIT_DEFAULT, message = Validations.textTooLong) + private String eatingOutVenueOther; + @Diseases({ + Disease.SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + @Size(max = FieldConstraints.CHARACTER_LIMIT_DEFAULT, message = Validations.textTooLong) + private String shoppingForFoodDetails; + public static ExposureDto build(ExposureType exposureType) { ExposureDto exposure = new ExposureDto(); @@ -466,6 +488,7 @@ public static ExposureDto build(ExposureType exposureType) { exposure.setSubSettings(new HashSet<>()); exposure.setContactFactors(new HashSet<>()); exposure.setProtectiveMeasures(new HashSet<>()); + exposure.setEatingOutVenues(new HashSet<>()); return exposure; } @@ -1150,6 +1173,30 @@ public void setProphylaxisAdherenceDetails(String prophylaxisAdherenceDetails) { this.prophylaxisAdherenceDetails = prophylaxisAdherenceDetails; } + public Set getEatingOutVenues() { + return eatingOutVenues; + } + + public void setEatingOutVenues(Set eatingOutVenues) { + this.eatingOutVenues = eatingOutVenues; + } + + public String getEatingOutVenueOther() { + return eatingOutVenueOther; + } + + public void setEatingOutVenueOther(String eatingOutVenueOther) { + this.eatingOutVenueOther = eatingOutVenueOther; + } + + public String getShoppingForFoodDetails() { + return shoppingForFoodDetails; + } + + public void setShoppingForFoodDetails(String shoppingForFoodDetails) { + this.shoppingForFoodDetails = shoppingForFoodDetails; + } + @Override public ExposureDto clone() throws CloneNotSupportedException { ExposureDto clone = (ExposureDto) super.clone(); diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/exposure/ExposureSubSetting.java b/sormas-api/src/main/java/de/symeda/sormas/api/exposure/ExposureSubSetting.java index b83eadecb62..6c565eb70e8 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/exposure/ExposureSubSetting.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/exposure/ExposureSubSetting.java @@ -49,6 +49,7 @@ public enum ExposureSubSetting { EATING_AT_HOME(ExposureCategory.FOOD_BORNE, null), EATING_OUTSIDE(ExposureCategory.FOOD_BORNE, null), + SHOPPING_FOR_FOOD(ExposureCategory.FOOD_BORNE, null), UNKNOWN(null, null), OTHER(null, null); diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java index 29e4766c21a..71ee50fe7e5 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java @@ -336,11 +336,9 @@ public interface Captions { String AefiInvestigation_aefiClassificationDetails = "AefiInvestigation.aefiClassificationDetails"; String AefiInvestigation_aefiClassificationSubType = "AefiInvestigation.aefiClassificationSubType"; String AefiInvestigation_allCasesInClusterReceivedVaccineFromSameVial = "AefiInvestigation.allCasesInClusterReceivedVaccineFromSameVial"; - String AefiInvestigation_allCasesInClusterReceivedVaccineFromSameVialDetails = - "AefiInvestigation.allCasesInClusterReceivedVaccineFromSameVialDetails"; + String AefiInvestigation_allCasesInClusterReceivedVaccineFromSameVialDetails = "AefiInvestigation.allCasesInClusterReceivedVaccineFromSameVialDetails"; String AefiInvestigation_anyOtherItemInRefrigerator = "AefiInvestigation.anyOtherItemInRefrigerator"; - String AefiInvestigation_anyStorageTemperatureDeviationOutsideTwoToEightDegrees = - "AefiInvestigation.anyStorageTemperatureDeviationOutsideTwoToEightDegrees"; + String AefiInvestigation_anyStorageTemperatureDeviationOutsideTwoToEightDegrees = "AefiInvestigation.anyStorageTemperatureDeviationOutsideTwoToEightDegrees"; String AefiInvestigation_autopsyDate = "AefiInvestigation.autopsyDate"; String AefiInvestigation_autopsyDone = "AefiInvestigation.autopsyDone"; String AefiInvestigation_autopsyPlannedDateTime = "AefiInvestigation.autopsyPlannedDateTime"; @@ -376,8 +374,7 @@ public interface Captions { String AefiInvestigation_errorPrescribingVaccine = "AefiInvestigation.errorPrescribingVaccine"; String AefiInvestigation_errorPrescribingVaccineDetails = "AefiInvestigation.errorPrescribingVaccineDetails"; String AefiInvestigation_eventIsAStressResponseRelatedToImmunization = "AefiInvestigation.eventIsAStressResponseRelatedToImmunization"; - String AefiInvestigation_eventIsAStressResponseRelatedToImmunizationDetails = - "AefiInvestigation.eventIsAStressResponseRelatedToImmunizationDetails"; + String AefiInvestigation_eventIsAStressResponseRelatedToImmunizationDetails = "AefiInvestigation.eventIsAStressResponseRelatedToImmunizationDetails"; String AefiInvestigation_externalId = "AefiInvestigation.externalId"; String AefiInvestigation_familyHistoryOfDiseaseOrAllergy = "AefiInvestigation.familyHistoryOfDiseaseOrAllergy"; String AefiInvestigation_familyHistoryOfDiseaseOrAllergyDetails = "AefiInvestigation.familyHistoryOfDiseaseOrAllergyDetails"; @@ -385,10 +382,8 @@ public interface Captions { String AefiInvestigation_formCompletionDate = "AefiInvestigation.formCompletionDate"; String AefiInvestigation_historyOfAllergyToVaccineDrugOrFood = "AefiInvestigation.historyOfAllergyToVaccineDrugOrFood"; String AefiInvestigation_historyOfAllergyToVaccineDrugOrFoodDetails = "AefiInvestigation.historyOfAllergyToVaccineDrugOrFoodDetails"; - String AefiInvestigation_historyOfHospitalizationInLastThirtyDaysWithCause = - "AefiInvestigation.historyOfHospitalizationInLastThirtyDaysWithCause"; - String AefiInvestigation_historyOfHospitalizationInLastThirtyDaysWithCauseDetails = - "AefiInvestigation.historyOfHospitalizationInLastThirtyDaysWithCauseDetails"; + String AefiInvestigation_historyOfHospitalizationInLastThirtyDaysWithCause = "AefiInvestigation.historyOfHospitalizationInLastThirtyDaysWithCause"; + String AefiInvestigation_historyOfHospitalizationInLastThirtyDaysWithCauseDetails = "AefiInvestigation.historyOfHospitalizationInLastThirtyDaysWithCauseDetails"; String AefiInvestigation_hospitalizationDate = "AefiInvestigation.hospitalizationDate"; String AefiInvestigation_injectionTechniqueAdditionalDetails = "AefiInvestigation.injectionTechniqueAdditionalDetails"; String AefiInvestigation_investigationCaseId = "AefiInvestigation.investigationCaseId"; @@ -400,17 +395,13 @@ public interface Captions { String AefiInvestigation_keySymptomDateTime = "AefiInvestigation.keySymptomDateTime"; String AefiInvestigation_lastTrainingReceivedByVaccinatorDate = "AefiInvestigation.lastTrainingReceivedByVaccinatorDate"; String AefiInvestigation_nonTouchTechniqueFollowed = "AefiInvestigation.nonTouchTechniqueFollowed"; - String AefiInvestigation_numberImmunizedConcernedVaccineSameBatchNumberLocationDetails = - "AefiInvestigation.numberImmunizedConcernedVaccineSameBatchNumberLocationDetails"; - String AefiInvestigation_numberImmunizedConcernedVaccineSameBatchNumberOtherLocations = - "AefiInvestigation.numberImmunizedConcernedVaccineSameBatchNumberOtherLocations"; + String AefiInvestigation_numberImmunizedConcernedVaccineSameBatchNumberLocationDetails = "AefiInvestigation.numberImmunizedConcernedVaccineSameBatchNumberLocationDetails"; + String AefiInvestigation_numberImmunizedConcernedVaccineSameBatchNumberOtherLocations = "AefiInvestigation.numberImmunizedConcernedVaccineSameBatchNumberOtherLocations"; String AefiInvestigation_numberImmunizedFromConcernedVaccineVial = "AefiInvestigation.numberImmunizedFromConcernedVaccineVial"; String AefiInvestigation_numberImmunizedWithConcernedVaccineInSameSession = "AefiInvestigation.numberImmunizedWithConcernedVaccineInSameSession"; - String AefiInvestigation_numberOfAefiReportedFromVaccineDistributionCenterLastThirtyDays = - "AefiInvestigation.numberOfAefiReportedFromVaccineDistributionCenterLastThirtyDays"; + String AefiInvestigation_numberOfAefiReportedFromVaccineDistributionCenterLastThirtyDays = "AefiInvestigation.numberOfAefiReportedFromVaccineDistributionCenterLastThirtyDays"; String AefiInvestigation_numberOfCasesDetectedInCluster = "AefiInvestigation.numberOfCasesDetectedInCluster"; - String AefiInvestigation_numberOfSimilarEventsReportedSamePeriodAndLocality = - "AefiInvestigation.numberOfSimilarEventsReportedSamePeriodAndLocality"; + String AefiInvestigation_numberOfSimilarEventsReportedSamePeriodAndLocality = "AefiInvestigation.numberOfSimilarEventsReportedSamePeriodAndLocality"; String AefiInvestigation_numberOfThoseAffectedNotVaccinated = "AefiInvestigation.numberOfThoseAffectedNotVaccinated"; String AefiInvestigation_numberOfThoseAffectedVaccinated = "AefiInvestigation.numberOfThoseAffectedVaccinated"; String AefiInvestigation_numberOfThoseAffectedVaccinatedUnknown = "AefiInvestigation.numberOfThoseAffectedVaccinatedUnknown"; @@ -431,8 +422,7 @@ public interface Captions { String AefiInvestigation_placeOfVaccination = "AefiInvestigation.placeOfVaccination"; String AefiInvestigation_placeOfVaccinationDetails = "AefiInvestigation.placeOfVaccinationDetails"; String AefiInvestigation_preExistingIllnessThirtyDaysOrCongenitalDisorder = "AefiInvestigation.preExistingIllnessThirtyDaysOrCongenitalDisorder"; - String AefiInvestigation_preExistingIllnessThirtyDaysOrCongenitalDisorderDetails = - "AefiInvestigation.preExistingIllnessThirtyDaysOrCongenitalDisorderDetails"; + String AefiInvestigation_preExistingIllnessThirtyDaysOrCongenitalDisorderDetails = "AefiInvestigation.preExistingIllnessThirtyDaysOrCongenitalDisorderDetails"; String AefiInvestigation_provisionalOrFinalDiagnosis = "AefiInvestigation.provisionalOrFinalDiagnosis"; String AefiInvestigation_reconstitutionAdditionalDetails = "AefiInvestigation.reconstitutionAdditionalDetails"; String AefiInvestigation_reportDate = "AefiInvestigation.reportDate"; @@ -452,17 +442,14 @@ public interface Captions { String AefiInvestigation_responsibleRegion = "AefiInvestigation.responsibleRegion"; String AefiInvestigation_sameReconstitutionSyringeForEachVaccination = "AefiInvestigation.sameReconstitutionSyringeForEachVaccination"; String AefiInvestigation_sameReconstitutionSyringeForEachVaccineVial = "AefiInvestigation.sameReconstitutionSyringeForEachVaccineVial"; - String AefiInvestigation_sameReconstitutionSyringeUsedForMultipleVialsOfSameVaccine = - "AefiInvestigation.sameReconstitutionSyringeUsedForMultipleVialsOfSameVaccine"; - String AefiInvestigation_sameReconstitutionSyringeUsedForReconstitutingDifferentVaccines = - "AefiInvestigation.sameReconstitutionSyringeUsedForReconstitutingDifferentVaccines"; + String AefiInvestigation_sameReconstitutionSyringeUsedForMultipleVialsOfSameVaccine = "AefiInvestigation.sameReconstitutionSyringeUsedForMultipleVialsOfSameVaccine"; + String AefiInvestigation_sameReconstitutionSyringeUsedForReconstitutingDifferentVaccines = "AefiInvestigation.sameReconstitutionSyringeUsedForReconstitutingDifferentVaccines"; String AefiInvestigation_seriousAefiInfoSource = "AefiInvestigation.seriousAefiInfoSource"; String AefiInvestigation_seriousAefiInfoSourceDetails = "AefiInvestigation.seriousAefiInfoSourceDetails"; String AefiInvestigation_seriousAefiVerbalAutopsyInfoSourceDetails = "AefiInvestigation.seriousAefiVerbalAutopsyInfoSourceDetails"; String AefiInvestigation_signsAndSymptomsFromTimeOfVaccination = "AefiInvestigation.signsAndSymptomsFromTimeOfVaccination"; String AefiInvestigation_similarEventsReportedSamePeriodAndLocality = "AefiInvestigation.similarEventsReportedSamePeriodAndLocality"; - String AefiInvestigation_similarEventsReportedSamePeriodAndLocalityDetails = - "AefiInvestigation.similarEventsReportedSamePeriodAndLocalityDetails"; + String AefiInvestigation_similarEventsReportedSamePeriodAndLocalityDetails = "AefiInvestigation.similarEventsReportedSamePeriodAndLocalityDetails"; String AefiInvestigation_statusOnDateOfInvestigation = "AefiInvestigation.statusOnDateOfInvestigation"; String AefiInvestigation_storageTemperatureMonitoringAdditionalDetails = "AefiInvestigation.storageTemperatureMonitoringAdditionalDetails"; String AefiInvestigation_syringesUsedAdditionalDetails = "AefiInvestigation.syringesUsedAdditionalDetails"; @@ -481,8 +468,7 @@ public interface Captions { String AefiInvestigation_vaccinationFacilityDetails = "AefiInvestigation.vaccinationFacilityDetails"; String AefiInvestigation_vaccineAdministeredIncorrectly = "AefiInvestigation.vaccineAdministeredIncorrectly"; String AefiInvestigation_vaccineAdministeredIncorrectlyDetails = "AefiInvestigation.vaccineAdministeredIncorrectlyDetails"; - String AefiInvestigation_vaccineCarrierReturnedFromSiteOnSameDateAsVaccination = - "AefiInvestigation.vaccineCarrierReturnedFromSiteOnSameDateAsVaccination"; + String AefiInvestigation_vaccineCarrierReturnedFromSiteOnSameDateAsVaccination = "AefiInvestigation.vaccineCarrierReturnedFromSiteOnSameDateAsVaccination"; String AefiInvestigation_vaccineCarrierSentToSiteOnSameDateAsVaccination = "AefiInvestigation.vaccineCarrierSentToSiteOnSameDateAsVaccination"; String AefiInvestigation_vaccineCarrierType = "AefiInvestigation.vaccineCarrierType"; String AefiInvestigation_vaccineCarrierTypeDetails = "AefiInvestigation.vaccineCarrierTypeDetails"; @@ -1914,6 +1900,8 @@ public interface Captions { String Exposure_deceasedPersonName = "Exposure.deceasedPersonName"; String Exposure_deceasedPersonRelation = "Exposure.deceasedPersonRelation"; String Exposure_domesticSwimming = "Exposure.domesticSwimming"; + String Exposure_eatingOutVenueOther = "Exposure.eatingOutVenueOther"; + String Exposure_eatingOutVenues = "Exposure.eatingOutVenues"; String Exposure_eatingRawAnimalProducts = "Exposure.eatingRawAnimalProducts"; String Exposure_endDate = "Exposure.endDate"; String Exposure_exposureCategory = "Exposure.exposureCategory"; @@ -1959,6 +1947,7 @@ public interface Captions { String Exposure_riskArea = "Exposure.riskArea"; String Exposure_seatNumber = "Exposure.seatNumber"; String Exposure_sexualExposureText = "Exposure.sexualExposureText"; + String Exposure_shoppingForFoodDetails = "Exposure.shoppingForFoodDetails"; String Exposure_shortDistance = "Exposure.shortDistance"; String Exposure_startDate = "Exposure.startDate"; String Exposure_subSettings = "Exposure.subSettings"; @@ -3028,6 +3017,7 @@ public interface Captions { String Symptoms_congenitalHeartDiseaseType = "Symptoms.congenitalHeartDiseaseType"; String Symptoms_conjunctivalInjection = "Symptoms.conjunctivalInjection"; String Symptoms_conjunctivitis = "Symptoms.conjunctivitis"; + String Symptoms_constipation = "Symptoms.constipation"; String Symptoms_convulsion = "Symptoms.convulsion"; String Symptoms_cough = "Symptoms.cough"; String Symptoms_coughingBlood = "Symptoms.coughingBlood"; @@ -3051,11 +3041,13 @@ public interface Captions { String Symptoms_dizzinessStandingUp = "Symptoms.dizzinessStandingUp"; String Symptoms_durationOfSymptoms = "Symptoms.durationOfSymptoms"; String Symptoms_dysphagia = "Symptoms.dysphagia"; + String Symptoms_dysuria = "Symptoms.dysuria"; String Symptoms_eggyBurps = "Symptoms.eggyBurps"; String Symptoms_encephalitis = "Symptoms.encephalitis"; String Symptoms_erraticBehaviour = "Symptoms.erraticBehaviour"; String Symptoms_excessSalivation = "Symptoms.excessSalivation"; String Symptoms_excitation = "Symptoms.excitation"; + String Symptoms_eyeIrritation = "Symptoms.eyeIrritation"; String Symptoms_eyePainLightSensitive = "Symptoms.eyePainLightSensitive"; String Symptoms_eyesBleeding = "Symptoms.eyesBleeding"; String Symptoms_fastHeartRate = "Symptoms.fastHeartRate"; 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 08fe3d8df5a..93bb725afe4 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 @@ -74,6 +74,7 @@ import static de.symeda.sormas.api.Disease.RESPIRATORY_SYNCYTIAL_VIRUS; import static de.symeda.sormas.api.Disease.RHINOVIRUS; import static de.symeda.sormas.api.Disease.RUBELLA; +import static de.symeda.sormas.api.Disease.SALMONELLOSIS; import static de.symeda.sormas.api.Disease.SCHISTOSOMIASIS; import static de.symeda.sormas.api.Disease.SNAKE_BITE; import static de.symeda.sormas.api.Disease.SOIL_TRANSMITTED_HELMINTHS; @@ -151,6 +152,7 @@ public class SymptomsDto extends PseudonymizableDto { public static final String CONGENITAL_HEART_DISEASE_TYPE = "congenitalHeartDiseaseType"; public static final String CONJUNCTIVAL_INJECTION = "conjunctivalInjection"; public static final String CONJUNCTIVITIS = "conjunctivitis"; + public static final String CONSTIPATION = "constipation"; public static final String CONVULSION = "convulsion"; public static final String COUGH = "cough"; public static final String COUGHING_BLOOD = "coughingBlood"; @@ -166,10 +168,12 @@ public class SymptomsDto extends PseudonymizableDto { public static final String DIFFICULTY_BREATHING = "difficultyBreathing"; public static final String DIGESTED_BLOOD_VOMIT = "digestedBloodVomit"; public static final String DYSPHAGIA = "dysphagia"; + public static final String DYSURIA = "dysuria"; public static final String ERRATIC_BEHAVIOUR = "erraticBehaviour"; public static final String EXCESS_SALIVATION = "excessSalivation"; public static final String EXCITATION = "excitation"; public static final String EYES_BLEEDING = "eyesBleeding"; + public static final String EYE_IRRITATION = "eyeIrritation"; public static final String EYE_PAIN_LIGHT_SENSITIVE = "eyePainLightSensitive"; public static final String FATIGUE_WEAKNESS = "fatigueWeakness"; public static final String FEVER = "fever"; @@ -398,6 +402,7 @@ public static SymptomsDto build() { UNSPECIFIED_VHF, GIARDIASIS, CRYPTOSPORIDIOSIS, + SALMONELLOSIS, UNDEFINED, DENGUE, OTHER }) @@ -421,6 +426,7 @@ public static SymptomsDto build() { GIARDIASIS, CRYPTOSPORIDIOSIS, MALARIA, + SALMONELLOSIS, UNDEFINED, OTHER }) @Outbreaks @@ -485,6 +491,7 @@ public static SymptomsDto build() { ANTHRAX, DENGUE, UNSPECIFIED_VHF, + SALMONELLOSIS, UNDEFINED, OTHER }) @Complication(value = { @@ -602,6 +609,13 @@ public static SymptomsDto build() { @SymptomGrouping(SymptomGroup.OTHER) private SymptomState conjunctivitis; + @Diseases({ + SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + @SymptomGrouping(SymptomGroup.GASTROINTESTINAL) + private SymptomState constipation; + @Diseases({ AFP, EVD, @@ -675,6 +689,7 @@ public static SymptomsDto build() { UNSPECIFIED_VHF, GIARDIASIS, CRYPTOSPORIDIOSIS, + SALMONELLOSIS, UNDEFINED, OTHER }) @HideForCountries @@ -698,6 +713,7 @@ public static SymptomsDto build() { GIARDIASIS, CRYPTOSPORIDIOSIS, MALARIA, + SALMONELLOSIS, UNDEFINED, OTHER }) @Complication({ @@ -770,6 +786,13 @@ public static SymptomsDto build() { @SymptomGrouping(SymptomGroup.OTHER) private SymptomState eyesBleeding; + @Diseases({ + SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + @SymptomGrouping(SymptomGroup.OTHER) + private SymptomState eyeIrritation; + @Diseases({ AFP, EVD, @@ -821,6 +844,7 @@ public static SymptomsDto build() { GIARDIASIS, CRYPTOSPORIDIOSIS, MALARIA, + SALMONELLOSIS, DENGUE, OTHER }) @Outbreaks @@ -879,6 +903,7 @@ public static SymptomsDto build() { ANTHRAX, CORONAVIRUS, MALARIA, + SALMONELLOSIS, DENGUE, UNDEFINED, OTHER }) @@ -967,6 +992,7 @@ public static SymptomsDto build() { CORONAVIRUS, UNSPECIFIED_VHF, MALARIA, + SALMONELLOSIS, DENGUE, UNDEFINED, OTHER }) @@ -1270,6 +1296,7 @@ public static SymptomsDto build() { CORONAVIRUS, UNSPECIFIED_VHF, MALARIA, + SALMONELLOSIS, DENGUE, UNDEFINED, OTHER }) @@ -1297,6 +1324,7 @@ public static SymptomsDto build() { CRYPTOSPORIDIOSIS, GIARDIASIS, MALARIA, + SALMONELLOSIS, DENGUE, UNDEFINED, OTHER }) @@ -1560,6 +1588,7 @@ public static SymptomsDto build() { RABIES, CORONAVIRUS, PERTUSSIS, + SALMONELLOSIS, UNDEFINED, OTHER }) @Outbreaks @@ -1583,6 +1612,7 @@ public static SymptomsDto build() { CONGENITAL_RUBELLA, CORONAVIRUS, PERTUSSIS, + SALMONELLOSIS, UNDEFINED, OTHER }) @Outbreaks @@ -1789,6 +1819,7 @@ public static SymptomsDto build() { DENGUE, POLIO, UNSPECIFIED_VHF, + SALMONELLOSIS, UNDEFINED, OTHER, CORONAVIRUS }) @@ -2072,6 +2103,7 @@ public static SymptomsDto build() { CRYPTOSPORIDIOSIS, GIARDIASIS, MALARIA, + SALMONELLOSIS, DENGUE, OTHER }) @Outbreaks @@ -2147,6 +2179,13 @@ public static SymptomsDto build() { @SymptomGrouping(SymptomGroup.NERVOUS_SYSTEM) private SymptomState dysphagia; + @Diseases({ + SALMONELLOSIS }) + @HideForCountriesExcept(countries = { + CountryHelper.COUNTRY_CODE_LUXEMBOURG }) + @SymptomGrouping(SymptomGroup.URINARY) + private SymptomState dysuria; + @Diseases({ RABIES }) @HideForCountries @@ -2994,7 +3033,7 @@ public static SymptomsDto build() { @Diseases({ MALARIA }) @SymptomGrouping(SymptomGroup.NERVOUS_SYSTEM) - //Impaired consciousness + //Impaired consciousness private SymptomState alteredLevelOfConsciousness; @Diseases({ MALARIA }) @@ -5193,4 +5232,28 @@ public void setFatalRisk(SymptomState fatalRisk) { this.fatalRisk = fatalRisk; } + public SymptomState getConstipation() { + return constipation; + } + + public void setConstipation(SymptomState constipation) { + this.constipation = constipation; + } + + public SymptomState getDysuria() { + return dysuria; + } + + public void setDysuria(SymptomState dysuria) { + this.dysuria = dysuria; + } + + public SymptomState getEyeIrritation() { + return eyeIrritation; + } + + public void setEyeIrritation(SymptomState eyeIrritation) { + this.eyeIrritation = eyeIrritation; + } + } diff --git a/sormas-api/src/main/resources/captions.properties b/sormas-api/src/main/resources/captions.properties index 4e17787ebfa..d5fa3d30c2b 100644 --- a/sormas-api/src/main/resources/captions.properties +++ b/sormas-api/src/main/resources/captions.properties @@ -1618,6 +1618,9 @@ Exposure.prophylaxisAdherence=Prophylaxis adherence Exposure.prophylaxisAdherenceDetails=Prophylaxis adherence details Exposure.travelPurpose=Reason for travel Exposure.travelPurposeDetails=Reason for travel details +Exposure.eatingOutVenues=Eating out venues +Exposure.eatingOutVenueOther=Other venue (please specify) +Exposure.shoppingForFoodDetails=Shopping for food (location/details) titleExposuresSection=Exposure details titleExposureActivitySection=Activity details titleExposureLocationSection=Location of exposure @@ -3080,6 +3083,9 @@ Symptoms.scantHemorrhage=Other minor bleeding Symptoms.otherNeurolocalSymptom=Other neurological symptoms Symptoms.otherNeurolocalSymptomText= Specify neurological symptom Symptoms.fatalRisk=Fatal risk +Symptoms.constipation=Constipation +Symptoms.dysuria=Dysuria (painful urination) +Symptoms.eyeIrritation=Eye irritation titleComplications=Complications titleNoComplications=No complications diff --git a/sormas-api/src/main/resources/enum.properties b/sormas-api/src/main/resources/enum.properties index acf95642795..5f01e59b6c3 100644 --- a/sormas-api/src/main/resources/enum.properties +++ b/sormas-api/src/main/resources/enum.properties @@ -3009,12 +3009,33 @@ ExposureSubSetting.FORESTED_GRASSY_RURAL=Forested, grassy, or rural areas ExposureSubSetting.WILDLIFE_RESERVOIR_AREAS=Areas with wildlife reservoirs for ticks ExposureSubSetting.EATING_AT_HOME=Eating at home ExposureSubSetting.EATING_OUTSIDE=Eating outside +ExposureSubSetting.SHOPPING_FOR_FOOD=Shopping for food ExposureSubSetting.BLOOD_TRANSFUSION_RECIPIENT=Blood transfusion recepient ExposureSubSetting.BONE_MARROW_TRANSPLANT_RECIPIENT=Bone marrow transplant recipient ExposureSubSetting.TRAVELED_ABROAD=Traveled abroad ExposureSubSetting.UNKNOWN=Unknown ExposureSubSetting.OTHER=Other +# EatingOutVenue +EatingOutVenue.CANTEENS=Canteens (work, school, hospital, nursing home, retirement home) +EatingOutVenue.FOOD_SERVED_ON_BOARD=Food served on board (airplane, train, ferry, cruise ship) +EatingOutVenue.GAS_STATIONS=Gas stations +EatingOutVenue.STREET_VENDORS=Street vendors/mobile food vendors (food trucks, markets) +EatingOutVenue.FOOD_COURTS=Food courts (shopping malls, entertainment venues) +EatingOutVenue.CAFES_OR_TEA_ROOMS=Cafés or tea rooms +EatingOutVenue.SANDWICH_SHOPS_BAKERIES_DELICATESSENS=Sandwich shops, bakeries, or delicatessens +EatingOutVenue.CAFETERIAS_OR_BARS=Cafeterias or bars +EatingOutVenue.KEBAB_FALAFEL_SNACK_SHOPS=Kebab/falafel snack shops +EatingOutVenue.BURGER_RESTAURANTS=Burger restaurants +EatingOutVenue.FAST_FOOD_SNACK_BARS=Fast food/snack bars +EatingOutVenue.PIZZERIAS=Pizzerias +EatingOutVenue.ASIAN_RESTAURANTS=Asian restaurants +EatingOutVenue.HOTELS=Hotels +EatingOutVenue.TAKEAWAY_FOOD=Takeaway food (including home delivery) +EatingOutVenue.BUFFET_STYLE_RESTAURANTS=Buffet-style restaurants (all-you-can-eat, self-service) +EatingOutVenue.FOOD_AT_EVENTS=Food at events (weddings, conferences, festivals) +EatingOutVenue.OTHER=Other (please specify) + # ExposureContactFactor ExposureContactFactor.DURATION_OF_EXPOSURE=Duration of Exposure ExposureContactFactor.PROXIMITY_TO_SOURCE=Proximity to Source diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiDataFacadeEjb.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiDataFacadeEjb.java index 1676e86a075..202accc3ea3 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiDataFacadeEjb.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/epidata/EpiDataFacadeEjb.java @@ -243,6 +243,9 @@ public Exposure fillOrBuildExposureEntity(ExposureDto source, Exposure target, b target.setProphylaxisAdherenceDetails(source.getProphylaxisAdherenceDetails()); target.setTravelPurpose(source.getTravelPurpose()); target.setTravelPurposeDetails(source.getTravelPurposeDetails()); + target.setEatingOutVenues(source.getEatingOutVenues() != null ? source.getEatingOutVenues() : new HashSet<>()); + target.setEatingOutVenueOther(source.getEatingOutVenueOther()); + target.setShoppingForFoodDetails(source.getShoppingForFoodDetails()); return target; } @@ -433,6 +436,9 @@ public static ExposureDto toExposureDto(Exposure source) { target.setProphylaxisAdherenceDetails(source.getProphylaxisAdherenceDetails()); target.setTravelPurpose(source.getTravelPurpose()); target.setTravelPurposeDetails(source.getTravelPurposeDetails()); + target.setEatingOutVenues(new HashSet<>(source.getEatingOutVenues())); + target.setEatingOutVenueOther(source.getEatingOutVenueOther()); + target.setShoppingForFoodDetails(source.getShoppingForFoodDetails()); return target; } diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/exposure/Exposure.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/exposure/Exposure.java index bf257543f27..3a71e41f40e 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/exposure/Exposure.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/exposure/Exposure.java @@ -42,6 +42,7 @@ import de.symeda.sormas.api.exposure.AnimalCategory; import de.symeda.sormas.api.exposure.AnimalContactType; import de.symeda.sormas.api.exposure.AnimalLocation; +import de.symeda.sormas.api.exposure.EatingOutVenue; import de.symeda.sormas.api.exposure.ExposureCategory; import de.symeda.sormas.api.exposure.ExposureContactFactor; import de.symeda.sormas.api.exposure.ExposureProtectiveMeasure; @@ -91,6 +92,9 @@ public class Exposure extends AbstractDomainObject { public static final String SUB_SETTINGS = "subSettings"; public static final String CONTACT_FACTORS = "contactFactors"; public static final String PROTECTIVE_MEASURES = "protectiveMeasures"; + public static final String EATING_OUT_VENUES = "eatingOutVenues"; + public static final String EATING_OUT_VENUE_OTHER = "eatingOutVenueOther"; + public static final String SHOPPING_FOR_FOOD_DETAILS = "shoppingForFoodDetails"; private EpiData epiData; private User reportingUser; @@ -197,6 +201,10 @@ public class Exposure extends AbstractDomainObject { private Set contactFactors = new HashSet<>(); private Set protectiveMeasures = new HashSet<>(); + private Set eatingOutVenues = new HashSet<>(); + private String eatingOutVenueOther; + private String shoppingForFoodDetails; + @ManyToOne @JoinColumn(nullable = false) public EpiData getEpiData() { @@ -948,6 +956,37 @@ public void setProtectiveMeasures(Set protectiveMeasu this.protectiveMeasures = protectiveMeasures; } + @ElementCollection(fetch = FetchType.EAGER) + @Enumerated(EnumType.STRING) + @CollectionTable(name = "exposures_eatingoutvenues", + joinColumns = @JoinColumn(name = "exposure_id", referencedColumnName = Exposure.ID, nullable = false)) + @Column(name = "eatingoutvenue", nullable = false) + public Set getEatingOutVenues() { + return eatingOutVenues; + } + + public void setEatingOutVenues(Set eatingOutVenues) { + this.eatingOutVenues = eatingOutVenues; + } + + @Column(length = CHARACTER_LIMIT_DEFAULT) + public String getEatingOutVenueOther() { + return eatingOutVenueOther; + } + + public void setEatingOutVenueOther(String eatingOutVenueOther) { + this.eatingOutVenueOther = eatingOutVenueOther; + } + + @Column(length = CHARACTER_LIMIT_DEFAULT) + public String getShoppingForFoodDetails() { + return shoppingForFoodDetails; + } + + public void setShoppingForFoodDetails(String shoppingForFoodDetails) { + this.shoppingForFoodDetails = shoppingForFoodDetails; + } + @Enumerated(EnumType.STRING) public TravelPurpose getTravelPurpose() { return travelPurpose; diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/symptoms/Symptoms.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/symptoms/Symptoms.java index b8674f7a254..f9e2d629bc4 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/symptoms/Symptoms.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/symptoms/Symptoms.java @@ -310,6 +310,9 @@ public class Symptoms extends AbstractDomainObject { private String otherNeurolocalSymptomText; private SymptomState disseminatedIntraVascularCoagulation; private SymptomState fatalRisk; + private SymptomState constipation; + private SymptomState dysuria; + private SymptomState eyeIrritation; // when adding new fields make sure to extend toHumanString @@ -2449,6 +2452,33 @@ public void setDisseminatedIntraVascularCoagulation(SymptomState disseminatedInt this.disseminatedIntraVascularCoagulation = disseminatedIntraVascularCoagulation; } + @Enumerated(EnumType.STRING) + public SymptomState getConstipation() { + return constipation; + } + + public void setConstipation(SymptomState constipation) { + this.constipation = constipation; + } + + @Enumerated(EnumType.STRING) + public SymptomState getDysuria() { + return dysuria; + } + + public void setDysuria(SymptomState dysuria) { + this.dysuria = dysuria; + } + + @Enumerated(EnumType.STRING) + public SymptomState getEyeIrritation() { + return eyeIrritation; + } + + public void setEyeIrritation(SymptomState eyeIrritation) { + this.eyeIrritation = eyeIrritation; + } + @Enumerated(EnumType.STRING) public SymptomState getFatalRisk() { return fatalRisk; 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 42528bc99d8..6fb8cb8599a 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 @@ -274,6 +274,9 @@ public Symptoms fillOrBuildEntity(SymptomsDto source, Symptoms target, boolean c target.setOtherNeurolocalSymptomText(source.getOtherNeurolocalSymptomText()); target.setDisseminatedIntraVascularCoagulation(source.getDisseminatedIntraVascularCoagulation()); target.setFatalRisk(source.getFatalRisk()); + target.setConstipation(source.getConstipation()); + target.setDysuria(source.getDysuria()); + target.setEyeIrritation(source.getEyeIrritation()); return target; } @@ -531,6 +534,9 @@ public static SymptomsDto toSymptomsDto(Symptoms symptoms) { target.setOtherNeurolocalSymptomText(source.getOtherNeurolocalSymptomText()); target.setDisseminatedIntraVascularCoagulation(source.getDisseminatedIntraVascularCoagulation()); target.setFatalRisk(source.getFatalRisk()); + target.setConstipation(source.getConstipation()); + target.setDysuria(source.getDysuria()); + target.setEyeIrritation(source.getEyeIrritation()); return target; } diff --git a/sormas-backend/src/main/resources/sql/sormas_schema.sql b/sormas-backend/src/main/resources/sql/sormas_schema.sql index c934347f95b..61fbf977d26 100644 --- a/sormas-backend/src/main/resources/sql/sormas_schema.sql +++ b/sormas-backend/src/main/resources/sql/sormas_schema.sql @@ -15849,5 +15849,45 @@ ALTER TABLE epidata_history ADD COLUMN IF NOT EXISTS exposureinvestigationtodate ALTER TABLE epidata_history ADD COLUMN IF NOT EXISTS activityascasefromdate timestamp; ALTER TABLE epidata_history ADD COLUMN IF NOT EXISTS activityascasetodate timestamp; -INSERT INTO schema_version (version_number, comment) VALUES (626, '#13915 - Salmonellosis EpiData investigation/activity windows'); +INSERT INTO schema_version (version_number, comment) VALUES (626, '#13916 - Salmonellosis EpiData investigation/activity windows'); + +-- 2026-04-29 Salmonellosis Exposure eating-out venues + shopping details #13915 +ALTER TABLE exposures ADD COLUMN IF NOT EXISTS eatingoutvenueother varchar(512); +ALTER TABLE exposures ADD COLUMN IF NOT EXISTS shoppingforfooddetails varchar(512); +ALTER TABLE exposures_history ADD COLUMN IF NOT EXISTS eatingoutvenueother varchar(512); +ALTER TABLE exposures_history ADD COLUMN IF NOT EXISTS shoppingforfooddetails varchar(512); + +-- exposures_eatingoutvenues join table (matches exposures_subsettings precedent) +CREATE TABLE IF NOT EXISTS exposures_eatingoutvenues ( + exposure_id BIGINT NOT NULL, + eatingoutvenue VARCHAR(255) NOT NULL, + sys_period TSTZRANGE NOT NULL +); + +ALTER TABLE exposures_eatingoutvenues OWNER TO sormas_user; +ALTER TABLE exposures_eatingoutvenues ADD CONSTRAINT fk_exposures_eatingoutvenues_exposure_id FOREIGN KEY (exposure_id) REFERENCES exposures; +ALTER TABLE exposures_eatingoutvenues ADD CONSTRAINT unq_exposures_eatingoutvenues_0 UNIQUE (exposure_id, eatingoutvenue); + +CREATE TABLE IF NOT EXISTS exposures_eatingoutvenues_history (LIKE exposures_eatingoutvenues); +DROP TRIGGER IF EXISTS versioning_trigger ON exposures_eatingoutvenues; +CREATE TRIGGER versioning_trigger + BEFORE INSERT OR UPDATE OR DELETE ON exposures_eatingoutvenues + FOR EACH ROW EXECUTE PROCEDURE versioning('sys_period', 'exposures_eatingoutvenues_history', true); +DROP TRIGGER IF EXISTS delete_history_trigger ON exposures_eatingoutvenues; +CREATE TRIGGER delete_history_trigger + AFTER DELETE ON exposures_eatingoutvenues + FOR EACH ROW EXECUTE PROCEDURE delete_history_trigger('exposures_eatingoutvenues_history', 'id'); +ALTER TABLE exposures_eatingoutvenues_history OWNER TO sormas_user; + +INSERT INTO schema_version (version_number, comment) VALUES (627, '#13917 - Salmonellosis Exposure eating-out venues + shopping details'); + +-- 2026-04-29 Salmonellosis Symptoms: constipation, dysuria, eye irritation #13915 +ALTER TABLE symptoms ADD COLUMN IF NOT EXISTS constipation varchar(255); +ALTER TABLE symptoms ADD COLUMN IF NOT EXISTS dysuria varchar(255); +ALTER TABLE symptoms ADD COLUMN IF NOT EXISTS eyeirritation varchar(255); +ALTER TABLE symptoms_history ADD COLUMN IF NOT EXISTS constipation varchar(255); +ALTER TABLE symptoms_history ADD COLUMN IF NOT EXISTS dysuria varchar(255); +ALTER TABLE symptoms_history ADD COLUMN IF NOT EXISTS eyeirritation varchar(255); + +INSERT INTO schema_version (version_number, comment) VALUES (628, '#13917 - Salmonellosis Symptoms: constipation, dysuria, eye irritation'); -- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. *** diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/exposure/ExposureForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/exposure/ExposureForm.java index 032da81001c..5d2ce485ce7 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/exposure/ExposureForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/exposure/ExposureForm.java @@ -58,6 +58,7 @@ import de.symeda.sormas.api.event.MeansOfTransport; import de.symeda.sormas.api.event.TypeOfPlace; import de.symeda.sormas.api.exposure.AnimalCategory; +import de.symeda.sormas.api.exposure.EatingOutVenue; import de.symeda.sormas.api.exposure.ExposureCategory; import de.symeda.sormas.api.exposure.ExposureContactFactor; import de.symeda.sormas.api.exposure.ExposureDto; @@ -109,6 +110,9 @@ public class ExposureForm extends AbstractEditForm { fluidRow( fluidColumn(4, 0, locs( ExposureDto.SUB_SETTINGS, + ExposureDto.EATING_OUT_VENUES, + ExposureDto.EATING_OUT_VENUE_OTHER, + ExposureDto.SHOPPING_FOR_FOOD_DETAILS, ExposureDto.CONDITION_OF_ANIMAL, ExposureDto.ANIMAL_CATEGORY, ExposureDto.TYPE_OF_ANIMAL, @@ -140,7 +144,7 @@ public class ExposureForm extends AbstractEditForm { loc(LOC_CUSTOMIZABLE_FIELDS_EXPOSURES_GENERAL) + loc(ExposureDto.DESCRIPTION); - private static final String LOCATION_DETAILS_LAYOUT = + private static final String LOCATION_DETAILS_LAYOUT = loc(LOC_LOCATION_HEADING) + fluidRow( fluidColumn(6, 0, locs(ExposureDto.TYPE_OF_PLACE)), @@ -320,6 +324,18 @@ private void addBasicFields() { subSettingsDetailsField = addField(exposureDetailsLayout, ExposureDto.EXPOSURE_SUB_SETTING_DETAILS, TextField.class); subSettingsDetailsField.setVisible(false); + OptionGroup eatingOutVenuesField = addField(exposureDetailsLayout, ExposureDto.EATING_OUT_VENUES, OptionGroup.class); + eatingOutVenuesField.setMultiSelect(true); + CssStyles.style(eatingOutVenuesField, CssStyles.CAPTION_ON_TOP); + FieldHelper.updateItems(eatingOutVenuesField, Arrays.asList(EatingOutVenue.values())); + eatingOutVenuesField.setVisible(false); + + TextField eatingOutVenueOtherField = addField(exposureDetailsLayout, ExposureDto.EATING_OUT_VENUE_OTHER, TextField.class); + eatingOutVenueOtherField.setVisible(false); + + TextField shoppingForFoodDetailsField = addField(exposureDetailsLayout, ExposureDto.SHOPPING_FOR_FOOD_DETAILS, TextField.class); + shoppingForFoodDetailsField.setVisible(false); + conditionOfAnimalField = addField(exposureDetailsLayout, ExposureDto.CONDITION_OF_ANIMAL, NullableOptionGroup.class); conditionOfAnimalField.setVisible(false); @@ -397,6 +413,30 @@ private void addBasicFields() { subSettingsDetailsField.setVisible(containsOther); boolean isProphylaxis = selectedSubSettings != null && selectedSubSettings.contains(ExposureSubSetting.TRAVELED_ABROAD); setVisibleClear(isProphylaxis, ExposureDto.PROPHYLAXIS_ADHERENCE, ExposureDto.TRAVEL_PURPOSE); + + // Salmonellosis Lu: Eating out venues + shopping-for-food details follow sub-setting selection + boolean showEatingOutVenues = selectedSubSettings != null && selectedSubSettings.contains(ExposureSubSetting.EATING_OUTSIDE); + eatingOutVenuesField.setVisible(showEatingOutVenues); + if (!showEatingOutVenues) { + eatingOutVenuesField.setValue(null); + eatingOutVenueOtherField.setVisible(false); + eatingOutVenueOtherField.setValue(null); + } + boolean showShoppingForFood = selectedSubSettings != null && selectedSubSettings.contains(ExposureSubSetting.SHOPPING_FOR_FOOD); + shoppingForFoodDetailsField.setVisible(showShoppingForFood); + if (!showShoppingForFood) { + shoppingForFoodDetailsField.setValue(null); + } + }); + + eatingOutVenuesField.addValueChangeListener(e -> { + @SuppressWarnings("unchecked") + Set selectedVenues = (Set) e.getProperty().getValue(); + boolean containsOther = selectedVenues != null && selectedVenues.contains(EatingOutVenue.OTHER); + eatingOutVenueOtherField.setVisible(eatingOutVenuesField.isVisible() && containsOther); + if (!containsOther) { + eatingOutVenueOtherField.setValue(null); + } }); contactFactorsField.addValueChangeListener(e -> { 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 92a9c907029..50edbbb7f58 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 @@ -144,6 +144,7 @@ public class SymptomsForm extends AbstractEditForm { Arrays.asList(Disease.MALARIA, Disease.INVASIVE_MENINGOCOCCAL_INFECTION, Disease.INVASIVE_PNEUMOCOCCAL_INFECTION, Disease.PERTUSSIS); final boolean isLuxDengue; final boolean isParasiticInfectiousDiseases; + final boolean isFoodborneGastrointestinal; //@formatter:off private static final String HTML_LAYOUT = @@ -259,6 +260,7 @@ public SymptomsForm( } isLuxDengue = FacadeProvider.getConfigFacade().isConfiguredCountry(CountryHelper.COUNTRY_CODE_LUXEMBOURG) && disease == Disease.DENGUE; isParasiticInfectiousDiseases = ImmutableList.of(Disease.GIARDIASIS, Disease.CRYPTOSPORIDIOSIS).contains(disease); + isFoodborneGastrointestinal = disease == Disease.SALMONELLOSIS; addFields(); hideValidationUntilNextCommit(); @@ -365,6 +367,9 @@ public String getFormattedHtmlMessage() { BLOOD_IN_STOOL, NAUSEA, ABDOMINAL_PAIN, + CONSTIPATION, + DYSURIA, + EYE_IRRITATION, HEADACHE, MUSCLE_PAIN, FATIGUE_WEAKNESS, @@ -668,9 +673,9 @@ public String getFormattedHtmlMessage() { clinicalMeasurementsHeadingLabel.setVisible( (Set.of(TEMPERATURE_SOURCE, BLOOD_PRESSURE_SYSTOLIC, BLOOD_PRESSURE_DIASTOLIC, HEART_RATE, RESPIRATORY_RATE, WEIGHT, GLASGOW_COMA_SCALE) .stream() - .anyMatch(e -> getFieldGroup().getField(e).isVisible())) || !(isParasiticInfectiousDiseases || isLuxDengue)); - // clinical Measurements HeadingLabel should be hidden for LUX Dengue and Crypto & Giardiasis - if ((isParasiticInfectiousDiseases || isLuxDengue)) { + .anyMatch(e -> getFieldGroup().getField(e).isVisible())) || !(isParasiticInfectiousDiseases || isLuxDengue || isFoodborneGastrointestinal)); + // clinical Measurements HeadingLabel should be hidden for LUX Dengue, Crypto & Giardiasis, and Salmonellosis + if ((isParasiticInfectiousDiseases || isLuxDengue || isFoodborneGastrointestinal)) { clinicalMeasurementsHeadingLabel.setVisible(false); } @@ -715,6 +720,9 @@ public String getFormattedHtmlMessage() { BLOOD_IN_STOOL, NAUSEA, ABDOMINAL_PAIN, + CONSTIPATION, + DYSURIA, + EYE_IRRITATION, HEADACHE, MUSCLE_PAIN, FATIGUE_WEAKNESS, From 99dc246adcc1c26404ba9d37e74235ce150d89a3 Mon Sep 17 00:00:00 2001 From: Harold Date: Thu, 30 Apr 2026 07:29:54 +0300 Subject: [PATCH 4/8] Adds guard to date pairs where if from is after to --- .../symeda/sormas/api/epidata/EpiDataDto.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java b/sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java index 5169eb4bb36..5f9671cd347 100644 --- a/sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java +++ b/sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java @@ -398,6 +398,11 @@ public Date getExposureInvestigationFromDate() { } public void setExposureInvestigationFromDate(Date exposureInvestigationFromDate) { + validateDateRange( + exposureInvestigationFromDate, + this.exposureInvestigationToDate, + "exposureInvestigationFromDate", + "exposureInvestigationToDate"); this.exposureInvestigationFromDate = exposureInvestigationFromDate; } @@ -406,6 +411,11 @@ public Date getExposureInvestigationToDate() { } public void setExposureInvestigationToDate(Date exposureInvestigationToDate) { + validateDateRange( + this.exposureInvestigationFromDate, + exposureInvestigationToDate, + "exposureInvestigationFromDate", + "exposureInvestigationToDate"); this.exposureInvestigationToDate = exposureInvestigationToDate; } @@ -414,6 +424,7 @@ public Date getActivityAsCaseFromDate() { } public void setActivityAsCaseFromDate(Date activityAsCaseFromDate) { + validateDateRange(activityAsCaseFromDate, this.activityAsCaseToDate, "activityAsCaseFromDate", "activityAsCaseToDate"); this.activityAsCaseFromDate = activityAsCaseFromDate; } @@ -422,9 +433,16 @@ public Date getActivityAsCaseToDate() { } public void setActivityAsCaseToDate(Date activityAsCaseToDate) { + validateDateRange(this.activityAsCaseFromDate, activityAsCaseToDate, "activityAsCaseFromDate", "activityAsCaseToDate"); this.activityAsCaseToDate = activityAsCaseToDate; } + private static void validateDateRange(Date from, Date to, String fromName, String toName) { + if (from != null && to != null && from.after(to)) { + throw new IllegalArgumentException(fromName + " must be before or equal to " + toName); + } + } + @Override public EpiDataDto clone() throws CloneNotSupportedException { EpiDataDto clone = (EpiDataDto) super.clone(); From e384627aa966601e9bb9c833899db6eed1628452 Mon Sep 17 00:00:00 2001 From: Harold Date: Thu, 30 Apr 2026 07:30:26 +0300 Subject: [PATCH 5/8] Adds SAL UI Css mapping --- .../main/java/de/symeda/sormas/ui/utils/CssStyles.java | 2 ++ .../main/webapp/VAADIN/themes/sormas/views/disease.scss | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/CssStyles.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/CssStyles.java index a97990c4eb7..8cb02a63f69 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/CssStyles.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/utils/CssStyles.java @@ -541,6 +541,8 @@ public static String getDiseaseColor(Disease disease) { return "background-disease-giardiasis"; case CRYPTOSPORIDIOSIS: return "background-disease-cryptosporidiosis"; + case SALMONELLOSIS: + return "background-disease-salmonellosis"; case UNDEFINED: default: return "background-disease-undefined"; diff --git a/sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/disease.scss b/sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/disease.scss index 9f08316279e..2236db5ae84 100644 --- a/sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/disease.scss +++ b/sormas-ui/src/main/webapp/VAADIN/themes/sormas/views/disease.scss @@ -594,4 +594,13 @@ fill: #4b066a; } } + .background-disease-salmonellosis { + background-color: #d35400; + fill: #d35400; + + &.background-darker { + background-color: #a04000; + fill: #a04000; + } + } } \ No newline at end of file From 209d6d10ec0100e0e8190d9529d8a74ac3c3d079 Mon Sep 17 00:00:00 2001 From: Harold Date: Thu, 30 Apr 2026 07:40:32 +0300 Subject: [PATCH 6/8] Removed broken delete_history_trigger that referenced a non-existent id column & uses a composite pk on exposure_id & eatingoutvenue --- sormas-backend/src/main/resources/sql/sormas_schema.sql | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sormas-backend/src/main/resources/sql/sormas_schema.sql b/sormas-backend/src/main/resources/sql/sormas_schema.sql index 61fbf977d26..9914c2d0034 100644 --- a/sormas-backend/src/main/resources/sql/sormas_schema.sql +++ b/sormas-backend/src/main/resources/sql/sormas_schema.sql @@ -15866,17 +15866,13 @@ CREATE TABLE IF NOT EXISTS exposures_eatingoutvenues ( ALTER TABLE exposures_eatingoutvenues OWNER TO sormas_user; ALTER TABLE exposures_eatingoutvenues ADD CONSTRAINT fk_exposures_eatingoutvenues_exposure_id FOREIGN KEY (exposure_id) REFERENCES exposures; -ALTER TABLE exposures_eatingoutvenues ADD CONSTRAINT unq_exposures_eatingoutvenues_0 UNIQUE (exposure_id, eatingoutvenue); +ALTER TABLE exposures_eatingoutvenues ADD CONSTRAINT exposures_eatingoutvenues_pk PRIMARY KEY (exposure_id, eatingoutvenue); CREATE TABLE IF NOT EXISTS exposures_eatingoutvenues_history (LIKE exposures_eatingoutvenues); DROP TRIGGER IF EXISTS versioning_trigger ON exposures_eatingoutvenues; CREATE TRIGGER versioning_trigger BEFORE INSERT OR UPDATE OR DELETE ON exposures_eatingoutvenues FOR EACH ROW EXECUTE PROCEDURE versioning('sys_period', 'exposures_eatingoutvenues_history', true); -DROP TRIGGER IF EXISTS delete_history_trigger ON exposures_eatingoutvenues; -CREATE TRIGGER delete_history_trigger - AFTER DELETE ON exposures_eatingoutvenues - FOR EACH ROW EXECUTE PROCEDURE delete_history_trigger('exposures_eatingoutvenues_history', 'id'); ALTER TABLE exposures_eatingoutvenues_history OWNER TO sormas_user; INSERT INTO schema_version (version_number, comment) VALUES (627, '#13917 - Salmonellosis Exposure eating-out venues + shopping details'); From 31a7b09f7374826e4f2102eebea9acb7a057cdb4 Mon Sep 17 00:00:00 2001 From: Harold Date: Thu, 30 Apr 2026 07:46:28 +0300 Subject: [PATCH 7/8] The ACTIVITY_AS_CASE_FROM_DATE / ACTIVITY_AS_CASE_TO_DATE fields are now added only when parentClass == CaseDataDto.class, alongside the rest of the activity-as-case block. --- .../main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java index 29cd4650bdc..b81c3ac448f 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/epidata/EpiDataForm.java @@ -219,6 +219,8 @@ protected void addFields() { if (parentClass == CaseDataDto.class) { addActivityAsCaseFields(); + addField(EpiDataDto.ACTIVITY_AS_CASE_FROM_DATE, DateField.class); + addField(EpiDataDto.ACTIVITY_AS_CASE_TO_DATE, DateField.class); } activityAsCasePanel = new CustomizableFieldsGroup(CustomizableFieldGroup.EPIDATA_ACTIVITY_AS_CASE); @@ -255,8 +257,6 @@ protected void addFields() { addField(EpiDataDto.EXPOSURE_INVESTIGATION_FROM_DATE, DateField.class); addField(EpiDataDto.EXPOSURE_INVESTIGATION_TO_DATE, DateField.class); - addField(EpiDataDto.ACTIVITY_AS_CASE_FROM_DATE, DateField.class); - addField(EpiDataDto.ACTIVITY_AS_CASE_TO_DATE, DateField.class); includeExposureDates(symptomOnsetDate, disease); addField(EpiDataDto.AIRPORT_WORKER, NullableOptionGroup.class); From eae51b755eb09b83a790178abeafbf86e01951d9 Mon Sep 17 00:00:00 2001 From: Harold Date: Thu, 30 Apr 2026 07:59:06 +0300 Subject: [PATCH 8/8] Fix SAL visbility logic in forms --- .../hospitalization/HospitalizationForm.java | 18 ++++++++++++------ .../sormas/ui/person/PersonEditForm.java | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java index 3224b2ab12e..05b8005970a 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/hospitalization/HospitalizationForm.java @@ -327,8 +327,8 @@ public String getFormattedHtmlMessage() { I18nProperties.getValidationError(Validations.afterDate, dischargeDateField.getCaption(), admissionDateField.getCaption()))); dischargeDateField.addValueChangeListener(event -> admissionDateField.markAsDirty()); // re-evaluate admission date for consistent validation of all fields - // RSV specific logic - if (List.of(Disease.RESPIRATORY_SYNCYTIAL_VIRUS, Disease.CRYPTOSPORIDIOSIS, Disease.GIARDIASIS, Disease.SALMONELLOSIS).contains(caze.getDisease())) { + // ICU validators + length-of-stay derivation: only diseases that actually render the ICU fields + if (List.of(Disease.RESPIRATORY_SYNCYTIAL_VIRUS, Disease.CRYPTOSPORIDIOSIS, Disease.GIARDIASIS).contains(caze.getDisease())) { intensiveCareUnitStart.addValidator( new DateComparisonValidator( intensiveCareUnitStart, @@ -373,9 +373,17 @@ public String getFormattedHtmlMessage() { icuLengthOfStayField.setValue("" + DateHelper.getDaysBetween(intensiveCareUnitStart.getValue(), intensiveCareUnitEnd.getValue())); } }); - // RSV-specific conditional visibility logic - // stillHospitalized should not be visible/writable if discharge date is filled + // Show icuLengthOfStay when ICU dates are not available but survey has length information + FieldHelper.setVisibleWhen(intensiveCareUnit, Collections.singletonList(icuLengthOfStayField), Arrays.asList(YesNoUnknown.YES), true); + } + // stillHospitalized should not be visible/writable if discharge date is filled. + // Applies to every disease that renders the stillHospitalized field (RSV, Giardiasis, Cryptosporidiosis, Salmonellosis). + if (List.of( + Disease.RESPIRATORY_SYNCYTIAL_VIRUS, + Disease.CRYPTOSPORIDIOSIS, + Disease.GIARDIASIS, + Disease.SALMONELLOSIS).contains(caze.getDisease())) { dischargeDateField.addValueChangeListener(event -> { boolean hasDischargeDate = dischargeDateField.getValue() != null; stillHospitalizedField.setVisible(!hasDischargeDate); @@ -388,8 +396,6 @@ public String getFormattedHtmlMessage() { durationOfHospitalization.setValue("" + DateHelper.getDaysBetween(admissionDateField.getValue(), dischargeDateField.getValue())); } }); - // Show icuLengthOfStay when ICU dates are not available but survey has length information - FieldHelper.setVisibleWhen(intensiveCareUnit, Collections.singletonList(icuLengthOfStayField), Arrays.asList(YesNoUnknown.YES), true); } hospitalizedPreviouslyField.addValueChangeListener(e -> updatePrevHospHint(hospitalizedPreviouslyField, previousHospitalizationsField)); diff --git a/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java b/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java index 1a92dbf7d76..8720a9fc2a3 100644 --- a/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java +++ b/sormas-ui/src/main/java/de/symeda/sormas/ui/person/PersonEditForm.java @@ -527,7 +527,7 @@ protected void addFields() { // other diseases keep the existing dependent-visibility binding. boolean isLuxSalmonellosis = disease == Disease.SALMONELLOSIS && isConfiguredServer(CountryHelper.COUNTRY_CODE_LUXEMBOURG); if (isLuxSalmonellosis) { - setVisible(false, PersonDto.WORK_PLACE); + setVisibleClear(false, PersonDto.WORK_PLACE); getField(PersonDto.WORK_PLACE_TEXT).setVisible(true); } else { FieldHelper.setVisibleWhen(getFieldGroup(), PersonDto.WORK_PLACE_TEXT, PersonDto.WORK_PLACE, WorkPlace.OTHER, true);