diff --git a/src/main/java/com/divudi/bean/common/NotificationController.java b/src/main/java/com/divudi/bean/common/NotificationController.java index 69cbe025cd..d15871e201 100644 --- a/src/main/java/com/divudi/bean/common/NotificationController.java +++ b/src/main/java/com/divudi/bean/common/NotificationController.java @@ -127,6 +127,15 @@ public void createNotification(PatientRoom pr, String action) { case "Discharge": createInwardRoomDischargeNotifications(pr); break; + case "Admit": + createInwardRoomAdmitNotifications(pr); + break; + case "Change": + createInwardRoomChangeNotifications(pr); + break; + case "CancelDischarge": + createInwardRoomDischargeCancelNotifications(pr); + break; default: throw new AssertionError(); @@ -165,6 +174,62 @@ private void createInwardRoomDischargeNotifications(PatientRoom pr) { } } + private void createInwardRoomAdmitNotifications(PatientRoom pr) { + Date date = new Date(); + for (TriggerType tt : TriggerType.getTriggersByParent(TriggerTypeParent.INWARD_PATIENT_ROOM_ADDED)) { + Notification nn = new Notification(); + nn.setCreatedAt(date); + nn.setPatientRoom(pr); + nn.setTriggerType(tt); + nn.setCreater(sessionController.getLoggedUser()); + String roomName = pr.getRoomFacilityCharge() != null ? pr.getRoomFacilityCharge().getName() : "Unknown Room"; + String patientName = ""; + if (pr.getPatientEncounter() != null && pr.getPatientEncounter().getPatient() != null + && pr.getPatientEncounter().getPatient().getPerson() != null) { + patientName = pr.getPatientEncounter().getPatient().getPerson().getNameWithTitle(); + } + String msg = "Patient " + patientName + " admitted to " + roomName; + nn.setMessage(msg); + getFacade().create(nn); + userNotificationController.createUserNotifications(nn); + } + } + + private void createInwardRoomChangeNotifications(PatientRoom pr) { + Date date = new Date(); + for (TriggerType tt : TriggerType.getTriggersByParent(TriggerTypeParent.INWARD_PATIENT_ROOM_CHANGED)) { + Notification nn = new Notification(); + nn.setCreatedAt(date); + nn.setPatientRoom(pr); + nn.setTriggerType(tt); + nn.setCreater(sessionController.getLoggedUser()); + String roomName = pr.getRoomFacilityCharge() != null ? pr.getRoomFacilityCharge().getName() : "Unknown Room"; + String msg = "Patient moved to " + roomName; + if (pr.getPatientEncounter() != null && pr.getPatientEncounter().getBhtNo() != null) { + msg = msg + " (BHT: " + pr.getPatientEncounter().getBhtNo() + ")"; + } + nn.setMessage(msg); + getFacade().create(nn); + userNotificationController.createUserNotifications(nn); + } + } + + private void createInwardRoomDischargeCancelNotifications(PatientRoom pr) { + Date date = new Date(); + for (TriggerType tt : TriggerType.getTriggersByParent(TriggerTypeParent.INWARD_PATIENT_ROOM_DISCHARGED)) { + Notification nn = new Notification(); + nn.setCreatedAt(date); + nn.setPatientRoom(pr); + nn.setTriggerType(tt); + nn.setCreater(sessionController.getLoggedUser()); + String roomName = pr.getRoomFacilityCharge() != null ? pr.getRoomFacilityCharge().getName() : "Unknown Room"; + String msg = "Room Discharge Cancelled for " + roomName; + nn.setMessage(msg); + getFacade().create(nn); + userNotificationController.createUserNotifications(nn); + } + } + private void createInwardClinicalDischargeNotifications(PatientEncounter pe) { Date date = new Date(); for (TriggerType tt : TriggerType.getTriggersByParent(TriggerTypeParent.INWARD_PATIENT_CLINICAL_DISCHARGED)) { diff --git a/src/main/java/com/divudi/bean/inward/AdmissionController.java b/src/main/java/com/divudi/bean/inward/AdmissionController.java index cdc82d5498..8a3659af7a 100644 --- a/src/main/java/com/divudi/bean/inward/AdmissionController.java +++ b/src/main/java/com/divudi/bean/inward/AdmissionController.java @@ -762,6 +762,34 @@ public String navigateToPatientRoomDetails() { return bhtSummeryController.navigateToPatientRoomDetails(); } + /** + * Returns the list of currently active (non-discharged, non-retired) + * PatientRoom and GuardianRoom records for the current admission. + * Used by the dashboard "Room Management" panel to display current room + * assignments. + */ + public List getActivePatientRooms() { + if (current == null) { + return java.util.Collections.emptyList(); + } + List activeRooms = new java.util.ArrayList<>(); + try { + String jpql = "SELECT pr FROM PatientRoom pr " + + "WHERE pr.retired = false " + + "AND pr.discharged = false " + + "AND pr.patientEncounter = :enc " + + "ORDER BY pr.createdAt"; + java.util.HashMap params = new java.util.HashMap<>(); + params.put("enc", current); + activeRooms = patientRoomFacade.findByJpql(jpql, params); + } catch (Exception e) { + logger.log(Level.SEVERE, "Failed to load active rooms for admission ID: " + + (current != null ? current.getId() : null), e); + JsfUtil.addErrorMessage("Unable to load current room assignments."); + } + return activeRooms; + } + public String navigateToAddBabyAdmission() { parentAdmission = current; Admission ad = new Admission(); diff --git a/src/main/java/com/divudi/bean/inward/RoomChangeController.java b/src/main/java/com/divudi/bean/inward/RoomChangeController.java index f060e9d6a4..5b206c5ea5 100644 --- a/src/main/java/com/divudi/bean/inward/RoomChangeController.java +++ b/src/main/java/com/divudi/bean/inward/RoomChangeController.java @@ -66,6 +66,8 @@ public class RoomChangeController implements Serializable { NotificationController notificationController; @Inject private InwardBeanController inwardBean; + @Inject + private com.divudi.bean.common.AuditEventApplicationController auditEventApplicationController; @EJB private AdmissionFacade ejbFacade; @@ -84,6 +86,7 @@ public class RoomChangeController implements Serializable { private List patientRoom; private PatientRoom currentPatientRoom; + private PatientRoom roomForReceipt; List selectedItems; private Admission current; private List items = null; @@ -242,6 +245,9 @@ public void admitRoom() { current.setRoomAdmitted(true); ejbFacade.edit(current); + recordRoomAuditEvent(getCurrentPatientRoom(), "Room Admitted", null); + notificationController.createNotification(getCurrentPatientRoom(), "Admit"); + JsfUtil.addSuccessMessage("Room admitted successfully"); } @@ -281,6 +287,10 @@ public void remove(PatientRoom pR) { return; } + String beforeState = "roomFacilityCharge=" + (pR.getRoomFacilityCharge() != null ? pR.getRoomFacilityCharge().getName() : "null") + + ", admittedAt=" + pR.getAdmittedAt() + + ", discharged=" + pR.isDischarged(); + pR.setRetirer(getSessionController().getLoggedUser()); pR.setRetired(true); pR.setRetiredAt(new Date()); @@ -294,6 +304,8 @@ public void remove(PatientRoom pR) { getCurrent().setCurrentPatientRoom(pR.getPreviousRoom()); getEjbFacade().edit(getCurrent()); } + + recordRoomAuditEvent(pR, "Room Removed", beforeState); } public void discharge(PatientRoom pR) { @@ -314,6 +326,8 @@ public void discharge(PatientRoom pR) { encounter.setRoomDischargedBy(getSessionController().getLoggedUser()); patientEncounterFacade.edit(encounter); } + + recordRoomAuditEvent(pR, "Room Discharged", null); } public void dischargeWithCurrentTime(PatientRoom pR) { @@ -323,7 +337,7 @@ public void dischargeWithCurrentTime(PatientRoom pR) { List rooms = bhtSummeryController.getPatientRooms(); if (rooms != null) { if (rooms.size() > 1) { - int currentRoomIndex = rooms.indexOf(patientRoom); + int currentRoomIndex = rooms.indexOf(pR); if (currentRoomIndex > 0) { PatientRoom previousRoom = rooms.get(currentRoomIndex - 1); @@ -349,12 +363,83 @@ public void dischargeWithCurrentTime(PatientRoom pR) { encounter.setRoomDischargedBy(getSessionController().getLoggedUser()); patientEncounterFacade.edit(encounter); } + + // Record audit event + recordRoomAuditEvent(pR, "Room Discharged", null); + + JsfUtil.addSuccessMessage("Successfully Discharged from Room"); } public void dischargeCancel(PatientRoom pR) { + if (pR == null) { + return; + } + // Capture state before cancel for audit + String beforeState = "discharged=true, dischargedAt=" + pR.getDischargedAt() + + ", dischargedBy=" + (pR.getDischargedBy() != null ? pR.getDischargedBy().getName() : "null"); + pR.setDischarged(false); pR.setDischargedBy(null); + pR.setDischargedAt(null); getPatientRoomFacade().edit(pR); + + // Record audit event + recordRoomAuditEvent(pR, "Room Discharge Cancelled", beforeState); + notificationController.createNotification(pR, "CancelDischarge"); + + JsfUtil.addSuccessMessage("Room Discharge Cancelled"); + } + + private void recordRoomAuditEvent(PatientRoom pr, String trigger, String beforeState) { + try { + com.divudi.core.entity.AuditEvent auditEvent = new com.divudi.core.entity.AuditEvent(); + auditEvent.setEventDataTime(new Date()); + auditEvent.setEventTrigger(trigger); + auditEvent.setEntityType(pr.getClass().getSimpleName()); + auditEvent.setObjectId(pr.getId()); + if (pr.getPatientEncounter() != null) { + auditEvent.setUrl("BHT: " + pr.getPatientEncounter().getBhtNo() + + " | Room: " + (pr.getRoomFacilityCharge() != null ? pr.getRoomFacilityCharge().getName() : "N/A")); + } + if (sessionController.getLoggedUser() != null) { + auditEvent.setWebUserId(sessionController.getLoggedUser().getId()); + } + if (sessionController.getInstitution() != null) { + auditEvent.setInstitutionId(sessionController.getInstitution().getId()); + } + if (sessionController.getDepartment() != null) { + auditEvent.setDepartmentId(sessionController.getDepartment().getId()); + } + if (beforeState != null) { + auditEvent.setBeforeJson(beforeState); + } + String afterState = "discharged=" + pr.isDischarged() + + ", dischargedAt=" + pr.getDischargedAt() + + ", dischargedBy=" + (pr.getDischargedBy() != null ? pr.getDischargedBy().getName() : "null"); + auditEvent.setAfterJson(afterState); + auditEvent.setEventStatus("Completed"); + auditEvent.setEventDuration(new Date().getTime() - auditEvent.getEventDataTime().getTime()); + auditEventApplicationController.logAuditEvent(auditEvent); + } catch (Exception e) { + // Silently fail â€" audit failure should not block the user action + } + } + + public String navigateToRoomDischargeReceipt(PatientRoom pr) { + if (pr == null) { + JsfUtil.addErrorMessage("No room selected"); + return ""; + } + this.roomForReceipt = pr; + return "/inward/inward_room_discharge_receipt?faces-redirect=true"; + } + + public PatientRoom getRoomForReceipt() { + return roomForReceipt; + } + + public void setRoomForReceipt(PatientRoom roomForReceipt) { + this.roomForReceipt = roomForReceipt; } public void removeRoom(PatientRoom pR) { @@ -362,11 +447,13 @@ public void removeRoom(PatientRoom pR) { JsfUtil.addErrorMessage("No Patient Room Detected"); return; } + String beforeState = "room=" + (pR.getRoomFacilityCharge() != null ? pR.getRoomFacilityCharge().getName() : "null"); pR.setRetired(true); pR.setRetiredAt(new Date()); pR.setRetirer(sessionController.getWebUser()); getPatientRoomFacade().edit(pR); bhtSummeryController.setPatientRooms(null); + recordRoomAuditEvent(pR, "Room Removed", beforeState); } public void removeGuardianRoom(PatientRoom pR) { @@ -376,6 +463,7 @@ public void removeGuardianRoom(PatientRoom pR) { return; } + String beforeState = "room=" + (pR.getRoomFacilityCharge() != null ? pR.getRoomFacilityCharge().getName() : "null"); pR.setRetirer(getSessionController().getLoggedUser()); pR.setRetired(true); pR.setRetiredAt(new Date()); @@ -388,6 +476,7 @@ public void removeGuardianRoom(PatientRoom pR) { getPatientRoomFacade().edit(pR.getPreviousRoom()); } + recordRoomAuditEvent(pR, "Guardian Room Removed", beforeState); } public void recreate() { @@ -456,6 +545,9 @@ public void change() { // Save consultant information saveConsultantInfo(newPatientRoom); + recordRoomAuditEvent(newPatientRoom, "Room Changed", "previousRoom=" + (oldPatientRoom.getRoomFacilityCharge() != null ? oldPatientRoom.getRoomFacilityCharge().getName() : "null")); + notificationController.createNotification(newPatientRoom, "Change"); + if (newConsultant != null || newPrimeConsultant != null) { JsfUtil.addSuccessMessage("Room changed with consultant details"); } else { @@ -489,7 +581,10 @@ public void addNewRoom() { // Save consultant information saveConsultantInfo(newPatientRoom); - JsfUtil.addSuccessMessage("Successfully Room Changed"); + recordRoomAuditEvent(newPatientRoom, "Room Added", null); + notificationController.createNotification(newPatientRoom, "Admit"); + + JsfUtil.addSuccessMessage("Successfully Room Added"); newRoomFacilityCharge = null; changeAt = null; @@ -535,7 +630,10 @@ public void changeGurdianRoom() { getPatientRoomFacade().edit(oldGaurdianRoom); } - JsfUtil.addSuccessMessage("Successfully Room Changed"); + recordRoomAuditEvent(newGuardianRoom, "Guardian Room Changed", oldGaurdianRoom != null ? "previousRoom=" + (oldGaurdianRoom.getRoomFacilityCharge() != null ? oldGaurdianRoom.getRoomFacilityCharge().getName() : "null") : "first guardian room"); + notificationController.createNotification(newGuardianRoom, "Change"); + + JsfUtil.addSuccessMessage("Successfully Guardian Room Changed"); newRoomFacilityCharge = null; changeAt = null; createGuardianRoom(); diff --git a/src/main/java/com/divudi/core/data/TriggerType.java b/src/main/java/com/divudi/core/data/TriggerType.java index 3a215f5a35..ce3abacc7c 100644 --- a/src/main/java/com/divudi/core/data/TriggerType.java +++ b/src/main/java/com/divudi/core/data/TriggerType.java @@ -63,7 +63,15 @@ public enum TriggerType { // Inward discharge — stage 2: room/bed discharge (bed released; typically notifies billing). INWARD_PATIENT_ROOM_DISCHARGED("Inward Patient Room Discharge - System Notification", NotificationMedium.SYSTEM_NOTIFICATION, TriggerTypeParent.INWARD_PATIENT_ROOM_DISCHARGED), INWARD_PATIENT_ROOM_DISCHARGED_SMS("Inward Patient Room Discharge - SMS", NotificationMedium.SMS, TriggerTypeParent.INWARD_PATIENT_ROOM_DISCHARGED), - INWARD_PATIENT_ROOM_DISCHARGED_EMAIL("Inward Patient Room Discharge - Email", NotificationMedium.EMAIL, TriggerTypeParent.INWARD_PATIENT_ROOM_DISCHARGED); + INWARD_PATIENT_ROOM_DISCHARGED_EMAIL("Inward Patient Room Discharge - Email", NotificationMedium.EMAIL, TriggerTypeParent.INWARD_PATIENT_ROOM_DISCHARGED), + // Inward room — patient admitted/added to a room + INWARD_PATIENT_ROOM_ADDED("Inward Patient Room Added - System Notification", NotificationMedium.SYSTEM_NOTIFICATION, TriggerTypeParent.INWARD_PATIENT_ROOM_ADDED), + INWARD_PATIENT_ROOM_ADDED_SMS("Inward Patient Room Added - SMS", NotificationMedium.SMS, TriggerTypeParent.INWARD_PATIENT_ROOM_ADDED), + INWARD_PATIENT_ROOM_ADDED_EMAIL("Inward Patient Room Added - Email", NotificationMedium.EMAIL, TriggerTypeParent.INWARD_PATIENT_ROOM_ADDED), + // Inward room — patient changed/moved to a different room + INWARD_PATIENT_ROOM_CHANGED("Inward Patient Room Changed - System Notification", NotificationMedium.SYSTEM_NOTIFICATION, TriggerTypeParent.INWARD_PATIENT_ROOM_CHANGED), + INWARD_PATIENT_ROOM_CHANGED_SMS("Inward Patient Room Changed - SMS", NotificationMedium.SMS, TriggerTypeParent.INWARD_PATIENT_ROOM_CHANGED), + INWARD_PATIENT_ROOM_CHANGED_EMAIL("Inward Patient Room Changed - Email", NotificationMedium.EMAIL, TriggerTypeParent.INWARD_PATIENT_ROOM_CHANGED); private final String label; private final NotificationMedium medium; diff --git a/src/main/java/com/divudi/core/data/TriggerTypeParent.java b/src/main/java/com/divudi/core/data/TriggerTypeParent.java index 085c0c6fcd..101af72cc4 100644 --- a/src/main/java/com/divudi/core/data/TriggerTypeParent.java +++ b/src/main/java/com/divudi/core/data/TriggerTypeParent.java @@ -13,6 +13,8 @@ public enum TriggerTypeParent { OPD_BILL_CANCELLATION, INWARD_PATIENT_CLINICAL_DISCHARGED, INWARD_PATIENT_ROOM_DISCHARGED, + INWARD_PATIENT_ROOM_ADDED, + INWARD_PATIENT_ROOM_CHANGED, INWARD_PATIENT_DISCHARGED, FLOAT_TRANSFER_REQUEST } diff --git a/src/main/webapp/inward/admission_profile.xhtml b/src/main/webapp/inward/admission_profile.xhtml index b2aaf13996..06e7a19e5d 100644 --- a/src/main/webapp/inward/admission_profile.xhtml +++ b/src/main/webapp/inward/admission_profile.xhtml @@ -512,8 +512,79 @@
+ +
+ +
+ + Not admitted to any room +
+
+ +
+
+
+ + + + Guardian + +
+
+ + Admitted: + + + +
+
+ + +
+
+
+ + +
+ + + +
+
+ + + +
+
- + + - + + - + + + + + + + + + + + + + +
+ +

No room discharge record selected. Please navigate from the Patient Room Details page.

+ + +
+
+ + + + +
+
+ + + + +
+ + + +
+ + +
+ +
+

+ +

+

Room Discharge Receipt

+
+
+ + + + + + + + + + + + + + + +
Patient Name + + BHT No + +
Age + + Admission Date + + + +
+ +
+ + + + + + + + + + + + + + + + + + + +
Room + + + Guardian + + Status + Discharged +
Admitted At + + + + Discharged At + + + +
Duration + (See admitted and discharged times above) +
+ +
+ + + + + + + + + +
Discharged By + + Printed By + +
+ + +
+
+

This is a computer-generated receipt.

+
+
+
+ +
+ + + + + + + + +
+
+
+