diff --git a/api-tests/src/test/java/org/openmrs/module/htmlformentry/ScheduleAppointmentTagTest.java b/api-tests/src/test/java/org/openmrs/module/htmlformentry/ScheduleAppointmentTagTest.java index 2e4edf6da..2ab42cd1a 100644 --- a/api-tests/src/test/java/org/openmrs/module/htmlformentry/ScheduleAppointmentTagTest.java +++ b/api-tests/src/test/java/org/openmrs/module/htmlformentry/ScheduleAppointmentTagTest.java @@ -35,12 +35,14 @@ public class ScheduleAppointmentTagTest extends BaseHtmlFormEntryTest { // Text concept added in scheduleAppointmentTest.xml for storing appointment UUID obs private static final String APPT_UUID_CONCEPT = "c36006e5-9fbb-4f20-866b-0ece245615a7"; - // Widget-label note: date and time widgets are found via the "Timing" section - // label as a stable anchor. "Timing!!2" = allday hidden input (3rd name="w" after - // "Timing"), "Timing!!3" = appointment date hidden input (4th), "Timing!!4" = - // time-hours select (5th). Using "Date##N" labels is fragile because the shared - // html variable in getLabeledWidgets is mutated by ## processing, making - // consecutive "Date##N" label lookups interact unexpectedly. + // Widget-label note: the date/time widgets have no text label of their own that's safe to + // anchor on ("Date" also matches the earlier "Encounter Date:" widget, and the wrapper's CSS + // class name is also a substring of the "); @@ -332,28 +322,7 @@ public String generateHtml(FormEntryContext context) { .append("\" value=\"").append(typeWidget.getOptions().get(0).getValue()).append("\"/>"); } - // All-day / Specific-time toggle — shown BEFORE the date/time row - String allDayFieldName = context.getFieldName(allDayWidget); - sb.append("

"); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append("

"); - - // Date + Time on one line; time portion shown/hidden by the toggle above + // Date + Time sb.append("

"); sb.append(""); @@ -370,13 +339,11 @@ public String generateHtml(FormEntryContext context) { .append(context.getFieldName(startDateTimeWidget.getDateWidget())).append("', '") .append(context.getFieldName(startDateTimeWidget.getTimeWidget())).append("')"); - // Duration (shown only for specific-time) - sb.append("

"); + // Duration sb.append(fieldRow("schedule-appointment-duration", msg("htmlformentry.scheduleAppointment.duration"), durationWidget.generateHtml(context) + durationErrorWidget.generateHtml(context), true)); - sb.append("
"); // Provider sb.append(fieldRow("schedule-appointment-provider", @@ -503,27 +470,22 @@ public Collection validateSubmission(FormEntryContext conte msg("htmlformentry.scheduleAppointment.error.dateRequired"))); } - // Time and duration required only for specific-time appointments - String allDayValue = (String) allDayWidget.getValue(context, submission); - boolean isAllDay = "allday".equals(allDayValue); - if (!isAllDay) { - Date time = (Date) startDateTimeWidget.getTimeWidget().getValue(context, submission); - if (time == null) { - errors.add(new FormSubmissionError(context.getFieldName(timeErrorWidget), - msg("htmlformentry.scheduleAppointment.error.timeRequired"))); - } + Date time = (Date) startDateTimeWidget.getTimeWidget().getValue(context, submission); + if (time == null) { + errors.add(new FormSubmissionError(context.getFieldName(timeErrorWidget), + msg("htmlformentry.scheduleAppointment.error.timeRequired"))); + } - Number durNum = null; - try { - durNum = (Number) durationWidget.getValue(context, submission); - } - catch (IllegalArgumentException e) { - // non-numeric input — falls through to error - } - if (durNum == null || durNum.intValue() < 1) { - errors.add(new FormSubmissionError(context.getFieldName(durationErrorWidget), - msg("htmlformentry.scheduleAppointment.error.durationRequired"))); - } + Number durNum = null; + try { + durNum = (Number) durationWidget.getValue(context, submission); + } + catch (IllegalArgumentException e) { + // non-numeric input — falls through to error + } + if (durNum == null || durNum.intValue() < 1) { + errors.add(new FormSubmissionError(context.getFieldName(durationErrorWidget), + msg("htmlformentry.scheduleAppointment.error.durationRequired"))); } Provider provider = (Provider) providerWidget.getValue(context, submission); @@ -552,8 +514,6 @@ public void handleSubmission(FormEntrySession session, HttpServletRequest submis String serviceUuid = (String) serviceWidget.getValue(context, submission); String typeValue = (String) typeWidget.getValue(context, submission); Date date = (Date) startDateTimeWidget.getDateWidget().getValue(context, submission); - String allDayValue = (String) allDayWidget.getValue(context, submission); - boolean isAllDay = "allday".equals(allDayValue); Provider selectedProvider = (Provider) providerWidget.getValue(context, submission); String note = (String) noteWidget.getValue(context, submission); @@ -563,44 +523,34 @@ public void handleSubmission(FormEntrySession session, HttpServletRequest submis // Build start datetime Calendar cal = Calendar.getInstance(); cal.setTime(date); - if (!isAllDay) { - Date time = (Date) startDateTimeWidget.getTimeWidget().getValue(context, submission); - if (time != null) { - Calendar timeCal = Calendar.getInstance(); - timeCal.setTime(time); - cal.set(Calendar.HOUR_OF_DAY, timeCal.get(Calendar.HOUR_OF_DAY)); - cal.set(Calendar.MINUTE, timeCal.get(Calendar.MINUTE)); - } - } else { - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); + Date time = (Date) startDateTimeWidget.getTimeWidget().getValue(context, submission); + if (time != null) { + Calendar timeCal = Calendar.getInstance(); + timeCal.setTime(time); + cal.set(Calendar.HOUR_OF_DAY, timeCal.get(Calendar.HOUR_OF_DAY)); + cal.set(Calendar.MINUTE, timeCal.get(Calendar.MINUTE)); } cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); Date startDateTime = cal.getTime(); // Build end datetime - Date endDateTime; - if (isAllDay) { - endDateTime = OpenmrsUtil.getLastMomentOfDay(date); - } else { - Integer durationMins = null; - try { - Number durNum = (Number) durationWidget.getValue(context, submission); - if (durNum != null) { - durationMins = durNum.intValue(); - } - } - catch (IllegalArgumentException e) { - // non-numeric input — validation should have caught this - } - if (durationMins == null && service != null) { - durationMins = service.getDurationMins(); + Integer durationMins = null; + try { + Number durNum = (Number) durationWidget.getValue(context, submission); + if (durNum != null) { + durationMins = durNum.intValue(); } - endDateTime = durationMins != null - ? new Date(startDateTime.getTime() + (long) durationMins * 60 * 1000) - : null; } + catch (IllegalArgumentException e) { + // non-numeric input — validation should have caught this + } + if (durationMins == null && service != null) { + durationMins = service.getDurationMins(); + } + Date endDateTime = durationMins != null + ? new Date(startDateTime.getTime() + (long) durationMins * 60 * 1000) + : null; Appointment appointment = new Appointment(); appointment.setPatient(session.getPatient()); diff --git a/api/src/main/resources/messages.properties b/api/src/main/resources/messages.properties index 74bbb3990..47f6b033f 100644 --- a/api/src/main/resources/messages.properties +++ b/api/src/main/resources/messages.properties @@ -196,9 +196,7 @@ htmlformentry.scheduleAppointment.service=Service htmlformentry.scheduleAppointment.appointmentType=Appointment Type htmlformentry.scheduleAppointment.date=Date htmlformentry.scheduleAppointment.appointmentTime=Time -htmlformentry.scheduleAppointment.timing=Timing htmlformentry.scheduleAppointment.allDay=All day -htmlformentry.scheduleAppointment.specificTime=Specific time htmlformentry.scheduleAppointment.duration=Duration (minutes) htmlformentry.scheduleAppointment.provider=Provider htmlformentry.scheduleAppointment.note=Note diff --git a/api/src/main/resources/messages_es.properties b/api/src/main/resources/messages_es.properties index 06408fd42..a0ad25efd 100644 --- a/api/src/main/resources/messages_es.properties +++ b/api/src/main/resources/messages_es.properties @@ -186,9 +186,7 @@ htmlformentry.scheduleAppointment.service=Servicio htmlformentry.scheduleAppointment.appointmentType=Tipo de cita htmlformentry.scheduleAppointment.date=Fecha htmlformentry.scheduleAppointment.appointmentTime=Hora -htmlformentry.scheduleAppointment.timing=Horario htmlformentry.scheduleAppointment.allDay=Todo el día -htmlformentry.scheduleAppointment.specificTime=Hora específica htmlformentry.scheduleAppointment.duration=Duración (minutos) htmlformentry.scheduleAppointment.provider=Proveedor htmlformentry.scheduleAppointment.note=Nota diff --git a/api/src/main/resources/messages_fr.properties b/api/src/main/resources/messages_fr.properties index 8a3c93cc1..b4c1a6a52 100644 --- a/api/src/main/resources/messages_fr.properties +++ b/api/src/main/resources/messages_fr.properties @@ -186,9 +186,7 @@ htmlformentry.scheduleAppointment.service=Service htmlformentry.scheduleAppointment.appointmentType=Type de rendez-vous htmlformentry.scheduleAppointment.date=Date htmlformentry.scheduleAppointment.appointmentTime=Heure -htmlformentry.scheduleAppointment.timing=Horaire htmlformentry.scheduleAppointment.allDay=Toute la journée -htmlformentry.scheduleAppointment.specificTime=Heure précise htmlformentry.scheduleAppointment.duration=Durée (minutes) htmlformentry.scheduleAppointment.provider=Prestataire de soins htmlformentry.scheduleAppointment.note=Note diff --git a/api/src/main/resources/messages_ht.properties b/api/src/main/resources/messages_ht.properties index 333b2b88c..60f3d1177 100644 --- a/api/src/main/resources/messages_ht.properties +++ b/api/src/main/resources/messages_ht.properties @@ -186,9 +186,7 @@ htmlformentry.scheduleAppointment.service=Sèvis htmlformentry.scheduleAppointment.appointmentType=Kalite randevou htmlformentry.scheduleAppointment.date=Dat htmlformentry.scheduleAppointment.appointmentTime=Lè -htmlformentry.scheduleAppointment.timing=Orè htmlformentry.scheduleAppointment.allDay=Tout jounen an -htmlformentry.scheduleAppointment.specificTime=Lè presi htmlformentry.scheduleAppointment.duration=Kantite tan (minit) htmlformentry.scheduleAppointment.provider=Moun ki bay swen htmlformentry.scheduleAppointment.note=Nòt diff --git a/api/src/test/java/org/openmrs/module/htmlformentry/element/ScheduleAppointmentElementTest.java b/api/src/test/java/org/openmrs/module/htmlformentry/element/ScheduleAppointmentElementTest.java index be62604d3..ce8375a2d 100644 --- a/api/src/test/java/org/openmrs/module/htmlformentry/element/ScheduleAppointmentElementTest.java +++ b/api/src/test/java/org/openmrs/module/htmlformentry/element/ScheduleAppointmentElementTest.java @@ -14,7 +14,9 @@ import static org.mockito.Mockito.when; import javax.servlet.http.HttpServletRequest; +import java.text.SimpleDateFormat; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -104,10 +106,28 @@ public void setup() throws Exception { mockedHtmlFormEntryUtil.when(() -> HtmlFormEntryUtil.getConcept(CONCEPT_ID)).thenReturn(concept); mockedHtmlFormEntryUtil.when(() -> HtmlFormEntryUtil.createObs(any(Concept.class), any(), any(), any())) .thenCallRealMethod(); + // real conversion logic per requested type, since different widgets request different types + // (DateWidget wants a Date, TimeWidget wants Integers) and a blanket canned value breaks whichever + // widget didn't ask for that type mockedHtmlFormEntryUtil .when(() -> HtmlFormEntryUtil.getParameterAsType(any(HttpServletRequest.class), anyString(), any(Class.class))) - .thenReturn(new java.util.Date()); + .thenAnswer(invocation -> { + HttpServletRequest req = invocation.getArgument(0); + String name = invocation.getArgument(1); + Class clazz = invocation.getArgument(2); + String val = req.getParameter(name); + if (val == null) { + return null; + } + if (Date.class.isAssignableFrom(clazz)) { + return new SimpleDateFormat("yyyy-MM-dd").parse(val); + } + if (Integer.class.isAssignableFrom(clazz)) { + return Integer.valueOf(val); + } + return val; + }); mockedContext.when(Context::getAdministrationService).thenReturn(administrationService); mockedContext.when(Context::getMessageSourceService).thenReturn(messageSourceService); @@ -240,7 +260,6 @@ private MockHttpServletRequest buildRequest() { MockHttpServletRequest req = new MockHttpServletRequest(); req.addParameter("dd", AppointmentKind.Scheduled.name()); // all DropdownWidgets share "dd"; value must be a valid AppointmentKind for the type widget - req.addParameter("tf", "allday"); // allDayWidget → all-day, no time widget needed req.addParameter("dt", "2025-01-15"); // date (parsed as yyyy-MM-dd by HtmlFormEntryUtil) // prov_hid not set → provider = null (appointment is created without provider) return req;