diff --git a/api/pom.xml b/api/pom.xml
index 64f741fed..4421b46a5 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -149,6 +149,13 @@
provided
+
+
+
+ com.squareup.okhttp
+ okhttp
+ 2.7.5
+
diff --git a/api/src/main/java/org/openmrs/module/appointments/notification/SmsSender.java b/api/src/main/java/org/openmrs/module/appointments/notification/SmsSender.java
new file mode 100644
index 000000000..43e312cc3
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/appointments/notification/SmsSender.java
@@ -0,0 +1,7 @@
+package org.openmrs.module.appointments.notification;
+
+import java.util.List;
+
+public interface SmsSender {
+ void send(String message, List to);
+}
diff --git a/api/src/main/java/org/openmrs/module/appointments/notification/impl/DefaultSmsSender.java b/api/src/main/java/org/openmrs/module/appointments/notification/impl/DefaultSmsSender.java
new file mode 100644
index 000000000..529774d76
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/appointments/notification/impl/DefaultSmsSender.java
@@ -0,0 +1,24 @@
+package org.openmrs.module.appointments.notification.impl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.openmrs.api.AdministrationService;
+import org.openmrs.module.appointments.notification.SmsSender;
+
+import java.util.List;
+
+public class DefaultSmsSender implements SmsSender {
+
+ private Log log = LogFactory.getLog(this.getClass());
+
+ private AdministrationService administrationService;
+
+ public DefaultSmsSender(AdministrationService administrationService) {
+ this.administrationService = administrationService;
+ }
+
+ @Override
+ public void send(String message, List to) {
+ //Write your implementation based on your SMS API
+ }
+}
diff --git a/api/src/main/java/org/openmrs/module/appointments/notification/impl/DefaultTCAppointmentSmsNotifier.java b/api/src/main/java/org/openmrs/module/appointments/notification/impl/DefaultTCAppointmentSmsNotifier.java
new file mode 100644
index 000000000..d600c2004
--- /dev/null
+++ b/api/src/main/java/org/openmrs/module/appointments/notification/impl/DefaultTCAppointmentSmsNotifier.java
@@ -0,0 +1,123 @@
+package org.openmrs.module.appointments.notification.impl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.openmrs.Patient;
+import org.openmrs.PersonAttribute;
+import org.openmrs.api.context.Context;
+import org.openmrs.module.appointments.model.Appointment;
+import org.openmrs.module.appointments.model.AppointmentKind;
+import org.openmrs.module.appointments.model.AppointmentProvider;
+import org.openmrs.module.appointments.model.AppointmentServiceDefinition;
+import org.openmrs.module.appointments.notification.*;
+import org.openmrs.util.LocaleUtility;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class DefaultTCAppointmentSmsNotifier implements AppointmentEventNotifier {
+
+ private final static String PROP_PATIENT_SMS_TEMPLATE = "bahmni.appointment.teleConsultation.patientSmsNotificationTemplate";
+ private final static String PROP_SEND_TC_APPT_SMS = "bahmni.appointment.teleConsultation.sendSms";
+ private final static String PROP_SEND_TC_APPT_CONTACT_ATTRIBUTE = "bahmni.appointment.teleConsultation.contactAttribute";
+
+ private static final String MEDIUM_SMS = "SMS";
+ private static final String SMS_NOT_CONFIGURED = "Notification can not be sent to patient. Mobile number not configured.";
+ private static final String SMS_SENT = "SMS sent to Patient";
+ private static final String SMS_FAILURE = "Failed to send sms to patient";
+ private static final String SMS_NOT_SENT = "SMS notification for tele-consultation not configured to be sent to patient.";
+
+
+ private Log log = LogFactory.getLog(this.getClass());
+ private SmsSender smsSender;
+
+ public DefaultTCAppointmentSmsNotifier() {}
+ public DefaultTCAppointmentSmsNotifier(SmsSender smsSender) {
+ this.smsSender = smsSender;
+ }
+
+ @Override
+ public String getMedium() {
+ return MEDIUM_SMS;
+ }
+
+ @Override
+ public boolean isApplicable(final Appointment appointment) {
+ boolean sendSmsToPatient = shouldSendSmsToPatient();
+ if (!sendSmsToPatient) {
+ log.warn(SMS_NOT_SENT);
+ }
+ if (appointment.getAppointmentKind() != null && appointment.getAppointmentKind().equals(AppointmentKind.Virtual)) {
+ return sendSmsToPatient;
+ }
+ return false;
+ }
+
+ @Override
+ public NotificationResult sendNotification(final Appointment appointment) throws NotificationException {
+ Patient patient = appointment.getPatient();
+ String contactAttribute = Context.getAdministrationService().getGlobalProperty(PROP_SEND_TC_APPT_CONTACT_ATTRIBUTE);
+ PersonAttribute patientContactAttribute = patient.getPerson().getAttribute(contactAttribute);
+ Set providers = appointment.getProviders();
+ List contacts = new ArrayList<>();
+
+ if (patientContactAttribute != null) {
+ contacts.add(patientContactAttribute.getValue());
+ }
+
+ if (providers != null) {
+ for (AppointmentProvider provider : providers) {
+ PersonAttribute providerContactAttribute = provider.getProvider().getPerson().getAttribute(contactAttribute);
+ if (providerContactAttribute != null) {
+ contacts.add(providerContactAttribute.getValue());
+ }
+ }
+ }
+
+ if (contacts.size() > 0) {
+ String patientName = appointment.getPatient().getGivenName();
+ String message = getMessage(patientName, appointment.getService(),
+ providers, appointment.getStartDateTime(),
+ appointment.getTeleHealthVideoLink());
+ try {
+ log.info("Sending sms through: " + smsSender.getClass());
+ smsSender.send(message, contacts);
+ return new NotificationResult("", "SMS", NotificationResult.SUCCESS_STATUS, SMS_SENT);
+ } catch (Exception e) {
+ log.error(SMS_FAILURE, e);
+ throw new NotificationException(SMS_FAILURE, e);
+ }
+ } else {
+ log.warn(SMS_NOT_CONFIGURED);
+ return new NotificationResult(null, "SMS", NotificationResult.GENERAL_ERROR, SMS_NOT_CONFIGURED);
+ }
+ }
+
+ private String getMessage(String patientName,
+ AppointmentServiceDefinition service,
+ Set providers,
+ Date appointmentDate, String link) {
+ String smsTemplate = Context.getAdministrationService().getGlobalProperty(PROP_PATIENT_SMS_TEMPLATE);
+ String practitioners =
+ providers != null ?
+ providers.stream()
+ .map(appointmentProvider -> appointmentProvider.getProvider().getName())
+ .collect(Collectors.joining(","))
+ : "";
+ Object[] arguments = {patientName, practitioners, appointmentDate, link};
+ if (smsTemplate == null || "".equals(smsTemplate)) {
+ return Context.getMessageSourceService().getMessage(PROP_PATIENT_SMS_TEMPLATE, arguments, LocaleUtility.getDefaultLocale());
+ } else {
+ return new MessageFormat(smsTemplate).format(arguments);
+ }
+ }
+
+ private boolean shouldSendSmsToPatient() {
+ String shouldSendSMS = Context.getAdministrationService().getGlobalProperty(PROP_SEND_TC_APPT_SMS, "false");
+ return Boolean.valueOf(shouldSendSMS);
+ }
+}
diff --git a/api/src/main/resources/moduleApplicationContext.xml b/api/src/main/resources/moduleApplicationContext.xml
index 3a40e543f..feee00843 100644
--- a/api/src/main/resources/moduleApplicationContext.xml
+++ b/api/src/main/resources/moduleApplicationContext.xml
@@ -28,9 +28,15 @@
+
+
+
+
+
+
@@ -38,6 +44,7 @@
+
diff --git a/api/src/test/java/org/openmrs/module/appointments/service/impl/DefaultTCAppointmentSmsNotifierTest.java b/api/src/test/java/org/openmrs/module/appointments/service/impl/DefaultTCAppointmentSmsNotifierTest.java
new file mode 100644
index 000000000..62d842e14
--- /dev/null
+++ b/api/src/test/java/org/openmrs/module/appointments/service/impl/DefaultTCAppointmentSmsNotifierTest.java
@@ -0,0 +1,93 @@
+package org.openmrs.module.appointments.service.impl;
+
+import org.databene.commons.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.AdditionalMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.openmrs.Patient;
+import org.openmrs.PersonAttribute;
+import org.openmrs.PersonAttributeType;
+import org.openmrs.api.AdministrationService;
+import org.openmrs.api.context.Context;
+import org.openmrs.messagesource.MessageSourceService;
+import org.openmrs.module.appointments.model.Appointment;
+import org.openmrs.module.appointments.notification.AppointmentEventNotifier;
+import org.openmrs.module.appointments.notification.NotificationException;
+import org.openmrs.module.appointments.notification.SmsSender;
+import org.openmrs.module.appointments.notification.impl.DefaultTCAppointmentPatientEmailNotifier;
+import org.openmrs.module.appointments.notification.impl.DefaultTCAppointmentSmsNotifier;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(Context.class)
+public class DefaultTCAppointmentSmsNotifierTest {
+ private static final String BAHMNI_APPOINTMENT_TELE_CONSULTATION_SMS_NOTIFICATION_TEMPLATE = "bahmni.appointment.teleConsultation.patientSmsNotificationTemplate";
+ private static final String BAHMNI_APPOINTMENT_TELE_CONSULTATION_CONTACT_ATTRIBUTE = "bahmni.appointment.teleConsultation.contactAttribute";
+ private AppointmentEventNotifier tcAppointmentEventNotifier;
+
+ @Mock
+ private AdministrationService administrationService;
+
+ @Mock
+ private SmsSender smsSender;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Before
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ mockStatic(Context.class);
+ tcAppointmentEventNotifier = new DefaultTCAppointmentSmsNotifier(smsSender);
+ PowerMockito.when(Context.getAdministrationService()).thenReturn(administrationService);
+ when(administrationService.getGlobalProperty(BAHMNI_APPOINTMENT_TELE_CONSULTATION_SMS_NOTIFICATION_TEMPLATE)).thenReturn("Email body");
+ when(administrationService.getGlobalProperty(BAHMNI_APPOINTMENT_TELE_CONSULTATION_CONTACT_ATTRIBUTE)).thenReturn("primaryContact");
+ }
+
+ @Test
+ public void shouldSendTeleconsultationAppointmentLinkSms() throws Exception {
+ Appointment appointment = buildAppointment();
+ tcAppointmentEventNotifier.sendNotification(appointment);
+ List contacts = new ArrayList<>();
+ contacts.add("+61411111111");
+ verify(smsSender).send(
+ eq("Email body"),
+ eq(contacts));
+ }
+
+ @Test
+ public void shouldThrowExceptionIfSendingFails() throws NotificationException {
+ Appointment appointment = buildAppointment();
+ doThrow(new RuntimeException()).when(smsSender).send(any(), any());
+ expectedException.expect(NotificationException.class);
+ tcAppointmentEventNotifier.sendNotification(appointment);
+ }
+
+ private Appointment buildAppointment() {
+ Appointment appointment = new Appointment();
+ Patient patient = new Patient();
+ patient.setUuid("patientUuid");
+ PersonAttributeType personAttributeType = new PersonAttributeType();
+ personAttributeType.setName("primaryContact");
+ patient.addAttribute(new PersonAttribute(personAttributeType, "+61411111111"));
+ appointment.setPatient(patient);
+ return appointment;
+ }
+}
diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml
index 65cf88ff7..2e6100fcb 100644
--- a/omod/src/main/resources/config.xml
+++ b/omod/src/main/resources/config.xml
@@ -96,6 +96,24 @@
false
Whether email should be sent for tele-consultation
+
+ bahmni.appointment.teleConsultation.sendSms
+ false
+ Whether sms should be sent for tele-consultation
+
+
+ bahmni.appointment.teleConsultation.patientSmsNotificationTemplate
+
+ Hi {0}, Your tele-consultation appointment with {1} for {2} has been scheduled! Please use this link {3}. If you have any questions, please reach out to administration for assistance. See you soon!
+
+ Template to use while sending sms. Format is define like in Java MessageFormat
+
+
+
+ bahmni.appointment.teleConsultation.contactAttribute
+ primaryContact
+ Person attribute from which the contact should be taken for sending sms
+