diff --git a/src/main/java/com/divudi/bean/clinical/DischargeConditionController.java b/src/main/java/com/divudi/bean/clinical/DischargeConditionController.java new file mode 100644 index 00000000000..b57fad799ab --- /dev/null +++ b/src/main/java/com/divudi/bean/clinical/DischargeConditionController.java @@ -0,0 +1,228 @@ +/* + * Open Hospital Management Information System + * + * Dr M H B Ariyaratne + * Acting Consultant (Health Informatics) + */ +package com.divudi.bean.clinical; + +import com.divudi.bean.common.SessionController; +import com.divudi.core.util.JsfUtil; +import com.divudi.core.data.SymanticType; +import com.divudi.core.entity.clinical.ClinicalEntity; +import com.divudi.core.facade.ClinicalEntityFacade; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.ejb.EJB; +import javax.enterprise.context.SessionScoped; +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.convert.Converter; +import javax.faces.convert.FacesConverter; +import javax.inject.Inject; +import javax.inject.Named; +import javax.servlet.http.HttpServletResponse; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +/** + * Manages configurable discharge condition values (e.g. Stable, DAMA, Referred) + * used in the clinical discharge workflow. + * + * @author Dr. M. H. B. Ariyaratne + */ +@Named +@SessionScoped +public class DischargeConditionController implements Serializable { + + private static final long serialVersionUID = 1L; + + @Inject + SessionController sessionController; + + @EJB + private ClinicalEntityFacade ejbFacade; + + private ClinicalEntity current; + private List items = null; + private String selectText = ""; + + public DischargeConditionController() { + } + + public void prepareAdd() { + current = new ClinicalEntity(); + current.setSymanticType(SymanticType.Discharge_Condition); + } + + public void saveSelected() { + current.setSymanticType(SymanticType.Discharge_Condition); + if (getCurrent().getId() != null && getCurrent().getId() > 0) { + getFacade().edit(current); + JsfUtil.addSuccessMessage("Updated"); + } else { + current.setCreatedAt(new Date()); + current.setCreater(getSessionController().getLoggedUser()); + getFacade().create(current); + JsfUtil.addSuccessMessage("Saved"); + } + recreateModel(); + getItems(); + } + + public void delete() { + if (current != null) { + current.setRetired(true); + current.setRetiredAt(new Date()); + current.setRetirer(getSessionController().getLoggedUser()); + getFacade().edit(current); + JsfUtil.addSuccessMessage("Deleted Successfully"); + } else { + JsfUtil.addErrorMessage("Nothing to Delete"); + } + recreateModel(); + getItems(); + current = null; + getCurrent(); + } + + public List getItems() { + if (items == null) { + Map m = new HashMap(); + m.put("t", SymanticType.Discharge_Condition); + String sql = "select c from ClinicalEntity c where c.retired=false and c.symanticType=:t order by c.name"; + items = getFacade().findByJpql(sql, m); + } + return items; + } + + public List completeDischargeConditions(String qry) { + if (qry == null || qry.trim().isEmpty()) { + return new ArrayList<>(); + } + List c; + Map m = new HashMap(); + m.put("t", SymanticType.Discharge_Condition); + m.put("n", "%" + qry.toUpperCase() + "%"); + String sql = "select c from ClinicalEntity c where c.retired=false and upper(c.name) like :n and c.symanticType=:t order by c.name"; + c = getFacade().findByJpql(sql, m, 10); + if (c == null) { + c = new ArrayList<>(); + } + return c; + } + + public void downloadAsExcel() { + getItems(); + FacesContext context = FacesContext.getCurrentInstance(); + try (Workbook workbook = new XSSFWorkbook()) { + Sheet sheet = workbook.createSheet("Discharge Conditions"); + + Row headerRow = sheet.createRow(0); + headerRow.createCell(0).setCellValue("No"); + headerRow.createCell(1).setCellValue("Name"); + headerRow.createCell(2).setCellValue("Code"); + headerRow.createCell(3).setCellValue("Description"); + + int rowNum = 1; + for (ClinicalEntity item : items) { + Row row = sheet.createRow(rowNum); + row.createCell(0).setCellValue(rowNum); + row.createCell(1).setCellValue(item.getName()); + row.createCell(2).setCellValue(item.getCode()); + row.createCell(3).setCellValue(item.getDescreption()); + rowNum++; + } + + HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse(); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setHeader("Content-Disposition", "attachment; filename=\"discharge_conditions.xlsx\""); + + workbook.write(response.getOutputStream()); + context.responseComplete(); + } catch (Exception e) { + JsfUtil.addErrorMessage("Error generating Excel file: " + e.getMessage()); + } + } + + private void recreateModel() { + items = null; + } + + private ClinicalEntityFacade getFacade() { + return ejbFacade; + } + + public ClinicalEntity getCurrent() { + if (current == null) { + current = new ClinicalEntity(); + } + return current; + } + + public void setCurrent(ClinicalEntity current) { + this.current = current; + } + + public String getSelectText() { + return selectText; + } + + public void setSelectText(String selectText) { + this.selectText = selectText; + } + + public SessionController getSessionController() { + return sessionController; + } + + public void setSessionController(SessionController sessionController) { + this.sessionController = sessionController; + } + + public ClinicalEntityFacade getEjbFacade() { + return ejbFacade; + } + + public void setEjbFacade(ClinicalEntityFacade ejbFacade) { + this.ejbFacade = ejbFacade; + } + + @FacesConverter("dischargeConditionConverter") + public static class DischargeConditionConverter implements Converter { + + @Override + public Object getAsObject(FacesContext facesContext, UIComponent component, String value) { + if (value == null || value.length() == 0) { + return null; + } + try { + DischargeConditionController controller = (DischargeConditionController) facesContext.getApplication() + .getELResolver().getValue(facesContext.getELContext(), null, "dischargeConditionController"); + return controller.getEjbFacade().find(Long.valueOf(value)); + } catch (NumberFormatException e) { + return null; + } + } + + @Override + public String getAsString(FacesContext facesContext, UIComponent component, Object object) { + if (object == null) { + return null; + } + if (object instanceof ClinicalEntity) { + ClinicalEntity o = (ClinicalEntity) object; + return String.valueOf(o.getId()); + } else { + throw new IllegalArgumentException("object " + object + " is of type " + + object.getClass().getName() + "; expected type: ClinicalEntity"); + } + } + } +} diff --git a/src/main/java/com/divudi/bean/inward/BhtSummeryController.java b/src/main/java/com/divudi/bean/inward/BhtSummeryController.java index 7d173a7cf24..2a92e99cf83 100644 --- a/src/main/java/com/divudi/bean/inward/BhtSummeryController.java +++ b/src/main/java/com/divudi/bean/inward/BhtSummeryController.java @@ -1620,6 +1620,10 @@ public void discharge() { return; } + if (!getPatientEncounter().isClinicallyDischarged()) { + JsfUtil.addErrorMessage("Warning: Clinical discharge has not been confirmed for this patient."); + } + getPatientEncounter().setDateOfDischarge(date); getDischargeController().setCurrent((Admission) getPatientEncounter()); getDischargeController().discharge(); diff --git a/src/main/java/com/divudi/bean/inward/InpatientClinicalDataController.java b/src/main/java/com/divudi/bean/inward/InpatientClinicalDataController.java index 03b38edc751..5fe19f2af9a 100644 --- a/src/main/java/com/divudi/bean/inward/InpatientClinicalDataController.java +++ b/src/main/java/com/divudi/bean/inward/InpatientClinicalDataController.java @@ -163,6 +163,9 @@ public class InpatientClinicalDataController implements Serializable { private List clinicalAssessments; private boolean viewOnly; + // Clinical discharge + private PatientEncounter clinicalDischargeRecord; + @Inject private FavouriteController favouriteController; @@ -2228,6 +2231,90 @@ public String navigateToDischargeMedicinesFromAdmission(PatientEncounter admissi return "/inward/inward_assessment_discharge_medicines?faces-redirect=true"; } + public String navigateToClinicalDischargeFromAdmission(PatientEncounter admission) { + this.parentAdmission = admission; + clinicalDischargeRecord = findOrCreateClinicalDischargeRecord(admission); + this.current = clinicalDischargeRecord; + fillCurrentPatientLists(admission.getPatient()); + fillCurrentEncounterLists(clinicalDischargeRecord); + return "/inward/inward_clinical_discharge?faces-redirect=true"; + } + + private PatientEncounter findOrCreateClinicalDischargeRecord(PatientEncounter admission) { + Map m = new HashMap<>(); + m.put("parent", admission); + m.put("type", PatientEncounterType.ClinicalDischarge); + m.put("ret", false); + String sql = "select e from PatientEncounter e " + + "where e.parentEncounter=:parent " + + "and e.patientEncounterType=:type " + + "and e.retired=:ret " + + "order by e.id desc"; + List existing = ejbFacade.findByJpql(sql, m, 1); + if (existing != null && !existing.isEmpty()) { + return existing.get(0); + } + PatientEncounter record = new PatientEncounter(); + record.setParentEncounter(admission); + record.setPatient(admission.getPatient()); + record.setPatientEncounterType(PatientEncounterType.ClinicalDischarge); + record.setEncounterDateTime(new Date()); + record.setInstitution(sessionController.getInstitution()); + record.setDepartment(sessionController.getDepartment()); + return record; + } + + public void saveClinicalDischarge() { + if (clinicalDischargeRecord == null) { + JsfUtil.addErrorMessage("No clinical discharge record found."); + return; + } + clinicalDischargeRecord.setDepartment(sessionController.getDepartment()); + if (clinicalDischargeRecord.getId() != null) { + getFacade().edit(clinicalDischargeRecord); + JsfUtil.addSuccessMessage("Clinical discharge details saved."); + } else { + clinicalDischargeRecord.setCreatedAt(new Date()); + clinicalDischargeRecord.setCreater(sessionController.getLoggedUser()); + getFacade().create(clinicalDischargeRecord); + JsfUtil.addSuccessMessage("Clinical discharge record created."); + } + fillCurrentEncounterLists(clinicalDischargeRecord); + } + + public void confirmClinicalDischarge() { + if (clinicalDischargeRecord == null) { + JsfUtil.addErrorMessage("No clinical discharge record found."); + return; + } + saveClinicalDischarge(); + parentAdmission.setClinicallyDischarged(Boolean.TRUE); + parentAdmission.setClinicalDischargeDateTime(new Date()); + parentAdmission.setClinicalDischargedBy(sessionController.getLoggedUser()); + getFacade().edit(parentAdmission); + JsfUtil.addSuccessMessage("Clinical discharge confirmed."); + } + + public void cancelClinicalDischarge() { + if (parentAdmission == null) { + JsfUtil.addErrorMessage("No admission found."); + return; + } + parentAdmission.setClinicallyDischarged(Boolean.FALSE); + parentAdmission.setClinicalDischargeDateTime(null); + parentAdmission.setClinicalDischargedBy(null); + getFacade().edit(parentAdmission); + JsfUtil.addSuccessMessage("Clinical discharge cancelled."); + } + + public PatientEncounter getClinicalDischargeRecord() { + return clinicalDischargeRecord; + } + + public void setClinicalDischargeRecord(PatientEncounter clinicalDischargeRecord) { + this.clinicalDischargeRecord = clinicalDischargeRecord; + } + public PatientEncounter getParentAdmission() { return parentAdmission; } diff --git a/src/main/java/com/divudi/bean/inward/RoomChangeController.java b/src/main/java/com/divudi/bean/inward/RoomChangeController.java index c71fdb518f3..26f0984f11c 100644 --- a/src/main/java/com/divudi/bean/inward/RoomChangeController.java +++ b/src/main/java/com/divudi/bean/inward/RoomChangeController.java @@ -20,6 +20,7 @@ import com.divudi.core.entity.inward.PatientRoom; import com.divudi.core.entity.inward.RoomFacilityCharge; import com.divudi.core.facade.AdmissionFacade; +import com.divudi.core.facade.PatientEncounterFacade; import com.divudi.core.facade.PatientFacade; import com.divudi.core.facade.PatientRoomFacade; import com.divudi.core.facade.PersonFacade; @@ -69,6 +70,8 @@ public class RoomChangeController implements Serializable { @EJB private AdmissionFacade ejbFacade; @EJB + private PatientEncounterFacade patientEncounterFacade; + @EJB private PersonFacade personFacade; @EJB private PatientFacade patientFacade; @@ -303,6 +306,14 @@ public void discharge(PatientRoom pR) { pR.setDischargedBy(getSessionController().getLoggedUser()); getPatientRoomFacade().edit(pR); notificationController.createNotification(pR, "Discharge"); + + // Sync room discharge to the parent encounter when this is the last (current) room + if (pR.getNextRoom() == null && pR.getPatientEncounter() != null) { + com.divudi.core.entity.PatientEncounter encounter = pR.getPatientEncounter(); + encounter.setRoomDischargeDateTime(pR.getDischargedAt()); + encounter.setRoomDischargedBy(getSessionController().getLoggedUser()); + patientEncounterFacade.edit(encounter); + } } public void dischargeWithCurrentTime(PatientRoom pR) { @@ -330,6 +341,14 @@ public void dischargeWithCurrentTime(PatientRoom pR) { pR.setDischargedBy(getSessionController().getLoggedUser()); getPatientRoomFacade().edit(pR); notificationController.createNotification(pR, "Discharge"); + + // Sync room discharge to the parent encounter when this is the last (current) room + if (pR.getNextRoom() == null && pR.getPatientEncounter() != null) { + com.divudi.core.entity.PatientEncounter encounter = pR.getPatientEncounter(); + encounter.setRoomDischargeDateTime(pR.getDischargedAt()); + encounter.setRoomDischargedBy(getSessionController().getLoggedUser()); + patientEncounterFacade.edit(encounter); + } } public void dischargeCancel(PatientRoom pR) { diff --git a/src/main/java/com/divudi/core/data/Privileges.java b/src/main/java/com/divudi/core/data/Privileges.java index 05eef470be4..d9af956b547 100644 --- a/src/main/java/com/divudi/core/data/Privileges.java +++ b/src/main/java/com/divudi/core/data/Privileges.java @@ -119,6 +119,7 @@ public enum Privileges { InwardBillSettleWithoutCheck("Inward Bill Settle Without Check"), TheaterIssueBHT("Theater Issue BHT"), InpatientClinicalAssessment("Inpatient Clinical Assessment"), + InpatientClinicalDischarge("Inpatient Clinical Discharge"), // // @@ -1013,6 +1014,7 @@ public String getCategory() { case InwardAppointmentUpdate: case InwardAppointmentCancel: case InpatientClinicalAssessment: + case InpatientClinicalDischarge: return "Inward"; default: diff --git a/src/main/java/com/divudi/core/data/SymanticType.java b/src/main/java/com/divudi/core/data/SymanticType.java index 0afaae812b8..9099bd634d6 100644 --- a/src/main/java/com/divudi/core/data/SymanticType.java +++ b/src/main/java/com/divudi/core/data/SymanticType.java @@ -159,5 +159,6 @@ public enum SymanticType { Employment, Blood_Group, Civil_status, - Relationships + Relationships, + Discharge_Condition } diff --git a/src/main/java/com/divudi/core/data/inward/PatientEncounterType.java b/src/main/java/com/divudi/core/data/inward/PatientEncounterType.java index 1c422a597a5..7ce5a21a1ab 100644 --- a/src/main/java/com/divudi/core/data/inward/PatientEncounterType.java +++ b/src/main/java/com/divudi/core/data/inward/PatientEncounterType.java @@ -15,5 +15,6 @@ public enum PatientEncounterType { Procedure, ETU, ClinicalAssessment, + ClinicalDischarge, } diff --git a/src/main/java/com/divudi/core/entity/PatientEncounter.java b/src/main/java/com/divudi/core/entity/PatientEncounter.java index 76ec8470c62..153c5284558 100644 --- a/src/main/java/com/divudi/core/entity/PatientEncounter.java +++ b/src/main/java/com/divudi/core/entity/PatientEncounter.java @@ -102,6 +102,19 @@ public class PatientEncounter implements Serializable, RetirableEntity { Date timeOfDischarge; @Temporal(javax.persistence.TemporalType.TIMESTAMP) Date dateOfDischarge; + + // Clinical discharge — set on the parent admission when doctor confirms clinical clearance + Boolean clinicallyDischarged = false; + @Temporal(javax.persistence.TemporalType.TIMESTAMP) + private Date clinicalDischargeDateTime; + @ManyToOne + private WebUser clinicalDischargedBy; + + // Room discharge mirror — set on the parent admission when the last room is vacated + @Temporal(javax.persistence.TemporalType.TIMESTAMP) + private Date roomDischargeDateTime; + @ManyToOne + private WebUser roomDischargedBy; double creditLimit; double creditUsedAmount; private double creditPaidAmount; @@ -307,6 +320,16 @@ public void setClaimable(boolean claimable) { String comments; @Lob private String planOfAction; + + // Clinical discharge content fields — used on the child ClinicalDischarge record + @ManyToOne + private ClinicalEntity dischargeCondition; + @Lob + private String followUpPlan; + @Lob + private String activityInstructions; + @Lob + private String dietInstructions; @Transient List diagnosis; @ManyToOne @@ -846,6 +869,82 @@ public void setPrintingDischargeTime(Date printingDischargeTime) { this.printingDischargeTime = printingDischargeTime; } + public Boolean getClinicallyDischarged() { + return clinicallyDischarged; + } + + public boolean isClinicallyDischarged() { + return Boolean.TRUE.equals(clinicallyDischarged); + } + + public void setClinicallyDischarged(Boolean clinicallyDischarged) { + this.clinicallyDischarged = clinicallyDischarged; + } + + public Date getClinicalDischargeDateTime() { + return clinicalDischargeDateTime; + } + + public void setClinicalDischargeDateTime(Date clinicalDischargeDateTime) { + this.clinicalDischargeDateTime = clinicalDischargeDateTime; + } + + public WebUser getClinicalDischargedBy() { + return clinicalDischargedBy; + } + + public void setClinicalDischargedBy(WebUser clinicalDischargedBy) { + this.clinicalDischargedBy = clinicalDischargedBy; + } + + public Date getRoomDischargeDateTime() { + return roomDischargeDateTime; + } + + public void setRoomDischargeDateTime(Date roomDischargeDateTime) { + this.roomDischargeDateTime = roomDischargeDateTime; + } + + public WebUser getRoomDischargedBy() { + return roomDischargedBy; + } + + public void setRoomDischargedBy(WebUser roomDischargedBy) { + this.roomDischargedBy = roomDischargedBy; + } + + public ClinicalEntity getDischargeCondition() { + return dischargeCondition; + } + + public void setDischargeCondition(ClinicalEntity dischargeCondition) { + this.dischargeCondition = dischargeCondition; + } + + public String getFollowUpPlan() { + return followUpPlan; + } + + public void setFollowUpPlan(String followUpPlan) { + this.followUpPlan = followUpPlan; + } + + public String getActivityInstructions() { + return activityInstructions; + } + + public void setActivityInstructions(String activityInstructions) { + this.activityInstructions = activityInstructions; + } + + public String getDietInstructions() { + return dietInstructions; + } + + public void setDietInstructions(String dietInstructions) { + this.dietInstructions = dietInstructions; + } + public Bill getFinalBill() { return finalBill; } diff --git a/src/main/webapp/emr/admin/discharge_conditions.xhtml b/src/main/webapp/emr/admin/discharge_conditions.xhtml new file mode 100644 index 00000000000..707067e5318 --- /dev/null +++ b/src/main/webapp/emr/admin/discharge_conditions.xhtml @@ -0,0 +1,99 @@ + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
diff --git a/src/main/webapp/inward/admission_profile.xhtml b/src/main/webapp/inward/admission_profile.xhtml index bab279c2810..b9030bb8885 100644 --- a/src/main/webapp/inward/admission_profile.xhtml +++ b/src/main/webapp/inward/admission_profile.xhtml @@ -422,6 +422,15 @@ icon="fa fa-prescription-bottle-alt" class="w-100"> + + + + + + + + + + + + +
+
+ + + + + +
+
+ + + +
+
+ + + +
+
+
+ + + + + + +
+
+ +
+
+
+ +
+
+ + + + + + + +
+
+
+ + +
+
+ + + + +
+
+
+
+ + + + + + + +
+ +
+
+ +
+
+ +
+
+
+ + + + + + + + + + + + + +
+
+ + + + + + + +
+
+
+ + +
+
+
+
+ + + + + + + +
+
+
+ +
+
+
+
+ + + + + + + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+ + + + + + + +
+ +
+
+
+ +
+
+
+
+