diff --git a/api/pom.xml b/api/pom.xml index 300b8b0f..6a61fe44 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -4,7 +4,7 @@ org.openmrs.module ugandaemrsync - 2.0.1-SNAPSHOT + 2.0.2-SNAPSHOT ugandaemrsync-api diff --git a/api/src/main/java/org/openmrs/module/ugandaemrsync/api/UgandaEMRSyncService.java b/api/src/main/java/org/openmrs/module/ugandaemrsync/api/UgandaEMRSyncService.java index a4e37be7..34cf0348 100644 --- a/api/src/main/java/org/openmrs/module/ugandaemrsync/api/UgandaEMRSyncService.java +++ b/api/src/main/java/org/openmrs/module/ugandaemrsync/api/UgandaEMRSyncService.java @@ -508,10 +508,11 @@ public Encounter addVLToEncounter(String vlQualitative, String vlQuantitative, S public Order getOrderByAccessionNumber(String assessionNumber); - public boolean validateVLFHIRBundle(String bundleJson); + public boolean validateTestFHIRBundle(String bundleJson,String orderConceptUuid); public boolean isValidCPHLBarCode(String accessionNumber); - public String getMissingVLFHIRCodesAsString(String bundleJson); + + public String getMissingVLFHIRCodesAsString(String bundleJson,String orderConceptUuid); public Concept getVLMissingCconcept(String code); diff --git a/api/src/main/java/org/openmrs/module/ugandaemrsync/api/impl/UgandaEMRSyncServiceImpl.java b/api/src/main/java/org/openmrs/module/ugandaemrsync/api/impl/UgandaEMRSyncServiceImpl.java index 865512fb..fbfc4da4 100644 --- a/api/src/main/java/org/openmrs/module/ugandaemrsync/api/impl/UgandaEMRSyncServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/ugandaemrsync/api/impl/UgandaEMRSyncServiceImpl.java @@ -16,6 +16,7 @@ import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.ServiceRequest; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -2325,7 +2326,6 @@ public void sendPrescription() { List drugOrders = generateDrugOrderToOtherSystem(concepts); - try { for (String drugOrderString : drugOrders) { // Send prescription via POST @@ -2339,7 +2339,6 @@ public void sendPrescription() { ); - int responseCode = response.get("responseCode") instanceof Integer ? (int) response.get("responseCode") : Integer.parseInt(response.get("responseCode").toString()); @@ -2433,11 +2432,11 @@ public Map sendSingleViralLoadOrder(Order order) { String payload = processResourceFromOrder(order); if (payload != null) { - if (!validateVLFHIRBundle(payload)) { + if (!validateTestFHIRBundle(payload,order.getConcept().getUuid())) { String missingObsInPayload = String.format( "Order: %s is not valid due to missing %s in the required field", order.getAccessionNumber(), - getMissingVLFHIRCodesAsString(payload) + getMissingVLFHIRCodesAsString(payload,order.getConcept().getUuid()) ); logTransaction(syncTaskType, 500, missingObsInPayload, order.getAccessionNumber(), missingObsInPayload, @@ -2604,14 +2603,14 @@ private Map sendViralLoadToCPHL(SyncTaskType syncTaskType, String payload, Ugand ); if (syncResponse != null) { - String responseFromCPHLServer=String.format("Response From CPHL Server: %s",syncResponse.get("responseMessage")); + String responseFromCPHLServer = String.format("Response From CPHL Server: %s", syncResponse.get("responseMessage")); Map responseType = handleReturnedResponses(order, syncResponse); int responseCode = Integer.parseInt(syncResponse.getOrDefault("responseCode", "500").toString()); // Handle duplicate case with a 400 response if (responseCode == 400 && "Duplicate".equalsIgnoreCase(String.valueOf(responseType.get("responseType")))) { responseCode = 200; - responseFromCPHLServer = String.format("Response From CPHL Server: %s",responseType.get("responseMessage")); + responseFromCPHLServer = String.format("Response From CPHL Server: %s", responseType.get("responseMessage")); } boolean isSuccess = responseCode == 200 || responseCode == 201 || responseCode == 202 || responseCode == 208; @@ -2807,8 +2806,8 @@ private JSONArray getProductCatalogFromEAFYA(SyncTaskType syncTaskType) { return eAFYAProductList; } - public boolean validateVLFHIRBundle(String bundleJson) { - List targetCodes = Arrays.asList(Context.getAdministrationService().getGlobalProperty("ugandaemrsync.viralloadRequiredProgramData").split(",")); + public boolean validateTestFHIRBundle(String bundleJson,String orderConceptUuid) { + List targetCodes = getTargetCodes(orderConceptUuid); Set foundCodes = new HashSet<>(); FhirContext ctx = FhirContext.forR4(); @@ -2835,12 +2834,23 @@ public boolean validateVLFHIRBundle(String bundleJson) { return foundCodes.containsAll(targetCodes); } - public String getMissingVLFHIRCodesAsString(String bundleJson) { - List targetCodes = Arrays.asList( - Context.getAdministrationService() - .getGlobalProperty("ugandaemrsync.viralloadRequiredProgramData") - .split(",") - ); + private List getTargetCodes(String orderConceptUuid) { + List targetCodes = new ArrayList<>(); + String testReferralValidators = Context.getAdministrationService().getGlobalProperty("ugandaemrsync.testReferralValidators"); + JSONObject testReferralValidatorsObject = new JSONObject(testReferralValidators); + + try { + targetCodes = Arrays.asList(testReferralValidatorsObject.getJSONObject("testValidators").getString(orderConceptUuid).split(",")); + } catch (Exception exception) { + log.error(exception.getMessage()); + } + + return targetCodes; + } + + public String getMissingVLFHIRCodesAsString(String bundleJson,String orderConceptUuid) { + List targetCodes = getTargetCodes(orderConceptUuid); + Set foundCodes = new HashSet<>(); FhirContext ctx = FhirContext.forR4(); @@ -2850,30 +2860,30 @@ public String getMissingVLFHIRCodesAsString(String bundleJson) { try { bundle = parser.parseResource(Bundle.class, bundleJson); - for (Bundle.BundleEntryComponent entry : bundle.getEntry()) { - if (entry.getResource() instanceof Observation) { - Observation obs = (Observation) entry.getResource(); - for (Coding coding : obs.getCode().getCoding()) { - if (targetCodes.contains(coding.getCode())) { - foundCodes.add(coding.getCode()); + for (Bundle.BundleEntryComponent entry : bundle.getEntry()) { + if (entry.getResource() instanceof Observation) { + Observation obs = (Observation) entry.getResource(); + for (Coding coding : obs.getCode().getCoding()) { + if (targetCodes.contains(coding.getCode())) { + foundCodes.add(coding.getCode()); + } } } } - } - // Identify missing codes + // Identify missing codes - for (String code : targetCodes) { - if (!foundCodes.contains(code)) { + for (String code : targetCodes) { + if (!foundCodes.contains(code)) { - Concept concept = getVLMissingCconcept(code); - if (concept != null) { - missingCodes.add(concept.getName().getName()); - } else { - missingCodes.add(code); + Concept concept = getVLMissingCconcept(code); + if (concept != null) { + missingCodes.add(concept.getName().getName()); + } else { + missingCodes.add(code); + } } } - } } catch (Exception exception) { log.error(exception); } diff --git a/api/src/main/resources/liquibase.xml b/api/src/main/resources/liquibase.xml index c07172e5..6c466857 100644 --- a/api/src/main/resources/liquibase.xml +++ b/api/src/main/resources/liquibase.xml @@ -2048,4 +2048,22 @@ SELECT patient_identifier_type_id, name, 'http://health.go.ug/cr/nationalid' as url, 1, '2025-04-28 08:43:43', 0, '5e8a626e-3d70-11f0-bcbd-bf35c84f3004' FROM patient_identifier_type WHERE uuid = 'f0c16a6d-dc5f-4118-a803-616d0075d282'; + + + + + + + + + + + + + + + + + + diff --git a/api/src/test/java/org/openmrs/module/ugandaemrsync/server/SyncFHIRRecordTest.java b/api/src/test/java/org/openmrs/module/ugandaemrsync/server/SyncFHIRRecordTest.java index e0fa2a71..6fa191bf 100644 --- a/api/src/test/java/org/openmrs/module/ugandaemrsync/server/SyncFHIRRecordTest.java +++ b/api/src/test/java/org/openmrs/module/ugandaemrsync/server/SyncFHIRRecordTest.java @@ -94,7 +94,8 @@ public void testValidateVLFHIRBundle_withAllRequiredCodes() { bundle.addEntry().setResource(createObservation("202501021")); String json = parser.encodeResourceToString(bundle); - boolean result = ugandaEMRSyncService.validateVLFHIRBundle(json); + boolean result = ugandaEMRSyncService.validateTestFHIRBundle(json,"1eb05918-f50c-4cad-a827-3c78f296a10a"); + Assert.assertTrue(result); } @@ -110,7 +111,8 @@ public void testValidateVLFHIRBundle_missingSomeCodes() { // Missing "868642" String json = parser.encodeResourceToString(bundle); - boolean result = ugandaEMRSyncService.validateVLFHIRBundle(json); + boolean result = ugandaEMRSyncService.validateTestFHIRBundle(json,"1eb05918-f50c-4cad-a827-3c78f296a10a"); + Assert.assertFalse("Payload Missing some data", result); } @@ -135,7 +137,7 @@ public void testGetMissingVLFHIRCodesAsString_withOutMissingCodes() { String json = parser.encodeResourceToString(bundle); - String result = service.getMissingVLFHIRCodesAsString(json); + String result = service.getMissingVLFHIRCodesAsString(json,"1eb05918-f50c-4cad-a827-3c78f296a10a"); Assert.assertEquals("", result); } @@ -159,7 +161,7 @@ public void testGetMissingVLFHIRCodesAsString_withTwoMissingCodes() { String missingConceptName1 = service.getVLMissingCconcept("202501020").getName().getName(); String missingConceptName2 = service.getVLMissingCconcept("202501021").getName().getName(); - String result = service.getMissingVLFHIRCodesAsString(json); + String result = service.getMissingVLFHIRCodesAsString(json,"1eb05918-f50c-4cad-a827-3c78f296a10a"); Assert.assertEquals(String.format("%s,%s", missingConceptName1, missingConceptName2), result); } diff --git a/api/src/test/resources/org/openmrs/module/ugandaemrsync/include/globalPropertiesDataSet.xml b/api/src/test/resources/org/openmrs/module/ugandaemrsync/include/globalPropertiesDataSet.xml index 62478ddb..dddda03e 100644 --- a/api/src/test/resources/org/openmrs/module/ugandaemrsync/include/globalPropertiesDataSet.xml +++ b/api/src/test/resources/org/openmrs/module/ugandaemrsync/include/globalPropertiesDataSet.xml @@ -5,5 +5,6 @@ + diff --git a/omod/pom.xml b/omod/pom.xml index a58f8ba5..e389d3a1 100644 --- a/omod/pom.xml +++ b/omod/pom.xml @@ -3,7 +3,7 @@ org.openmrs.module ugandaemrsync - 2.0.1-SNAPSHOT + 2.0.2-SNAPSHOT ugandaemrsync-omod diff --git a/omod/src/main/java/org/openmrs/module/ugandaemrsync/web/resource/ReferralOrderResource.java b/omod/src/main/java/org/openmrs/module/ugandaemrsync/web/resource/ReferralOrderResource.java index cc512261..86773e69 100644 --- a/omod/src/main/java/org/openmrs/module/ugandaemrsync/web/resource/ReferralOrderResource.java +++ b/omod/src/main/java/org/openmrs/module/ugandaemrsync/web/resource/ReferralOrderResource.java @@ -4,11 +4,15 @@ import io.swagger.models.ModelImpl; import io.swagger.models.properties.RefProperty; import io.swagger.models.properties.StringProperty; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.openmrs.*; import org.openmrs.api.ConceptService; import org.openmrs.api.OrderService; import org.openmrs.api.context.Context; import org.openmrs.module.ugandaemrsync.api.UgandaEMRSyncService; +import org.openmrs.module.ugandaemrsync.api.impl.UgandaEMRSyncServiceImpl; import org.openmrs.module.ugandaemrsync.model.SyncTask; import org.openmrs.module.ugandaemrsync.model.SyncTaskType; import org.openmrs.module.ugandaemrsync.web.resource.DTO.ReferralOrder; @@ -36,6 +40,7 @@ @Resource(name = RestConstants.VERSION_1 + "/referredorders", supportedClass = ReferralOrder.class, supportedOpenmrsVersions = {"1.9.* - 9.*"}) public class ReferralOrderResource extends DelegatingCrudResource { + @Override public ReferralOrder newDelegate() { throw new ResourceDoesNotSupportOperationException("Operation not supported"); @@ -66,6 +71,8 @@ public ReferralOrder getByUniqueId(String uniqueId) { @Override public NeedsPaging doGetAll(RequestContext context) throws ResponseException { + + List concepts = getConceptsFromSyncTaskType(); OrderService orderService = Context.getOrderService(); CareSetting careSetting = orderService.getCareSettingByUuid(CARE_SETTING_UUID_OPD); @@ -74,7 +81,7 @@ public NeedsPaging doGetAll(RequestContext context) throws Respon OrderSearchCriteria orderSearchCriteria = new OrderSearchCriteria( null, careSetting, - Collections.singletonList(Context.getConceptService().getConcept(165412)), + concepts, Collections.singletonList(orderType), null, null, @@ -279,4 +286,58 @@ private Order.FulfillerStatus parseFulfillerStatus(String status) { return Order.FulfillerStatus.IN_PROGRESS; } } + + /** + * Builds a list of Concepts from the SyncTaskType configured for Viral Load sync. + * The SyncTaskType.getDataType() must contain a comma-separated list of Concept IDs, UUIDs, or Names. + * + * @return List of Concepts resolved from the SyncTaskType dataType. + */ + public static List getConceptsFromSyncTaskType() { + + UgandaEMRSyncService syncService = Context.getService(UgandaEMRSyncService.class); + SyncTaskType syncTaskType = syncService.getSyncTaskTypeByUUID(VIRAL_LOAD_SYNC_TYPE_UUID); + + List concepts = new ArrayList<>(); + + // Early exit if syncTaskType or dataType is missing + if (syncTaskType == null) { + //log.warning("SyncTaskType not found for UUID: " + VIRAL_LOAD_SYNC_TYPE_UUID); + return concepts; + } + + String dataType = syncTaskType.getDataType(); + if (StringUtils.isBlank(dataType)) { + //log.warning("SyncTaskType dataType is blank for UUID: " + VIRAL_LOAD_SYNC_TYPE_UUID); + return concepts; + } + + // Parse and resolve each token + for (String token : dataType.split(",")) { + token = token.trim(); + if (token.isEmpty()) { + continue; + } + + Concept concept = null; + if (StringUtils.isNumeric(token)) { + // Concept ID + concept = Context.getConceptService().getConcept(Integer.parseInt(token)); + } else { + // Try UUID, then Name + concept = Context.getConceptService().getConceptByUuid(token); + if (concept == null) { + concept = Context.getConceptService().getConceptByName(token); + } + } + + if (concept != null) { + concepts.add(concept); + } else { + //log.warning("No concept found for token: " + token); + } + } + + return concepts; + } } diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml index 3cc3a10e..6e43c22d 100644 --- a/omod/src/main/resources/config.xml +++ b/omod/src/main/resources/config.xml @@ -425,6 +425,14 @@ The Concept ID for allowable referral orders to be synced to CPHL + + + ugandaemrsync.testReferralValidators + {"validatableTest":["1eb05918-f50c-4cad-a827-3c78f296a10a","163610AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","dc8d4af2-30ab-102d-86b0-7a5022ba4115"],"testValidators":{"1eb05918-f50c-4cad-a827-3c78f296a10a":"413946009,385354005,202501002,LL5723-3,202501009,202501016,202501020,33882-2","163610AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA":"413946009,385354005,202501002,LL5723-3,202501009,202501016,202501020,33882-2","dc8d4af2-30ab-102d-86b0-7a5022ba4115":""}} + + The allowable test order to be referred and their data requirements. + + diff --git a/pom.xml b/pom.xml index 00bf0bd2..0047b2bf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.openmrs.module ugandaemrsync - 2.0.1-SNAPSHOT + 2.0.2-SNAPSHOT pom UgandaemrSync Provides data sharing capabilities from the facility level installation with other systems