Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
a41c732
Throw exception if ORC order control Id is null
stefpiatek May 16, 2025
f61f73e
Apply linting suggestions
stefpiatek May 16, 2025
9e7ac61
Remove unused imports
stefpiatek May 16, 2025
deb2c83
add admission type to interchange
skeating Aug 10, 2025
567f6d9
add tests for admission type
skeating Aug 10, 2025
e0b5d52
Revert "add tests for admission type"
skeating Aug 10, 2025
a1141cd
Adds admission type to PV1 segment parsing
skeating Aug 10, 2025
5f5d6ad
fixed check style
skeating Aug 11, 2025
9bfe53f
Fixes infinate loop in hl7 parser and adds get admission type to the …
thompson318 Aug 12, 2025
351b16b
Added test (failing) for emap star data sink
thompson318 Aug 12, 2025
6814fc3
Updated HL7 test message to match test
thompson318 Aug 13, 2025
e2c45dc
Add admission type to A01 message for test
thompson318 Aug 13, 2025
ee84638
Add admission type to hospital visit schema
thompson318 Aug 13, 2025
4784d9d
added admission type to admitpatient message and adder to VisitContro…
thompson318 Aug 13, 2025
2924b5c
Merge branch 'develop' into stefpiatek/fix_malformed_hl7
skeating Aug 16, 2025
d5f8401
Merge pull request #91 from SAFEHR-data/stefpiatek/fix_malformed_hl7
skeating Aug 16, 2025
6841a46
Merge remote-tracking branch 'origin/develop' into feature/add_admiss…
skeating Aug 28, 2025
3d7d5aa
See what happens if we remove admissiontype from generic message
thompson318 Sep 4, 2025
26d2fee
Merge branch 'develop' into feature/add_admission_type
thompson318 Sep 24, 2025
d748f9c
Revert "See what happens if we remove admissiontype from generic mess…
thompson318 Sep 24, 2025
60cbc3f
Try adding admission type to other message types
thompson318 Oct 1, 2025
43a408a
Update adt message yaml to reflect that they now contain admission type
thompson318 Oct 2, 2025
0bffc18
Adds hl7 for Z99 message and message factory test
thompson318 Oct 6, 2025
d92806b
Style fix
thompson318 Oct 6, 2025
25fc8ed
Style fix
thompson318 Oct 6, 2025
963ad71
Attemping to get sub speciality change working.
thompson318 Oct 7, 2025
11934d3
Style fixes
thompson318 Oct 7, 2025
810f65f
Style fixes
thompson318 Oct 7, 2025
0167e4e
Fixed missing import
thompson318 Oct 7, 2025
12d21c6
Put the subspeciality controller in same file as pending controller, …
thompson318 Oct 9, 2025
e52832f
Implementing controller
thompson318 Oct 9, 2025
2744830
Fix style
thompson318 Oct 14, 2025
7b22778
Implementation - time search not quite working
thompson318 Oct 14, 2025
a021e84
Add matched movement value to table and populate
thompson318 Oct 15, 2025
237dbc0
style fixes
thompson318 Oct 15, 2025
fe1a713
Update HL7, implement tests, and get last value in list
thompson318 Oct 15, 2025
50a60e5
Add hospital service to data
thompson318 Oct 15, 2025
a15c101
Add in event date time
thompson318 Oct 15, 2025
5cab8c1
Update logic so only changed services are put in table, and update te…
thompson318 Oct 16, 2025
e37d4e7
Added test cases and tidied up test file
thompson318 Oct 16, 2025
d69046f
add new variables
skeating Oct 19, 2025
80ff91d
change check style to allow more than seven arguments to a function
skeating Oct 19, 2025
ffd29fe
Add the variables to the metadata class
skeating Oct 19, 2025
ad22070
fix the location controller to reflect the new metadata values
skeating Oct 19, 2025
32f117e
files for testing hoover
skeating Oct 20, 2025
7ccf9c6
Added a test case for pending transfers after the edit
thompson318 Oct 20, 2025
dddead6
Simplified setting of event time, and updated test
thompson318 Oct 20, 2025
016c315
Adds site location to department metadata
skeating Oct 21, 2025
d9ed94f
fix linting
skeating Oct 21, 2025
6bdf74c
missed the capitalization form
skeating Oct 21, 2025
0aefbf6
update all the comparison messages to include site location
skeating Oct 22, 2025
4eff55f
no I didn't check they are all UCLH
skeating Oct 22, 2025
5e7dd29
Add time check assert to test
thompson318 Oct 23, 2025
f25f2bb
Revert "no I didn't check they are all UCLH"
skeating Oct 25, 2025
e159567
Revert "update all the comparison messages to include site location"
skeating Oct 25, 2025
baaaa0a
take out the site location variable
skeating Oct 25, 2025
d60ad27
add site location as metadata to be red by the hoover
skeating Oct 25, 2025
9492f6e
fix check style
skeating Oct 25, 2025
79767f1
add additional metadata and remove the files I don't need
skeating Oct 28, 2025
b373249
Merge branch 'sk/add_location_metadata' into test_branch_zella
skeating Oct 28, 2025
19f6e81
Merge remote-tracking branch 'origin/feature/add_admission_type' into…
skeating Oct 28, 2025
5e9e40c
Merge remote-tracking branch 'origin/feature/sub_specialty_change' in…
skeating Oct 28, 2025
9f38de2
Reordered variables
skeating Oct 29, 2025
57da9ee
FIX THE VALUES TO MATCH THOSE EXPECTED
skeating Oct 30, 2025
7f1d695
Fixup department queries
stefpiatek Oct 30, 2025
da5c6b5
Merge branch 'sk/add_location_metadata' into test_branch_zella
skeating Oct 30, 2025
3dc603e
Merge branch 'main' of https://github.com/UCLH-DHCT/emap into test_br…
stefpiatek Dec 9, 2025
f58d6d2
Extend planned_movement hospital service fallback to ADT^A01 and ADT^A08
thompson318 Jul 21, 2026
cd814a1
Cancel matching planned_movement row when an admission is cancelled
thompson318 Jul 21, 2026
3f31784
A01 admissions insert their own ADMISSION row instead of editing TRAN…
thompson318 Jul 22, 2026
86eb154
run tests for test_branch_zella
skeating Jul 31, 2026
c44d611
Merge pull request #168 from SAFEHR-data/st/set_location_on_admission
skeating Jul 31, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/emap-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop, sk/waveform-dev ]
branches: [ main, develop, sk/waveform-dev, test_branch_zella ]
repository_dispatch:
permissions:
contents: read
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import uk.ac.ucl.rits.inform.interchange.adt.PendingDischarge;
import uk.ac.ucl.rits.inform.interchange.adt.PendingTransfer;
import uk.ac.ucl.rits.inform.interchange.adt.SwapLocations;
import uk.ac.ucl.rits.inform.interchange.adt.UpdateSubSpeciality;
import uk.ac.ucl.rits.inform.interchange.form.FormMetadataMsg;
import uk.ac.ucl.rits.inform.interchange.form.FormMsg;
import uk.ac.ucl.rits.inform.interchange.form.FormQuestionMetadataMsg;
Expand Down Expand Up @@ -361,5 +362,16 @@ public void processMessage(FormQuestionMetadataMsg msg) {
formProcessor.processQuestionMetadataMessage(msg, storedFrom);
}

/**
* Process an Update Sub Speciality message.
* @param updateSubSpeciality the message
* @throws EmapOperationMessageProcessingException if message could not be processed
*/
@Override
@Transactional
public void processMessage(UpdateSubSpeciality updateSubSpeciality) throws EmapOperationMessageProcessingException {
Instant storedFrom = Instant.now();
adtProcessor.processUpdateSubSpeciality(updateSubSpeciality, storedFrom);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ class DepartmentController {
}

/**
* Get or create minomal department entity.
* Get or create minimal department entity.
* @param msg minimal department message
* @return saved department entity
*/
Expand Down Expand Up @@ -257,7 +257,10 @@ private static boolean updateFieldIfNull(String currentData, String newdata, Con
void processDepartmentStates(DepartmentMetadata msg, Department department, Instant storedFrom) throws IncompatibleDatabaseStateException {
Instant validFrom = msg.getSpecialityUpdate() == null ? msg.getDepartmentContactDate() : msg.getSpecialityUpdate();
DepartmentState currentState = new DepartmentState(
department, msg.getDepartmentRecordStatus().toString(), msg.getDepartmentSpeciality(), validFrom, storedFrom);
department, msg.getDepartmentRecordStatus().toString(),
msg.getDepartmentSpeciality(), msg.getDepartmentType(),
msg.getIsWardOrFlowArea(), msg.getIsCoreInpatientArea(),
msg.getSiteLocation(), validFrom, storedFrom);

if (departmentStateRepo.existsByDepartmentIdAndSpecialityAndValidFrom(department, msg.getDepartmentSpeciality(), validFrom)) {
logger.debug("Department State already exists in the database, no need to process further");
Expand All @@ -274,9 +277,11 @@ void processDepartmentStates(DepartmentMetadata msg, Department department, Inst
invalidatePreviousStateIfChanged(msg.getPreviousDepartmentSpeciality(), currentState, possiblePreviousState.get());
} else if (msg.getPreviousDepartmentSpeciality() != null) {
// if the previous department speciality is not in the database
DepartmentState previousState = new DepartmentState(
department, msg.getDepartmentRecordStatus().toString(), msg.getPreviousDepartmentSpeciality(),
msg.getDepartmentContactDate(), storedFrom);
DepartmentState previousState = new DepartmentState(department,
msg.getDepartmentRecordStatus().toString(),
msg.getPreviousDepartmentSpeciality(), msg.getDepartmentType(),
msg.getIsWardOrFlowArea(), msg.getIsCoreInpatientArea(),
msg.getSiteLocation(), msg.getDepartmentContactDate(), storedFrom);
previousState.setStoredUntil(currentState.getStoredFrom());
previousState.setValidUntil(currentState.getValidFrom());
departmentStateRepo.saveAll(List.of(previousState, currentState));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@
import uk.ac.ucl.rits.inform.informdb.movement.Location;
import uk.ac.ucl.rits.inform.informdb.movement.PlannedMovement;
import uk.ac.ucl.rits.inform.informdb.movement.PlannedMovementAudit;
import uk.ac.ucl.rits.inform.interchange.adt.AdmitPatient;
import uk.ac.ucl.rits.inform.interchange.adt.AdtCancellation;
import uk.ac.ucl.rits.inform.interchange.adt.AdtMessage;
import uk.ac.ucl.rits.inform.interchange.adt.CancelPendingTransfer;
import uk.ac.ucl.rits.inform.interchange.adt.HospitalService;
import uk.ac.ucl.rits.inform.interchange.adt.PendingTransfer;
import uk.ac.ucl.rits.inform.interchange.adt.CancelPendingDischarge;
import uk.ac.ucl.rits.inform.interchange.adt.PendingDischarge;

import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.Optional;


Expand Down Expand Up @@ -174,6 +179,96 @@ public void processMsg(HospitalVisit visit, CancelPendingTransfer msg, Instant v
plannedState.saveEntityOrAuditLogIfRequired(plannedMovementRepo, plannedMovementAuditRepo);
}

/**
* Process a real ADT-triggered change of hospital service/subspeciality, as a fallback for when
* a matching pending transfer request was never sent (e.g. Z99 edits, or a real admission/update
* that carries a hospital service different to the one currently recorded).
* <p>
* The Hl7 feed will eventually be changed so that we have an identifier per pending transfer, until then we guarantee the order of cancellations.
* If we get messages out of order and have several cancellation messages before we receive any requests,
* then the first request message for the location and encounter will add the eventDatetime to the earliest cancellation.
* Subsequent requests will add the eventDatetime to the earliest cancellation with no eventDatetime, or create a new request if none exist
* after the pending request eventDatetime.
* @param visit associated visit
* @param msg the ADT message, for the fields shared by all ADT messages
* @param serviceMsg the same message, as its HospitalService view
* @param validFrom time in the hospital when the message was created
* @param storedFrom time that emap core started processing the message
*/
public void processHospitalServiceFallback(HospitalVisit visit, AdtMessage msg, HospitalService serviceMsg,
Instant validFrom, Instant storedFrom) {
if (serviceMsg.getHospitalService().isUnknown()) {
return;
}

Location fullLocation = null;
if (msg.getFullLocationString().isSave()) {
fullLocation = locationController.getOrCreateLocation(msg.getFullLocationString().get());
}

Instant eventDateTime = msg.getEventOccurredDateTime();

List<PlannedMovement> movements = plannedMovementRepo.findMatchingMovementsForHospitalServiceFallback(visit, fullLocation, eventDateTime);
if (!movements.isEmpty()) {

int mostRecentMoveIndex = movements.size() - 1;
String currentService = movements.get(mostRecentMoveIndex).getHospitalService();
String editedService = serviceMsg.getHospitalService().get();

if (!Objects.equals(currentService, editedService)) {
Long matchedMovementId = movements.get(mostRecentMoveIndex).getPlannedMovementId();
RowState<PlannedMovement, PlannedMovementAudit> plannedState = getOrCreate(
allFromRequest, visit, fullLocation, "EDIT/HOSPITAL_SERVICE_CHANGE", eventDateTime, validFrom, storedFrom
);
PlannedMovement movement = plannedState.getEntity();
// not sure why but event date time isn't being set. Add it here.
plannedState.assignIfDifferent(eventDateTime, movement.getEventDatetime(), movement::setEventDatetime);
plannedState.assignInterchangeValue(serviceMsg.getHospitalService(), movement.getHospitalService(), movement::setHospitalService);
plannedState.assignIfDifferent(matchedMovementId, movement.getMatchedMovementId(), movement::setMatchedMovementId);
plannedState.saveEntityOrAuditLogIfRequired(plannedMovementRepo, plannedMovementAuditRepo);
}
}
}

/**
* Record a real admission as its own planned movement.
* <p>
* Unlike {@link #processHospitalServiceFallback}, this always inserts (or idempotently reuses) an
* ADMISSION row, regardless of whether a matching planned movement already exists or has the same
* hospital service. Any matched prior movement (e.g. a TRANSFER row from a pending transfer request)
* is only referenced via matchedMovementId - it is never itself modified.
* @param visit associated visit
* @param msg the admission message
* @param validFrom time in the hospital when the message was created
* @param storedFrom time that emap core started processing the message
*/
public void processAdmission(HospitalVisit visit, AdmitPatient msg, Instant validFrom, Instant storedFrom) {
if (msg.getHospitalService().isUnknown()) {
return;
}

Location fullLocation = null;
if (msg.getFullLocationString().isSave()) {
fullLocation = locationController.getOrCreateLocation(msg.getFullLocationString().get());
}
Instant eventDateTime = msg.getEventOccurredDateTime();

List<PlannedMovement> priorMovements = plannedMovementRepo.findMatchingMovementsForHospitalServiceFallback(
visit, fullLocation, eventDateTime);
Long matchedMovementId = priorMovements.isEmpty() ? null : priorMovements.get(priorMovements.size() - 1).getPlannedMovementId();

RowState<PlannedMovement, PlannedMovementAudit> plannedState = getOrCreate(
allFromRequest, visit, fullLocation, "ADMISSION", eventDateTime, validFrom, storedFrom
);
PlannedMovement movement = plannedState.getEntity();
plannedState.assignIfDifferent(eventDateTime, movement.getEventDatetime(), movement::setEventDatetime);
plannedState.assignInterchangeValue(msg.getHospitalService(), movement.getHospitalService(), movement::setHospitalService);
if (matchedMovementId != null) {
plannedState.assignIfDifferent(matchedMovementId, movement.getMatchedMovementId(), movement::setMatchedMovementId);
}
plannedState.saveEntityOrAuditLogIfRequired(plannedMovementRepo, plannedMovementAuditRepo);
}

/**
* Process pending ADT cancellation.
* <p>
Expand Down Expand Up @@ -203,6 +298,42 @@ public void processMsg(HospitalVisit visit, CancelPendingDischarge msg, Instant
}


/**
* Cancel the matching ADMISSION row for a cancelled admission.
* <p>
* Only rows created by {@link #processAdmission} (event type ADMISSION) are ever cancelled here.
* A row originating from a real pending transfer/discharge request is left alone, since it
* represents a separately-tracked plan that may still be valid regardless of this one ADT message.
* @param visit associated visit
* @param msg the ADT message, for the fields shared by all ADT messages
* @param cancellation the same message, as its AdtCancellation view
* @param validFrom time in the hospital when the message was created
* @param storedFrom time that emap core started processing the message
*/
public void processAdmissionCancellation(HospitalVisit visit, AdtMessage msg, AdtCancellation cancellation,
Instant validFrom, Instant storedFrom) {
Location fullLocation = null;
if (msg.getFullLocationString().isSave()) {
fullLocation = locationController.getOrCreateLocation(msg.getFullLocationString().get());
}

List<PlannedMovement> movements = plannedMovementRepo.findMatchingMovementsForHospitalServiceFallback(
visit, fullLocation, cancellation.getCancelledDateTime());
if (movements.isEmpty()) {
return;
}

PlannedMovement movement = movements.get(movements.size() - 1);
if (!"ADMISSION".equals(movement.getEventType()) || movement.getCancelledDatetime() != null) {
return;
}

RowState<PlannedMovement, PlannedMovementAudit> plannedState = new RowState<>(movement, validFrom, storedFrom, false);
plannedState.assignIfDifferent(cancellation.getCancelledDateTime(), movement.getCancelledDatetime(), movement::setCancelledDatetime);
plannedState.assignIfDifferent(true, movement.getCancelled(), movement::setCancelled);
plannedState.saveEntityOrAuditLogIfRequired(plannedMovementRepo, plannedMovementAuditRepo);
}

/**
* Delete planned movements from a delete patient information message.
* @param visit Hospital visit that should have their planned movements deleted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ public HospitalVisit getOrCreateMinimalHospitalVisit(
return visit.getEntity();
}

/**
* Look up an existing hospital visit without creating one.
* @param encounter encounter number
* @return the existing visit, or null if none exists for this encounter
*/
public HospitalVisit getHospitalVisitIfExists(final String encounter) {
if (encounter == null || encounter.isEmpty()) {
return null;
}
return hospitalVisitRepo.findByEncounter(encounter).orElse(null);
}

/**
* Get or create minimal hospital visit, and update whether it was created.
* @param encounter encounter number
Expand Down Expand Up @@ -138,7 +150,7 @@ public HospitalVisit updateOrCreateHospitalVisit(
Instant validFrom = msg.bestGuessAtValidFrom();
RowState<HospitalVisit, HospitalVisitAudit> visitState = getOrCreateHospitalVisit(
msg.getVisitNumber(), mrn, msg.getSourceSystem(), validFrom, storedFrom);

addAdmissionType(msg, visitState);
if (visitShouldBeUpdated(validFrom, msg.getSourceSystem(), visitState)) {
updateGenericData(msg, visitState);

Expand Down Expand Up @@ -219,6 +231,17 @@ private void addAdmissionDateTime(final AdmissionDateTime msg, RowState<Hospital
visitState.assignInterchangeValue(msg.getAdmissionDateTime(), visit.getAdmissionDatetime(), visit::setAdmissionDatetime);
}

/**
* Add admission type.
* @param msg AdmissionDateTime
* @param visitState visit wrapped in state class
*/
private void addAdmissionType(final AdtMessage msg, RowState<HospitalVisit, HospitalVisitAudit> visitState) {
HospitalVisit visit = visitState.getEntity();
visitState.assignInterchangeValue(msg.getAdmissionType(), visit.getAdmissionType(), visit::setAdmissionType);
}


/**
* Delete admission specific information.
* @param msg cancellation message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@
import uk.ac.ucl.rits.inform.informdb.identity.HospitalVisit;
import uk.ac.ucl.rits.inform.informdb.identity.Mrn;
import uk.ac.ucl.rits.inform.interchange.EmapOperationMessageProcessingException;
import uk.ac.ucl.rits.inform.interchange.adt.AdmitPatient;
import uk.ac.ucl.rits.inform.interchange.adt.AdtMessage;
import uk.ac.ucl.rits.inform.interchange.adt.CancelAdmitPatient;
import uk.ac.ucl.rits.inform.interchange.adt.CancelPendingDischarge;
import uk.ac.ucl.rits.inform.interchange.adt.CancelPendingTransfer;
import uk.ac.ucl.rits.inform.interchange.adt.ChangePatientIdentifiers;
import uk.ac.ucl.rits.inform.interchange.adt.DeletePersonInformation;
import uk.ac.ucl.rits.inform.interchange.adt.HospitalService;
import uk.ac.ucl.rits.inform.interchange.adt.MergePatient;
import uk.ac.ucl.rits.inform.interchange.adt.MoveVisitInformation;
import uk.ac.ucl.rits.inform.interchange.adt.PendingDischarge;
import uk.ac.ucl.rits.inform.interchange.adt.PendingTransfer;
import uk.ac.ucl.rits.inform.interchange.adt.SwapLocations;
import uk.ac.ucl.rits.inform.interchange.adt.UpdateSubSpeciality;

import java.time.Instant;
import java.util.List;
Expand All @@ -42,11 +46,11 @@ public class AdtProcessor {

/**
* Implicitly wired spring beans.
* @param personController person interactions.
* @param visitController encounter interactions.
* @param patientLocationController location interactions.
* @param pendingAdtController pending ADT interactions.
* @param deletionController cascading deletes for hospital visits.
* @param personController person interactions.
* @param visitController encounter interactions.
* @param patientLocationController location interactions.
* @param pendingAdtController pending ADT interactions.
* @param deletionController cascading deletes for hospital visits.
*/
public AdtProcessor(PersonController personController, VisitController visitController,
PatientLocationController patientLocationController, PendingAdtController pendingAdtController,
Expand All @@ -70,6 +74,18 @@ public void processMessage(final AdtMessage msg, final Instant storedFrom) throw
Instant messageDateTime = msg.bestGuessAtValidFrom();
HospitalVisit visit = processPersonAndVisit(msg, storedFrom, messageDateTime);
patientLocationController.processVisitLocation(visit, msg, storedFrom);
if (msg instanceof AdmitPatient) {
pendingAdtController.processAdmission(visit, (AdmitPatient) msg, messageDateTime, storedFrom);
} else if (msg instanceof HospitalService) {
// UpdatePatientInfo doesn't create a visit of its own, so look up an existing one for the fallback.
HospitalVisit visitForFallback = (visit != null) ? visit : visitController.getHospitalVisitIfExists(msg.getVisitNumber());
if (visitForFallback != null) {
pendingAdtController.processHospitalServiceFallback(visitForFallback, msg, (HospitalService) msg, messageDateTime, storedFrom);
}
}
if (msg instanceof CancelAdmitPatient) {
pendingAdtController.processAdmissionCancellation(visit, msg, (CancelAdmitPatient) msg, messageDateTime, storedFrom);
}
}

private HospitalVisit processPersonAndVisit(AdtMessage msg, Instant storedFrom, Instant validFrom) throws RequiredDataMissingException {
Expand Down Expand Up @@ -244,4 +260,21 @@ public void processPendingAdt(CancelPendingDischarge msg, Instant storedFrom) th
pendingAdtController.processMsg(visit, msg, validFrom, storedFrom);
}


/**
* Process an update subspeciality message.
* <p>
* Updates the sub speciality in the hospital visit table
* @param msg change sub speciality adt message
* @param storedFrom time that emap core started processing the message
* @throws RequiredDataMissingException if the visit number is missing
*/
@Transactional
public void processUpdateSubSpeciality(UpdateSubSpeciality msg, Instant storedFrom) throws RequiredDataMissingException {
Instant validFrom = msg.bestGuessAtValidFrom();
HospitalVisit visit = processPersonAndVisit(msg, storedFrom, validFrom);
// patientLocationController.processVisitLocation(visit, msg, storedFrom);
pendingAdtController.processHospitalServiceFallback(visit, msg, msg, validFrom, storedFrom);
}

}
Loading
Loading