Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public class CaseDataDto extends SormasToSormasShareableDto implements IsCase {
public static final String EPI_DATA = "epiData";
public static final String THERAPY = "therapy";
public static final String CLINICAL_COURSE = "clinicalCourse";
public static final String LAB_RESULTS = "labResults";
public static final String MATERNAL_HISTORY = "maternalHistory";
public static final String PORT_HEALTH_INFO = "portHealthInfo";
public static final String HEALTH_CONDITIONS = "healthConditions";
Expand Down Expand Up @@ -166,6 +167,9 @@ public class CaseDataDto extends SormasToSormasShareableDto implements IsCase {
public static final String POINT_OF_ENTRY = "pointOfEntry";
public static final String POINT_OF_ENTRY_DETAILS = "pointOfEntryDetails";
public static final String ADDITIONAL_DETAILS = "additionalDetails";
public static final String DATE_OTHER = "dateOther";
public static final String DATE_OTHER_DETAILS = "dateOtherDetails";
public static final String EXTERNAL_COMMENTS = "externalComments";
public static final String EXTERNAL_ID = "externalID";
public static final String EXTERNAL_TOKEN = "externalToken";
public static final String INTERNAL_TOKEN = "internalToken";
Expand Down Expand Up @@ -484,6 +488,15 @@ public class CaseDataDto extends SormasToSormasShareableDto implements IsCase {
@SensitiveData
@Size(max = FieldConstraints.CHARACTER_LIMIT_TEXT, message = Validations.textTooLong)
private String additionalDetails;
@S2SIgnoreProperty(configProperty = SormasToSormasConfig.SORMAS2SORMAS_IGNORE_ADDITIONAL_DETAILS)
private Date dateOther;
@S2SIgnoreProperty(configProperty = SormasToSormasConfig.SORMAS2SORMAS_IGNORE_ADDITIONAL_DETAILS)
@Size(max = FieldConstraints.CHARACTER_LIMIT_DEFAULT, message = Validations.textTooLong)
private String dateOtherDetails;
@S2SIgnoreProperty(configProperty = SormasToSormasConfig.SORMAS2SORMAS_IGNORE_ADDITIONAL_DETAILS)
@SensitiveData
@Size(max = FieldConstraints.CHARACTER_LIMIT_TEXT, message = Validations.textTooLong)
private String externalComments;
@HideForCountriesExcept(countries = {
COUNTRY_CODE_GERMANY,
COUNTRY_CODE_SWITZERLAND })
Expand Down Expand Up @@ -1383,6 +1396,30 @@ public void setAdditionalDetails(String additionalDetails) {
this.additionalDetails = additionalDetails;
}

public Date getDateOther() {
return dateOther;
}

public void setDateOther(Date dateOther) {
this.dateOther = dateOther;
}

public String getDateOtherDetails() {
return dateOtherDetails;
}

public void setDateOtherDetails(String dateOtherDetails) {
this.dateOtherDetails = dateOtherDetails;
}

public String getExternalComments() {
return externalComments;
}

public void setExternalComments(String externalComments) {
this.externalComments = externalComments;
}

public String getExternalID() {
return externalID;
}
Expand Down
17 changes: 17 additions & 0 deletions sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,8 @@ public interface Captions {
String CaseData_contactTracingFirstContactDate = "CaseData.contactTracingFirstContactDate";
String CaseData_contactTracingFirstContactType = "CaseData.contactTracingFirstContactType";
String CaseData_creationDate = "CaseData.creationDate";
String CaseData_dateOther = "CaseData.dateOther";
String CaseData_dateOtherDetails = "CaseData.dateOtherDetails";
String CaseData_deletionReason = "CaseData.deletionReason";
String CaseData_dengueFeverType = "CaseData.dengueFeverType";
String CaseData_department = "CaseData.department";
Expand All @@ -700,6 +702,7 @@ public interface Captions {
String CaseData_epidNumber = "CaseData.epidNumber";
String CaseData_eventCount = "CaseData.eventCount";
String CaseData_expectedFollowUpUntil = "CaseData.expectedFollowUpUntil";
String CaseData_externalComments = "CaseData.externalComments";
String CaseData_externalData = "CaseData.externalData";
String CaseData_externalID = "CaseData.externalID";
String CaseData_externalToken = "CaseData.externalToken";
Expand All @@ -720,6 +723,7 @@ public interface Captions {
String CaseData_investigatedDate = "CaseData.investigatedDate";
String CaseData_investigationStatus = "CaseData.investigationStatus";
String CaseData_laboratoryDiagnosticConfirmation = "CaseData.laboratoryDiagnosticConfirmation";
String CaseData_labResults = "CaseData.labResults";
String CaseData_latestEventId = "CaseData.latestEventId";
String CaseData_latestEventStatus = "CaseData.latestEventStatus";
String CaseData_latestEventTitle = "CaseData.latestEventTitle";
Expand Down Expand Up @@ -918,6 +922,19 @@ public interface Captions {
String caseImportMergeCase = "caseImportMergeCase";
String caseInfrastructureDataChanged = "caseInfrastructureDataChanged";
String caseJurisdictionType = "caseJurisdictionType";
String caseLabResultsAntibiotic = "caseLabResultsAntibiotic";
String caseLabResultsClinicalInterpretation = "caseLabResultsClinicalInterpretation";
String caseLabResultsComments = "caseLabResultsComments";
String caseLabResultsDateCollected = "caseLabResultsDateCollected";
String caseLabResultsDrugSusceptibilityHeading = "caseLabResultsDrugSusceptibilityHeading";
String caseLabResultsMethod = "caseLabResultsMethod";
String caseLabResultsMicValue = "caseLabResultsMicValue";
String caseLabResultsSample = "caseLabResultsSample";
String caseLabResultsSamplesHeading = "caseLabResultsSamplesHeading";
String caseLabResultsSurveillanceInterpretation = "caseLabResultsSurveillanceInterpretation";
String caseLabResultsTestsHeading = "caseLabResultsTestsHeading";
String caseLabResultsTestsPerformed = "caseLabResultsTestsPerformed";
String caseLabResultsZoneDiameter = "caseLabResultsZoneDiameter";
String caseLinkToSamples = "caseLinkToSamples";
String caseMergeDuplicates = "caseMergeDuplicates";
String caseMinusDays = "caseMinusDays";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,8 @@ public interface Strings {
String infoNoAccessToPersonEntities = "infoNoAccessToPersonEntities";
String infoNoAdditionalTests = "infoNoAdditionalTests";
String infoNoAefiInvestigations = "infoNoAefiInvestigations";
String infoNoCaseLabResultsSamples = "infoNoCaseLabResultsSamples";
String infoNoCaseLabResultsTests = "infoNoCaseLabResultsTests";
String infoNoCasesFoundStatistics = "infoNoCasesFoundStatistics";
String infoNoCustomizableEnumTranslations = "infoNoCustomizableEnumTranslations";
String infoNoCustomizableFieldOptions = "infoNoCustomizableFieldOptions";
Expand Down
17 changes: 17 additions & 0 deletions sormas-api/src/main/resources/captions.properties
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,19 @@ CampaignFormData.edit=Edit
# CaseData
caseCasesList=Cases list
caseInfrastructureDataChanged=Infrastructure data has changed
caseLabResultsTestsHeading=Tests
caseLabResultsSamplesHeading=Samples
caseLabResultsDrugSusceptibilityHeading=Drug susceptibility tests
caseLabResultsSample=Sample
caseLabResultsDateCollected=Date collected
caseLabResultsTestsPerformed=Tests performed
caseLabResultsAntibiotic=Antibiotic
caseLabResultsMethod=Method
caseLabResultsMicValue=MIC value (mg/l)
caseLabResultsZoneDiameter=Zone (mm)
caseLabResultsClinicalInterpretation=Clinical interpretation
caseLabResultsSurveillanceInterpretation=Surveillance interpretation
caseLabResultsComments=Comments
caseCloneCaseWithNewDisease=Generate new case for
caseContacts=Contacts
caseDocuments=Case Documents
Expand Down Expand Up @@ -440,6 +453,8 @@ CaseData.clinicianName=Name of responsible clinician
CaseData.clinicianPhone=Phone number of responsible clinician
CaseData.clinicianEmail=Email address of responsible clinician
CaseData.contactOfficer=Contact officer
CaseData.dateOther=Date other
CaseData.dateOtherDetails=Date other details
CaseData.dengueFeverType=Dengue fever type
CaseData.diseaseVariant=Disease variant
CaseData.diseaseDetails=Disease name
Expand All @@ -448,8 +463,10 @@ CaseData.districtLevelDate=Date received at district level
CaseData.doses=How many doses
CaseData.epiData=Epidemiological data
CaseData.epidNumber=EPID number
CaseData.labResults=Laboratory results
CaseData.postMortem=Post Mortem
CaseData.department=Facility Department
CaseData.externalComments=External comments
CaseData.externalID=External ID
CaseData.externalToken=External Token
CaseData.internalToken=Internal Token
Expand Down
2 changes: 2 additions & 0 deletions sormas-api/src/main/resources/strings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,8 @@ infoCaseIncidenceNotPossible = The following regions have missing population dat
infoCaseIncidenceMissingPopulationData = The following regions and/or districts have missing population data:<br/><br/>%s.<br/><br/>Incidence proportion cannot be calculated for these regions and/or districts.
infoCaseIncidenceIncompatible = No population data is available for communities and facilities. Case incidence cannot be calculated and case counts are displayed instead. If you want to view case incidence, please remove any community and facilitiy filters and groupings.
infoNoPathogenTests = No pathogen tests have been created for this sample
infoNoCaseLabResultsTests = No tests have been performed for this case
infoNoCaseLabResultsSamples = No samples have been collected for this case
infoPickOrCreateCase = There are existing cases in the database that seem very similar to the one you are about to create. Please have a look at the list of existing cases and verify that the case you want to create is not a duplicate of one of them. If you find a case that is most likely the same as yours, please <b>click on it in the list</b> and confirm.
infoPickOrCreateCaseNewCase = Newly added case information
infoPickOrCreateImmunization = The system already contains at least one immunization for %s for this person and means of immunization that overlaps with the specified immunziation period. Please compare the most recent existing immunization with the one you created and decide whether to discard the new immunization, use its information to update the existing one, or create it anyway.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ public class Case extends CoreAdo implements IsCase, SormasToSormasShareable, Ha
public static final String POINT_OF_ENTRY_DETAILS = "pointOfEntryDetails";
public static final String COMPLETENESS = "completeness";
public static final String ADDITIONAL_DETAILS = "additionalDetails";
public static final String DATE_OTHER = "dateOther";
public static final String DATE_OTHER_DETAILS = "dateOtherDetails";
public static final String EXTERNAL_COMMENTS = "externalComments";
public static final String EXTERNAL_ID = "externalID";
public static final String EXTERNAL_TOKEN = "externalToken";
public static final String INTERNAL_TOKEN = "internalToken";
Expand Down Expand Up @@ -364,6 +367,9 @@ public class Case extends CoreAdo implements IsCase, SormasToSormasShareable, Ha

private Float completeness;
private String additionalDetails;
private Date dateOther;
private String dateOtherDetails;
private String externalComments;
private String externalID;
private String externalToken;
private String internalToken;
Expand Down Expand Up @@ -1258,6 +1264,33 @@ public void setAdditionalDetails(String additionalDetails) {
this.additionalDetails = additionalDetails;
}

@Temporal(TemporalType.TIMESTAMP)
public Date getDateOther() {
return dateOther;
}

public void setDateOther(Date dateOther) {
this.dateOther = dateOther;
}

@Column(length = CHARACTER_LIMIT_DEFAULT)
public String getDateOtherDetails() {
return dateOtherDetails;
}

public void setDateOtherDetails(String dateOtherDetails) {
this.dateOtherDetails = dateOtherDetails;
}

@Column(columnDefinition = "text")
public String getExternalComments() {
return externalComments;
}

public void setExternalComments(String externalComments) {
this.externalComments = externalComments;
}

@Column(length = CHARACTER_LIMIT_DEFAULT)
public String getExternalID() {
return externalID;
Expand All @@ -1279,7 +1312,7 @@ public String getExternalId() {

/**
* Extra setter for externalID needed to comply with the HasExternalData interface
*
*
* @param externalId
* the value to be set for externalID
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3189,6 +3189,9 @@ public static CaseDataDto toCaseDto(Case source) {
target.setPointOfEntry(PointOfEntryFacadeEjb.toReferenceDto(source.getPointOfEntry()));
target.setPointOfEntryDetails(source.getPointOfEntryDetails());
target.setAdditionalDetails(source.getAdditionalDetails());
target.setDateOther(source.getDateOther());
target.setDateOtherDetails(source.getDateOtherDetails());
target.setExternalComments(source.getExternalComments());
target.setExternalID(source.getExternalID());
target.setExternalToken(source.getExternalToken());
target.setInternalToken(source.getInternalToken());
Expand Down Expand Up @@ -3418,6 +3421,9 @@ public Case fillOrBuildEntity(@NotNull CaseDataDto source, Case target, boolean
target.setPointOfEntry(pointOfEntryService.getByReferenceDto(source.getPointOfEntry()));
target.setPointOfEntryDetails(source.getPointOfEntryDetails());
target.setAdditionalDetails(source.getAdditionalDetails());
target.setDateOther(source.getDateOther());
target.setDateOtherDetails(source.getDateOtherDetails());
target.setExternalComments(source.getExternalComments());
target.setExternalID(source.getExternalID());
target.setExternalToken(source.getExternalToken());
target.setInternalToken(source.getInternalToken());
Expand Down
11 changes: 11 additions & 0 deletions sormas-backend/src/main/resources/sql/sormas_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16295,4 +16295,15 @@ alter table samples_history alter column retestrequested set not null;

INSERT INTO schema_version (version_number, comment) VALUES (637, 'Reference laboratory + retest indicators on sample #13954 (#13948)');

-- 2026-06-08 Laboratory results tab: date other + external comments on case #13948 (#13955)
alter table cases add column IF NOT EXISTS dateother timestamp;
alter table cases add column IF NOT EXISTS dateotherdetails varchar(255);
alter table cases add column IF NOT EXISTS externalcomments text;

alter table cases_history add column IF NOT EXISTS dateother timestamp;
alter table cases_history add column IF NOT EXISTS dateotherdetails varchar(255);
alter table cases_history add column IF NOT EXISTS externalcomments text;

INSERT INTO schema_version (version_number, comment) VALUES (638, 'Laboratory results tab: dateOther, dateOtherDetails, externalComments on case #13955');

-- *** Insert new sql commands BEFORE this line. Remember to always consider _history tables. ***
Original file line number Diff line number Diff line change
Expand Up @@ -3443,6 +3443,32 @@ public void testBulkUpdateBulkEditDiseaseClearVariantOfChangedDisease() {
assertThat(caze.getDiseaseVariantDetails(), is(nullValue()));
}

@Test
public void testLabResultsFieldsRoundTrip() {
// Guards the hand-written CaseFacadeEjb mapper for the lab-results tab header fields (#13948).
CaseDataDto caze = creator.createCase(
surveillanceSupervisor.toReference(),
creator.createPerson("Lab", "Case").toReference(),
Disease.EVD,
CaseClassification.PROBABLE,
InvestigationStatus.PENDING,
new Date(),
rdcf);

// Truncate to day granularity so the round-trip assertion is not brittle to sub-second precision
// differences in DB/JPA timestamp handling.
Date dateOther = DateHelper.getStartOfDay(new Date());
caze.setDateOther(dateOther);
caze.setDateOtherDetails("Sampling visit");
caze.setExternalComments("Lab sent confirmation");
getCaseFacade().save(caze);

CaseDataDto reloaded = getCaseFacade().getCaseDataByUuid(caze.getUuid());
assertEquals(dateOther, reloaded.getDateOther());
assertEquals("Sampling visit", reloaded.getDateOtherDetails());
assertEquals("Lab sent confirmation", reloaded.getExternalComments());
}

private static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ";
private static final SecureRandom rnd = new SecureRandom();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ public void refreshMenu(SubMenu menu, String params) {
I18nProperties.getPrefixCaption(CaseDataDto.I18N_PREFIX, CaseDataDto.CLINICAL_COURSE),
params);
}

if (UiUtil.permitted(FeatureType.SAMPLES_LAB, UserRight.SAMPLE_VIEW) && !caze.checkIsUnreferredPortHealthCase()) {
menu.addView(CaseLabResultsView.VIEW_NAME, I18nProperties.getPrefixCaption(CaseDataDto.I18N_PREFIX, CaseDataDto.LAB_RESULTS), params);
}
}
if (FacadeProvider.getDiseaseConfigurationFacade().hasFollowUp(caze.getDisease())
&& UiUtil.permitted(UserRight.CONTACT_VIEW)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ public void registerViews(Navigator navigator) {
if (UiUtil.permitted(FeatureType.VIEW_TAB_CASES_CLINICAL_COURSE, UserRight.CLINICAL_COURSE_VIEW)) {
navigator.addView(ClinicalCourseView.VIEW_NAME, ClinicalCourseView.class);
}
if (UiUtil.permitted(FeatureType.SAMPLES_LAB, UserRight.SAMPLE_VIEW)) {
navigator.addView(CaseLabResultsView.VIEW_NAME, CaseLabResultsView.class);
}
if (UiUtil.enabled(FeatureType.CASE_FOLLOWUP)) {
navigator.addView(CaseVisitsView.VIEW_NAME, CaseVisitsView.class);
}
Expand Down Expand Up @@ -1479,6 +1482,33 @@ public CommitDiscardWrapperComponent<TherapyForm> getTherapyEditComponent(final
return editView;
}

public CommitDiscardWrapperComponent<CaseLabResultsForm> getLabResultsEditComponent(final String caseUuid, boolean isEditAllowed) {

CaseDataDto caseDataDto = findCase(caseUuid);

CaseLabResultsForm form = new CaseLabResultsForm(caseDataDto.isPseudonymized(), caseDataDto.isInJurisdiction(), isEditAllowed);
form.setValue(caseDataDto);
// The onset date is shown read-only and sourced from the case's symptoms; it is not bound to the form.
if (caseDataDto.getSymptoms() != null) {
form.setOnsetDate(caseDataDto.getSymptoms().getOnsetDate());
}

CommitDiscardWrapperComponent<CaseLabResultsForm> editView =
new CommitDiscardWrapperComponent<>(form, UiUtil.permitted(isEditAllowed, UserRight.CASE_EDIT), form.getFieldGroup());

editView.addCommitListener(() -> {
// Re-fetch and copy back only the lab-results fields so concurrent edits to other tabs are not lost.
CaseDataDto cazeDto = FacadeProvider.getCaseFacade().getCaseDataByUuid(caseUuid);
CaseDataDto edited = form.getValue();
cazeDto.setDateOther(edited.getDateOther());
cazeDto.setDateOtherDetails(edited.getDateOtherDetails());
cazeDto.setExternalComments(edited.getExternalComments());
saveCase(cazeDto);
});

return editView;
}

public CommitDiscardWrapperComponent<ClinicalCourseForm> getClinicalCourseComponent(String caseUuid) {

CaseDataDto caze = FacadeProvider.getCaseFacade().getCaseDataByUuid(caseUuid);
Expand Down
Loading
Loading