diff --git a/api-tests/pom.xml b/api-tests/pom.xml index 7e231352a..786767c64 100644 --- a/api-tests/pom.xml +++ b/api-tests/pom.xml @@ -61,6 +61,10 @@ org.openmrs.module providermanagement-api + + org.bahmni.module + appointments-api + org.openmrs.test openmrs-test diff --git a/api-tests/src/test/java/org/openmrs/module/htmlformentry/AppointmentTagTest.java b/api-tests/src/test/java/org/openmrs/module/htmlformentry/AppointmentTagTest.java new file mode 100644 index 000000000..676b73d35 --- /dev/null +++ b/api-tests/src/test/java/org/openmrs/module/htmlformentry/AppointmentTagTest.java @@ -0,0 +1,287 @@ +package org.openmrs.module.htmlformentry; + +import java.util.Date; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.api.context.Context; +import org.openmrs.module.appointments.model.Appointment; +import org.openmrs.module.appointments.model.AppointmentStatus; +import org.openmrs.module.appointments.service.AppointmentsService; +import org.springframework.mock.web.MockHttpServletRequest; + +public class AppointmentTagTest extends BaseHtmlFormEntryTest { + + @Before + public void loadTestData() throws Exception { + executeVersionedDataSet("org/openmrs/module/htmlformentry/data/RegressionTest-data-openmrs-2.3.xml"); + executeVersionedDataSet("org/openmrs/module/htmlformentry/data/appointmentCheckInTest.xml"); + } + + @Test + public void testAppointmentCheckInTag_shouldDisplayCheckboxesForScheduledAppointmentsForPatient() throws Exception { + new RegressionTestHelper() { + + @Override + public String getFormName() { + return "appointmentCheckInForm"; + } + + @Override + public String[] widgetLabels() { + return new String[] { "Date:", "Location:", "Provider:", "Appointments:" }; + } + + @Override + public void testBlankFormHtml(String html) { + TestUtil.assertFuzzyContains("value=\"05f2ad92-1cc8-4cec-bf54-9cac0200746d\"/>", html); + TestUtil.assertFuzzyContains("2024-08-16, 13:00:00 - of Cos, Hippocrates - Never Never Land", html); + TestUtil.assertFuzzyContains("value=\"75504r42-3ca8-11e3-bf2b-0800271c1111\"/>", html); + TestUtil.assertFuzzyContains("2024-08-15, 12:00:00 - of Cos, Hippocrates - Xanadu", html); + } + }.run(); + } + + @Test + public void testAppointmentCheckInTag_shouldCheckPatientInForSingleAppointment() throws Exception { + final Date date = new Date(); + new RegressionTestHelper() { + + @Override + public String getFormName() { + return "appointmentCheckInForm"; + } + + @Override + public String[] widgetLabels() { + return new String[] { "Date:", "Location:", "Provider:", "Appointments:" }; + } + + @Override + public void setupRequest(MockHttpServletRequest request, Map widgets) { + request.addParameter(widgets.get("Date:"), dateAsString(date)); + request.addParameter(widgets.get("Location:"), "2"); + request.addParameter(widgets.get("Provider:"), "502"); + request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + } + + @Override + public void testResults(SubmissionResults results) { + results.assertNoErrors(); + results.assertEncounterCreated(); + Appointment appointment1 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + Assert.assertEquals(AppointmentStatus.CheckedIn, appointment1.getStatus()); + Assert.assertEquals(results.getEncounterCreated(), + appointment1.getFulfillingEncounters().stream().findFirst().get()); + + // just confirm the other appointment has not been checked in or linked to the encounter + Appointment appointment2 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("75504r42-3ca8-11e3-bf2b-0800271c1111"); + Assert.assertEquals(AppointmentStatus.Scheduled, appointment2.getStatus()); + Assert.assertEquals(0, appointment2.getFulfillingEncounters().size()); + } + }.run(); + } + + @Test + public void testAppointmentCheckInTag_shouldCheckPatientInForMultipleAppointments() throws Exception { + final Date date = new Date(); + new RegressionTestHelper() { + + @Override + public String getFormName() { + return "appointmentCheckInForm"; + } + + @Override + public String[] widgetLabels() { + return new String[] { "Date:", "Location:", "Provider:", "Appointments:" }; + } + + @Override + public void setupRequest(MockHttpServletRequest request, Map widgets) { + request.addParameter(widgets.get("Date:"), dateAsString(date)); + request.addParameter(widgets.get("Location:"), "2"); + request.addParameter(widgets.get("Provider:"), "502"); + request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + request.addParameter(widgets.get("Appointments:").replace("_1", "_2"), + "75504r42-3ca8-11e3-bf2b-0800271c1111"); + } + + @Override + public void testResults(SubmissionResults results) { + results.assertNoErrors(); + results.assertEncounterCreated(); + + Appointment appointment1 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + Assert.assertEquals(AppointmentStatus.CheckedIn, appointment1.getStatus()); + Assert.assertEquals(results.getEncounterCreated(), + appointment1.getFulfillingEncounters().stream().findFirst().get()); + + Appointment appointment2 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("75504r42-3ca8-11e3-bf2b-0800271c1111"); + Assert.assertEquals(AppointmentStatus.CheckedIn, appointment2.getStatus()); + Assert.assertEquals(results.getEncounterCreated(), + appointment2.getFulfillingEncounters().stream().findFirst().get()); + } + }.run(); + } + + @Test + public void testAppointmentCheckInTag_editShouldDisassociateEncounterFromAppointmentAndAssociateNewEncounter() + throws Exception { + final Date date = new Date(); + new RegressionTestHelper() { + + @Override + public String getFormName() { + return "appointmentCheckInForm"; + } + + @Override + public String[] widgetLabels() { + return new String[] { "Date:", "Location:", "Provider:", "Appointments:" }; + } + + @Override + public void setupRequest(MockHttpServletRequest request, Map widgets) { + request.addParameter(widgets.get("Date:"), dateAsString(date)); + request.addParameter(widgets.get("Location:"), "2"); + request.addParameter(widgets.get("Provider:"), "502"); + request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + } + + @Override + public boolean doEditEncounter() { + return true; + } + + @Override + public String[] widgetLabelsForEdit() { + return new String[] { "Appointments:" }; + } + + public void setupEditRequest(MockHttpServletRequest request, Map widgets) { + request.addParameter(widgets.get("Appointments:"), ""); + request.addParameter(widgets.get("Appointments:").replace("_1", "_2"), + "75504r42-3ca8-11e3-bf2b-0800271c1111"); + } + + @Override + public void testResults(SubmissionResults results) { + results.assertNoErrors(); + results.assertEncounterCreated(); + + // sanity check + Appointment appointment1 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + Assert.assertEquals(AppointmentStatus.CheckedIn, appointment1.getStatus()); + Assert.assertEquals(results.getEncounterCreated(), + appointment1.getFulfillingEncounters().stream().findFirst().get()); + + Appointment appointment2 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("75504r42-3ca8-11e3-bf2b-0800271c1111"); + Assert.assertEquals(AppointmentStatus.Scheduled, appointment2.getStatus()); + Assert.assertEquals(0, appointment2.getFulfillingEncounters().size()); + } + + // confirm that the associated appointment is now checked by default + @Override + public void testEditFormHtml(String html) { + TestUtil.assertFuzzyContains("value=\"05f2ad92-1cc8-4cec-bf54-9cac0200746d\" checked=\"true\"/>", html); + } + + @Override + public void testEditedResults(SubmissionResults results) { + results.assertNoErrors(); + results.assertEncounterCreated(); + + Appointment appointment1 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + Assert.assertEquals(AppointmentStatus.CheckedIn, appointment1.getStatus()); // Note that we are NOT changing the status back to Scheduled + // TODO debug after testing IRL + Assert.assertEquals(0, appointment1.getFulfillingEncounters().size()); // but encounter should be removed + + Appointment appointment2 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("75504r42-3ca8-11e3-bf2b-0800271c1111"); + Assert.assertEquals(AppointmentStatus.CheckedIn, appointment2.getStatus()); + Assert.assertEquals(results.getEncounterCreated(), + appointment2.getFulfillingEncounters().stream().findFirst().get()); + } + }.run(); + } + + @Test + public void testAppointmentCheckInTag_editingFormWithoutChangingStatusShouldNotCauseError() throws Exception { + final Date date = new Date(); + new RegressionTestHelper() { + + @Override + public String getFormName() { + return "appointmentCheckInForm"; + } + + @Override + public String[] widgetLabels() { + return new String[] { "Date:", "Location:", "Provider:", "Appointments:" }; + } + + @Override + public void setupRequest(MockHttpServletRequest request, Map widgets) { + request.addParameter(widgets.get("Date:"), dateAsString(date)); + request.addParameter(widgets.get("Location:"), "2"); + request.addParameter(widgets.get("Provider:"), "502"); + request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + } + + @Override + public boolean doEditEncounter() { + return true; + } + + @Override + public String[] widgetLabelsForEdit() { + return new String[] { "Appointments:" }; + } + + public void setupEditRequest(MockHttpServletRequest request, Map widgets) { + // upon edit keep the same checked + request.addParameter(widgets.get("Appointments:"), "05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + } + + @Override + public void testResults(SubmissionResults results) { + results.assertNoErrors(); + results.assertEncounterCreated(); + + // sanity check + Appointment appointment1 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + Assert.assertEquals(AppointmentStatus.CheckedIn, appointment1.getStatus()); + Assert.assertEquals(results.getEncounterCreated(), + appointment1.getFulfillingEncounters().stream().findFirst().get()); + } + + @Override + public void testEditedResults(SubmissionResults results) { + results.assertNoErrors(); + results.assertEncounterCreated(); + + Appointment appointment1 = Context.getService(AppointmentsService.class) + .getAppointmentByUuid("05f2ad92-1cc8-4cec-bf54-9cac0200746d"); + Assert.assertEquals(AppointmentStatus.CheckedIn, appointment1.getStatus()); + Assert.assertEquals(results.getEncounterCreated(), + appointment1.getFulfillingEncounters().stream().findFirst().get()); + } + }.run(); + } +} + +// TODOS: make sure test to remove works +// TODOs: review specs +// TODOs: add to check in form for SL and format/test IRL! +// TODOs ticket separate functionality to pass in appointment to automatically mark as checked in diff --git a/api-tests/src/test/resources/test-hibernate.cfg.xml b/api-tests/src/test/resources/test-hibernate.cfg.xml index a237592da..e99851903 100644 --- a/api-tests/src/test/resources/test-hibernate.cfg.xml +++ b/api-tests/src/test/resources/test-hibernate.cfg.xml @@ -5,6 +5,15 @@ + + + + + + + + + diff --git a/api/pom.xml b/api/pom.xml index bb0f99977..b79f82cbd 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -45,6 +45,11 @@ org.openmrs.module providermanagement-api + + org.bahmni.module + appointments-api + + diff --git a/api/src/main/java/org/openmrs/module/htmlformentry/FormEntryContext.java b/api/src/main/java/org/openmrs/module/htmlformentry/FormEntryContext.java index 4690f8885..59acbe7eb 100644 --- a/api/src/main/java/org/openmrs/module/htmlformentry/FormEntryContext.java +++ b/api/src/main/java/org/openmrs/module/htmlformentry/FormEntryContext.java @@ -151,21 +151,31 @@ public Mode getMode() { * @param widget the widget to register * @return the field id used to identify this widget in the HTML Form */ - public String registerWidget(Widget widget) { + // TODO update documentation + public String registerWidget(Widget widget, String fieldName) { if (fieldNames.containsKey(widget)) throw new IllegalArgumentException("This widget is already registered"); - int thisVal = 0; - synchronized (sequenceNextVal) { - thisVal = sequenceNextVal; - sequenceNextVal = sequenceNextVal + 1; + + if (StringUtils.isBlank(fieldName)) { + int thisVal = 0; + synchronized (sequenceNextVal) { + thisVal = sequenceNextVal; + sequenceNextVal = sequenceNextVal + 1; + } + fieldName = "w" + thisVal; } - String fieldName = "w" + thisVal; + fieldNames.put(widget, fieldName); if (log.isTraceEnabled()) log.trace("Registered widget " + widget.getClass() + " as " + fieldName); return fieldName; } + // TODO document + public String registerWidget(Widget widget) { + return registerWidget(widget, null); + } + /** * Registers an error widget within the Context * diff --git a/api/src/main/java/org/openmrs/module/htmlformentry/FormEntrySession.java b/api/src/main/java/org/openmrs/module/htmlformentry/FormEntrySession.java index baadf0542..1dc08d95b 100644 --- a/api/src/main/java/org/openmrs/module/htmlformentry/FormEntrySession.java +++ b/api/src/main/java/org/openmrs/module/htmlformentry/FormEntrySession.java @@ -28,6 +28,9 @@ import org.openmrs.Relationship; import org.openmrs.api.ObsService; import org.openmrs.api.context.Context; +import org.openmrs.module.appointments.model.Appointment; +import org.openmrs.module.appointments.model.AppointmentStatus; +import org.openmrs.module.appointments.service.AppointmentsService; import org.openmrs.module.htmlformentry.FormEntryContext.Mode; import org.openmrs.module.htmlformentry.property.ExitFromCareProperty; import org.openmrs.module.htmlformentry.velocity.VelocityContextContentProvider; @@ -44,6 +47,7 @@ import java.io.StringWriter; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -636,6 +640,31 @@ public void applyActions() throws BadFormDesignException { } } + // handle appointments (needs to happen after encounter is saved?) + if (submissionActions.getAppointmentsToMarkCheckedInAndAssociateWithEncounter() != null) { + for (Appointment appointment : submissionActions.getAppointmentsToMarkCheckedInAndAssociateWithEncounter()) { + if (appointment.getStatus() == AppointmentStatus.Scheduled) { + Context.getService(AppointmentsService.class).changeStatus(appointment, + AppointmentStatus.CheckedIn.toString(), encounter.getEncounterDatetime()); + } + if (appointment.getFulfillingEncounters() != null) { + appointment.getFulfillingEncounters().add(encounter); + } else { + appointment.setFulfillingEncounters(Collections.singleton(encounter)); + } + Context.getService(AppointmentsService.class).validateAndSave(appointment); + } + } + + if (submissionActions.getAppointmentsToDisassociateFromEncounter() != null) { + for (Appointment appointment : submissionActions.getAppointmentsToDisassociateFromEncounter()) { + if (appointment.getFulfillingEncounters() != null) { + appointment.getFulfillingEncounters().remove(encounter); + Context.getService(AppointmentsService.class).validateAndSave(appointment); + } + } + } + //deal with relationships if (submissionActions.getRelationshipsToCreate() != null) { for (Relationship r : submissionActions.getRelationshipsToCreate()) { @@ -986,7 +1015,7 @@ public void applyActions() throws BadFormDesignException { exitFromCareProperty.getReasonExitConcept()); } } - + // handle any custom actions (for an example of a custom action, see: https://github.com/PIH/openmrs-module-appointmentschedulingui/commit/e2cda8de1caa8a45d319ae4fbf7714c90c9adb8b) if (submissionActions.getCustomFormSubmissionActions() != null) { for (CustomFormSubmissionAction customFormSubmissionAction : submissionActions diff --git a/api/src/main/java/org/openmrs/module/htmlformentry/FormSubmissionActions.java b/api/src/main/java/org/openmrs/module/htmlformentry/FormSubmissionActions.java index d74cdcf2c..736931b7b 100644 --- a/api/src/main/java/org/openmrs/module/htmlformentry/FormSubmissionActions.java +++ b/api/src/main/java/org/openmrs/module/htmlformentry/FormSubmissionActions.java @@ -19,6 +19,7 @@ import org.openmrs.ProgramWorkflowState; import org.openmrs.Relationship; import org.openmrs.api.context.Context; +import org.openmrs.module.appointments.model.Appointment; import org.openmrs.module.htmlformentry.property.ExitFromCareProperty; import org.openmrs.util.OpenmrsUtil; @@ -79,6 +80,10 @@ public class FormSubmissionActions { private List identifiersToVoid = new Vector(); + private List appointmentsToMarkCheckedInAndAssociateWithEncounter = new Vector(); + + private List appointmentsToDisassociateFromEncounter = new Vector(); + private ExitFromCareProperty exitFromCareProperty; private List customFormSubmissionActions; @@ -946,4 +951,21 @@ public List getCustomFormSubmissionActions() { public void setCustomFormSubmissionActions(List customFormSubmissionActions) { this.customFormSubmissionActions = customFormSubmissionActions; } + + public List getAppointmentsToMarkCheckedInAndAssociateWithEncounter() { + return appointmentsToMarkCheckedInAndAssociateWithEncounter; + } + + public void setAppointmentsToMarkCheckedInAndAssociateWithEncounter( + List appointmentsToMarkCheckedInAndAssociateWithEncounter) { + this.appointmentsToMarkCheckedInAndAssociateWithEncounter = appointmentsToMarkCheckedInAndAssociateWithEncounter; + } + + public List getAppointmentsToDisassociateFromEncounter() { + return appointmentsToDisassociateFromEncounter; + } + + public void setAppointmentsToDisassociateFromEncounter(List appointmentsToDisassociateFromEncounter) { + this.appointmentsToDisassociateFromEncounter = appointmentsToDisassociateFromEncounter; + } } diff --git a/api/src/main/java/org/openmrs/module/htmlformentry/ObsGroupComponent.java b/api/src/main/java/org/openmrs/module/htmlformentry/ObsGroupComponent.java index 2c7acfaa3..695360915 100644 --- a/api/src/main/java/org/openmrs/module/htmlformentry/ObsGroupComponent.java +++ b/api/src/main/java/org/openmrs/module/htmlformentry/ObsGroupComponent.java @@ -92,7 +92,7 @@ public Drug getAnswerDrug() { public void setAnswerDrug(Drug answerDrug) { this.answerDrug = answerDrug; } - + public static int supportingRank(List obsGroupComponents, Set obsSet) { int rank = 0; diff --git a/api/src/main/java/org/openmrs/module/htmlformentry/element/AppointmentsElement.java b/api/src/main/java/org/openmrs/module/htmlformentry/element/AppointmentsElement.java new file mode 100644 index 000000000..1227ea7a5 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/htmlformentry/element/AppointmentsElement.java @@ -0,0 +1,88 @@ +package org.openmrs.module.htmlformentry.element; + +import javax.servlet.http.HttpServletRequest; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; + +import org.joda.time.DateTime; +import org.openmrs.Patient; +import org.openmrs.api.context.Context; +import org.openmrs.module.appointments.model.Appointment; +import org.openmrs.module.appointments.model.AppointmentSearchRequest; +import org.openmrs.module.appointments.model.AppointmentStatus; +import org.openmrs.module.appointments.service.AppointmentsService; +import org.openmrs.module.htmlformentry.FormEntryContext; +import org.openmrs.module.htmlformentry.FormEntrySession; +import org.openmrs.module.htmlformentry.FormSubmissionError; +import org.openmrs.module.htmlformentry.action.FormSubmissionControllerAction; +import org.openmrs.module.htmlformentry.widget.AppointmentsWidget; + +public class AppointmentsElement implements HtmlGeneratorElement, FormSubmissionControllerAction { + + private AppointmentsWidget appointmentsWidget; + + private List appointments = new ArrayList<>(); + + public AppointmentsElement(FormEntryContext context) { + Patient patient = context.getExistingPatient(); + if (patient != null) { + + // first, get all scheduled appointments for this patient + AppointmentSearchRequest request = new AppointmentSearchRequest(); + request.setPatientUuid(patient.getUuid()); + request.setStartDate(new DateTime().minusYears(1000).toDate()); // TODO hack, we want all appts for patient regardless of start date, but start date is required, this will start to fail in a thousand years + appointments = Context.getService(AppointmentsService.class).search(request); + + appointments.sort(Comparator.comparing(Appointment::getStartDateTime).reversed()); + appointments.removeIf(appointment -> appointment.getStatus() != AppointmentStatus.Scheduled + && (appointment.getFulfillingEncounters() == null + && !appointment.getFulfillingEncounters().contains(context.getExistingEncounter()))); + } + } + + @Override + public String generateHtml(FormEntryContext context) { + appointmentsWidget = new AppointmentsWidget(appointments, context); + return appointmentsWidget.generateHtml(context); + } + + @Override + public Collection validateSubmission(FormEntryContext context, HttpServletRequest submission) { + return null; + } + + @Override + public void handleSubmission(FormEntrySession session, HttpServletRequest submission) { + List selectedAppointmentUuids = (List) appointmentsWidget.getValue(session.getContext(), submission); + List appointmentsToMarkCheckedIn = new ArrayList<>(); + List appointmentsToDisassociateFromEncounter = new ArrayList<>(); + + // find appointments that need to be marked as checked in + for (String uuid : selectedAppointmentUuids) { + appointments.stream().filter(appointment -> appointment.getUuid().equals(uuid)).findFirst() + .ifPresent(appointment -> { + appointmentsToMarkCheckedIn.add(appointment); + }); + } + if (appointmentsToMarkCheckedIn.size() > 0) { + session.getSubmissionActions() + .setAppointmentsToMarkCheckedInAndAssociateWithEncounter(appointmentsToMarkCheckedIn); + } + + // find appointments that need to be disassociated from the encounter + appointments.stream() + .filter(appointment -> (!selectedAppointmentUuids.contains(appointment.getUuid()) + && appointment.getFulfillingEncounters() != null + && appointment.getFulfillingEncounters().contains(session.getEncounter()))) + .forEach(appointment -> { + appointmentsToDisassociateFromEncounter.add(appointment); + }); + if (appointmentsToDisassociateFromEncounter.size() > 0) { + session.getSubmissionActions() + .setAppointmentsToDisassociateFromEncounter(appointmentsToDisassociateFromEncounter); + } + } +} diff --git a/api/src/main/java/org/openmrs/module/htmlformentry/handler/AppointmentsTagHandler.java b/api/src/main/java/org/openmrs/module/htmlformentry/handler/AppointmentsTagHandler.java new file mode 100644 index 000000000..15eb2ee19 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/htmlformentry/handler/AppointmentsTagHandler.java @@ -0,0 +1,27 @@ +package org.openmrs.module.htmlformentry.handler; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.openmrs.module.htmlformentry.FormEntrySession; +import org.openmrs.module.htmlformentry.FormSubmissionController; +import org.openmrs.module.htmlformentry.element.AppointmentsElement; + +public class AppointmentsTagHandler extends SubstitutionTagHandler { + + @Override + protected List createAttributeDescriptors() { + List attributeDescriptors = new ArrayList(); + return Collections.unmodifiableList(attributeDescriptors); + } + + @Override + protected String getSubstitution(FormEntrySession session, FormSubmissionController controllerActions, + Map parameters) { + AppointmentsElement element = new AppointmentsElement(session.getContext()); + session.getSubmissionController().addAction(element); + return element.generateHtml(session.getContext()); + } +} diff --git a/api/src/main/java/org/openmrs/module/htmlformentry/widget/AppointmentsWidget.java b/api/src/main/java/org/openmrs/module/htmlformentry/widget/AppointmentsWidget.java new file mode 100644 index 000000000..bb6ed89c1 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/htmlformentry/widget/AppointmentsWidget.java @@ -0,0 +1,94 @@ +package org.openmrs.module.htmlformentry.widget; + +import javax.servlet.http.HttpServletRequest; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.openmrs.api.context.Context; +import org.openmrs.module.appointments.model.Appointment; +import org.openmrs.module.htmlformentry.FormEntryContext; +import org.openmrs.module.htmlformentry.HtmlFormEntryConstants; +import org.openmrs.module.htmlformentry.HtmlFormEntryUtil; +import org.springframework.util.StringUtils; + +public class AppointmentsWidget implements Widget { + + private List appointments; + + private List checkboxWidgets = new ArrayList(); + + public AppointmentsWidget(List appointments, FormEntryContext context) { + this.appointments = appointments; + String fieldName = context.registerWidget(this); + + // TODO: document why we need the special registration for form consistency + int i = 1; + for (Appointment appointment : appointments) { + CheckboxWidget checkboxWidget = new CheckboxWidget(); + checkboxWidget.setLabel( + dateTimeFormat().format(appointment.getStartDateTime()) + " - " + renderProviderNames(appointment) + " - " + + (appointment.getLocation() != null ? appointment.getLocation().getName() : "")); + checkboxWidget.setValue(appointment.getUuid()); + if (appointment.getFulfillingEncounters() != null && context.getExistingEncounter() != null + && appointment.getFulfillingEncounters().contains(context.getExistingEncounter())) { + checkboxWidget.setInitialValue(appointment.getUuid()); + } + context.registerWidget(checkboxWidget, fieldName + "_" + i++); + checkboxWidgets.add(checkboxWidget); + } + + } + + @Override + public void setInitialValue(Object initialValue) { + // TODO? + } + + @Override + public String generateHtml(FormEntryContext context) { + if (appointments == null || appointments.isEmpty()) { + return "No appointments found"; // TODO translate, style + } + + return checkboxWidgets.stream().map(checkboxWidget -> { + return checkboxWidget.generateHtml(context); + }).collect(Collectors.joining()); + } + + @Override + public Object getValue(FormEntryContext context, HttpServletRequest request) { + List selectedAppointmentUuids = new ArrayList(); + for (CheckboxWidget checkboxWidget : checkboxWidgets) { + String value = (String) checkboxWidget.getValue(context, request); + if (value != null) { + selectedAppointmentUuids.add(value); + } + } + return selectedAppointmentUuids; + } + + // TODO move to util method? + private SimpleDateFormat dateTimeFormat() { + String df = Context.getAdministrationService().getGlobalProperty(HtmlFormEntryConstants.GP_FORMATTER_DATETIME, + "yyyy-MM-dd, HH:mm:ss"); + if (StringUtils.hasText(df)) { + return new SimpleDateFormat(df, Context.getLocale()); + } else { + return Context.getDateTimeFormat(); + } + } + + private String renderProviderNames(Appointment appointment) { + if (appointment.getProviders() != null) { + return appointment.getProviders().stream().map(provider -> { + return provider.getProvider().getPerson() != null ? HtmlFormEntryUtil.getFullNameWithFamilyNameFirst( + provider.getProvider().getPerson().getPersonName()) : provider.getProvider().getName(); + }).collect(Collectors.joining("; ")); + } + return ""; + + } +} diff --git a/api/src/main/resources/moduleApplicationContext.xml b/api/src/main/resources/moduleApplicationContext.xml index e19f18a58..12061c426 100644 --- a/api/src/main/resources/moduleApplicationContext.xml +++ b/api/src/main/resources/moduleApplicationContext.xml @@ -113,6 +113,9 @@ + + + diff --git a/api/src/test/resources/org/openmrs/module/htmlformentry/data/appointmentCheckInTest.xml b/api/src/test/resources/org/openmrs/module/htmlformentry/data/appointmentCheckInTest.xml new file mode 100644 index 000000000..6484483c4 --- /dev/null +++ b/api/src/test/resources/org/openmrs/module/htmlformentry/data/appointmentCheckInTest.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/api/src/test/resources/org/openmrs/module/htmlformentry/include/appointmentCheckInForm.xml b/api/src/test/resources/org/openmrs/module/htmlformentry/include/appointmentCheckInForm.xml new file mode 100644 index 000000000..b4321892e --- /dev/null +++ b/api/src/test/resources/org/openmrs/module/htmlformentry/include/appointmentCheckInForm.xml @@ -0,0 +1,8 @@ + + Date: + Location: + Provider: + + Appointments: + + diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml index 7074847cd..b4b6eac98 100644 --- a/omod/src/main/resources/config.xml +++ b/omod/src/main/resources/config.xml @@ -22,6 +22,7 @@ org.openmrs.module.legacyui org.openmrs.module.metadatamapping org.openmrs.module.providermanagement + org.bahmni.module.appointments diff --git a/pom.xml b/pom.xml index c7b9c65f0..2b456d2a0 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,12 @@ 2.11.0 provided + + org.bahmni.module + appointments-api + 2.0.0-SNAPSHOT + provided + org.codehaus.jackson jackson-mapper-asl