From 093382200cd4996a603a688bfac32ddb29b71d7e Mon Sep 17 00:00:00 2001 From: Patrick Waweru Date: Mon, 30 Sep 2024 10:29:22 +0300 Subject: [PATCH] Added RMS integration for patient registration, bills and payments --- .../advice/NewBillCreationSyncToRMS.java | 310 +++++++++++++++++ .../advice/NewBillPaymentSyncToRMS.java | 329 ++++++++++++++++++ .../NewPatientRegistrationSyncToRMS.java | 324 +++++++++++++++++ .../kenyaemr/cashier/api/IBillService.java | 6 + .../api/base/entity/IObjectDataService.java | 5 + .../db/hibernate/BaseHibernateRepository.java | 4 + .../BaseHibernateRepositoryImpl.java | 51 +++ .../impl/BaseObjectDataServiceImpl.java | 13 + .../cashier/api/impl/BillServiceImpl.java | 13 + .../kenyaemr/cashier/api/model/Bill.java | 3 +- .../cashier/api/util/AdviceUtils.java | 154 ++++++++ .../api/util/CashierModuleConstants.java | 11 + .../resources/moduleApplicationContext.xml | 20 +- .../cashier/rest/resource/BillResource.java | 4 +- omod/src/main/resources/config.xml | 35 ++ 15 files changed, 1278 insertions(+), 4 deletions(-) create mode 100644 api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewBillCreationSyncToRMS.java create mode 100644 api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewBillPaymentSyncToRMS.java create mode 100644 api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewPatientRegistrationSyncToRMS.java create mode 100644 api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/util/AdviceUtils.java diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewBillCreationSyncToRMS.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewBillCreationSyncToRMS.java new file mode 100644 index 0000000..eae8757 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewBillCreationSyncToRMS.java @@ -0,0 +1,310 @@ +package org.openmrs.module.kenyaemr.cashier.advice; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import javax.net.ssl.HttpsURLConnection; +import javax.validation.constraints.NotNull; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.openmrs.api.context.Context; +import org.openmrs.module.kenyaemr.cashier.api.model.Bill; +import org.openmrs.module.kenyaemr.cashier.api.model.BillLineItem; +import org.openmrs.module.kenyaemr.cashier.api.util.AdviceUtils; +import org.openmrs.module.kenyaemr.cashier.util.Utils; +import org.openmrs.ui.framework.SimpleObject; +import org.springframework.aop.AfterReturningAdvice; + +/** + * Detects when a new bill has been created and syncs to RMS Financial System + */ +public class NewBillCreationSyncToRMS implements AfterReturningAdvice { + + private Boolean debugMode = false; + + @Override + public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { + try { + debugMode = AdviceUtils.isRMSLoggingEnabled(); + if(AdviceUtils.isRMSIntegrationEnabled()) { + if (method.getName().equals("save") && args.length > 0 && args[0] instanceof Bill) { + Bill bill = (Bill) args[0]; + + if (bill == null) { + return; + } + + Date billCreationDate = bill.getDateCreated(); + if(debugMode) System.out.println("RMS Sync Cashier Module: bill was created on: " + billCreationDate); + + if (billCreationDate != null && AdviceUtils.checkIfCreateModetOrEditMode(billCreationDate)) { + // CREATE Mode + if(debugMode) System.out.println("RMS Sync Cashier Module: New Bill being created"); + // Use a thread to send the data. This frees up the frontend to proceed + syncBillRunnable runner = new syncBillRunnable(bill); + Thread thread = new Thread(runner); + thread.start(); + } else { + // EDIT Mode + if(debugMode) System.out.println("RMS Sync Cashier Module: Bill being edited. We ignore"); + } + } + } + } catch(Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error getting new Bill: " + ex.getMessage()); + ex.printStackTrace(); + } + } + + private String prepareNewBillRMSPayload(@NotNull Bill bill) { + String ret = ""; + + try { + Context.openSession(); + if (bill != null) { + if(debugMode) System.out.println( + "RMS Sync Cashier Module: New bill created: UUID" + bill.getUuid() + ", Total: " + bill.getTotal()); + SimpleObject payloadPrep = new SimpleObject(); + payloadPrep.put("bill_reference", bill.getUuid()); + payloadPrep.put("total_cost", bill.getTotal()); + payloadPrep.put("hospital_code", Utils.getDefaultLocationMflCode(null)); + payloadPrep.put("patient_id", bill.getPatient().getUuid()); + List items = new LinkedList<>(); + List billItems = bill.getLineItems(); + for(BillLineItem billLineItem : billItems) { + SimpleObject itemsPayload = new SimpleObject(); + if(billLineItem.getBillableService() != null) { + itemsPayload.put("service_code", "3f500af5-3139-45b0-ab47-57f9c504f92d"); + itemsPayload.put("service_name", "service"); + } else if(billLineItem.getItem() != null) { + itemsPayload.put("service_code", "a3dd3be8-05c5-425e-8e08-6765f6a50b76"); + itemsPayload.put("service_name", "stock_item"); + } else { + itemsPayload.put("service_code", ""); + itemsPayload.put("service_name", ""); + } + itemsPayload.put("unique_id", billLineItem.getUuid()); + itemsPayload.put("bill_id", bill.getUuid()); + itemsPayload.put("quantity", billLineItem.getQuantity()); + itemsPayload.put("price", billLineItem.getPrice()); + itemsPayload.put("excempted", "no"); + + items.add(itemsPayload); + } + payloadPrep.put("bill_items", items); + ret = payloadPrep.toJson(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got bill details: " + ret); + } else { + if(debugMode) System.out.println("RMS Sync Cashier Module: bill is null"); + } + } catch (Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error getting new bill payload: " + ex.getMessage()); + ex.printStackTrace(); + } finally { + Context.closeSession(); + } + + return (ret); + } + + /** + * Send the new bill payload to RMS + * @param patient + * @return + */ + private Boolean sendRMSNewBill(@NotNull Bill bill) { + Boolean ret = false; + String payload = prepareNewBillRMSPayload(bill); + + HttpsURLConnection con = null; + HttpsURLConnection connection = null; + try { + if(debugMode) System.out.println("RMS Sync Cashier Module: using bill payload: " + payload); + + // Create URL + String baseURL = AdviceUtils.getRMSEndpointURL(); + String completeURL = baseURL + "/login"; + if(debugMode) System.out.println("RMS Sync Cashier Module: Auth URL: " + completeURL); + URL url = new URL(completeURL); + String rmsUser = AdviceUtils.getRMSAuthUserName(); + String rmsPassword = AdviceUtils.getRMSAuthPassword(); + SimpleObject authPayloadCreator = SimpleObject.create("email", rmsUser != null ? rmsUser : "", "password", + rmsPassword != null ? rmsPassword : ""); + String authPayload = authPayloadCreator.toJson(); + + // Get token + con = (HttpsURLConnection) url.openConnection(); + con.setRequestMethod("POST"); + con.setDoOutput(true); + con.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + con.setRequestProperty("Accept", "application/json"); + con.setConnectTimeout(10000); // set timeout to 10 seconds + + PrintStream os = new PrintStream(con.getOutputStream()); + os.print(authPayload); + os.close(); + + int responseCode = con.getResponseCode(); + + if (responseCode == HttpURLConnection.HTTP_OK) { //success + BufferedReader in = null; + in = new BufferedReader(new InputStreamReader(con.getInputStream())); + + String input; + StringBuffer response = new StringBuffer(); + + while ((input = in.readLine()) != null) { + response.append(input); + } + in.close(); + + String returnResponse = response.toString(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got Auth Response as: " + returnResponse); + + // Extract the token and token expiry date + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = null; + String token = ""; + String expires_at = ""; + SimpleObject authObj = new SimpleObject(); + + try { + jsonNode = mapper.readTree(returnResponse); + if (jsonNode != null) { + token = jsonNode.get("token") == null ? "" : jsonNode.get("token").getTextValue(); + authObj.put("token", token); + expires_at = jsonNode.get("expires_at") == null ? "" : jsonNode.get("expires_at").getTextValue(); + authObj.put("expires_at", expires_at); + } + } + catch (Exception e) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error getting auth token: " + e.getMessage()); + e.printStackTrace(); + } + + if (!token.isEmpty()) { + try { + // We send the payload to RMS + if(debugMode) System.err.println( + "RMS Sync Cashier Module: We got the Auth token. Now sending the new bill details. Token: " + + token); + String finalUrl = baseURL + "/create-bill"; + if(debugMode) System.out.println("RMS Sync Cashier Module: Final Create Bill URL: " + finalUrl); + URL finUrl = new URL(finalUrl); + + connection = (HttpsURLConnection) finUrl.openConnection(); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + connection.setRequestProperty("Authorization", "Bearer " + token); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setConnectTimeout(10000); + + PrintStream pos = new PrintStream(connection.getOutputStream()); + pos.print(payload); + pos.close(); + + int finalResponseCode = connection.getResponseCode(); + + if (finalResponseCode == HttpURLConnection.HTTP_OK) { //success + BufferedReader fin = null; + fin = new BufferedReader(new InputStreamReader(connection.getInputStream())); + + String finalOutput; + StringBuffer finalResponse = new StringBuffer(); + + while ((finalOutput = fin.readLine()) != null) { + finalResponse.append(finalOutput); + } + in.close(); + + String finalReturnResponse = finalResponse.toString(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got New Bill Response as: " + finalReturnResponse); + + ObjectMapper finalMapper = new ObjectMapper(); + JsonNode finaljsonNode = null; + Boolean success = false; + String message = ""; + + try { + finaljsonNode = finalMapper.readTree(finalReturnResponse); + if (finaljsonNode != null) { + success = finaljsonNode.get("success") == null ? false + : finaljsonNode.get("success").getBooleanValue(); + message = finaljsonNode.get("message") == null ? "" + : finaljsonNode.get("message").getTextValue(); + } + + if(debugMode) System.err.println("RMS Sync Cashier Module: Got New Bill final response: success: " + success + + " message: " + message); + } + catch (Exception e) { + if(debugMode) System.err.println( + "RMS Sync Cashier Module: Error getting New Bill final response: " + e.getMessage()); + e.printStackTrace(); + } + + if (success != null && success == true) { + ret = true; + } + + } else { + if(debugMode) System.err.println("RMS Sync Cashier Module: Failed to send New Bill final payload: " + finalResponseCode); + } + } + catch (Exception em) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to send the New Bill final payload: " + em.getMessage()); + em.printStackTrace(); + } + } + } else { + if(debugMode) System.err.println("RMS Sync Cashier Module: Failed to get auth: " + responseCode); + } + + } + catch (Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to get auth token: " + ex.getMessage()); + ex.printStackTrace(); + } + + return (ret); + } + + /** + * A thread to free up the frontend + */ + private class syncBillRunnable implements Runnable { + + Bill bill = new Bill(); + Boolean debugMode = AdviceUtils.isRMSLoggingEnabled(); + + public syncBillRunnable(@NotNull Bill bill) { + this.bill = bill; + } + + @Override + public void run() { + // Run the thread + + try { + if(debugMode) System.out.println("RMS Sync Cashier Module: Start sending Bill to RMS"); + + sendRMSNewBill(bill); + + if(debugMode) System.out.println("RMS Sync Cashier Module: Finished sending Bill to RMS"); + } catch(Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to send Bill to RMS: " + ex.getMessage()); + ex.printStackTrace(); + } + } + } + +} diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewBillPaymentSyncToRMS.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewBillPaymentSyncToRMS.java new file mode 100644 index 0000000..bb30cdb --- /dev/null +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewBillPaymentSyncToRMS.java @@ -0,0 +1,329 @@ +package org.openmrs.module.kenyaemr.cashier.advice; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashSet; +import java.util.Set; + +import javax.net.ssl.HttpsURLConnection; +import javax.validation.constraints.NotNull; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.openmrs.module.kenyaemr.cashier.api.IBillService; +import org.openmrs.module.kenyaemr.cashier.api.model.Bill; +import org.openmrs.module.kenyaemr.cashier.api.model.Payment; +import org.openmrs.module.kenyaemr.cashier.api.model.PaymentMode; +import org.openmrs.module.kenyaemr.cashier.api.util.AdviceUtils; +import org.openmrs.ui.framework.SimpleObject; + +/** + * Detects when a new payment has been made to a bill and syncs to RMS Financial System + */ +public class NewBillPaymentSyncToRMS implements MethodInterceptor { + + private Boolean debugMode = false; + + private IBillService billService; + + public IBillService getBillService() { + return billService; + } + + public void setBillService(IBillService billService) { + this.billService = billService; + } + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + + Object result = null; + try { + debugMode = AdviceUtils.isRMSLoggingEnabled(); + if(AdviceUtils.isRMSIntegrationEnabled()) { + String methodName = invocation.getMethod().getName(); + if(debugMode) System.out.println("RMS Sync Cashier Module: method intercepted: " + methodName); + Bill oldBill = new Bill(); + + if ("save".equalsIgnoreCase(methodName)) { + if(debugMode) System.out.println("RMS Sync Cashier Module: Intercepting save bill method"); + + Object[] args = invocation.getArguments(); + Set oldPayments = new HashSet<>(); + + if (args.length > 0 && args[0] instanceof Bill) { + oldBill = (Bill) args[0]; + + Integer oldBillId = oldBill.getId(); + oldPayments = billService.getPaymentsByBillId(oldBillId); + } + + // Proceed with the original method + result = invocation.proceed(); + + try { + Bill newBill = (Bill) result; + + Set newPayments = newBill.getPayments(); + + if(debugMode) System.out.println("RMS Sync Cashier Module: Got a bill edit. checking if it is a payment. OldPayments: " + oldPayments.size() + " NewPayments: " + newPayments.size()); + + if(newPayments.size() > oldPayments.size()) { + if(debugMode) System.out.println("RMS Sync Cashier Module: New bill payment detected"); + + Set payments = AdviceUtils.symmetricPaymentDifference(oldPayments, newPayments); + if(debugMode) System.out.println("RMS Sync Cashier Module: New bill payments made: " + payments.size()); + + for(Payment payment : payments) { + // Use a thread to send the data. This frees up the frontend to proceed + syncPaymentRunnable runner = new syncPaymentRunnable(payment); + Thread thread = new Thread(runner); + thread.start(); + } + } + + } catch(Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error checking for bill payment: " + ex.getMessage()); + ex.printStackTrace(); + } + } else { + if(debugMode) System.out.println("RMS Sync Cashier Module: This is not the save method. We ignore."); + result = invocation.proceed(); + } + } + } catch(Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error checking for bill payment: " + ex.getMessage()); + ex.printStackTrace(); + // Any failure in RMS should not cause the payment to fail so we always proceed the invocation + result = invocation.proceed(); + } + + return (result); + } + + /** + * Prepare the payment payload + * @param bill + * @return + */ + public static String prepareBillPaymentRMSPayload(@NotNull Payment payment) { + String ret = ""; + Boolean debugMode = AdviceUtils.isRMSLoggingEnabled(); + + if (payment != null) { + if(debugMode) System.out.println( + "RMS Sync Cashier Module: New bill payment created: UUID: " + payment.getUuid() + ", Amount Tendered: " + payment.getAmountTendered()); + SimpleObject payloadPrep = new SimpleObject(); + payloadPrep.put("bill_reference", payment.getBill().getUuid()); + payloadPrep.put("amount_paid", payment.getAmountTendered()); + PaymentMode paymentMode = payment.getInstanceType(); + payloadPrep.put("payment_method_id", paymentMode != null ? paymentMode.getId() : 1); + + ret = payloadPrep.toJson(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got payment details: " + ret); + } else { + if(debugMode) System.out.println("RMS Sync Cashier Module: payment is null"); + } + return (ret); + } + + /** + * Send the new payment payload to RMS + * @param patient + * @return + */ + public static Boolean sendRMSNewPayment(@NotNull Payment payment) { + Boolean ret = false; + Boolean debugMode = AdviceUtils.isRMSLoggingEnabled(); + + String payload = prepareBillPaymentRMSPayload(payment); + + HttpsURLConnection con = null; + HttpsURLConnection connection = null; + try { + if(debugMode) System.out.println("RMS Sync Cashier Module: using payment payload: " + payload); + + // Create URL + String baseURL = AdviceUtils.getRMSEndpointURL(); + String completeURL = baseURL + "/login"; + if(debugMode) System.out.println("RMS Sync Cashier Module: Auth URL: " + completeURL); + URL url = new URL(completeURL); + String rmsUser = AdviceUtils.getRMSAuthUserName(); + String rmsPassword = AdviceUtils.getRMSAuthPassword(); + SimpleObject authPayloadCreator = SimpleObject.create("email", rmsUser != null ? rmsUser : "", "password", + rmsPassword != null ? rmsPassword : ""); + String authPayload = authPayloadCreator.toJson(); + + // Get token + con = (HttpsURLConnection) url.openConnection(); + con.setRequestMethod("POST"); + con.setDoOutput(true); + con.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + con.setRequestProperty("Accept", "application/json"); + con.setConnectTimeout(10000); // set timeout to 10 seconds + + PrintStream os = new PrintStream(con.getOutputStream()); + os.print(authPayload); + os.close(); + + int responseCode = con.getResponseCode(); + + if (responseCode == HttpURLConnection.HTTP_OK) { //success + BufferedReader in = null; + in = new BufferedReader(new InputStreamReader(con.getInputStream())); + + String input; + StringBuffer response = new StringBuffer(); + + while ((input = in.readLine()) != null) { + response.append(input); + } + in.close(); + + String returnResponse = response.toString(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got Auth Response as: " + returnResponse); + + // Extract the token and token expiry date + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = null; + String token = ""; + String expires_at = ""; + SimpleObject authObj = new SimpleObject(); + + try { + jsonNode = mapper.readTree(returnResponse); + if (jsonNode != null) { + token = jsonNode.get("token") == null ? "" : jsonNode.get("token").getTextValue(); + authObj.put("token", token); + expires_at = jsonNode.get("expires_at") == null ? "" : jsonNode.get("expires_at").getTextValue(); + authObj.put("expires_at", expires_at); + } + } + catch (Exception e) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error getting auth token: " + e.getMessage()); + e.printStackTrace(); + } + + if (!token.isEmpty()) { + try { + // We send the payload to RMS + if(debugMode) System.out.println( + "RMS Sync Cashier Module: We got the Auth token. Now sending the new bill details. Token: " + + token); + String finalUrl = baseURL + "/bill-payment"; + if(debugMode) System.out.println("RMS Sync Cashier Module: Final Create Payment URL: " + finalUrl); + URL finUrl = new URL(finalUrl); + + connection = (HttpsURLConnection) finUrl.openConnection(); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + connection.setRequestProperty("Authorization", "Bearer " + token); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setConnectTimeout(10000); + + PrintStream pos = new PrintStream(connection.getOutputStream()); + pos.print(payload); + pos.close(); + + int finalResponseCode = connection.getResponseCode(); + + if (finalResponseCode == HttpURLConnection.HTTP_OK) { //success + BufferedReader fin = null; + fin = new BufferedReader(new InputStreamReader(connection.getInputStream())); + + String finalOutput; + StringBuffer finalResponse = new StringBuffer(); + + while ((finalOutput = fin.readLine()) != null) { + finalResponse.append(finalOutput); + } + in.close(); + + String finalReturnResponse = finalResponse.toString(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got New Payment Response as: " + finalReturnResponse); + + ObjectMapper finalMapper = new ObjectMapper(); + JsonNode finaljsonNode = null; + Boolean success = false; + String message = ""; + + try { + finaljsonNode = finalMapper.readTree(finalReturnResponse); + if (finaljsonNode != null) { + success = finaljsonNode.get("success") == null ? false + : finaljsonNode.get("success").getBooleanValue(); + message = finaljsonNode.get("message") == null ? "" + : finaljsonNode.get("message").getTextValue(); + } + + if(debugMode) System.out.println("RMS Sync Cashier Module: Got New Payment final response: success: " + success + + " message: " + message); + } + catch (Exception e) { + if(debugMode) System.err.println( + "RMS Sync Cashier Module: Error getting New Payment final response: " + e.getMessage()); + e.printStackTrace(); + } + + if (success != null && success == true) { + ret = true; + } + + } else { + if(debugMode) System.err.println("RMS Sync Cashier Module: Failed to send New Payment final payload: " + finalResponseCode); + } + } + catch (Exception em) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to send the New Payment final payload: " + em.getMessage()); + em.printStackTrace(); + } + } + } else { + if(debugMode) System.err.println("RMS Sync Cashier Module: Failed to get auth: " + responseCode); + } + + } + catch (Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to get auth token: " + ex.getMessage()); + ex.printStackTrace(); + } + + return (ret); + } + + /** + * A thread to free up the frontend + */ + private class syncPaymentRunnable implements Runnable { + + Payment payment = new Payment(); + Boolean debugMode = AdviceUtils.isRMSLoggingEnabled(); + + public syncPaymentRunnable(@NotNull Payment payment) { + this.payment = payment; + } + + @Override + public void run() { + // Run the thread + + try { + if(debugMode) System.out.println("RMS Sync Cashier Module: Start sending payment to RMS"); + + sendRMSNewPayment(payment); + + if(debugMode) System.out.println("RMS Sync Cashier Module: Finished sending payment to RMS"); + } catch(Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to send payment to RMS: " + ex.getMessage()); + ex.printStackTrace(); + } + } + } + +} \ No newline at end of file diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewPatientRegistrationSyncToRMS.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewPatientRegistrationSyncToRMS.java new file mode 100644 index 0000000..a70e8ae --- /dev/null +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/advice/NewPatientRegistrationSyncToRMS.java @@ -0,0 +1,324 @@ +package org.openmrs.module.kenyaemr.cashier.advice; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.net.ssl.HttpsURLConnection; +import javax.validation.constraints.NotNull; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; +import org.openmrs.Patient; +import org.openmrs.PatientIdentifier; +import org.openmrs.PatientIdentifierType; +import org.openmrs.api.context.Context; +import org.openmrs.module.kenyaemr.cashier.api.util.AdviceUtils; +import org.openmrs.module.kenyaemr.cashier.util.Utils; +import org.openmrs.ui.framework.SimpleObject; +import org.openmrs.util.PrivilegeConstants; +import org.springframework.aop.AfterReturningAdvice; + +/** + * Detects when a new patient has been registered and syncs to RMS Financial System + */ +public class NewPatientRegistrationSyncToRMS implements AfterReturningAdvice { + + private Boolean debugMode = false; + + @Override + public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { + try { + debugMode = AdviceUtils.isRMSLoggingEnabled(); + if(AdviceUtils.isRMSIntegrationEnabled()) { + // Check if the method is "savePatient" + if (method.getName().equals("savePatient") && args.length > 0 && args[0] instanceof Patient) { + Patient patient = (Patient) args[0]; + + // Log patient info + if (patient != null) { + Date patientCreationDate = patient.getDateCreated(); + if(debugMode) System.out.println("RMS Sync Cashier Module: patient was created on: " + patientCreationDate); + + if(patientCreationDate != null && AdviceUtils.checkIfCreateModetOrEditMode(patientCreationDate)) { + // CREATE MODE + if(debugMode) System.out.println("RMS Sync Cashier Module: New patient registered:"); + if(debugMode) System.out.println("RMS Sync Cashier Module: Name: " + patient.getPersonName().getFullName()); + if(debugMode) System.out.println("RMS Sync Cashier Module: DOB: " + patient.getBirthdate()); + if(debugMode) System.out.println("RMS Sync Cashier Module: Age: " + patient.getAge()); + + // Use a thread to send the data. This frees up the frontend to proceed + syncPatientRunnable runner = new syncPatientRunnable(patient); + Thread thread = new Thread(runner); + thread.start(); + } else { + // EDIT MODE + if(debugMode) System.out.println("RMS Sync Cashier Module: patient in edit mode. we ignore"); + } + } else { + if(debugMode) System.out.println("RMS Sync Cashier Module: Attempted to save a null patient."); + } + } + } + } catch(Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error getting new patient: " + ex.getMessage()); + ex.printStackTrace(); + } + } + + /** + * Prepare the JSON payload for patient registration + * @param patient + * @return + */ + private String preparePatientRMSPayload(@NotNull Patient patient) { + String ret = ""; + try { + Context.openSession(); + Context.addProxyPrivilege(PrivilegeConstants.GET_IDENTIFIER_TYPES); + if (patient != null) { + if(debugMode) System.out.println( + "RMS Sync Cashier Module: New patient created: " + patient.getPersonName().getFullName() + ", Age: " + patient.getAge()); + SimpleObject payloadPrep = new SimpleObject(); + payloadPrep.put("first_name", patient.getPersonName().getGivenName()); + payloadPrep.put("middle_name", patient.getPersonName().getMiddleName()); + payloadPrep.put("patient_unique_id", patient.getUuid()); + payloadPrep.put("last_name", patient.getPersonName().getFamilyName()); + PatientIdentifierType nationalIDIdentifierType = Context.getPatientService() + .getPatientIdentifierTypeByUuid("49af6cdc-7968-4abb-bf46-de10d7f4859f"); + String natID = ""; + if (nationalIDIdentifierType != null) { + PatientIdentifier piNatId = patient.getPatientIdentifier(nationalIDIdentifierType); + + if (piNatId != null) { + natID = piNatId.getIdentifier(); + if(debugMode) System.err.println("RMS Sync Cashier Module: Got the national id as: " + natID); + } + } + payloadPrep.put("id_number", natID); + String phoneNumber = patient.getAttribute("Telephone contact") != null + ? patient.getAttribute("Telephone contact").getValue() + : ""; + payloadPrep.put("phone", phoneNumber); + payloadPrep.put("hospital_code", Utils.getDefaultLocationMflCode(null)); + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + payloadPrep.put("dob", formatter.format(patient.getBirthdate())); + payloadPrep + .put("gender", + patient.getGender() != null + ? (patient.getGender().equalsIgnoreCase("M") ? "Male" + : (patient.getGender().equalsIgnoreCase("F") ? "Female" : "")) + : ""); + ret = payloadPrep.toJson(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got patient registration details: " + ret); + } else { + if(debugMode) System.out.println("RMS Sync Cashier Module: patient is null"); + } + } catch (Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error getting new patient payload: " + ex.getMessage()); + ex.printStackTrace(); + } finally { + Context.closeSession(); + } + + return (ret); + } + + /** + * Send the patient registration payload to RMS + * @param patient + * @return + */ + private Boolean sendRMSPatientRegistration(@NotNull Patient patient) { + Boolean ret = false; + String payload = preparePatientRMSPayload(patient); + + HttpsURLConnection con = null; + HttpsURLConnection connection = null; + try { + if(debugMode) System.out.println("RMS Sync Cashier Module: using payload: " + payload); + + // Create URL + String baseURL = AdviceUtils.getRMSEndpointURL(); + String completeURL = baseURL + "/login"; + if(debugMode) System.out.println("RMS Sync Cashier Module: Auth URL: " + completeURL); + URL url = new URL(completeURL); + String rmsUser = AdviceUtils.getRMSAuthUserName(); + String rmsPassword = AdviceUtils.getRMSAuthPassword(); + SimpleObject authPayloadCreator = SimpleObject.create("email", rmsUser != null ? rmsUser : "", "password", + rmsPassword != null ? rmsPassword : ""); + String authPayload = authPayloadCreator.toJson(); + + // Get token + con = (HttpsURLConnection) url.openConnection(); + con.setRequestMethod("POST"); + con.setDoOutput(true); + con.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + con.setRequestProperty("Accept", "application/json"); + con.setConnectTimeout(10000); // set timeout to 10 seconds + + PrintStream os = new PrintStream(con.getOutputStream()); + os.print(authPayload); + os.close(); + + int responseCode = con.getResponseCode(); + + if (responseCode == HttpURLConnection.HTTP_OK) { //success + BufferedReader in = null; + in = new BufferedReader(new InputStreamReader(con.getInputStream())); + + String input; + StringBuffer response = new StringBuffer(); + + while ((input = in.readLine()) != null) { + response.append(input); + } + in.close(); + + String returnResponse = response.toString(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got Auth Response as: " + returnResponse); + + // Extract the token and token expiry date + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = null; + String token = ""; + String expires_at = ""; + SimpleObject authObj = new SimpleObject(); + + try { + jsonNode = mapper.readTree(returnResponse); + if (jsonNode != null) { + token = jsonNode.get("token") == null ? "" : jsonNode.get("token").getTextValue(); + authObj.put("token", token); + expires_at = jsonNode.get("expires_at") == null ? "" : jsonNode.get("expires_at").getTextValue(); + authObj.put("expires_at", expires_at); + } + } + catch (Exception e) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error getting auth token: " + e.getMessage()); + e.printStackTrace(); + } + + if (!token.isEmpty()) { + try { + // We send the payload to RMS + if(debugMode) System.err.println( + "RMS Sync Cashier Module: We got the Auth token. Now sending the patient registration details. Token: " + + token); + String finalUrl = baseURL + "/create-patient-profile"; + if(debugMode) System.out.println("RMS Sync Cashier Module: Final patient registration URL: " + finalUrl); + URL finUrl = new URL(finalUrl); + + connection = (HttpsURLConnection) finUrl.openConnection(); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + connection.setRequestProperty("Authorization", "Bearer " + token); + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setConnectTimeout(10000); + + PrintStream pos = new PrintStream(connection.getOutputStream()); + pos.print(payload); + pos.close(); + + int finalResponseCode = connection.getResponseCode(); + + if (finalResponseCode == HttpURLConnection.HTTP_OK) { //success + BufferedReader fin = null; + fin = new BufferedReader(new InputStreamReader(connection.getInputStream())); + + String finalOutput; + StringBuffer finalResponse = new StringBuffer(); + + while ((finalOutput = fin.readLine()) != null) { + finalResponse.append(finalOutput); + } + in.close(); + + String finalReturnResponse = finalResponse.toString(); + if(debugMode) System.out.println("RMS Sync Cashier Module: Got patient registration Response as: " + finalReturnResponse); + + ObjectMapper finalMapper = new ObjectMapper(); + JsonNode finaljsonNode = null; + Boolean success = false; + String message = ""; + + try { + finaljsonNode = finalMapper.readTree(finalReturnResponse); + if (finaljsonNode != null) { + success = finaljsonNode.get("success") == null ? false + : finaljsonNode.get("success").getBooleanValue(); + message = finaljsonNode.get("message") == null ? "" + : finaljsonNode.get("message").getTextValue(); + } + + if(debugMode) System.err.println("RMS Sync Cashier Module: Got patient registration final response: success: " + success + + " message: " + message); + } + catch (Exception e) { + if(debugMode) System.err.println( + "RMS Sync Cashier Module: Error getting patient registration final response: " + e.getMessage()); + e.printStackTrace(); + } + + if (success != null && success == true) { + ret = true; + } + + } else { + if(debugMode) System.err.println("RMS Sync Cashier Module: Failed to send final payload: " + finalResponseCode); + } + } + catch (Exception em) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to send the final payload: " + em.getMessage()); + em.printStackTrace(); + } + } + } else { + if(debugMode) System.err.println("RMS Sync Cashier Module: Failed to get auth: " + responseCode); + } + + } + catch (Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to get auth token: " + ex.getMessage()); + ex.printStackTrace(); + } + + return (ret); + } + + /** + * A thread to free up the frontend + */ + private class syncPatientRunnable implements Runnable { + + Patient patient = new Patient(); + Boolean debugMode = AdviceUtils.isRMSLoggingEnabled(); + + public syncPatientRunnable(@NotNull Patient patient) { + this.patient = patient; + } + + @Override + public void run() { + // Run the thread + + try { + if(debugMode) System.out.println("RMS Sync Cashier Module: Start sending patient to RMS"); + + sendRMSPatientRegistration(patient); + + if(debugMode) System.out.println("RMS Sync Cashier Module: Finished sending patient to RMS"); + } catch(Exception ex) { + if(debugMode) System.err.println("RMS Sync Cashier Module: Error. Failed to send patient to RMS: " + ex.getMessage()); + ex.printStackTrace(); + } + } + } + +} diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/IBillService.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/IBillService.java index cc7a66a..c5493b3 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/IBillService.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/IBillService.java @@ -16,12 +16,14 @@ import java.io.File; import java.util.List; +import java.util.Set; import org.openmrs.Patient; import org.openmrs.annotation.Authorized; import org.openmrs.module.kenyaemr.cashier.api.base.PagingInfo; import org.openmrs.module.kenyaemr.cashier.api.base.entity.IEntityDataService; import org.openmrs.module.kenyaemr.cashier.api.model.Bill; +import org.openmrs.module.kenyaemr.cashier.api.model.Payment; import org.openmrs.module.kenyaemr.cashier.api.search.BillSearch; import org.openmrs.module.kenyaemr.cashier.api.util.PrivilegeConstants; import org.springframework.transaction.annotation.Transactional; @@ -46,6 +48,10 @@ public interface IBillService extends IEntityDataService { @Authorized({ PrivilegeConstants.VIEW_BILLS }) Bill getBillByReceiptNumber(String receiptNumber); + @Transactional(readOnly = true) + @Authorized({ PrivilegeConstants.VIEW_BILLS }) + Set getPaymentsByBillId(Integer billId); + /** * Returns all {@link Bill}s for the specified patient with the specified paging. * @param patient The {@link Patient}. diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/IObjectDataService.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/IObjectDataService.java index d822678..069ef8a 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/IObjectDataService.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/IObjectDataService.java @@ -15,10 +15,12 @@ import java.util.Collection; import java.util.List; +import java.util.Set; import org.openmrs.OpenmrsObject; import org.openmrs.api.OpenmrsService; import org.openmrs.module.kenyaemr.cashier.api.base.entity.db.hibernate.BaseHibernateRepository; +import org.openmrs.module.kenyaemr.cashier.api.model.Payment; import org.openmrs.module.kenyaemr.cashier.api.base.PagingInfo; import org.springframework.transaction.annotation.Transactional; @@ -104,6 +106,9 @@ public interface IObjectDataService extends OpenmrsServ @Transactional(readOnly = true) E getById(int id); + @Transactional(readOnly = true) + Set getPaymentsByBillId(Integer billId); + /** * Gets an object by uuid. * @param uuid is the uuid of the desired object. diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/db/hibernate/BaseHibernateRepository.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/db/hibernate/BaseHibernateRepository.java index 2fe3914..6017fd5 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/db/hibernate/BaseHibernateRepository.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/db/hibernate/BaseHibernateRepository.java @@ -16,10 +16,12 @@ import java.io.Serializable; import java.util.Collection; import java.util.List; +import java.util.Set; import org.hibernate.Criteria; import org.hibernate.Query; import org.openmrs.OpenmrsObject; +import org.openmrs.module.kenyaemr.cashier.api.model.Payment; /** * Represents types that can provide access to a data source through hibernate. @@ -93,6 +95,8 @@ public interface BaseHibernateRepository { */ E selectSingle(Class cls, Serializable id); + Set getPaymentsByBillId(Integer billId); + /** * Selects a single entity from the database using the specified {@link org.hibernate.Criteria}. If more than one entity * is found only the first is returned. diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/db/hibernate/BaseHibernateRepositoryImpl.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/db/hibernate/BaseHibernateRepositoryImpl.java index 001660a..e5da763 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/db/hibernate/BaseHibernateRepositoryImpl.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/db/hibernate/BaseHibernateRepositoryImpl.java @@ -14,15 +14,24 @@ package org.openmrs.module.kenyaemr.cashier.api.base.entity.db.hibernate; import java.io.Serializable; +import java.math.BigDecimal; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import org.hibernate.CacheMode; import org.hibernate.Criteria; import org.hibernate.Query; +import org.hibernate.SQLQuery; import org.openmrs.OpenmrsObject; +import org.openmrs.Patient; import org.openmrs.api.APIException; import org.openmrs.api.db.hibernate.DbSession; import org.openmrs.api.db.hibernate.DbSessionFactory; +import org.openmrs.module.kenyaemr.cashier.api.model.Bill; +import org.openmrs.module.kenyaemr.cashier.api.model.Payment; +import org.openmrs.module.kenyaemr.cashier.api.model.PaymentMode; import org.springframework.transaction.annotation.Transactional; /** @@ -129,6 +138,48 @@ public E selectSingle(Class cls, Serializable id) { } } + @Override + @SuppressWarnings("unchecked") + public Set getPaymentsByBillId(Integer billId) { + // Get the current Hibernate session from DbSessionFactory + DbSession session = sessionFactory.getCurrentSession(); + + // Ensure no caching is used by ignoring the cache + session.setCacheMode(CacheMode.IGNORE); + + String sqlQuery = "SELECT cbp.bill_payment_id, cbp.uuid, cbp.bill_id, cbp.payment_mode_id, cbp.amount_tendered, cbp.amount FROM cashier_bill cb inner join cashier_bill_payment cbp on cbp.bill_id = cb.bill_id and cb.bill_id =:billId"; + + // Execute the query and fetch the result + List resultList = session.createSQLQuery(sqlQuery) + .setParameter("billId", billId) + .list(); + + System.out.println("RMS Sync Cashier Module: Payments got SQL payments: " + resultList.size()); + + // Create a Set to hold the resulting Patient objects + Set payments = new HashSet<>(); + + // Iterate through the results and map them to Patient objects + for (Object[] row : resultList) { + Payment payment = new Payment(); + payment.setId((Integer) row[0]); // payment_id + // System.out.println("RMS Sync Cashier Module: Payments got SQL payments: injecting ID" + (Integer) row[0]); + payment.setUuid((String) row[1]); // payment uuid + // System.out.println("RMS Sync Cashier Module: Payments got SQL payments: injecting UUID" + (String) row[1]); + Bill newBill = new Bill(); + newBill.setId(billId); + payment.setBill(newBill); // bill + PaymentMode newPaymentMode = new PaymentMode(); + newPaymentMode.setId((Integer) row[3]); + payment.setInstanceType(newPaymentMode); // payment mode + payment.setAmountTendered((BigDecimal) row[4]); //Amount Tendered + payment.setAmount((BigDecimal) row[5]); //Total Amount + payments.add(payment); + } + + return(payments); + } + @Override @SuppressWarnings("unchecked") public E selectSingle(Class cls, Criteria criteria) { diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/impl/BaseObjectDataServiceImpl.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/impl/BaseObjectDataServiceImpl.java index f4ae8a0..d19fa4b 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/impl/BaseObjectDataServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/base/entity/impl/BaseObjectDataServiceImpl.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.hibernate.Criteria; @@ -36,6 +37,7 @@ import org.openmrs.module.kenyaemr.cashier.api.base.entity.db.hibernate.BaseHibernateRepository; import org.openmrs.module.kenyaemr.cashier.api.base.f.Action1; import org.openmrs.module.kenyaemr.cashier.api.base.util.PrivilegeUtil; +import org.openmrs.module.kenyaemr.cashier.api.model.Payment; import org.springframework.transaction.annotation.Transactional; /** @@ -188,6 +190,17 @@ public E getById(int entityId) { return repository.selectSingle(getEntityClass(), entityId); } + @Override + @Transactional(readOnly = true) + public Set getPaymentsByBillId(Integer billId) { + P privileges = getPrivileges(); + if (privileges != null && !StringUtils.isEmpty(privileges.getGetPrivilege())) { + PrivilegeUtil.requirePrivileges(Context.getAuthenticatedUser(), privileges.getGetPrivilege()); + } + + return repository.getPaymentsByBillId(billId); + } + @Override @Transactional(readOnly = true) public E getByUuid(String uuid) { diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/impl/BillServiceImpl.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/impl/BillServiceImpl.java index 8175718..d54b950 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/impl/BillServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/impl/BillServiceImpl.java @@ -44,6 +44,7 @@ import org.openmrs.Patient; import org.openmrs.annotation.Authorized; import org.openmrs.api.context.Context; +import org.openmrs.module.kenyaemr.cashier.advice.NewBillPaymentSyncToRMS; import org.openmrs.module.kenyaemr.cashier.api.IBillService; import org.openmrs.module.kenyaemr.cashier.api.IReceiptNumberGenerator; import org.openmrs.module.kenyaemr.cashier.api.ReceiptNumberGeneratorFactory; @@ -56,6 +57,8 @@ import org.openmrs.module.kenyaemr.cashier.api.model.BillStatus; import org.openmrs.module.kenyaemr.cashier.api.model.Payment; import org.openmrs.module.kenyaemr.cashier.api.search.BillSearch; +import org.openmrs.module.kenyaemr.cashier.api.util.AdviceUtils; +import org.openmrs.module.kenyaemr.cashier.api.util.CashierModuleConstants; import org.openmrs.module.kenyaemr.cashier.api.util.PrivilegeConstants; import org.openmrs.module.kenyaemr.cashier.util.Utils; import org.springframework.transaction.annotation.Transactional; @@ -69,7 +72,9 @@ import java.security.AccessControlException; import java.text.DecimalFormat; import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Data service implementation class for {@link Bill}s. @@ -139,6 +144,14 @@ public Bill save(Bill bill) { return super.save(bill); } + @Override + @Authorized({ PrivilegeConstants.VIEW_BILLS }) + @Transactional(readOnly = true) + public Set getPaymentsByBillId(Integer billId) { + Set payments = super.getPaymentsByBillId(billId); + return payments; + } + @Override @Authorized({ PrivilegeConstants.VIEW_BILLS }) @Transactional(readOnly = true) diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/model/Bill.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/model/Bill.java index 8ecc3bb..45ec675 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/model/Bill.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/model/Bill.java @@ -25,6 +25,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -259,7 +260,7 @@ public void addPayment(Payment payment) { } if (this.payments == null) { - this.payments = new HashSet(); + this.payments = new LinkedHashSet(); } this.payments.add(payment); payment.setBill(this); diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/util/AdviceUtils.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/util/AdviceUtils.java new file mode 100644 index 0000000..c939a65 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/util/AdviceUtils.java @@ -0,0 +1,154 @@ +package org.openmrs.module.kenyaemr.cashier.api.util; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import org.openmrs.GlobalProperty; +import org.openmrs.api.context.Context; +import org.openmrs.module.kenyaemr.cashier.api.model.Payment; + +public class AdviceUtils { + + /** + * Checks if a bill/patient is in create mode or edit mode (using dateCreated) + * CREATE MODE = true, EDIT MODE = false + * @param date + * @return + */ + public static boolean checkIfCreateModetOrEditMode(Date date) { + // Get the current time in milliseconds + long now = System.currentTimeMillis(); + + // Get the time of the provided date in milliseconds + long timeOfDate = date.getTime(); + + // Calculate the difference in milliseconds + long diffInMillis = now - timeOfDate; + + // Check if the difference is positive (date is before now) and less than 60 seconds (60,000 ms) + return diffInMillis >= 0 && diffInMillis < 60 * 1000; + } + + /** + * Check if there are any new payments + * @param oldSet + * @param newSet + * @return + */ + public static Set symmetricPaymentDifference(Set oldSet, Set newSet) { + Set result = new HashSet<>(newSet); + Boolean debugMode = isRMSLoggingEnabled(); + + // Add elements from newSet that are not in oldSet based on amount comparison + for (Payment item1 : oldSet) { + for (Payment item2 : newSet) { + if(debugMode) System.out.println("RMS Sync Cashier Module: Payments comparison: Oldset comparing item uuid " + item2.getAmountTendered() + " with Newset: " + item1.getAmountTendered()); + // BigDecimal behaves different. You cannot use == + if (item1.getAmountTendered().compareTo(item2.getAmountTendered()) == 0) { + if(debugMode) System.out.println("RMS Sync Cashier Module: Payments comparison: Found a match: " + item2.getAmountTendered()+ " and: " + item1.getAmountTendered()); + if(debugMode) System.out.println("RMS Sync Cashier Module: Payments comparison: Removing item amount " + item2.getAmountTendered() + " size before: " + result.size()); + // result.remove(item2); + for(Payment test : result) { + if (item2.getAmountTendered().compareTo(test.getAmountTendered()) == 0) { + result.remove(test); + break; + } + } + if(debugMode) System.out.println("RMS Sync Cashier Module: Payments comparison: Removing item: size after: " + result.size()); + break; + } + } + } + + if(debugMode) System.out.println("RMS Sync Cashier Module: Payments comparison: " + result.size()); + + return result; + } + + /** + * Checks whether RMS Logging is enabled + * @return true (Enabled) and false (Disabled) + */ + public static Boolean isRMSLoggingEnabled() { + Boolean ret = false; + + GlobalProperty globalRMSEnabled = Context.getAdministrationService() + .getGlobalPropertyObject(CashierModuleConstants.RMS_LOGGING_ENABLED); + String isRMSLoggingEnabled = globalRMSEnabled.getPropertyValue(); + + if(isRMSLoggingEnabled != null && isRMSLoggingEnabled.trim().equalsIgnoreCase("true")) { + ret = true; + } + + return(ret); + } + + /** + * Checks whether RMS Integration is enabled + * @return true (Enabled) and false (Disabled) + */ + public static Boolean isRMSIntegrationEnabled() { + Boolean ret = false; + + GlobalProperty globalRMSEnabled = Context.getAdministrationService() + .getGlobalPropertyObject(CashierModuleConstants.RMS_SYNC_ENABLED); + String isRMSLoggingEnabled = globalRMSEnabled.getPropertyValue(); + + if(isRMSLoggingEnabled != null && isRMSLoggingEnabled.trim().equalsIgnoreCase("true")) { + ret = true; + } + + return(ret); + } + + /** + * Gets the RMS endpoint URL + * @return + */ + public static String getRMSEndpointURL() { + String ret = ""; + + GlobalProperty globalPostUrl = Context.getAdministrationService() + .getGlobalPropertyObject(CashierModuleConstants.RMS_ENDPOINT_URL); + String baseURL = globalPostUrl.getPropertyValue(); + if (baseURL == null || baseURL.trim().isEmpty()) { + baseURL = "https://siaya.tsconect.com/api"; + } + ret = baseURL; + + return(ret); + } + + /** + * Gets the RMS Auth Username + * @return + */ + public static String getRMSAuthUserName() { + String ret = ""; + + GlobalProperty rmsUserGP = Context.getAdministrationService() + .getGlobalPropertyObject(CashierModuleConstants.RMS_USERNAME); + String rmsUser = rmsUserGP.getPropertyValue(); + ret = (rmsUser == null || rmsUser.trim().isEmpty()) ? "" : rmsUser; + + return(ret); + } + + /** + * Gets the RMS Auth Password + * @return + */ + public static String getRMSAuthPassword() { + String ret = ""; + + GlobalProperty rmsPasswordGP = Context.getAdministrationService() + .getGlobalPropertyObject(CashierModuleConstants.RMS_PASSWORD); + String rmsPassword = rmsPasswordGP.getPropertyValue(); + ret = (rmsPassword == null || rmsPassword.trim().isEmpty()) ? "" : rmsPassword; + + return(ret); + } + +} + diff --git a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/util/CashierModuleConstants.java b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/util/CashierModuleConstants.java index cd30d04..24dc1f9 100644 --- a/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/util/CashierModuleConstants.java +++ b/api/src/main/java/org/openmrs/module/kenyaemr/cashier/api/util/CashierModuleConstants.java @@ -18,7 +18,18 @@ */ public class CashierModuleConstants { public static final String MODULE_NAME = "cashier"; + public static final String BILLING_EXEMPTIONS_CONFIG_FILE_PATH = "kenyaemr.cashier.billing.exemptions.config"; + public static final String RMS_SYNC_ENABLED = "kenyaemr.cashier.rms.integration.enabled"; + + public static final String RMS_ENDPOINT_URL = "kenyaemr.cashier.rms.integration.endpoint.url"; + + public static final String RMS_USERNAME = "kenyaemr.cashier.rms.integration.username"; + + public static final String RMS_PASSWORD = "kenyaemr.cashier.rms.integration.password"; + + public static final String RMS_LOGGING_ENABLED = "kenyaemr.cashier.rms.integration.logging"; + protected CashierModuleConstants() {} } diff --git a/api/src/main/resources/moduleApplicationContext.xml b/api/src/main/resources/moduleApplicationContext.xml index 4021ddd..925e906 100644 --- a/api/src/main/resources/moduleApplicationContext.xml +++ b/api/src/main/resources/moduleApplicationContext.xml @@ -13,10 +13,26 @@ http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop - http://www.springframework.org/schema/aop/spring-aop-3.0.xsd + http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> + + + + + + + + + + + + billSavePaymentAspect + + + + @@ -128,7 +144,7 @@ - + diff --git a/omod/src/main/java/org/openmrs/module/kenyaemr/cashier/rest/resource/BillResource.java b/omod/src/main/java/org/openmrs/module/kenyaemr/cashier/rest/resource/BillResource.java index 591c88b..8d132af 100644 --- a/omod/src/main/java/org/openmrs/module/kenyaemr/cashier/rest/resource/BillResource.java +++ b/omod/src/main/java/org/openmrs/module/kenyaemr/cashier/rest/resource/BillResource.java @@ -50,6 +50,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.LinkedHashSet; /** * REST resource representing a {@link Bill}. @@ -96,7 +97,8 @@ public void setBillLineItems(Bill instance, List lineItems) { @PropertySetter("payments") public void setBillPayments(Bill instance, Set payments) { if (instance.getPayments() == null) { - instance.setPayments(new HashSet(payments.size())); + // instance.setPayments(new HashSet(payments.size())); + instance.setPayments(new LinkedHashSet(payments.size())); } BaseRestDataResource.syncCollection(instance.getPayments(), payments); for (Payment payment : instance.getPayments()) { diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml index 933621a..9b4e22e 100644 --- a/omod/src/main/resources/config.xml +++ b/omod/src/main/resources/config.xml @@ -38,6 +38,16 @@ ${project.parent.groupId}.${project.parent.artifactId}.advice.GenerateBillFromOrderable + + org.openmrs.api.PatientService + org.openmrs.module.kenyaemr.cashier.advice.NewPatientRegistrationSyncToRMS + + + + org.openmrs.module.kenyaemr.cashier.api.IBillService + org.openmrs.module.kenyaemr.cashier.advice.NewBillCreationSyncToRMS + + ${project.parent.artifactId}.defaultReceiptReportId ID of the default Jasper report to use for generating a receipt on the Bill page @@ -166,6 +176,31 @@ Full address and contact details to put in the receipt. Use "\n" for line break + + ${project.parent.artifactId}.rms.integration.enabled + Is RMS integration enabled - (true) = enabled, (false) = disabled + false + + + ${project.parent.artifactId}.rms.integration.endpoint.url + RMS integration Endpoint URL + https://siaya.tsconect.com/api + + + ${project.parent.artifactId}.rms.integration.username + RMS integration Username + pdsl@gmail.com + + + ${project.parent.artifactId}.rms.integration.password + RMS integration Password + password + + + ${project.parent.artifactId}.rms.integration.logging + Is RMS integration logging enabled - (true) = enabled, (false) = disabled + false +