Skip to content

#13948 sample & test form changes#13967

Merged
roldy merged 12 commits into
developmentfrom
feature/13948-sample-test-form-changes
Jun 12, 2026
Merged

#13948 sample & test form changes#13967
roldy merged 12 commits into
developmentfrom
feature/13948-sample-test-form-changes

Conversation

@roldy

@roldy roldy commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #13953 #13954 #13958 #13959

Summary by CodeRabbit

  • New Features
    • Added configuration page for administrators to manage server-wide feature toggles
    • New sample fields to track reference laboratory processing and retest requests
    • Configurable pathogen test result validation with automatic defaults
    • Updated sample exports to include new tracking fields

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0850ad6d-7615-4147-97d5-d0a7da5581b4

📥 Commits

Reviewing files that changed from the base of the PR and between 5af4ffc and 22707c1.

📒 Files selected for processing (1)
  • sormas-backend/src/main/resources/sql/sormas_schema.sql

📝 Walkthrough

Walkthrough

This PR introduces a server-side feature configuration framework alongside sample lab field enhancements. It adds two feature toggles for lab workflows (SAMPLE_ADD_PATHOGEN_TEST and PATHOGEN_TEST_RESULT_REQUIRED), a UI screen to manage feature states, two new sample boolean fields (performedByReferenceLaboratory and retestRequested), makes pathogen test result requirement conditional, hides the "request pathogen tests" form section, and gates the add-test button by feature flag.

Changes

Feature Configuration and Sample Lab Workflow

Layer / File(s) Summary
Feature configuration API and enum types
sormas-api/src/main/java/de/symeda/sormas/api/feature/FeatureConfigurationFacade.java, FeatureType.java
New setServerFeatureEnabled remote method and two feature types SAMPLE_ADD_PATHOGEN_TEST and PATHOGEN_TEST_RESULT_REQUIRED configured to depend on SAMPLES_LAB and enabled by default.
Feature configuration backend service and EJB
sormas-backend/src/main/java/de/symeda/sormas/backend/feature/FeatureConfigurationService.java, FeatureConfigurationFacadeEjb.java, sormas-backend/src/test/java/de/symeda/sormas/backend/feature/FeatureConfigurationFacadeEjbTest.java
Service adds feature lookup methods; facade implements setServerFeatureEnabled to create or update configurations; tests validate default enablement and toggle behavior.
Sample lab fields across DTO, entity, schema, and export
sormas-api/src/main/java/de/symeda/sormas/api/sample/SampleDto.java, SampleExportDto.java, sormas-backend/src/main/java/de/symeda/sormas/backend/sample/Sample.java, SampleFacadeEjb.java, sormas-backend/src/main/resources/sql/sormas_schema.sql, sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java, sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java, sormas-api/src/main/resources/captions.properties
Adds performedByReferenceLaboratory and retestRequested boolean fields to sample DTO, entity, and schema; includes them in export; migrates attributes during referral creation; adds UI captions.
Feature configuration UI screen and navigation wiring
sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/featureconfiguration/FeatureConfigurationView.java, AbstractConfigurationView.java, sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java, Captions.java, sormas-api/src/main/resources/strings.properties, captions.properties, enum.properties
Creates FeatureConfigurationView Vaadin screen with checkboxes and save button; registers in configuration navigation/menu; adds comprehensive i18n keys and enum captions.
Sample form field additions and pathogen test hiding
sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java
Adds two new lab flag checkboxes to form layout, disables them in read-only mode, and unconditionally hides the "Request pathogen tests" section while preserving bound data.
Pathogen test result requiredness feature gating
sormas-ui/src/main/java/de/symeda/sormas/ui/samples/diseasesection/PathogenTestFormConfig.java, PathogenTestForm.java, components/TestResultComponent.java, sormas-backend/src/main/java/de/symeda/sormas/backend/sample/PathogenTestFacadeEjb.java, sormas-backend/src/test/java/de/symeda/sormas/backend/sample/PathogenTestFacadeEjbTest.java
Form config reads PATHOGEN_TEST_RESULT_REQUIRED feature; TestResultComponent uses it to control required indicators and validation; backend defaults null result to PENDING when requirement disabled; tests validate both states.
Gate sample add-pathogen-test button by feature flag
sormas-ui/src/main/java/de/symeda/sormas/ui/samples/humansample/SampleController.java
Conditionally wires "Add pathogen test" button based on SAMPLE_ADD_PATHOGEN_TEST feature flag; button not shown when disabled.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #13953: This PR implements the acceptance criteria for hiding "Request pathogen tests" and making "Add pathogen test" admin-configurable via the Feature Configuration page.

Possibly related PRs

Suggested reviewers

  • obinna-h-n
  • KarnaiahPesula
  • raulbob

Poem

🐰 A rabbit hops through the config maze,
Feature toggles shimmer in admin's gaze,
Sample fields dance through schema and store,
Lab flags flutter—admins want more!
From DTO to screen, the journey's long,
With tests to verify nothing goes wrong. ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title '#13948 sample & test form changes' is vague and doesn't clearly describe the main changes, using generic phrasing that doesn't highlight the key feature additions. Use a more descriptive title such as 'Add feature configuration UI and pathogen test result requirement flag' or 'Implement feature toggles for sample pathogen tests and test result requirements'.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description lists all linked issue numbers (#13953, #13954, #13958, #13959) clearly, following the template format and providing the required information.
Linked Issues check ✅ Passed The code changes successfully implement #13953's requirements: permanently hiding 'Request pathogen tests' section, adding admin-configurable 'Add pathogen test' feature under Configuration, defaulting it ON, and preserving existing data.
Out of Scope Changes check ✅ Passed All changes are scoped to the stated objectives: adding feature configuration UI, implementing pathogen test toggles, adding sample fields, and updating relevant forms/services. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/13948-sample-test-form-changes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java (1)

1233-1235: ⚡ Quick win

Consider verifying performedByReferenceLaboratory's default value.

The comment on line 1234 states that performedByReferenceLaboratory is "unset" initially, but there's no assertion to validate this. Only retestRequested is checked. For comprehensive default-value coverage, consider adding an assertion for performedByReferenceLaboratory as well.

📋 Suggested addition
 		SampleDto sample = creator.createSample(caze.toReference(), user.toReference(), rdcf.facility);
 		// New samples default retestRequested to false and leave performedByReferenceLaboratory unset.
+		assertNull(getSampleFacade().getSampleByUuid(sample.getUuid()).getPerformedByReferenceLaboratory());
 		assertEquals(Boolean.FALSE, getSampleFacade().getSampleByUuid(sample.getUuid()).getRetestRequested());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java`
around lines 1233 - 1235, The test creates a SampleDto via creator.createSample
and currently only asserts retestRequested; add an assertion that the returned
SampleDto's performedByReferenceLaboratory is unset (null) to match the comment.
Locate the call using SampleDto sample = creator.createSample(...) and the fetch
via getSampleFacade().getSampleByUuid(sample.getUuid()), then add an assertNull
(or assertEquals(null, ...)) against getPerformedByReferenceLaboratory() on that
fetched SampleDto to verify the default.
sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java (1)

192-219: ⚡ Quick win

Remove now-unused disease fetch logic after unconditional sample-source hiding.

Lines 192-200 no longer influence behavior because Line 218 always hides SAMPLE_SOURCE. Keeping this block adds unnecessary facade calls during form initialization.

Proposed cleanup
-		Disease disease = null;
-		final CaseReferenceDto associatedCase = getValue().getAssociatedCase();
-		if (associatedCase != null && UiUtil.permitted(UserRight.CASE_VIEW)) {
-			disease = FacadeProvider.getCaseFacade().getCaseDataByUuid(associatedCase.getUuid()).getDisease();
-		} else {
-			final ContactReferenceDto associatedContact = getValue().getAssociatedContact();
-			if (associatedContact != null && UiUtil.permitted(UserRight.CONTACT_VIEW)) {
-				disease = FacadeProvider.getContactFacade().getByUuid(associatedContact.getUuid()).getDisease();
-			}
-		}
-
 		// The sample source dropdown is no longer shown for any disease (`#13948` issue `#13959`); the field is
 		// kept bound so existing values still round-trip and appear in exports.
 		getField(SampleDto.SAMPLE_SOURCE).setVisible(false);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java`
around lines 192 - 219, Remove the now-unused disease lookup block: delete the
local variable "Disease disease = null;" and the conditional block that reads
associated case/contact using getValue().getAssociatedCase(),
CaseReferenceDto/ContactReferenceDto and
FacadeProvider.getCaseFacade()/getContactFacade() since SAMPLE_SOURCE is
unconditionally hidden via getField(SampleDto.SAMPLE_SOURCE).setVisible(false)
and those facade calls only cause unnecessary work; after removal, ensure any
imports or references exclusively used by that block (e.g., Disease,
CaseReferenceDto, ContactReferenceDto, FacadeProvider calls) are cleaned up.
sormas-backend/src/main/java/de/symeda/sormas/backend/feature/FeatureConfigurationFacadeEjb.java (1)

357-360: ⚡ Quick win

Align initial creation with entity-scoped server feature contracts.

Line 358 currently creates a single row when none exists. If a server feature supports multiple entity types, this can persist an incomplete configuration set and diverge from the entity-aware creation contract already used in FeatureConfigurationService.createMissingFeatureConfigurations.

Proposed adjustment
 		List<FeatureConfiguration> configurations = service.getServerFeatureConfigurationsByType(featureType);
 		if (configurations.isEmpty()) {
-			FeatureConfiguration configuration = FeatureConfiguration.build(featureType, enabled);
-			configuration.setProperties(featureType.getSupportedPropertyDefaults());
-			service.ensurePersisted(configuration);
+			if (featureType.getEntityTypes() != null) {
+				for (DeletableEntityType entityType : featureType.getEntityTypes()) {
+					FeatureConfiguration configuration = FeatureConfiguration.build(featureType, enabled);
+					configuration.setEntityType(entityType);
+					configuration.setProperties(featureType.getSupportedPropertyDefaults());
+					service.ensurePersisted(configuration);
+				}
+			} else {
+				FeatureConfiguration configuration = FeatureConfiguration.build(featureType, enabled);
+				configuration.setProperties(featureType.getSupportedPropertyDefaults());
+				service.ensurePersisted(configuration);
+			}
 		} else {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sormas-backend/src/main/java/de/symeda/sormas/backend/feature/FeatureConfigurationFacadeEjb.java`
around lines 357 - 360, When no configurations exist (the
configurations.isEmpty() branch in FeatureConfigurationFacadeEjb), do not create
a single generic row via FeatureConfiguration.build; instead create per-entity
configurations to match the entity-scoped contract used by
FeatureConfigurationService.createMissingFeatureConfigurations. Replace the
single-feature creation with logic that iterates the featureType's supported
entity types (or delegate to
FeatureConfigurationService.createMissingFeatureConfigurations) and call
service.ensurePersisted for each generated FeatureConfiguration (using
FeatureConfiguration.build and setting properties via
featureType.getSupportedPropertyDefaults()) so all required entity-specific rows
are created.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sormas-api/src/main/java/de/symeda/sormas/api/sample/SampleDto.java`:
- Around line 140-141: migrateAttributesOfPhysicalSample and buildReferralDto
currently omit the performedByReferenceLaboratory and retestRequested flags,
causing them to be lost during referral creation; update
migrateAttributesOfPhysicalSample to copy
sampleDto.getPerformedByReferenceLaboratory() and sampleDto.getRetestRequested()
into the target/referral DTO, and ensure buildReferralDto sets those same fields
on the new SampleDto (or copies the source SampleDto fields) so both
performedByReferenceLaboratory and retestRequested are preserved during referral
migration.

In `@sormas-backend/src/main/resources/sql/sormas_schema.sql`:
- Around line 16307-16312: The migration currently adds a nullable boolean
column retestrequested and only backfills existing rows, allowing future NULLs;
change the migration to enforce a DB-level default and NOT NULL so new rows
default to FALSE. Specifically, for both samples and samples_history ensure you:
(1) ALTER TABLE ... ADD COLUMN IF NOT EXISTS retestrequested boolean DEFAULT
false; (2) UPDATE ... SET retestrequested = false WHERE retestrequested IS NULL;
and (3) ALTER TABLE ... ALTER COLUMN retestrequested SET NOT NULL; reference the
tables samples and samples_history and the column name retestrequested when
making these changes.

---

Nitpick comments:
In
`@sormas-backend/src/main/java/de/symeda/sormas/backend/feature/FeatureConfigurationFacadeEjb.java`:
- Around line 357-360: When no configurations exist (the
configurations.isEmpty() branch in FeatureConfigurationFacadeEjb), do not create
a single generic row via FeatureConfiguration.build; instead create per-entity
configurations to match the entity-scoped contract used by
FeatureConfigurationService.createMissingFeatureConfigurations. Replace the
single-feature creation with logic that iterates the featureType's supported
entity types (or delegate to
FeatureConfigurationService.createMissingFeatureConfigurations) and call
service.ensurePersisted for each generated FeatureConfiguration (using
FeatureConfiguration.build and setting properties via
featureType.getSupportedPropertyDefaults()) so all required entity-specific rows
are created.

In
`@sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java`:
- Around line 1233-1235: The test creates a SampleDto via creator.createSample
and currently only asserts retestRequested; add an assertion that the returned
SampleDto's performedByReferenceLaboratory is unset (null) to match the comment.
Locate the call using SampleDto sample = creator.createSample(...) and the fetch
via getSampleFacade().getSampleByUuid(sample.getUuid()), then add an assertNull
(or assertEquals(null, ...)) against getPerformedByReferenceLaboratory() on that
fetched SampleDto to verify the default.

In `@sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java`:
- Around line 192-219: Remove the now-unused disease lookup block: delete the
local variable "Disease disease = null;" and the conditional block that reads
associated case/contact using getValue().getAssociatedCase(),
CaseReferenceDto/ContactReferenceDto and
FacadeProvider.getCaseFacade()/getContactFacade() since SAMPLE_SOURCE is
unconditionally hidden via getField(SampleDto.SAMPLE_SOURCE).setVisible(false)
and those facade calls only cause unnecessary work; after removal, ensure any
imports or references exclusively used by that block (e.g., Disease,
CaseReferenceDto, ContactReferenceDto, FacadeProvider calls) are cleaned up.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 97623fea-8d9d-42a2-9acf-9cc1c4760ad1

📥 Commits

Reviewing files that changed from the base of the PR and between 36b32dc and aba0f03.

📒 Files selected for processing (29)
  • sormas-api/src/main/java/de/symeda/sormas/api/feature/FeatureConfigurationFacade.java
  • sormas-api/src/main/java/de/symeda/sormas/api/feature/FeatureType.java
  • sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
  • sormas-api/src/main/java/de/symeda/sormas/api/i18n/Strings.java
  • sormas-api/src/main/java/de/symeda/sormas/api/sample/SampleDto.java
  • sormas-api/src/main/java/de/symeda/sormas/api/sample/SampleExportDto.java
  • sormas-api/src/main/resources/captions.properties
  • sormas-api/src/main/resources/enum.properties
  • sormas-api/src/main/resources/strings.properties
  • sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageService.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/feature/FeatureConfigurationFacadeEjb.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/feature/FeatureConfigurationService.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/sample/PathogenTestFacadeEjb.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/sample/Sample.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/sample/SampleFacadeEjb.java
  • sormas-backend/src/main/resources/sql/sormas_schema.sql
  • sormas-backend/src/test/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjbUnitTest.java
  • sormas-backend/src/test/java/de/symeda/sormas/backend/feature/FeatureConfigurationFacadeEjbTest.java
  • sormas-backend/src/test/java/de/symeda/sormas/backend/sample/PathogenTestFacadeEjbTest.java
  • sormas-backend/src/test/java/de/symeda/sormas/backend/sample/SampleFacadeEjbTest.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/AbstractConfigurationView.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/featureconfiguration/FeatureConfigurationView.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/samples/AbstractSampleForm.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/samples/PathogenTestController.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/samples/PathogenTestForm.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/samples/components/TestResultComponent.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/samples/diseasesection/PathogenTestFormConfig.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/samples/humansample/SampleController.java

Comment thread sormas-api/src/main/java/de/symeda/sormas/api/sample/SampleDto.java
Comment thread sormas-backend/src/main/resources/sql/sormas_schema.sql
…migration & Enforce a DB-level default/constraint for retestrequested
@roldy roldy requested review from KarnaiahPesula and raulbob June 5, 2026 15:06
Re-sequence schema versions after development renumbered the tail:
the sample-form "reference laboratory + retest indicators" migration
moves from 638 to 637.

Revert ExternalMessage* changes (schema JSONB->JSON / config-key
rename block and the countDistinct(...get(ID)) Java fixes).
@roldy roldy merged commit 0a39398 into development Jun 12, 2026
3 of 5 checks passed
@roldy roldy deleted the feature/13948-sample-test-form-changes branch June 12, 2026 03:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Laboratory - Hide "Request pathogen tests" and make "Add pathogen test" admin-configurable

3 participants