From 11cda0d7c08656b9d9c3c847e94916d22a1d56e7 Mon Sep 17 00:00:00 2001 From: Dr M H B Ariyaratne Date: Thu, 2 Apr 2026 09:18:34 +0530 Subject: [PATCH 1/3] Implement three-tier discharge system: clinical, room, and hospital discharge - Add PatientEncounterType.ClinicalDischarge and SymanticType.Discharge_Condition - Add InpatientClinicalDischarge privilege - Add clinical discharge fields to PatientEncounter (clinicallyDischarged, clinicalDischargeDateTime, clinicalDischargedBy, roomDischargeDateTime, roomDischargedBy, dischargeCondition, followUpPlan, activityInstructions, dietInstructions) - Add DischargeConditionController and discharge_conditions.xhtml admin page for configurable discharge condition values (Stable, DAMA, Referred, etc.) - Add inward_clinical_discharge.xhtml page with condition, diagnoses, summary, follow-up, activity/diet sections and confirm/cancel actions - Add navigateToClinicalDischargeFromAdmission, saveClinicalDischarge, confirmClinicalDischarge, cancelClinicalDischarge to InpatientClinicalDataController - Sync roomDischargeDateTime to PatientEncounter when last room is discharged - Warn on hospital discharge if clinical discharge not yet confirmed - Add Clinical Discharge button to admission_profile.xhtml (green/orange status) Closes #19657 Co-Authored-By: Claude Sonnet 4.6 --- .../DischargeConditionController.java | 222 +++++++++++++ .../bean/inward/BhtSummeryController.java | 4 + .../InpatientClinicalDataController.java | 87 ++++++ .../bean/inward/RoomChangeController.java | 8 + .../java/com/divudi/core/data/Privileges.java | 2 + .../com/divudi/core/data/SymanticType.java | 3 +- .../data/inward/PatientEncounterType.java | 1 + .../divudi/core/entity/PatientEncounter.java | 99 ++++++ .../emr/admin/discharge_conditions.xhtml | 99 ++++++ .../webapp/inward/admission_profile.xhtml | 8 + .../inward/inward_clinical_discharge.xhtml | 294 ++++++++++++++++++ 11 files changed, 826 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/divudi/bean/clinical/DischargeConditionController.java create mode 100644 src/main/webapp/emr/admin/discharge_conditions.xhtml create mode 100644 src/main/webapp/inward/inward_clinical_discharge.xhtml 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..9ef25fd781c --- /dev/null +++ b/src/main/java/com/divudi/bean/clinical/DischargeConditionController.java @@ -0,0 +1,222 @@ +/* + * 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("Saved"); + } else { + current.setCreatedAt(new Date()); + current.setCreater(getSessionController().getLoggedUser()); + getFacade().create(current); + JsfUtil.addSuccessMessage("Updated"); + } + 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) { + 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(); + 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()); + } + + FacesContext context = FacesContext.getCurrentInstance(); + 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()); + workbook.close(); + context.responseComplete(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + 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; + } + DischargeConditionController controller = (DischargeConditionController) facesContext.getApplication() + .getELResolver().getValue(facesContext.getELContext(), null, "dischargeConditionController"); + return controller.getEjbFacade().find(Long.valueOf(value)); + } + + @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..7350fcfd9b1 100644 --- a/src/main/java/com/divudi/bean/inward/RoomChangeController.java +++ b/src/main/java/com/divudi/bean/inward/RoomChangeController.java @@ -330,6 +330,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()); + ejbFacade.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..0f69021d0ce --- /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..06304fd1d95 100644 --- a/src/main/webapp/inward/admission_profile.xhtml +++ b/src/main/webapp/inward/admission_profile.xhtml @@ -422,6 +422,14 @@ icon="fa fa-prescription-bottle-alt" class="w-100"> + + + + + + + + + + + + +
+
+ + + + + +
+
+ + + +
+
+ + + +
+
+
+ + + + + + +
+
+ +
+
+
+ +
+
+ + + + + + + +
+
+
+ + +
+
+ + + + +
+
+
+
+ + + + + + + +
+ +
+
+ +
+
+ +
+
+
+ + + + + + + + + + + + + +
+
+ + + + + + + +
+
+
+ + +
+
+
+
+ + + + + + + +
+
+
+ +
+
+
+
+ + + + + + + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+ + + + + + + +
+ +
+
+
+ +
+
+
+
+ From 90225adbbf4bc7f98b8b1cc760da9fd4065b36c2 Mon Sep 17 00:00:00 2001 From: Dr M H B Ariyaratne Date: Thu, 2 Apr 2026 09:31:12 +0530 Subject: [PATCH 2/3] Fix compilation error: use PatientEncounterFacade for encounter update in RoomChangeController AdmissionFacade cannot accept PatientEncounter directly; inject and use PatientEncounterFacade for the roomDischargeDateTime sync. Co-Authored-By: Claude Sonnet 4.6 --- .../java/com/divudi/bean/inward/RoomChangeController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/divudi/bean/inward/RoomChangeController.java b/src/main/java/com/divudi/bean/inward/RoomChangeController.java index 7350fcfd9b1..63cae8e9587 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; @@ -336,7 +339,7 @@ public void dischargeWithCurrentTime(PatientRoom pR) { com.divudi.core.entity.PatientEncounter encounter = pR.getPatientEncounter(); encounter.setRoomDischargeDateTime(pR.getDischargedAt()); encounter.setRoomDischargedBy(getSessionController().getLoggedUser()); - ejbFacade.edit(encounter); + patientEncounterFacade.edit(encounter); } } From fd9a04dc34156616c73ec565972fb613509c1a59 Mon Sep 17 00:00:00 2001 From: Dr M H B Ariyaratne Date: Fri, 3 Apr 2026 04:32:32 +0530 Subject: [PATCH 3/3] Address CodeRabbit review comments on three-tier discharge system - Fix swapped success messages in DischargeConditionController.saveSelected() - Add null/empty guard in completeDischargeConditions() to prevent NPE - Fix row numbering bug in downloadAsExcel() (first row showed 2 instead of 1) - Use try-with-resources for Workbook and show user-facing error on failure - Wrap NumberFormatException in converter getAsObject() - Add privilege guard (InpatientClinicalDischarge) to discharge_conditions.xhtml form - Replace raw HTML labels with p:outputLabel in discharge_conditions.xhtml - Add for/id associations on labels in inward_clinical_discharge.xhtml - Add title tooltip to Clinical Discharge button in admission_profile.xhtml - Add room discharge sync logic to discharge() in RoomChangeController (was only in dischargeWithCurrentTime(), leaving inconsistent state when discharge() is called from BhtSummeryController) Co-Authored-By: Claude Sonnet 4.6 --- .../DischargeConditionController.java | 28 +++++++++++-------- .../bean/inward/RoomChangeController.java | 8 ++++++ .../emr/admin/discharge_conditions.xhtml | 8 +++--- .../webapp/inward/admission_profile.xhtml | 1 + .../inward/inward_clinical_discharge.xhtml | 5 ++-- 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/divudi/bean/clinical/DischargeConditionController.java b/src/main/java/com/divudi/bean/clinical/DischargeConditionController.java index 9ef25fd781c..b57fad799ab 100644 --- a/src/main/java/com/divudi/bean/clinical/DischargeConditionController.java +++ b/src/main/java/com/divudi/bean/clinical/DischargeConditionController.java @@ -65,12 +65,12 @@ public void saveSelected() { current.setSymanticType(SymanticType.Discharge_Condition); if (getCurrent().getId() != null && getCurrent().getId() > 0) { getFacade().edit(current); - JsfUtil.addSuccessMessage("Saved"); + JsfUtil.addSuccessMessage("Updated"); } else { current.setCreatedAt(new Date()); current.setCreater(getSessionController().getLoggedUser()); getFacade().create(current); - JsfUtil.addSuccessMessage("Updated"); + JsfUtil.addSuccessMessage("Saved"); } recreateModel(); getItems(); @@ -103,6 +103,9 @@ public List getItems() { } 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); @@ -117,8 +120,8 @@ public List completeDischargeConditions(String qry) { public void downloadAsExcel() { getItems(); - try { - Workbook workbook = new XSSFWorkbook(); + FacesContext context = FacesContext.getCurrentInstance(); + try (Workbook workbook = new XSSFWorkbook()) { Sheet sheet = workbook.createSheet("Discharge Conditions"); Row headerRow = sheet.createRow(0); @@ -129,23 +132,22 @@ public void downloadAsExcel() { int rowNum = 1; for (ClinicalEntity item : items) { - Row row = sheet.createRow(rowNum++); + 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++; } - FacesContext context = FacesContext.getCurrentInstance(); 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()); - workbook.close(); context.responseComplete(); } catch (Exception e) { - e.printStackTrace(); + JsfUtil.addErrorMessage("Error generating Excel file: " + e.getMessage()); } } @@ -200,9 +202,13 @@ public Object getAsObject(FacesContext facesContext, UIComponent component, Stri if (value == null || value.length() == 0) { return null; } - DischargeConditionController controller = (DischargeConditionController) facesContext.getApplication() - .getELResolver().getValue(facesContext.getELContext(), null, "dischargeConditionController"); - return controller.getEjbFacade().find(Long.valueOf(value)); + 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 diff --git a/src/main/java/com/divudi/bean/inward/RoomChangeController.java b/src/main/java/com/divudi/bean/inward/RoomChangeController.java index 63cae8e9587..26f0984f11c 100644 --- a/src/main/java/com/divudi/bean/inward/RoomChangeController.java +++ b/src/main/java/com/divudi/bean/inward/RoomChangeController.java @@ -306,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) { diff --git a/src/main/webapp/emr/admin/discharge_conditions.xhtml b/src/main/webapp/emr/admin/discharge_conditions.xhtml index 0f69021d0ce..707067e5318 100644 --- a/src/main/webapp/emr/admin/discharge_conditions.xhtml +++ b/src/main/webapp/emr/admin/discharge_conditions.xhtml @@ -7,7 +7,7 @@ xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://primefaces.org/ui"> - + @@ -57,7 +57,7 @@
- +
- +
- +
diff --git a/src/main/webapp/inward/inward_clinical_discharge.xhtml b/src/main/webapp/inward/inward_clinical_discharge.xhtml index 27e3a7daf8a..7ce72add000 100644 --- a/src/main/webapp/inward/inward_clinical_discharge.xhtml +++ b/src/main/webapp/inward/inward_clinical_discharge.xhtml @@ -96,8 +96,9 @@ placeholder="Select discharge condition" />
- + @@ -182,7 +183,7 @@
- +