Skip to content

Bugfix 13625 add handling person multiple contact info#13872

Merged
KarnaiahPesula merged 7 commits intodevelopmentfrom
bugfix-13625-add_handling_person_multiple_contact_info
Mar 11, 2026
Merged

Bugfix 13625 add handling person multiple contact info#13872
KarnaiahPesula merged 7 commits intodevelopmentfrom
bugfix-13625-add_handling_person_multiple_contact_info

Conversation

@raulbob
Copy link
Contributor

@raulbob raulbob commented Mar 9, 2026

Fixes #13625 - Added handling of person multiple contact email and phone

  • The handling permits merging of multiple contacts for a person
  • Phone/email contacts are added if they do not match the existing ones
  • ExternalMessage's person phone/email are taken as primary contacts while the rest are added as secondary contact info
  • The merge is done by comparing the phone/email with the existing ones
  • Fixed the sequence handling to avoid persisting the person if the external message processing is canceled by the user

Summary by CodeRabbit

  • New Features

    • External messages can include additional person contact details and addresses; guardian and occupation data are also extracted and applied to person records.
  • Improvements

    • Person data (addresses, contacts) is merged, deduplicated, and persisted reliably during case/contact selection and creation flows; primary contact semantics preserved and in-memory changes are saved.
  • Tests

    • New unit and integration tests validate merging, deduplication, primary promotion/demotion, guardian and occupation mapping.

raulbob added 2 commits March 9, 2026 10:52
- The handling permits merging of multiple contacts for a person
- Phone/email contacts are added if they do not match the existing ones
- ExternalMessage's person phone/email are taken as primary contacts while the rest are added as secondary contact info
- The merge is done by comparing the phone/email with the existing ones
- Fixed the sequence handling to avoid persisting the person if the external message processing is canceled by the user
- AutomaticLabMessageProcessorPersonContactTest for automatic processing of person contacts
- ExternalMessageMapperPersonTest for mapping of person contacts
@raulbob raulbob requested a review from KarnaiahPesula March 9, 2026 16:07
@raulbob raulbob linked an issue Mar 9, 2026 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

Adds JSONB fields for additional person contact details and addresses; moves parsing, mapping, deduplication and merge logic into ExternalMessageMapper; removes the old doPersonUpdates hook and invokes mapper-based merge after person selection; updates facades, UI post-update callbacks, lab processing persistence, schema, and tests.

Changes

Cohort / File(s) Summary
DTO / Entity
sormas-api/.../ExternalMessageDto.java, sormas-backend/.../ExternalMessage.java
Added ADDITIONAL_PERSON_CONTACT_DETAILS and ADDITIONAL_PERSON_ADDRESSES constants, JSONB-backed fields and getters/setters on DTO and entity.
Mapper
sormas-api/.../ExternalMessageMapper.java
Introduced ObjectMapper/logger; new public methods including getExternalMessage(), mapAdditionalPersonContactDetails, mapAdditionalPersonAddresses, mapGuardianData, mapOccupationData, mergePersonAddress, mergePersonContactDetails; implements JSON deserialization, deduplication, mapping and merge semantics.
Processing Flows (API)
sormas-api/.../AbstractProcessingFlow.java, sormas-api/.../AbstractMessageProcessingFlowBase.java
Removed doPersonUpdates hook; added mapper calls in person build; added mergePerson and updated pickOrCreatePerson to call it after selection.
Backend Facade & Processor
sormas-backend/.../ExternalMessageFacadeEjb.java, sormas-backend/.../labmessage/AutomaticLabMessageProcessor.java
Propagated new fields in DTO↔entity mapping; ensured person persistence (save) during lab-message case selection/create flows to persist merged person changes.
UI Flows / Helpers
sormas-ui/.../DoctorDeclarationMessageProcessingFlow.java, sormas-ui/.../LabMessageProcessingFlow.java, sormas-ui/.../ExternalMessageProcessingUIHelper.java, sormas-ui/.../AbstractPhysiciansReportProcessingFlow.java
Replaced inline address/contact update logic with centralized mapper-based merge calls and post-update callbacks that reload and save PersonDto; removed obsolete doPersonUpdates override.
Schema / DB
sormas-backend/.../sql/sormas_schema.sql
Schema evolution adding JSONB columns for additional contact/address data (and numerous broader migration changes and new entities/triggers/indices).
Tests
sormas-backend/src/test/.../ExternalMessageMapperPersonTest.java, sormas-backend/src/test/.../AutomaticLabMessageProcessorPersonContactTest.java
Added unit and integration tests validating mapping/merging of contact details, addresses, guardian/occupation data, primary/demotion semantics, deduplication, and persistence during lab-message flows.

Sequence Diagram(s)

sequenceDiagram
    participant ExternalMessage as ExternalMessageDto
    participant Flow as ProcessingFlow
    participant Mapper as ExternalMessageMapper
    participant Facade as PersonFacade/DB

    ExternalMessage->>Flow: receive message
    Flow->>Mapper: buildPerson() / map additional fields
    Mapper->>Mapper: deserialize additionalContactDetails & addresses
    Mapper-->>Flow: PersonDto with mapped/merged suggestions
    Flow->>Facade: mergePerson/save PersonDto (mapper.mergePerson*)
    Facade-->>Flow: persisted PersonDto
    Flow-->>ExternalMessage: processing result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • obinna-h-n
  • KarnaiahPesula

Poem

"I nibble JSON crumbs beneath the moon,
Phones and emails join the data tune,
Guardians, streets and addresses too,
I merge, I dedupe — a floppy-eared crew,
Hooray — the rabbit saved them soon!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and directly related to the main change: adding handling for person multiple contact information in external message processing.
Description check ✅ Passed The description clearly explains the fix, mentions the issue number, and describes the key features: merging multiple contacts, treating message contacts as primary, and fixing sequence handling to avoid unwanted persistence.
Linked Issues check ✅ Passed The PR addresses all coding requirements from issue #13625: transferring multiple phone/email contacts for patients, handling multiple contacts in XML, and managing prescriber contact updates appropriately.
Out of Scope Changes check ✅ Passed The schema migration and test additions are in-scope (supporting new contact handling infrastructure). Database schema additions enable JSONB storage for additional contact details. All changes align with the stated objectives.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix-13625-add_handling_person_multiple_contact_info

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
sormas-backend/src/test/java/de/symeda/sormas/backend/externalmessage/labmessage/AutomaticLabMessageProcessorPersonContactTest.java (1)

149-169: Assert the extra contact count, not just its existence.

This test still passes if +49-extra-mobile gets stored twice. Counting the matching entries here would turn the new-person duplication path into a deterministic regression test.

Diff to tighten the assertion
-        boolean hasExtraContact =
-            persons.get(0).getPersonContactDetails().stream().anyMatch(pcd -> "+49-extra-mobile".equals(pcd.getContactInformation()));
-        assertThat(hasExtraContact, is(true));
+        long extraContactCount = persons.get(0)
+            .getPersonContactDetails()
+            .stream()
+            .filter(pcd -> "+49-extra-mobile".equals(pcd.getContactInformation()))
+            .count();
+        assertThat(extraContactCount, is(1L));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sormas-backend/src/test/java/de/symeda/sormas/backend/externalmessage/labmessage/AutomaticLabMessageProcessorPersonContactTest.java`
around lines 149 - 169, The test testNewPersonAdditionalContactDetailFromJson
currently only asserts existence of a contact with "+49-extra-mobile" which
won't catch duplicates; modify the assertion to count matching
PersonContactDetails instead of using anyMatch. Locate the test method
(testNewPersonAdditionalContactDetailFromJson) and replace the boolean
hasExtraContact logic with a count of
persons.get(0).getPersonContactDetails().stream().filter(pcd ->
"+49-extra-mobile".equals(pcd.getContactInformation())).count() and assert that
the count equals 1 (using assertThat(count, is(1))) so the test fails if the
extra contact is stored more than once.
sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java (1)

183-191: Consider reusing ObjectMapper instance for better performance.

A new ObjectMapper instance is created each time mapAdditionalPersonContactDetails is called. ObjectMapper is thread-safe and expensive to instantiate. Consider making it a static final field.

♻️ Proposed fix

Add a static ObjectMapper field at class level:

 public final class ExternalMessageMapper {

 	private static final Logger logger = LoggerFactory.getLogger(ExternalMessageMapper.class);
+	private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

 	private final ExternalMessageDto externalMessage;

Then update the usages:

-			List<PersonContactDetailDto> additionalDetails =
-				new ObjectMapper().readValue(externalMessage.getAdditionalPersonContactDetails(), new TypeReference<List<PersonContactDetailDto>>() {
-				});
+			List<PersonContactDetailDto> additionalDetails =
+				OBJECT_MAPPER.readValue(externalMessage.getAdditionalPersonContactDetails(), new TypeReference<List<PersonContactDetailDto>>() {
+				});

Apply the same change to mapAdditionalPersonAddresses at line 203-205.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java`
around lines 183 - 191, ExternalMessageMapper instantiates a new ObjectMapper
for each call (in mapAdditionalPersonContactDetails and
mapAdditionalPersonAddresses); create a single shared thread-safe instance by
adding a private static final ObjectMapper (e.g., objectMapper) at class level
and replace the two occurrences of new ObjectMapper() in
mapAdditionalPersonContactDetails and mapAdditionalPersonAddresses with this
static objectMapper to improve performance.
sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractMessageProcessingFlowBase.java (1)

1065-1070: Truncated comment on line 1066.

The comment appears to be cut off: "// no merges for new person but we still need to update the additional conta". This should be completed for clarity.

📝 Suggested fix
 		if (personSelection.isNew()) {
-			// no merges for new person but we still need to update the additional conta
+			// no merges for new person but we still need to update the additional contact details and addresses
 			getMapper().mapAdditionalPersonAddresses(person);
 			getMapper().mapAdditionalPersonContactDetails(person);
 			return;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractMessageProcessingFlowBase.java`
around lines 1065 - 1070, The inline comment in
AbstractMessageProcessingFlowBase near the personSelection.isNew() branch is
truncated ("// no merges for new person but we still need to update the
additional conta"); update that comment to a complete, clear sentence explaining
behavior for new persons (e.g., "// no merges for new person but we still need
to update the additional contact details and addresses via the mapper"), placing
it immediately above the if (personSelection.isNew()) block and referencing the
mapper calls (getMapper().mapAdditionalPersonAddresses and
getMapper().mapAdditionalPersonContactDetails) so future readers understand why
the method returns early.
sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java (1)

384-390: Name splitting logic may produce unexpected results for certain name formats.

The hyphen-based name splitting assumes the reporter name format is FirstName-LastName. However:

  • Names like "Jean-Pierre Dupont" would incorrectly split as firstName="Jean" and lastName="Pierre Dupont"
  • Names without hyphens are not handled (the contains("-") check passes, but edge cases within the split could occur)
  • Line 389: When nameParts.length > 0 is checked but nameParts.length - 1 could be 0 if there's only one part after split

Consider documenting the expected input format or adding defensive handling.

📝 Suggested improvement for edge case handling
 		if (externalMessage.getReporterName() != null && externalMessage.getReporterName().contains("-")) {
 			// Split the reporter name into first and last names if it contains a hyphen
-			// Some names may already contain hyphens, assume first parts are the first name and last parts are the last name
+			// Format expected: "FirstName-LastName" where hyphen is the delimiter
+			// For compound names like "Jean-Pierre-Dupont", all but the last segment become firstName
 			final String[] nameParts = externalMessage.getReporterName().split("-");
-			notifierDto.setAgentFirstName(Arrays.stream(nameParts).limit(nameParts.length - 1).map(String::trim).collect(Collectors.joining(" ")));
-			notifierDto.setAgentLastName(nameParts.length > 0 ? nameParts[nameParts.length - 1].trim() : "");
+			if (nameParts.length >= 2) {
+				notifierDto.setAgentFirstName(Arrays.stream(nameParts).limit(nameParts.length - 1).map(String::trim).collect(Collectors.joining(" ")));
+				notifierDto.setAgentLastName(nameParts[nameParts.length - 1].trim());
+			} else if (nameParts.length == 1) {
+				notifierDto.setAgentLastName(nameParts[0].trim());
+			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java`
around lines 384 - 390, The current hyphen-based parsing in
DoctorDeclarationMessageProcessingFlow can mis-split names (e.g., "Jean-Pierre
Dupont") and doesn't handle single-part results safely; replace the block that
reads externalMessage.getReporterName() and sets notifierDto to robust parsing:
first trim the reporterName, if empty set both names empty; otherwise split on
whitespace to get tokens and treat the last token as agentLastName and all
preceding tokens joined as agentFirstName (this preserves hyphenated given names
like "Jean-Pierre"); if the name contains no whitespace but contains a single
hyphen still use the same rule (split into parts, last part = last name, rest =
first name) and ensure you guard against nameParts.length == 1 by placing the
whole string into agentFirstName and leaving agentLastName empty; always trim
parts and document the expected input format in a comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractProcessingFlow.java`:
- Around line 180-181: The buildPerson() method currently calls
mapper.mapAdditionalPersonContactDetails(...) and
mapper.mapAdditionalPersonAddresses(...) which causes duplicate add-only
mappings because AbstractMessageProcessingFlowBase.mergePerson(...) also invokes
those when personSelection.isNew(); remove the calls from buildPerson() (leave
mergePerson(...) responsible for applying add-only mappings for new persons) so
additional contacts/addresses are only mapped once, or alternatively make the
mapper methods idempotent if you prefer to keep them in buildPerson().

In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java`:
- Around line 401-413: The new PersonContactDetailDto instances created in
ExternalMessageMapper (the personContactDetail for phone and the analogous email
contact block) are missing UUIDs; mirror the approach used in mapGuardianData by
calling setUuid(DataHelper.createUuid()) on the newly created
PersonContactDetailDto before adding it to personContactDetails so persistence
and entity tracking receive a proper UUID (locate the personContactDetail and
email contact creation blocks in ExternalMessageMapper and add
setUuid(DataHelper.createUuid()) to each new PersonContactDetailDto).
- Around line 326-331: When personAddress is null you create a new LocationDto
via LocationDto.build() and call mapToLocation(location) but never assign that
LocationDto back to the person, so the value is lost; fix by assigning the newly
created LocationDto to the person's address field (e.g., set personAddress or
call person.setAddress(location)) before returning mapToLocation(location) so
the person retains the new address; ensure you update the same variable used
elsewhere (personAddress) or call the appropriate setter on the person object.

In `@sormas-backend/src/main/resources/sql/sormas_schema.sql`:
- Line 15267: The schema_version INSERT currently records the wrong issue
number; update the INSERT INTO schema_version (version_number, comment) VALUES
(611, '...') so the comment references "#13625 - Multiple person contacts and
addresses" instead of "#13754" to match the PR and SQL comment; locate the
VALUES row for version_number 611 and replace the issue number in its comment
field accordingly.

In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/labmessage/LabMessageProcessingFlow.java`:
- Around line 144-164: The existing-case path currently skips persisting the
in-memory result of mergePerson(...) and thus loses merged address/contact
updates; update the selected-case flow (same place where postUpdateCallback is
used for create-case) to load the merged PersonDto and persist it before
finishing: call FacadeProvider.getPersonFacade().getByUuid(...) to reload the
person and then
ExternalMessageProcessingUIHelper.updateAddressAndSavePerson(processedPerson,
getMapper()) (or the equivalent save used in
doCaseSelectedFlow/AutomaticLabMessageProcessor) so that merged changes are
saved when an existing case is selected.

---

Nitpick comments:
In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractMessageProcessingFlowBase.java`:
- Around line 1065-1070: The inline comment in AbstractMessageProcessingFlowBase
near the personSelection.isNew() branch is truncated ("// no merges for new
person but we still need to update the additional conta"); update that comment
to a complete, clear sentence explaining behavior for new persons (e.g., "// no
merges for new person but we still need to update the additional contact details
and addresses via the mapper"), placing it immediately above the if
(personSelection.isNew()) block and referencing the mapper calls
(getMapper().mapAdditionalPersonAddresses and
getMapper().mapAdditionalPersonContactDetails) so future readers understand why
the method returns early.

In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java`:
- Around line 183-191: ExternalMessageMapper instantiates a new ObjectMapper for
each call (in mapAdditionalPersonContactDetails and
mapAdditionalPersonAddresses); create a single shared thread-safe instance by
adding a private static final ObjectMapper (e.g., objectMapper) at class level
and replace the two occurrences of new ObjectMapper() in
mapAdditionalPersonContactDetails and mapAdditionalPersonAddresses with this
static objectMapper to improve performance.

In
`@sormas-backend/src/test/java/de/symeda/sormas/backend/externalmessage/labmessage/AutomaticLabMessageProcessorPersonContactTest.java`:
- Around line 149-169: The test testNewPersonAdditionalContactDetailFromJson
currently only asserts existence of a contact with "+49-extra-mobile" which
won't catch duplicates; modify the assertion to count matching
PersonContactDetails instead of using anyMatch. Locate the test method
(testNewPersonAdditionalContactDetailFromJson) and replace the boolean
hasExtraContact logic with a count of
persons.get(0).getPersonContactDetails().stream().filter(pcd ->
"+49-extra-mobile".equals(pcd.getContactInformation())).count() and assert that
the count equals 1 (using assertThat(count, is(1))) so the test fails if the
extra contact is stored more than once.

In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java`:
- Around line 384-390: The current hyphen-based parsing in
DoctorDeclarationMessageProcessingFlow can mis-split names (e.g., "Jean-Pierre
Dupont") and doesn't handle single-part results safely; replace the block that
reads externalMessage.getReporterName() and sets notifierDto to robust parsing:
first trim the reporterName, if empty set both names empty; otherwise split on
whitespace to get tokens and treat the last token as agentLastName and all
preceding tokens joined as agentFirstName (this preserves hyphenated given names
like "Jean-Pierre"); if the name contains no whitespace but contains a single
hyphen still use the same rule (split into parts, last part = last name, rest =
first name) and ensure you guard against nameParts.length == 1 by placing the
whole string into agentFirstName and leaving agentLastName empty; always trim
parts and document the expected input format in a comment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2857dc86-3cba-41c7-8d9e-6b3ac30dd00a

📥 Commits

Reviewing files that changed from the base of the PR and between 2b09617 and 35aff93.

📒 Files selected for processing (14)
  • sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/ExternalMessageDto.java
  • sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractMessageProcessingFlowBase.java
  • sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractProcessingFlow.java
  • sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessage.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/ExternalMessageFacadeEjb.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/externalmessage/labmessage/AutomaticLabMessageProcessor.java
  • sormas-backend/src/main/resources/sql/sormas_schema.sql
  • sormas-backend/src/test/java/de/symeda/sormas/backend/externalmessage/labmessage/AutomaticLabMessageProcessorPersonContactTest.java
  • sormas-backend/src/test/java/de/symeda/sormas/backend/externalmessage/processing/ExternalMessageMapperPersonTest.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/labmessage/LabMessageProcessingFlow.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/physiciansreport/AbstractPhysiciansReportProcessingFlow.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/processing/ExternalMessageProcessingUIHelper.java
💤 Files with no reviewable changes (1)
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/physiciansreport/AbstractPhysiciansReportProcessingFlow.java

@sormas-vitagroup
Copy link
Contributor

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java (2)

183-186: Consider reusing ObjectMapper as a static field.

A new ObjectMapper is instantiated for each deserialization call. Since ObjectMapper is thread-safe after configuration, consider extracting it to a private static final field to avoid repeated instantiation overhead.

♻️ Proposed refactor
 public final class ExternalMessageMapper {

 	private static final Logger logger = LoggerFactory.getLogger(ExternalMessageMapper.class);
+	private static final ObjectMapper JSON_MAPPER = new ObjectMapper();

 	private final ExternalMessageDto externalMessage;

Then replace:

-			new ObjectMapper().readValue(externalMessage.getAdditionalPersonContactDetails(), new TypeReference<List<PersonContactDetailDto>>() {
+			JSON_MAPPER.readValue(externalMessage.getAdditionalPersonContactDetails(), new TypeReference<List<PersonContactDetailDto>>() {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java`
around lines 183 - 186, The code repeatedly instantiates a new ObjectMapper when
deserializing additionalPersonContactDetails in ExternalMessageMapper; extract a
single shared instance by adding a private static final ObjectMapper (e.g.,
private static final ObjectMapper OBJECT_MAPPER) to the ExternalMessageMapper
class, initialize/configure it once, and replace new ObjectMapper() usages (the
call that produces List<PersonContactDetailDto> additionalDetails via readValue)
with OBJECT_MAPPER to avoid repeated instantiation and leverage ObjectMapper's
thread-safety.

355-358: Minor: Always returns ADDRESS as changed, even when no fields were modified.

The method returns PersonDto.ADDRESS in the changed fields list regardless of whether any address fields were actually updated (e.g., when all external message address fields are null). This differs from other methods that return an empty list when nothing changes. While not incorrect, it may trigger unnecessary UI updates.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java`
around lines 355 - 358, The method in ExternalMessageMapper currently always
returns Collections.singletonList(new String[] { PersonDto.ADDRESS }), which
causes ADDRESS to be reported changed even when no address data was provided;
change the logic so PersonDto.ADDRESS is only included when at least one address
subfield from the external message is present and actually differs from the
target Person's address (or when any external address field is non-null
indicating a change), otherwise return an empty list (Collections.emptyList()).
Locate the return that builds the singleton with PersonDto.ADDRESS and replace
it with a conditional that inspects the relevant external message address fields
and compares them to the existing Person/DTO address values before adding
PersonDto.ADDRESS to the changed-fields list.
sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java (1)

409-415: Name-splitting heuristic may produce unexpected results for compound surnames.

The logic splits reporter names by hyphen, treating all parts except the last as the first name. For names like "Jean-Pierre Dupont-Martin", this would yield firstName="Jean Pierre Dupont" and lastName="Martin", which may not match the intended split.

Given the comment acknowledges this limitation, the heuristic seems intentional. Consider documenting expected input format if the source system has a known convention.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java`
around lines 409 - 415, The current hyphen-based name split in
DoctorDeclarationMessageProcessingFlow (externalMessage.getReporterName(),
notifierDto.setAgentFirstName, notifierDto.setAgentLastName) can mis-handle
compound surnames; either replace the heuristic with a safer rule (e.g. split on
the last hyphen only by using lastIndexOf('-') to take substring(0,lastIndex) as
first name and substring(lastIndex+1) as last name) or, if the source has a
known convention, add a clear code comment and Javadoc documenting the expected
reporterName format and add a defensive log/warning when multiple hyphens are
detected so downstream users are aware; update the block around
externalMessage.getReporterName() accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractMessageProcessingFlowBase.java`:
- Around line 1059-1063: The mergePerson(EntitySelection<PersonDto>
personSelection) method risks an NPE because it dereferences personSelection
without checking it; update mergePerson to first check if personSelection is
null and return early (e.g., if (personSelection == null) return;), then proceed
to call personSelection.getEntity() and the existing null check on the
PersonDto; this change applies to the mergePerson method in
AbstractMessageProcessingFlowBase to safely handle
result.getData().getSelectedPerson() possibly being null.

In `@sormas-backend/src/main/resources/sql/sormas_schema.sql`:
- Around line 15261-15265: Update the two new JSON fields
additionalPersonContactDetails and additionalPersonAddresses in the
ExternalMessage entity to use the project-standard JSON mapping: replace any
`@Type`(type = "jsonb") with `@Type`(type = ModelConstants.HIBERNATE_TYPE_JSON) and
replace `@Column`(columnDefinition = "jsonb") with `@Column`(columnDefinition =
ModelConstants.COLUMN_DEFINITION_JSON) in ExternalMessage.java; also update the
SQL schema statements in sormas_schema.sql to use "json" instead of "jsonb" for
these columns so the DB column type matches the entity mappings.

In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/labmessage/LabMessageProcessingFlow.java`:
- Around line 260-264: Update the misleading block comment in
LabMessageProcessingFlow (around the contact re-load logic) to reference
"contact form changes" instead of "case form changes" so it accurately describes
why the person is reloaded; keep the rest of the comment intact and ensure it
still points to ContactController#getContactCreateComponent and the surrounding
rationale for re-fetching the person after automatic processing.

---

Nitpick comments:
In
`@sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java`:
- Around line 183-186: The code repeatedly instantiates a new ObjectMapper when
deserializing additionalPersonContactDetails in ExternalMessageMapper; extract a
single shared instance by adding a private static final ObjectMapper (e.g.,
private static final ObjectMapper OBJECT_MAPPER) to the ExternalMessageMapper
class, initialize/configure it once, and replace new ObjectMapper() usages (the
call that produces List<PersonContactDetailDto> additionalDetails via readValue)
with OBJECT_MAPPER to avoid repeated instantiation and leverage ObjectMapper's
thread-safety.
- Around line 355-358: The method in ExternalMessageMapper currently always
returns Collections.singletonList(new String[] { PersonDto.ADDRESS }), which
causes ADDRESS to be reported changed even when no address data was provided;
change the logic so PersonDto.ADDRESS is only included when at least one address
subfield from the external message is present and actually differs from the
target Person's address (or when any external address field is non-null
indicating a change), otherwise return an empty list (Collections.emptyList()).
Locate the return that builds the singleton with PersonDto.ADDRESS and replace
it with a conditional that inspects the relevant external message address fields
and compares them to the existing Person/DTO address values before adding
PersonDto.ADDRESS to the changed-fields list.

In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java`:
- Around line 409-415: The current hyphen-based name split in
DoctorDeclarationMessageProcessingFlow (externalMessage.getReporterName(),
notifierDto.setAgentFirstName, notifierDto.setAgentLastName) can mis-handle
compound surnames; either replace the heuristic with a safer rule (e.g. split on
the last hyphen only by using lastIndexOf('-') to take substring(0,lastIndex) as
first name and substring(lastIndex+1) as last name) or, if the source has a
known convention, add a clear code comment and Javadoc documenting the expected
reporterName format and add a defensive log/warning when multiple hyphens are
detected so downstream users are aware; update the block around
externalMessage.getReporterName() accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b7f63ee8-01b3-4f0b-ab5c-f0e3c6ba7b48

📥 Commits

Reviewing files that changed from the base of the PR and between 35aff93 and f9ca77e.

📒 Files selected for processing (5)
  • sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractMessageProcessingFlowBase.java
  • sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/ExternalMessageMapper.java
  • sormas-backend/src/main/resources/sql/sormas_schema.sql
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/labmessage/LabMessageProcessingFlow.java

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/caze/notifier/CaseNotifierSideViewController.java`:
- Around line 347-355: The code in CaseNotifierSideViewController
unconditionally overwrites surveillanceReport.reportDate from
notifierForm.getNotificationDate(), but CaseNotifierForm.populateFields always
seeds notificationDateField so the else branch is dead and existing timestamps
get reset; change the logic to only call surveillanceReport.setReportDate(...)
if the user actually changed the notification date (e.g., check a dirty/modified
flag on the notificationDateField or compare notifierForm.getNotificationDate()
to the original surveillanceReport.getReportDate() date portion and only update
when they differ, or when surveillanceReport.getReportDate() is null), ensuring
you reference notifierForm.getNotificationDate(),
CaseNotifierForm.populateFields, surveillanceReport.getReportDate(), and
surveillanceReport.setReportDate() when locating where to apply this guard.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 30375ce0-b76e-4f87-92d2-117965174ca3

📥 Commits

Reviewing files that changed from the base of the PR and between f9ca77e and 138a191.

📒 Files selected for processing (1)
  • sormas-ui/src/main/java/de/symeda/sormas/ui/caze/notifier/CaseNotifierSideViewController.java

@sormas-vitagroup
Copy link
Contributor

2 similar comments
@sormas-vitagroup
Copy link
Contributor

@sormas-vitagroup
Copy link
Contributor

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/labmessage/LabMessageProcessingFlow.java (1)

254-259: Drop the blame from this inline comment.

The controller rationale is useful, but the last sentence reads like a commit aside rather than durable source documentation.

✏️ Suggested cleanup
 			// we need to call again the updates here
 			// compared to automatic processing the person is processed by the contact controller
 			// `@see`{ContactController#getContactCreateComponent} methods
 			// we need to load the person again from the database to get the updates following contact form changes
-			// ofc. here we have another way to do it because whoever did it was too lazy to fix the contact controller
 			final ContactDto processedContact = contactCreateComponent.getWrappedComponent().getValue();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/labmessage/LabMessageProcessingFlow.java`
around lines 254 - 259, The inline comment in LabMessageProcessingFlow.java
contains an unprofessional/blameful sentence; edit the comment above the line
that reads final ContactDto processedContact =
contactCreateComponent.getWrappedComponent().getValue(); to keep the useful
rationale (why the person must be reloaded and the reference to
ContactController#getContactCreateComponent) but remove the last sentence that
blames the original author — make the comment concise and factual about
reloading the person for contact form updates and why the additional update call
is required.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/labmessage/LabMessageProcessingFlow.java`:
- Around line 254-259: The inline comment in LabMessageProcessingFlow.java
contains an unprofessional/blameful sentence; edit the comment above the line
that reads final ContactDto processedContact =
contactCreateComponent.getWrappedComponent().getValue(); to keep the useful
rationale (why the person must be reloaded and the reference to
ContactController#getContactCreateComponent) but remove the last sentence that
blames the original author — make the comment concise and factual about
reloading the person for contact form updates and why the additional update call
is required.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58a6784b-4488-4a82-9b41-60b16add132d

📥 Commits

Reviewing files that changed from the base of the PR and between 138a191 and 2d2417d.

📒 Files selected for processing (4)
  • sormas-api/src/main/java/de/symeda/sormas/api/externalmessage/processing/AbstractMessageProcessingFlowBase.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/doctordeclaration/DoctorDeclarationMessageProcessingFlow.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/labmessage/LabMessageProcessingFlow.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/externalmessage/processing/ExternalMessageProcessingUIHelper.java

@sormas-vitagroup
Copy link
Contributor

@KarnaiahPesula KarnaiahPesula merged commit 8fac782 into development Mar 11, 2026
8 of 14 checks passed
@KarnaiahPesula KarnaiahPesula deleted the bugfix-13625-add_handling_person_multiple_contact_info branch March 11, 2026 07:56
@raulbob raulbob restored the bugfix-13625-add_handling_person_multiple_contact_info branch March 11, 2026 08:54
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.

Error when parsing contact data in external messages

3 participants