3 * /library/MedEx/API.php
6 * @author MedEx <support@MedExBank.com>
7 * @link http://www.MedExBank.com
8 * @copyright Copyright (c) 2018 MedEx <support@MedExBank.com>
9 * @license https://www.gnu.org/licenses/agpl-3.0.en.html GNU Affero General Public License 3
19 private $postData = array();
20 private $cookies = array();
21 private $response = '';
25 public function __construct($sessionFile)
27 $this->sessionFile
= $sessionFile;
28 $this->restoreSession();
31 private function restoreSession()
33 if (file_exists($this->sessionFile
)) {
34 $this->cookies
= json_decode(file_get_contents($this->sessionFile
), true);
38 public function makeRequest()
40 $this->handle
= curl_init($this->url
);
42 curl_setopt($this->handle
, CURLOPT_VERBOSE
, 1);
43 curl_setopt($this->handle
, CURLOPT_HEADER
, true);
44 curl_setopt($this->handle
, CURLOPT_RETURNTRANSFER
, true);
45 curl_setopt($this->handle
, CURLOPT_POST
, true);
46 curl_setopt($this->handle
, CURLOPT_SSL_VERIFYPEER
, true);
47 curl_setopt($this->handle
, CURLOPT_POSTFIELDS
, http_build_query($this->postData
));
48 if (!empty($this->cookies
)) {
49 curl_setopt($this->handle
, CURLOPT_COOKIE
, $this->getCookies());
52 $this->response
= curl_exec($this->handle
);
53 $header_size = curl_getinfo($this->handle
, CURLINFO_HEADER_SIZE
);
54 $headers = substr($this->response
, 0, $header_size);
55 $this->response
= substr($this->response
, $header_size);
57 preg_match_all('/^Set-Cookie:\s*([^;]*)/mi', $headers, $matches);
58 $cookies = $matches[1];
59 foreach ($cookies as $cookie) {
60 $parts = explode('=', $cookie);
61 $name = array_shift($parts);
62 $value = implode('=', $parts);
63 $this->cookies
[$name] = $value;
65 curl_close($this->handle
);
69 private function getCookies()
72 foreach ($this->cookies
as $name => $value) {
73 $cookies[] = $name . '=' . $value;
75 return implode('; ', $cookies);
78 private function saveSession()
80 if (empty($this->sessionFile
)) {
84 if (!file_exists(dirname($this->sessionFile
))) {
85 /** @noinspection PhpMethodParametersCountMismatchInspection */mkdir(dirname($this->sessionFile
, 0755, true));
88 file_put_contents($this->sessionFile
, json_encode($this->cookies
));
91 public function setUrl($url)
95 public function setData($postData)
97 $this->postData
= $postData; }
98 public function getResponse()
100 return json_decode($this->response
, true); }
101 public function getRawResponse()
103 return $this->response
; }
111 public function __construct($MedEx)
113 $this->MedEx
= $MedEx;
114 $this->curl
= $MedEx->curl
;
118 class Practice
extends Base
120 public function sync($token)
125 $callback = "https://".$GLOBALS['_SERVER']['SERVER_NAME'].$GLOBALS['_SERVER']['PHP_SELF'];
126 $callback = str_replace('ajax/execute_background_services.php', 'MedEx/MedEx.php', $callback);
127 $fields2['callback_url'] = $callback;
128 //get the providers list:
129 $sqlQuery = "SELECT * FROM medex_prefs";
130 $my_status = sqlQuery($sqlQuery);
131 $providers = explode('|', $my_status['ME_providers']);
132 foreach ($providers as $provider) {
133 $runQuery = "SELECT * FROM users WHERE id=?";
134 $ures = sqlStatement($runQuery, array($provider));
135 while ($urow = sqlFetchArray($ures)) {
136 $fields2['providers'][] = $urow;
139 //get the facilities list and flag which we do messaging for (checkboxes in Preferences)
140 $facilities = explode('|', $my_status['ME_facilities']);
141 $runQuery = "SELECT * FROM facility WHERE service_location='1'";
142 $ures = sqlStatement($runQuery);
143 while ($urow = sqlFetchArray($ures)) {
144 if (in_array($urow['id'], $facilities)) {
145 $urow['messages_active'] = '1';
147 $fields2['facilities'][] = $urow;
149 //get the categories list:
150 $runQuery = "SELECT pc_catid, pc_catname, pc_catdesc, pc_catcolor, pc_seq
151 FROM openemr_postcalendar_categories WHERE pc_active = 1 AND pc_cattype='0' ORDER BY pc_catid";
152 $ures = sqlStatement($runQuery);
153 while ($urow = sqlFetchArray($ures)) {
154 $fields2['categories'][] = $urow;
157 $runQuery = "SELECT * FROM `list_options` WHERE `list_id` LIKE 'apptstat' AND activity='1'";
158 $ures = sqlStatement($runQuery);
159 while ($urow = sqlFetchArray($ures)) {
160 $fields2['apptstats'][] = $urow;
162 //get definition for "Checked Out"
163 $runQuery = "SELECT option_id FROM list_options WHERE toggle_setting_2='1' AND list_id='apptstat' AND activity='1'";
164 $ures = sqlStatement($runQuery);
165 while ($urow = sqlFetchArray($ures)) {
166 $fields2['checkedOut'][] = $urow;
168 //get clinical reminders for practice
169 $sql = "SELECT * FROM `clinical_rules`,`list_options`,`rule_action`,`rule_action_item`
171 `clinical_rules`.`pid`=0 AND
172 `clinical_rules`.`patient_reminder_flag` = 1 AND
173 `clinical_rules`.id = `list_options`.option_id AND
174 `clinical_rules`.id = `rule_action`.id AND
175 `list_options`.option_id=`clinical_rules`.id AND
176 `rule_action`.category =`rule_action_item`.category AND
177 `rule_action`.item =`rule_action_item`.item ";
179 $ures = sqlStatementCdrEngine($sql);
180 while ($urow = sqlFetchArray($ures)) {
181 $fields2['clinical_reminders'][] = $urow;
184 $data = array($fields2);
185 if (!is_array($data)) {
186 // return false; //throw new InvalidProductException('Invalid practice information');
188 $this->curl
->setUrl($this->MedEx
->getUrl('custom/addpractice&token='.$token));
189 $this->curl
->setData($fields2);
190 $this->curl
->makeRequest();
191 $response = $this->curl
->getResponse();
193 //Practice: Since the last MedEx login, who has responded to one of our messages and do we know about these responses?
194 //Have we deleted, cancelled or changed an appointment that we thought we were supposed to send, and now don't want to?
195 //1. Check to see if anything pending was cancelled/changed.
196 //2. Make sure any recalls don't have appointments just scheduled. If so no Recall message needed.
197 //3. Sync any responses received on MedEx that we didn't see already.
198 // Send and Receive everything since last timestamp/update noted in medex_prefs and check.
199 //Finally we may have manually made an appointment (which deletes a Recall) or manually confirmed an appt too.
200 //4. We need to send this data to MedEx so it stops processing events that are confirmed/completed.
202 //1. Check to see if anything pending was cancelled/changed.
203 //for appts, we are just looking for appts that are flagged as 'To Send' BUT
204 // were confirmed, cancelled, moved by the staff since the appt was added to table medex_outgoing...
205 $sql = "SELECT * FROM medex_outgoing WHERE msg_pc_eid != 'recall_%' AND msg_reply LIKE 'To Send'";
206 $test = sqlStatement($sql);
207 while ($result1 = sqlFetchArray($test)) {
208 $query = "SELECT * FROM openemr_postcalendar_events WHERE pc_eid = ?";
209 $test2 = sqlStatement($query, array($result1['msg_pc_eid']));
210 $result2 = sqlFetchArray($test2);
211 //for custom installs, insert custom apptstatus here that mean appt is not happening/changed
212 if ($result2['pc_apptstatus'] =='*' ||
//confirmed
213 $result2['pc_apptstatus'] =='%' ||
//cancelled < 24hour
214 $result2['pc_apptstatus'] =='x' ) { //cancelled
216 $sqlUPDATE = "UPDATE medex_outgoing SET msg_reply = 'DONE',msg_extra_text=? WHERE msg_uid = ?";
217 sqlQuery($sqlUPDATE, array($result2['pc_apptstatus'],$result2['msg_uid']));
218 //we need to update MedEx regarding actions to try to cancel
219 $tell_MedEx['DELETE_MSG'][] = $result1['msg_pc_eid'];
223 // 2. Make sure any recalls don't have appointments, ie. just a recall is scheduled
224 // Go through each recall event in medex_outgoing and check to see if it is scheduled. If so tell MedEx.
225 // We do this when we load the Recall Board........ and now when running a cron job/background_service
226 $sql = "SELECT * FROM medex_outgoing WHERE msg_pc_eid LIKE 'recall_%' GROUP BY msg_pc_eid";
227 $result = sqlStatement($sql);
228 while ($row = sqlFetchArray($result)) {
229 $pid = trim($row['msg_pc_eid'], "recall_");
230 //if there is a future appointment now in calendar, stop this recall message from going out
231 $query = "SELECT pc_eid FROM openemr_postcalendar_events WHERE (pc_eventDate > CURDATE()) AND pc_pid=?";
232 $test3 = sqlStatement($query, array($pid));
233 $result3 = sqlFetchArray($test3);
235 $sqlUPDATE = "UPDATE medex_outgoing SET msg_reply = 'SCHEDULED', msg_extra_text=? WHERE msg_uid = ?";
236 sqlQuery($sqlUPDATE, array($result3['pc_eid'],$result2['msg_uid']));
237 //we need to update MedEx regarding actions to try to cancel
238 $tell_MedEx['DELETE_MSG'][] = $row['msg_pc_eid'];
242 //3. Sync any responses received on MedEx that we didn't see already.
244 $sqlQuery = "SELECT * FROM medex_prefs";
245 $my_status = sqlStatement($sqlQuery);
246 while ($urow = sqlFetchArray($my_status)) {
247 $fields3['MedEx_lastupdated'] = $urow['MedEx_lastupdated'];
248 $fields3['ME_providers'] = $urow['ME_providers'];
251 $this->curl
->setUrl($this->MedEx
->getUrl('custom/sync_responses&token='.$token));
252 $this->curl
->setData($fields3);
253 $this->curl
->makeRequest();
254 $responses = $this->curl
->getResponse();
256 foreach ($responses['messages'] as $data) {
257 //check to see if this response is present already
258 $data['msg_extra'] = $data['msg_extra']?
:'';
259 $sqlQuery ="SELECT * FROM medex_outgoing WHERE medex_uid=?";
260 $checker = sqlStatement($sqlQuery, array($data['msg_uid']));
261 if (sqlNumRows($checker)=='0') {
262 $this->MedEx
->callback
->receive($data);
265 $sqlUPDATE = "UPDATE medex_prefs SET MedEx_lastupdated=utc_timestamp()";
266 sqlStatement($sqlUPDATE);
268 if ($tell_MedEx['DELETE_MSG']) {
269 $this->curl
->setUrl($this->MedEx
->getUrl('custom/remMessaging&token='.$token));
270 $this->curl
->setData($tell_MedEx['DELETE_MSG']);
271 $this->curl
->makeRequest();
272 $response = $this->curl
->getResponse();
274 if (!empty($response['found_replies'])) {
275 $response['success']['message'] = xlt("Replies retrieved").": ".$response['found_replies'];
277 $response['success']['message'] = xlt("No new messages on")." MedEx.";
280 if (isset($response['success'])) {
282 } else if (isset($response['error'])) {
283 $this->lastError
= $response['error'];
289 class Campaign
extends Base
291 public function events($token)
293 $this->curl
->setUrl($this->MedEx
->getUrl('custom/showEvents&token='.$token));
294 $this->curl
->makeRequest();
295 $response = $this->curl
->getResponse();
297 if (isset($response['success'])) {
299 } else if (isset($response['error'])) {
300 $this->lastError
= $response['error'];
306 class Events
extends Base
309 * In this function we are generating all appts that match scheduled campaign events so MedEx can conduct the campaign events.
311 * This is run via MedEx_background.php or MedEx.php to find appointments that match our Campaign events and rules.
312 * Messaging Campaigns are categorized by function:
313 * There are RECALLs, REMINDERs, CLINICAL_REMINDERs, SURVEYs and ANNOUNCEments (and later whatever comes down the pike).
314 * To meet a campaign's goals, we schedule campaign events.
315 * REMINDER and RECALL campaigns each have their own events which the user creates and personalizes.
316 * There is only one REMINDER Campaign and one RECALL Campaign, each with unlimited events.
318 * SURVEYs and ANNOUNCEments can have unlimited campaigns, each with their own events...
319 * You can ANNOUNCE a weather related closure (campaign) by SMS and AVM (events) going out now/ASAP
321 * at the same time also ANNOUNCE that in two months Dr. X will be out of the office and patient needs to reschedule (campaign)
322 * using SMS and email and voice (events) with messages going out 2 days from now, spread out over 6 business days...
324 * So SURVEY AND ANNOUNCE events have parent Campaigns they are attached to
325 * but RECALLS and REMINDERS do not (they are a RECALL or a REMINDER).
326 * Clinical Reminders can be handled locally in full, some parts local, some on MedEx or all on MedEx.
327 * ie. some practices may want to send emails themselves,
328 * but have MedEx handle the SMS and phone arms of the campaign, etc.
329 * Whatever is decided, MedEx events are created on MedEx and we are notified it exists in $MedEx->campaign->events($token).
330 * We are taking those events to build our list of recipients, they are logged in local medex_outgoing table,
331 * and are sent to MedEx to process the event.
336 public function generate($token, $events)
338 if (empty($events)) {
339 return false; //throw new InvalidDataException("You have no Campaign Events on MedEx at this time.");
340 //You have no campaign events on MedEx!
344 // For future appts, we don't want to run anything on the weekend so do them Friday.
345 // There is a GLOBALS value for weekend days, maybe use that LTR.
346 // -->If Friday, send all appts matching campaign fire_Time + 2 days
347 // For past appts, we also don't want to send messages on the weekend, so do them Monday.
348 foreach ($events as $event) {
350 $escapedArr = []; //will hold prepared statement items for query.
351 if ($event['M_group'] == 'REMINDER') {
352 if ($event['time_order'] > '0') {
354 //NOTE IF you have customized the pc_appstatus flags, you need to adjust them here too.
355 if ($event['E_instructions'] == "stop") { // ie. don't send this if it has been confirmed.
356 $appt_status = " and pc_apptstatus='-'";//we only look at future appts w/ apptstatus == NONE ='-'
357 // OR send anyway - unless appstatus is not cancelled, then it is no longer an appointment to confirm...
358 } elseif ($event['E_instructions'] == "always") { //send anyway
359 $appt_status = " and pc_apptstatus != '%'
360 and pc_apptstatus != 'x' ";
361 } else { //reminders are always or stop, that's it
362 $event['E_instructions'] ='stop';
363 $appt_status = " and pc_apptstatus='-'";//we only look at future appts w/ apptstatus == NONE ='-'
366 // Past appts -> appts that are completed.
367 // Need to exclude appts that were cancelled or noshowed
368 // Granular Functionality - select out via appt_stats/Visit Types/Doc/Fac is in Go Green Messaging
369 // Use the flag in the list_options to note that the appointment is completed
370 $interval ='-';// appts completed - this is defined by list_option->toggle_setting2=1 for Flow Board
371 $appt_status = " and pc_apptstatus in (SELECT option_id from list_options where toggle_setting_2='1' and list_id='apptstat')
372 and pc_apptstatus != '%'
373 and pc_apptstatus != 'x' ";
375 $timing = (int)$event['E_fire_time']-1;//the minus one is a critical change
376 // if it is Friday do stuff as scheduled + 2 days more
377 // for future appts, widen the net to get Timing2 = $timing +2.":1:1";
378 // eg an event/message is scheduled to go out 2 days in advance - reminder SMS.
379 // It is Friday. 2 days ahead is Sunday, but Monday's would run on Saturday and Tuesday's on Sunday.
380 // We should run them all on Friday... So load them that way now.
383 if (($today =="Sunday")||
($today =="Saturday")) {
386 if ($today == "Friday") {
387 $timing2 = ($timing +
3).":0:1"; //this is + 3 day, 0 hour and 1 minute...
389 $timing2 = ($timing +
1).":1:1"; //this is + 1 day, 1 hour and 1 minute...
391 $sql2= "SELECT ME_facilities FROM medex_prefs";
392 $pref_facilities = sqlQuery($sql2);
394 if (!empty($pref_facilities['ME_facilities'])) {
395 $places = str_replace("|", ",", $pref_facilities['ME_facilities']);
396 $query = "SELECT * FROM openemr_postcalendar_events AS cal
397 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
398 WHERE (pc_eventDate > CURDATE() ".$interval." INTERVAL ".$timing." DAY
399 AND pc_eventDate < (curdate() ".$interval." INTERVAL '".$timing2."' DAY_MINUTE))
400 AND pc_facility IN (".$places.")
401 AND pat.pid=cal.pc_pid ORDER BY pc_eventDate,pc_startTime";
403 $result = sqlStatement($query, $escapedArr);
404 while ($appt= sqlFetchArray($result)) {
405 list($response,$results) = $this->MedEx
->checkModality($event, $appt);
406 if ($results==false) {
407 continue; //not happening - either not allowed or not possible
412 $appt2['pc_pid'] = $appt['pc_pid'];
413 $appt2['pc_eventDate'] = $appt['pc_eventDate'];
414 $appt2['pc_startTime'] = $appt['pc_startTime'];
415 $appt2['pc_eid'] = $appt['pc_eid'];
416 $appt2['pc_aid'] = $appt['pc_aid'];
417 $appt2['e_reason'] = (!empty($appt['e_reason']))?
:'';
418 $appt2['e_is_subEvent_of']= (!empty($appt['e_is_subEvent_of']))?
:"0";
419 $appt2['language'] = $appt['language'];
420 $appt2['pc_facility'] = $appt['pc_facility'];
421 $appt2['fname'] = $appt['fname'];
422 $appt2['lname'] = $appt['lname'];
423 $appt2['mname'] = $appt['mname'];
424 $appt2['street'] = $appt['street'];
425 $appt2['postal_code'] = $appt['postal_code'];
426 $appt2['city'] = $appt['city'];
427 $appt2['state'] = $appt['state'];
428 $appt2['country_code'] = $appt['country_code'];
429 $appt2['phone_home'] = $appt['phone_home'];
430 $appt2['phone_cell'] = $appt['phone_cell'];
431 $appt2['email'] = $appt['email'];
432 $appt2['pc_apptstatus'] = $appt['pc_apptstatus'];
434 $appt2['C_UID'] = $event['C_UID'];
435 $appt2['reply'] = "To Send";
436 $appt2['extra'] = "QUEUED";
437 $appt2['status'] = "SENT";
439 $appt2['to'] = $results;
443 } else if ($event['M_group'] == 'RECALL') {
444 if ($event['time_order'] > '0') {
449 $timing = $event['E_fire_time'];
452 $query = "SELECT * FROM medex_recalls AS recall
453 LEFT JOIN patient_data AS pat ON recall.r_pid=pat.pid
454 WHERE (recall.r_eventDate < CURDATE() ".$interval." INTERVAL ".$timing." DAY)
455 ORDER BY recall.r_eventDate";
456 $result = sqlStatement($query);
458 while ($recall = sqlFetchArray($result)) {
459 list($response,$results) = $this->MedEx
->checkModality($event, $recall);
460 if ($results==false) {
461 continue; //not happening - either not allowed or not possible
463 $show = $this->MedEx
->display
->show_progress_recall($recall, $event);
464 if ($show['DONE'] == '1') {
465 // Appointment was made -the Recall about to be deleted, so don't process this RECALL
466 // MedEx needs to delete this RECALL!
467 $RECALLS_completed[] = $recall;
471 if ($show['status']!=="reddish") {
472 // OK there is status for this recall. Something happened. Maybe something was sent/postcard/phone call
473 // But despite what transpired there has been no appointment yet (yellowish) or it was just
474 // made today (greenish)? Either way, we don't want to regenerate this - don't add it to our Appt list.
478 // OK the list of recalls may include "older than today" recalls, they should only be included once...
479 // Otherwise you'll be sending messages every day to recalls that have passed. Not the intended work flow...
480 // If a recall date has passed but no MedEx messages have been sent yet, we want to do something.
481 // ie. loading recalls for the first time CAN include recalls that are already due, not just upcoming.
482 // If there is more than one campaign event that could fire for these "old recalls",
483 // we are only going to do the first one. Generate one thing for MedEx to do for this event.
484 // Maybe the practice schedules 2 RECALL Campaign Event Messages for the same day? We would want to run both right?
485 // Yes, and for new ones, ones just due today, we will follow that directive.
486 // However we will limit recalls that are in the past to one event!
487 // Note: This only is an issue the first time loading a recall whose due date has already passed.
488 // If a new recall is added for this patient eg. two years from now we need a new one,
489 // the old one will be deleted from memory.
490 if (strtotime($recall['r_eventDate']) < mktime(0, 0, 0)) {
491 if ($this->recursive_array_search("recall_".$recall['r_pid'], $appt3)) {
498 $recall2['pc_pid'] = $recall['r_pid'];
499 $recall2['pc_eventDate'] = $recall['r_eventDate'];
500 $recall2['pc_startTime'] = '10:42:00';
501 $recall2['pc_eid'] = "recall_".$recall['r_pid'];
502 $recall2['pc_aid'] = $recall['r_provider'];
503 $recall2['e_is_subEvent_of']= "0";
504 $recall2['language'] = $recall['language'];
505 $recall2['pc_facility'] = $recall['r_facility'];
506 $recall2['fname'] = $recall['fname'];
507 $recall2['lname'] = $recall['lname'];
508 $recall2['mname'] = $recall['mname'];
509 $recall2['street'] = $recall['street'];
510 $recall2['postal_code'] = $recall['postal_code'];
511 $recall2['city'] = $recall['city'];
512 $recall2['state'] = $recall['state'];
513 $recall2['country_code'] = $recall['country_code'];
514 $recall2['phone_home'] = $recall['phone_home'];
515 $recall2['phone_cell'] = $recall['phone_cell'];
516 $recall2['email'] = $recall['email'];
517 $recall2['C_UID'] = $event['C_UID'];
518 $recall2['reply'] = "To Send";
519 $recall2['extra'] = "QUEUED";
520 $recall2['status'] = "SENT";
521 $recall2['to'] = $results;
525 } else if ($event['M_group'] == 'ANNOUNCE') {
526 if (empty($event['start_date'])) {
529 $now = strtotime('now');
530 $delivery_date = strtotime($event['start_date']);
532 if ($now < $delivery_date) {
535 if ((!empty($event['appts_start'])) && (empty($event['appts_end']))) {
536 $target_dates = "pc_eventDate = ?";
537 $escapedArr[]=$event['appts_start'];
538 } else if ((!empty($event['appts_start'])) && (!empty($event['appts_end']))) {
539 $target_dates = "pc_eventDate >= ? and pc_eventDate <= ?";
540 $escapedArr[]=$event['appts_start'];
541 $escapedArr[]=$event['appts_end'];
544 //N.B. we shouldn't get here so move to next event if we do
547 if (!empty($event['appt_stats'])) {
549 $appt_stats = explode('|', $event['appt_stats']);
550 foreach ($appt_stats as $appt_stat) {
552 $escapedArr[]=$appt_stat;
554 $prepare_me = rtrim($prepare_me, ",");
555 $appt_status = " AND cal.pc_apptstatus in (".$prepare_me.") ";
560 if (!empty($event['providers'])) {
562 $providers = explode('|', $event['providers']);
563 foreach ($providers as $provider) {
565 $escapedArr[]=$provider;
567 $prepare_me = rtrim($prepare_me, ",");
568 $providers = " AND cal.pc_aid in (".$prepare_me.") ";
573 if (!empty($event['facilities'])) {
575 $facilities = explode('|', $event['facilities']);
576 foreach ($facilities as $facility) {
578 $escapedArr[]=$facility;
580 $prepare_me = rtrim($prepare_me, ",");
581 $places = " AND cal.pc_facility in (".$prepare_me.") ";
586 if (!empty($event['visit_types'])) {
588 $visit_types = explode('|', $event['visit_types']);
589 foreach ($visit_types as $visit_type) {
591 $escapedArr[]=$visit_type;
593 $prepare_me = rtrim($prepare_me, ",");
594 $visit_types = " AND cal.pc_catid in (".$prepare_me.") ";
599 $sql_ANNOUNCE = "SELECT * FROM openemr_postcalendar_events AS cal
600 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
601 WHERE (".$target_dates.")
606 ORDER BY pc_eventDate,pc_startTime";
607 $result = sqlStatement($sql_ANNOUNCE, $escapedArr);
609 while ($appt= sqlFetchArray($result)) {
610 list($response,$results) = $this->MedEx
->checkModality($event, $appt);
611 if ($results==false) {
612 continue; //not happening - either not allowed or not possible
614 $req_appt = print_r($appt, true);
615 $count_announcements++
;
618 $appt2['pc_pid'] = $appt['pc_pid'];
619 $appt2['pc_eventDate'] = $appt['pc_eventDate'];
620 $appt2['pc_startTime'] = $appt['pc_startTime'];
621 $appt2['pc_eid'] = $event['C_UID'].'_'.$appt['pc_eid'];
622 $appt2['pc_aid'] = $appt['pc_aid'];
623 $appt2['e_reason'] = (!empty($appt['e_reason']))?
:'';
624 $appt2['e_is_subEvent_of']= (!empty($appt['e_is_subEvent_of']))?
:"0";
625 $appt2['language'] = $appt['language'];
626 $appt2['pc_facility'] = $appt['pc_facility'];
627 $appt2['fname'] = $appt['fname'];
628 $appt2['lname'] = $appt['lname'];
629 $appt2['mname'] = $appt['mname'];
630 $appt2['street'] = $appt['street'];
631 $appt2['postal_code'] = $appt['postal_code'];
632 $appt2['city'] = $appt['city'];
633 $appt2['state'] = $appt['state'];
634 $appt2['country_code'] = $appt['country_code'];
635 $appt2['phone_home'] = $appt['phone_home'];
636 $appt2['phone_cell'] = $appt['phone_cell'];
637 $appt2['email'] = $appt['email'];
638 $appt2['e_apptstatus'] = $appt['pc_apptstatus'];
639 $appt2['C_UID'] = $event['C_UID'];
641 $appt2['reply'] = "To Send";
642 $appt2['extra'] = "QUEUED";
643 $appt2['status'] = "SENT";
645 $appt2['to'] = $results;
648 } else if ($event['M_group'] == 'SURVEY') {
649 // Look at appts on a per-provider basis for now...
650 if (empty($event['timing'])) {
651 $event['timing'] = "180";
653 // appts completed - this is defined by list_option->toggle_setting2=1 for Flow Board
654 $appt_status = " and pc_apptstatus in (SELECT option_id from list_options where toggle_setting_2='1' and list_id='apptstat') ";
655 //if we are to refine further, to not limit by completed status but with a specific appt_stats
656 // eg survey people who left without appt or
657 //T_appt_stats = array -> list of appstat(s) to restrict event to in a ',' separated list
658 if (!empty($event['T_appt_stats'])) {
659 foreach ($event['T_appt_stats'] as $stat) {
660 $escapedArr[] = $stat;
661 $escClause['Stat'] .= "?,";
663 rtrim($escStat, ",");
664 $appt_status = " and pc_appstatus in (".$escClause['Stat'].") ";
667 $sql2= "SELECT * FROM medex_prefs";
668 $pref = sqlQuery($sql2);
669 //if we are refining survey by facility
670 $facility_clause = '';
671 if (!empty($event['T_facilities'])) {
672 foreach ($event['T_facilities'] as $fac) {
673 $escapedArr[] = $fac;
674 $escClause['Fac'] .= "?,";
677 $facility_clause = " AND cal.pc_facility in (".$escClause['Fac'].") ";
679 $all_providers = explode('|', $pref['ME_providers']);
680 foreach ($event['survey'] as $k => $v) {
681 if (($v <= 0) ||
(empty($event['providers'])) ||
(!in_array($k, $all_providers))) {
685 $query = "SELECT * FROM openemr_postcalendar_events AS cal
686 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
688 cal.pc_eventDate > CURDATE() - INTERVAL ".$event['timing']." DAY AND
689 cal.pc_eventDate < CURDATE() - INTERVAL 3 DAY) AND
690 pat.pid=cal.pc_pid AND
691 pc_apptstatus !='%' AND
692 pc_apptstatus != 'x' ".
695 AND cal.pc_aid IN (?)
697 AND hipaa_allowemail NOT LIKE 'NO'
699 ORDER BY pc_eventDate,pc_startTime
701 //Survey opt-out option should feed into here also but there is no field for it in patient_data
702 //MedEx tracks opt-out requests however so unless there are a lot, it won't matter here.
703 $result = sqlStatement($query, $escapedArr);
704 while ($appt= sqlFetchArray($result)) {
705 list($response,$results) = $this->MedEx
->checkModality($event, $appt);
706 if ($results==false) {
707 continue; //not happening - either not allowed or not possible
710 $appt2['pc_pid'] = $appt['pc_pid'];
711 $appt2['pc_eventDate'] = $appt['pc_eventDate'];
712 $appt2['pc_startTime'] = $appt['pc_startTime'];
713 $appt2['pc_eid'] = $appt['pc_eid'];
714 $appt2['pc_aid'] = $appt['pc_aid'];
715 $appt2['e_reason'] = (!empty($appt['e_reason']))?
:'';
716 $appt2['e_is_subEvent_of']= (!empty($appt['e_is_subEvent_of']))?
:"0";
717 $appt2['language'] = $appt['language'];
718 $appt2['pc_facility'] = $appt['pc_facility'];
719 $appt2['fname'] = $appt['fname'];
720 $appt2['lname'] = $appt['lname'];
721 $appt2['mname'] = $appt['mname'];
722 $appt2['street'] = $appt['street'];
723 $appt2['postal_code'] = $appt['postal_code'];
724 $appt2['city'] = $appt['city'];
725 $appt2['state'] = $appt['state'];
726 $appt2['country_code'] = $appt['country_code'];
727 $appt2['phone_home'] = $appt['phone_home'];
728 $appt2['phone_cell'] = $appt['phone_cell'];
729 $appt2['email'] = $appt['email'];
730 $appt2['pc_apptstatus'] = $appt['pc_apptstatus'];
732 $appt2['C_UID'] = $event['C_UID'];
733 $appt2['E_fire_time'] = $event['E_fire_time'];
734 $appt2['time_order'] = $event['time_order'];
735 $appt2['M_type'] = $event['M_type'];
736 $appt2['reply'] = "To Send";
737 $appt2['extra'] = "QUEUED";
738 $appt2['status'] = "SENT";
740 $appt2['to'] = $results;
744 } else if ($event['M_group'] == 'CLINICAL_REMINDER') {
746 // from function fetch_reminders($patient_id = '', $type = '', $due_status = '', $select = '*')
747 // we need to run the update_reminders first... Use the batch feature probably.
748 // Perhaps in MedEx.php?
749 // $MedEx->campaign->events($token); to get the list of things we need to do:
750 // eg. category and item.
751 // $sql = "SELECT * FROM `patient_reminders` where `active`='1' AND
752 // `date_sent` IS NULL ORDER BY `patient_reminders`.`due_status` DESC ";
753 // and category=$CR['category']
754 // MedEx needs this - we are extrapolating it's name for display and item = $CR['item'] (M_name)
755 // We should already have the patient id in the $appt array.
756 // We should also have the reminder info we are targeting.
757 // Then we just need to the matching patient data.
758 // Check if possible_modalities here to see if the event is even possible?
759 // Think we should do that in $MedEx->campaign->events($token);
760 // if not possible we will need to add that info into a Clinical Reminders Flow Board........ visually.
761 // We will need to create this which means we need to overhaul the whole reminders code too?
765 $sql = "SELECT * FROM `patient_reminders`,`patient_data`
767 `patient_reminders`.pid ='".$event['PID']."' AND
768 `patient_reminders`.active='1' AND
769 `patient_reminders`.date_sent IS NULL AND
770 `patient_reminders`.pid=`patient_data`.pid
771 ORDER BY `due_status`, `date_created`";
772 $ures = sqlStatementCdrEngine($sql);
774 while ($urow = sqlFetchArray($ures)) {
775 list($response,$results) = $this->MedEx
->checkModality($event, $urow);
776 if ($results==false) {
777 continue; //not happening - either not allowed or not possible
779 $fields2['clinical_reminders'][] = $urow;
781 } else if ($event['M_group'] == 'GOGREEN') {
784 if (!empty($event['appt_stats'])) {
786 $appt_stats = explode('|', $event['appt_stats']);
787 foreach ($appt_stats as $appt_stat) {
789 $escapedArr[]=$appt_stat;
791 $prepare_me = rtrim($prepare_me, ",");
792 $appt_status = " AND cal.pc_apptstatus in (".$prepare_me.") ";
797 if (!empty($event['providers'])) {
799 $providers = explode('|', $event['providers']);
800 foreach ($providers as $provider) {
802 $escapedArr[]=$provider;
804 $prepare_me = rtrim($prepare_me, ",");
805 $providers = " AND cal.pc_aid in (".$prepare_me.") ";
810 if (!empty($event['facilities'])) {
812 $facilities = explode('|', $event['facilities']);
813 foreach ($facilities as $facility) {
815 $escapedArr[]=$facility;
817 $prepare_me = rtrim($prepare_me, ",");
818 $places = " AND cal.pc_facility in (".$prepare_me.") ";
823 if (!empty($event['visit_types'])) {
825 $visit_types = explode('|', $event['visit_types']);
826 foreach ($visit_types as $visit_type) {
828 $escapedArr[]=$visit_type;
830 $prepare_me = rtrim($prepare_me, ",");
831 $visit_types = " AND cal.pc_catid in (".$prepare_me.") ";
837 if ($event['E_instructions'] == 'once') {
838 //once - this patient only gets one
839 $frequency = " AND cal.pc_pid NOT in (
840 SELECT msg_pid from medex_outgoing where
842 $escapedArr[] = (int)$event['C_UID'];
843 } else if ($event['E_instructions'] == 'yearly') {
844 //yearly - this patient only gets this once a year
845 $frequency = " AND cal.pc_pid NOT in (
846 SELECT msg_pid from medex_outgoing where
848 msg_date > curdate() - interval 1 year )";
849 $escapedArr[] = (int)$event['C_UID'];
851 //otherwise Campaign was set to send with each appointment...
853 //make sure this only gets sent once for this campaign
854 $no_dupes = " AND cal.pc_eid NOT IN (
855 SELECT msg_pc_eid from medex_outgoing where
857 $escapedArr[] = (int)$event['C_UID'];
858 // don't waste our time with people w/o email addresses
859 // Go Green is just EMAIL for now
860 $no_dupes .= "AND pat.email >'' ";
861 //now we need to look to see if this is timed around an event occurrence
864 if ($event['E_timing'] == '5') {
865 $target_dates = " cal.pc_eventDate > curdate() ";
867 if (!is_numeric($event['E_fire_time'])) { //this would be an error in building the event
868 $event['E_fire_time'] ='0';
870 $timing = (int)$event['E_fire_time'];
871 if (($event['E_timing'] == '1') ||
($event['E_timing'] == '2')) {
872 //then this is X days prior to appt
873 $target_dates = "cal.pc_eventDate = curdate() + interval ".$timing." day ";
874 if ($today == "Friday") {
875 $timing2 = ($timing +
3);
876 $target_dates = "cal.pc_eventDate >= curdate() + interval ".$timing." day AND cal.pc_eventDate < (curdate() + INTERVAL '".$timing2."' DAY) ";
878 } else if (($event['E_timing'] == '3') ||
($event['E_timing'] == '4')) {
879 //then this is X days post appt
881 $target_dates = "cal.pc_eventDate = curdate() - interval ".$timing." day";
882 if ($today == "Monday") {
883 $timing2 = ($timing +
3);
884 $target_dates .= "cal.pc_eventDate <= curdate() - interval ".$timing." day AND cal.pc_eventDate > (curdate() - INTERVAL '".$timing2."' DAY) ";
888 $sql_GOGREEN = "SELECT * FROM openemr_postcalendar_events AS cal
889 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
898 ORDER BY cal.pc_eventDate,cal.pc_startTime";
899 $result = sqlStatement($sql_GOGREEN, $escapedArr);
900 while ($appt= sqlFetchArray($result)) {
901 list($response,$results) = $this->MedEx
->checkModality($event, $appt);
902 if ($results==false) {
903 continue; //not happening - either not allowed or not possible
908 $appt2['pc_pid'] = $appt['pc_pid'];
909 $appt2['pc_eventDate'] = $appt['pc_eventDate'];
910 $appt2['pc_startTime'] = $appt['pc_startTime'];
911 $appt2['pc_eid'] = $appt['pc_eid'];
912 $appt2['pc_aid'] = $appt['pc_aid'];
913 $appt2['e_reason'] = (!empty($appt['e_reason']))?
:'';
914 $appt2['e_is_subEvent_of']= (!empty($appt['e_is_subEvent_of']))?
:"0";
915 $appt2['language'] = $appt['language'];
916 $appt2['pc_facility'] = $appt['pc_facility'];
917 $appt2['fname'] = $appt['fname'];
918 $appt2['lname'] = $appt['lname'];
919 $appt2['mname'] = $appt['mname'];
920 $appt2['street'] = $appt['street'];
921 $appt2['postal_code'] = $appt['postal_code'];
922 $appt2['city'] = $appt['city'];
923 $appt2['state'] = $appt['state'];
924 $appt2['country_code'] = $appt['country_code'];
925 $appt2['phone_home'] = $appt['phone_home'];
926 $appt2['phone_cell'] = $appt['phone_cell'];
927 $appt2['email'] = $appt['email'];
928 $appt2['pc_apptstatus'] = $appt['pc_apptstatus'];
930 $appt2['C_UID'] = $event['C_UID'];
931 $appt2['reply'] = "To Send";
932 $appt2['extra'] = "QUEUED";
933 $appt2['status'] = "SENT";
935 $appt2['to'] = $results;
941 if (!empty($RECALLS_completed)) {
942 $deletes = $this->process_deletes($token, $RECALLS_completed);
945 if (!empty($appt3)) {
946 $this->process($token, $appt3);
948 $responses['deletes'] = $hipaa;
949 $responses['count_appts'] = $count_appts;
950 $responses['count_recalls'] = $count_recalls;
951 $responses['count_announcements'] = $count_announcements;
952 $responses['count_surveys'] = $count_surveys;
953 $responses['count_clinical_reminders'] = $count_clinical_reminders;
958 private function recursive_array_search($needle, $haystack)
960 foreach ($haystack as $key => $value) {
962 if ($needle===$value or (is_array($value) && $this->recursive_array_search($needle, $value))) {
963 return true; //$current_key;
970 * This function deletes Recalls from MedEx when they are completed and no further processing is
971 * needed. They are in an array = $data.
976 private function process_deletes($token, $data)
978 $this->curl
->setUrl($this->MedEx
->getUrl('custom/remRecalls&token='.$token));
979 $this->curl
->setData($data);
980 $this->curl
->makeRequest();
981 $response = $this->curl
->getResponse();
983 if (isset($response['success'])) {
985 } else if (isset($response['error'])) {
986 $this->lastError
= $response['error'];
992 * This function processes appointments/recalls that meet the timimg requirements for a MedEx Campaign Event
997 private function process($token, $appts)
1000 throw new InvalidDataException("You have no appointments that need processing at this time.");
1004 foreach ($appts as $appt) {
1005 $data['appts'][] = $appt;
1006 $sqlUPDATE = "UPDATE medex_outgoing SET msg_reply=?, msg_extra_text=?, msg_date=NOW()
1007 WHERE msg_pc_eid=? AND campaign_uid=? AND msg_type=? AND msg_reply='To Send'";
1008 sqlQuery($sqlUPDATE, array($appt['reply'],$appt['extra'],$appt['pc_eid'],$appt['C_UID'], $appt['M_type']));
1009 if (count($data['appts'])>'100') {
1010 $this->curl
->setUrl($this->MedEx
->getUrl('custom/loadAppts&token='.$token));
1011 $this->curl
->setData($data);
1012 $this->curl
->makeRequest();
1013 $response = $this->curl
->getResponse();
1018 //finish those $data < 100.
1019 $this->curl
->setUrl($this->MedEx
->getUrl('custom/loadAppts&token='.$token));
1020 $this->curl
->setData($data);
1021 $this->curl
->makeRequest();
1022 $response = $this->curl
->getResponse();
1024 if (isset($response['success'])) {
1026 } else if (isset($response['error'])) {
1027 $this->lastError
= $response['error'];
1032 public function save_recall($saved)
1034 $this->delete_Recall();
1035 $mysqldate = DateToYYYYMMDD($_REQUEST['form_recall_date']);
1036 $queryINS = "INSERT INTO medex_recalls (r_pid,r_reason,r_eventDate,r_provider,r_facility)
1039 UPDATE r_reason=?, r_eventDate=?, r_provider=?,r_facility=?";
1040 $result = sqlStatement($queryINS, array($_REQUEST['new_pid'],$_REQUEST['new_reason'],$mysqldate,$_REQUEST['new_provider'],$_REQUEST['new_facility'],$_REQUEST['new_reason'],$mysqldate,$_REQUEST['new_provider'],$_REQUEST['new_facility']));
1041 $query = "UPDATE patient_data
1042 SET phone_home=?,phone_cell=?,email=?,
1043 hipaa_allowemail=?,hipaa_voice=?,hipaa_allowsms=?,
1044 street=?,postal_code=?,city=?,state=?
1046 $sqlValues = array($_REQUEST['new_phone_home'],$_REQUEST['new_phone_cell'],$_REQUEST['new_email'],
1047 $_REQUEST['new_email_allow'],$_REQUEST['new_voice'],$_REQUEST['new_allowsms'],
1048 $_REQUEST['new_address'],$_REQUEST['new_postal_code'],$_REQUEST['new_city'],$_REQUEST['new_state'],
1049 $_REQUEST['new_pid']);
1050 $result = sqlStatement($query, $sqlValues);
1053 public function delete_Recall()
1055 $sqlQuery = "DELETE FROM medex_recalls WHERE r_pid=? OR r_ID=?";
1056 sqlStatement($sqlQuery, array($_POST['pid'],$_POST['r_ID']));
1058 $sqlDELETE = "DELETE FROM medex_outgoing WHERE msg_pc_eid = ?";
1059 sqlStatement($sqlDELETE, array('recall_'.$_POST['pid']));
1061 public function getAge($dob, $asof = '')
1064 $asof = date('Y-m-d');
1066 $a1 = explode('-', substr($dob, 0, 10));
1067 $a2 = explode('-', substr($asof, 0, 10));
1068 $age = $a2[0] - $a1[0];
1069 if ($a2[1] < $a1[1] ||
($a2[1] == $a1[1] && $a2[2] < $a1[2])) {
1077 * Process updates and message replies received from MedEx.
1078 * Lets MedEx know if we did anything manually to a queued event.
1080 class Callback
extends Base
1082 public function receive($data = '')
1087 if (empty($data['campaign_uid'])) {
1088 // throw new InvalidDataException("There must be a Campaign to update...");
1089 $response['success'] = "No campaigns to process.";
1091 //why aren't they the same??
1092 if (!$data['patient_id']) { //process AVM responses??
1093 if ($data['e_pid']) {
1094 $data['patient_id'] = $data['e_pid'];
1095 } else if ($data['pc_eid']) { //process responses from callback into MedEx.php - no pid just pc_eid
1096 $query = "SELECT * FROM openemr_postcalendar_events WHERE pc_eid=?"; //assume one patient per appointment pc_eid/slot...
1097 $patient = sqlFetchArray(sqlStatement($query, array($data['pc_eid']))); //otherwise this will need to be a loop
1098 $data['patient_id'] = $patient['pid'];
1101 if ($data['patient_id']) {
1102 //Store responses in TABLE medex_outgoing
1103 $sqlINSERT = "INSERT INTO medex_outgoing (msg_pc_eid, msg_pid, campaign_uid, msg_type, msg_reply, msg_extra_text, msg_date, medex_uid)
1104 VALUES (?,?,?,?,?,?,utc_timestamp(),?)";
1106 sqlQuery($sqlINSERT, array($data['pc_eid'],$data['patient_id'], $data['campaign_uid'], $data['M_type'],$data['msg_reply'],$data['msg_extra'],$data['msg_uid']));
1108 if ($data['msg_reply']=="CONFIRMED") {
1109 $sqlUPDATE = "UPDATE openemr_postcalendar_events SET pc_apptstatus = ? WHERE pc_eid=?";
1110 sqlStatement($sqlUPDATE, array($data['msg_type'],$data['pc_eid']));
1111 //need to insert this into patient_tracker
1112 $query = "SELECT * FROM patient_tracker WHERE eid=?";
1113 $tracker = sqlFetchArray(sqlStatement($query, array($data['pc_eid']))); //otherwise this will need to be a loop
1114 #Update lastseq in tracker.
1116 "UPDATE `patient_tracker` SET `lastseq` = ? WHERE eid=?",
1117 array(($tracker['lastseq']+
1),$data['pc_eid'])
1119 #Add a tracker item.
1120 $datetime = date("Y-m-d H:i:s");
1122 "INSERT INTO `patient_tracker_element` " .
1123 "(`pt_tracker_id`, `start_datetime`, `user`, `status`, `seq`) " .
1124 "VALUES (?,?,?,?,?,?)",
1125 array($tracker['id'],$datetime,'MedEx',$data['msg_type'],($tracker['lastseq']+
1))
1127 } elseif ($data['msg_reply']=="CALL") {
1128 $sqlUPDATE = "UPDATE openemr_postcalendar_events SET pc_apptstatus = 'CALL' WHERE pc_eid=?";
1129 $test = sqlQuery($sqlUPDATE, array($data['pc_eid']));
1130 //this requires attention. Send up the FLAG!
1131 //$this->MedEx->logging->new_message($data);
1132 } elseif (($data['msg_type']=="AVM") && ($data['msg_reply']=="STOP")) {
1133 //if reply = "STOP" update patient demographics to disallow this mode of communication
1134 $sqlUPDATE = "UPDATE patient_data SET hipaa_voice = 'NO' WHERE pid=?";
1135 sqlQuery($sqlUPDATE, array($data['patient_id']));
1136 } elseif (($data['msg_type']=="SMS") && ($data['msg_reply']=="STOP")) {
1137 $sqlUPDATE = "UPDATE patient_data SET hipaa_allowsms = 'NO' WHERE pid=?";
1138 sqlQuery($sqlUPDATE, array($data['patient_id']));
1139 } elseif (($data['msg_type']=="EMAIL") && ($data['msg_reply']=="STOP")) {
1140 $sqlUPDATE = "UPDATE patient_data SET hipaa_allowemail = 'NO' WHERE pid=?";
1141 sqlQuery($sqlUPDATE, array($data['patient_id']));
1143 if (($data['msg_type']=="SMS")&&($data['msg_reply']=="Other")) {
1144 //ideally we would be incrementing the "new Message Icon", perhaps using:
1145 //$this->MedEx->logging->new_message($data);
1147 if (($data['msg_reply']=="SENT")||
($data['msg_reply']=="READ")) {
1148 $sqlDELETE = "DELETE FROM medex_outgoing WHERE msg_pc_eid=? AND msg_reply='To Send'";
1149 sqlQuery($sqlDELETE, array($data['pc_eid']));
1151 $response['comments'] = $data['pc_eid']." - ".$data['campaign_uid']." - ".$data['msg_type']." - ".$data['reply']." - ".$data['extra'];
1152 $response['pid'] = $data['patient_id'];
1153 $response['success'] = $data['msg_type']." reply";
1155 $response['success'] = "completed";
1161 class Logging
extends base
1163 public function log_this($data)
1165 //truly a debug function, that we will probably find handy to keep on end users' servers;)
1167 $log = "/tmp/medex.log" ;
1168 $std_log = fopen($log, 'a');// or die(print_r(error_get_last(), true));
1169 $timed = date(DATE_RFC2822
);
1170 fputs($std_log, "**********************\nlibrary/MedEx/API.php fn log_this(data): ".$timed."\n");
1171 if (is_array($data)) {
1172 $dumper = print_r($data, true);
1173 fputs($std_log, $dumper);
1174 foreach ($data as $key => $value) {
1175 fputs($stdlog, $key.": ".$value."\n");
1178 fputs($std_log, "\nDATA= ".$data. "\n");
1185 class Display
extends base
1187 public function navigation($logged_in)
1189 global $setting_bootstrap_submenu;
1193 function toggle_menu() {
1194 var x
= document
.getElementById('hide_nav');
1195 if (x
.style
.display
=== 'none') {
1196 $
.post( "<?php echo $GLOBALS['webroot']."/interface/main
/messages
/messages
.php
"; ?>", {
1197 'setting_bootstrap_submenu' : 'show',
1198 success
: function (data
) {
1199 x
.style
.display
= 'block';
1204 $
.post( "<?php echo $GLOBALS['webroot']."/interface/main
/messages
/messages
.php
"; ?>", {
1205 'setting_bootstrap_submenu' : 'hide',
1206 success
: function (data
) {
1207 x
.style
.display
= 'none';
1211 $
("#patient_caret").toggleClass('fa-caret-up').toggleClass('fa-caret-down');
1217 function SMS_bot_list() {
1218 top
.restoreSession();
1219 var myWindow
= window
.open('<?php echo $GLOBALS['webroot
']; ?>/interface/main/messages/messages.php?nomenu=1&go=SMS_bot&dir=back&show=new','SMS_bot', 'width=383,height=600,resizable=0');
1224 <i
class="fa fa-caret-<?php
1225 if ($setting_bootstrap_submenu == 'hide') {
1229 } ?> menu_arrow" style
="position:fixed;left:5px;top:5px;z-index:1099;" id
="patient_caret" onclick
='toggle_menu();' aria
-hidden
="true"></i
>
1230 <div id
="hide_nav" style
="<?php if ($setting_bootstrap_submenu == 'hide') {
1231 echo "display
:none
;"; } ?>">
1232 <nav id
="navbar_oe" class="bgcolor2 navbar-fixed-top navbar-custom navbar-bright navbar-inner" name
="kiosk_hide"
1233 data
-role
="page banner navigation">
1234 <!-- Brand
and toggle get grouped
for better mobile display
-->
1235 <div
class="container-fluid">
1236 <div
class="navbar-header brand">
1237 <button type
="button" class="navbar-toggle" data
-toggle
="collapse" data
-target
="#oer-navbar-collapse-1">
1238 <span
class="sr-only"><?php
echo xlt("Toggle navigation"); ?
></span
>
1239 <span
class="icon-bar"></span
>
1240 <span
class="icon-bar"></span
>
1241 <span
class="icon-bar"></span
>
1244 <div
class="navbar-collapse collapse" id
="oer-navbar-collapse-1">
1245 <ul
class="navbar-nav">
1247 if ($GLOBALS['medex_enable'] == '1') {
1249 <li
class="dropdown">
1250 <a
class="dropdown-toggle" data
-toggle
="dropdown" id
="menu_dropdown_file" role
="button" aria
-expanded
="true"><?php
echo xlt("File"); ?
> </a
>
1251 <ul
class="bgcolor2 dropdown-menu" role
="menu">
1255 <li id
="menu_PREFERENCES" name
="menu_PREFERENCES" class=""><a onclick
="tabYourIt('prefs','main/messages/messages.php?go=Preferences');"><?php
echo xlt("Preferences"); ?
></a
></li
>
1256 <li id
="icons" name
="icons"><a onclick
="doRecallclick_edit('icons');"><?php
echo xlt('Icon Legend'); ?
></a
></li
>
1260 <li id
="menu_PREFERENCES" name
="menu_PREFERENCES" class="">
1261 <a href
="<?php echo $GLOBALS['web_root']; ?>/interface/main/messages/messages.php?go=setup&stage=1"><?php
echo xlt("Setup MedEx"); ?
></a
></li
>
1271 <li
class="dropdown">
1272 <a
class="dropdown-toggle" data
-toggle
="dropdown" id
="menu_dropdown_msg" role
="button" aria
-expanded
="true"><?php
echo xlt("Messages"); ?
> </a
>
1273 <ul
class="bgcolor2 dropdown-menu" role
="menu">
1274 <li id
="menu_new_msg"> <a href
="<?php echo $GLOBALS['web_root']; ?>/interface/main/messages/messages.php?showall=no&sortby=users.lname&sortorder=asc&begin=0&task=addnew&form_active=1"> <?php
echo xlt("New Message"); ?
></a
></li
>
1276 <li
class="divider"><hr
/></li
>
1278 <li id
="menu_new_msg"> <a href
="<?php echo $GLOBALS['web_root']; ?>/interface/main/messages/messages.php?show_all=no&form_active=1"> <?php
echo xlt("My Messages"); ?
></a
></li
>
1279 <li id
="menu_all_msg"> <a href
="<?php echo $GLOBALS['web_root']; ?>/interface/main/messages/messages.php?show_all=yes&form_active=1"> <?php
echo xlt("All Messages"); ?
></a
></li
>
1281 <li
class="divider"><hr
/></li
>
1283 <li id
="menu_active_msg"> <a href
="<?php echo $GLOBALS['web_root']; ?>/interface/main/messages/messages.php?show_all=yes&form_active=1"> <?php
echo xlt("Active Messages"); ?
></a
></li
>
1284 <li id
="menu_inactive_msg"> <a href
="<?php echo $GLOBALS['web_root']; ?>/interface/main/messages/messages.php?form_inactive=1"> <?php
echo xlt("Inactive Messages"); ?
></a
></li
>
1285 <li id
="menu_log_msg"> <a onclick
="openLogScreen();" > <?php
echo xlt("Message Log"); ?
></a
></li
>
1288 <li
class="dropdown" > <a
class="dropdown-toggle" data
-toggle
="dropdown" id
="menu_dropdown_recalls" role
="button" aria
-expanded
="true"><?php
echo xlt("Appt. Reminders"); ?
> </a
>
1289 <ul
class="bgcolor2 dropdown-menu" role
="menu">
1291 if ($GLOBALS['disable_calendar'] != '1') { ?
>
1292 <li
><a id
="BUTTON_ApRem_menu" onclick
="tabYourIt('cal','main/main_info.php');"> <?php
echo xlt("Calendar"); ?
></a
></li
>
1293 <li
class="divider"><hr
/></li
>
1296 if ($GLOBALS['disable_pat_trkr'] != '1') {
1298 <li id
="menu_pend_recalls" name
="menu_pend_recalls"> <a id
="BUTTON_pend_recalls_menu" onclick
="tabYourIt('flb','patient_tracker/patient_tracker.php?skip_timeout_reset=1');"> <?php
echo xlt("Flow Board"); ?
></a
></li
>
1302 <li
class="divider"><hr
/></li
>
1303 <li id
="menu_pend_recalls" name
="menu_pend_recalls"> <a href
='https://medexbank.com/cart/upload/index.php?route=information/campaigns&g=rem' target
="_medex" class='nowrap text-left' id
="BUTTON_pend_recalls_menu"> <?php
echo xlt("Reminder Campaigns"); ?
></a
></li
>
1311 if ($GLOBALS['disable_rcb'] != '1') { ?
>
1312 <li
class="dropdown">
1313 <a
class="dropdown-toggle" data
-toggle
="dropdown" id
="menu_dropdown_recalls" role
="button" aria
-expanded
="true"><?php
echo xlt("Patient Recalls"); ?
> </a
>
1314 <ul
class="bgcolor2 dropdown-menu" role
="menu">
1315 <li id
="menu_new_recall" name
="menu_new_recall"> <a id
="BUTTON_new_recall_menu" onclick
="tabYourIt('rcb','main/messages/messages.php?go=addRecall');"> <?php
echo xlt("New Recall"); ?
></a
></li
>
1316 <li id
="menu_pend_recalls" name
="menu_pend_recalls"> <a onclick
="goReminderRecall('Recalls');" id
="BUTTON_pend_recalls_menu" href
="#"> <?php
echo xlt("Recall Board"); ?
></a
></li
>
1320 <li
class="divider"><hr
/></li
>
1321 <li id
="menu_pend_recalls" name
="menu_pend_recalls"> <a href
='https://medexbank.com/cart/upload/index.php?route=information/campaigns&g=rec' target
="_medex" class='nowrap text-left' id
="BUTTON_pend_recalls_menu"> <?php
echo xlt("Recall Campaigns"); ?
></a
></li
>
1331 if (!empty($logged_in['products']['ordered'])) {
1332 foreach ($logged_in['products']['ordered'] as $ordered) {
1333 echo $ordered['menu'];
1340 </div
><!-- /.navbar
-collapse
-->
1345 if ($GLOBALS['medex_enable'] == '1') {
1346 $error=$this->MedEx
->getLastError();
1347 if (!empty($error['ip'])) {
1349 <div
class="alert alert-danger" style
="width:50%;margin:30px auto 5px;font-size:0.9em;text-align:center;">
1358 public function preferences($prefs = '')
1361 if (empty($prefs)) {
1362 $prefs = sqlFetchArray(sqlStatement("SELECT * FROM medex_prefs"));
1366 <div
class="col-sm-12 text-center">
1367 <div
class="showRecalls" id
="show_recalls">
1368 <div
class="title">MedEx
<?php
echo xlt('Preferences'); ?
></div
>
1369 <div name
="div_response" id
="div_response" class="form-inline"><br
/>
1371 <form action
="#" name
="save_prefs" id
="save_prefs">
1373 <input type
="hidden" name
="go" id
="go" value
="Preferences">
1374 <div
class="col-sm-5 div-center col-sm-offset-1" id
="daform2">
1375 <div
class="divTable2">
1376 <div
class="divTableBody prefs">
1377 <div
class="divTableRow">
1378 <div
class="divTableCell divTableHeading">MedEx
<?php
echo xlt('Username'); ?
></div
>
1379 <div
class="divTableCell indent20">
1380 <?php
echo $prefs['ME_username']; ?
>
1383 <div
class="divTableRow">
1384 <div
class="divTableCell divTableHeading"><?php
echo xlt('General'); ?
></div
>
1385 <div
class="divTableCell indent20">
1386 <input type
="checkbox" class="update" name
="ME_hipaa_default_override" id
="ME_hipaa_default_override" value
="1"
1388 if ($prefs['ME_hipaa_default_override']=='1') {
1389 echo 'checked ="checked"';
1392 <label
for="ME_hipaa_default_override" class="input-helper input-helper--checkbox"
1393 data
-toggle
='tooltip'
1394 data
-placement
='auto right'
1395 title
='<?php echo xla('Default'); ?>: "<?php echo xla('checked
'); ?>".
1396 <?php echo xla('When checked
, messages are processed
for patients with Patient Demographic Choice
: "Hipaa Notice Received" set to
"Unassigned" or "Yes". When unchecked
, this choice must
= "YES" to process the patient reminder
. For patients with Choice
="No", Reminders will need to be processed manually
.'); //or no translation... ?>'>
1397 <?php
echo xlt('Assume patients receive HIPAA policy'); ?
>
1399 <input type
="checkbox" class="update" name
="MSGS_default_yes" id
="MSGS_default_yes" value
="1" <?php
if ($prefs['MSGS_default_yes']=='1') {
1400 echo "checked='checked'";} ?
>>
1401 <label
for="MSGS_default_yes" class="input-helper input-helper--checkbox" data
-toggle
="tooltip" data
-placement
="auto" title
="<?php echo xla('Default: Checked. When checked, messages are processed for patients with Patient Demographic Choice (Phone/Text/Email) set to \'Unassigned\' or \'Yes\'. If this is unchecked, a given type of message can only be sent if its Demographic Choice = \'Yes\'.'); ?>">
1402 <?php
echo xlt('Assume patients permit Messaging'); ?
></label
>
1405 <div
class="divTableRow">
1406 <div
class="divTableCell divTableHeading"><?php
echo xlt('Enable Facility'); ?
></div
>
1407 <div
class="divTableCell indent20">
1410 $query = "SELECT * FROM facility";
1411 $result = sqlStatement($query);
1412 while ($fac = sqlFetchArray($result)) {
1415 $facs = explode('|', $prefs['ME_facilities']);
1416 foreach ($facs as $place) {
1417 if ($place == $fac['id']) {
1418 $checked = 'checked ="checked"';
1423 <input
<?php
echo $checked; ?
> class="update" type
="checkbox" name
="facilities[]" id
="facility_<?php echo attr($fac['id']); ?>" value
="<?php echo attr($fac['id']); ?>">
1424 <label
for="facility_<?php echo attr($fac['id']); ?>"><?php
echo text($fac['name']); ?
></label
><br
/><?php
1429 <div
class="divTableRow">
1430 <div
class="divTableCell divTableHeading"><?php
echo xlt('Included Providers'); ?
></div
>
1431 <div
class="divTableCell indent20">
1434 $ures = sqlStatement("SELECT * FROM users WHERE authorized != 0 AND active = 1 ORDER BY lname, fname");
1435 while ($prov = sqlFetchArray($ures)) {
1439 $provs = explode('|', $prefs['ME_providers']);
1440 foreach ($provs as $doc) {
1441 if ($doc == $prov['id']) {
1442 $checked = 'checked ="checked"';
1446 if (!empty($prov['suffix'])) {
1447 $suffix = ', '.$prov['suffix'];
1450 <input
<?php
echo $checked; ?
> class="update" type
="checkbox" name
="providers[]" id
="provider_<?php echo attr($prov['id']); ?>" value
="<?php echo attr($prov['id']); ?>">
1451 <label
for="provider_<?php echo attr($prov['id']); ?>"><?php
echo text($prov['fname'])." ".text($prov['lname']).text($suffix); ?
></label
><br
/><?php
1456 <div
class="divTableRow">
1457 <div
class="divTableCell divTableHeading"><?php
echo xlt('Labels'); ?
></div
>
1458 <div
class="divTableCell indent20">
1459 <input type
="checkbox" class="update" name
="LABELS_local" id
="LABELS_local" value
="1" <?php
if ($prefs['LABELS_local']) {
1460 echo "checked='checked'";} ?
> />
1461 <label
for="LABELS_local" class="input-helper input-helper--checkbox" data
-toggle
='tooltip' data
-placement
='auto' title
='<?php echo xla('Check
if you plan to
use Avery Labels
for Reminders
or Recalls
'); ?>'>
1462 <?php
echo xlt('Use Avery Labels'); ?
></label
>
1463 <select
class="update form-control ui-selectmenu-button ui-button ui-widget ui-selectmenu-button-closed ui-corner-all" id
="chart_label_type" name
="chart_label_type">
1464 <option value
='1' <?php
if ($prefs['LABELS_choice'] == '1') {
1465 echo "selected";} ?
>>5160</option
>
1466 <option value
='2' <?php
if ($prefs['LABELS_choice'] == '2') {
1467 echo "selected";} ?
>>5161</option
>
1468 <option value
='3' <?php
if ($prefs['LABELS_choice'] == '3') {
1469 echo "selected";} ?
>>5162</option
>
1470 <option value
='4' <?php
if ($prefs['LABELS_choice'] == '4') {
1471 echo "selected";} ?
>>5163</option
>
1472 <option value
='5' <?php
if ($prefs['LABELS_choice'] == '5') {
1473 echo "selected";} ?
>>5164</option
>
1474 <option value
='6' <?php
if ($prefs['LABELS_choice'] == '6') {
1475 echo "selected";} ?
>>8600</option
>
1476 <option value
='7' <?php
if ($prefs['LABELS_choice'] == '7') {
1477 echo "selected";} ?
>>L7163
</option
>
1478 <option value
='8' <?php
if ($prefs['LABELS_choice'] == '8') {
1479 echo "selected";} ?
>>3422</option
>
1484 <div
class="divTableRow">
1485 <div
class="divTableCell divTableHeading"><?php
echo xlt('Postcards'); ?
></div
>
1486 <div
class="divTableCell indent20">
1488 <input type
="checkbox" class="update" name
="POSTCARDS_local" id
="POSTCARDS_local" value
="1" <?php
if ($prefs['POSTCARDS_local']) {
1489 echo "checked='checked'";} ?
>" />
1490 <label for="POSTCARDS_local
" name="POSTCARDS_local
" class="input
-helper input
-helper
--checkbox
" data-toggle='tooltip' data-placement='auto' title='<?php echo xla('Check if you plan to print postcards locally'); ?>'><?php echo xlt('Print locally'); ?></label><br />
1491 <input type="checkbox
" class="update
" name="POSTCARDS_remote
" id="POSTCARDS_remote
" value="1" <?php if ($prefs['POSTCARDS_remote']) {
1492 echo "checked
='checked'";} ?>" />
1493 <label
for="POSTCARDS_remote" name
="POSTCARDS_remote" class="input-helper input-helper--checkbox" data
-toggle
='tooltip' data
-placement
='auto' title
='<?php echo xla('Check
if you plan to send postcards via MedEx
'); ?>'><?php
echo xlt('Print remotely'); ?
></label
>
1495 <label
for="postcards_top" data
-toggle
="tooltip" data
-placement
="auto" title
="<?php echo xla('Custom text for Flow Board postcards. After changing text, print samples before printing mass quantities!'); ?>"><u
><?php
echo xlt('Custom Greeting'); ?
>:</u
></label
><br
/>
1496 <textarea rows
=3 columns
=70 id
="postcard_top" name
="postcard_top" class="update form-control" style
="font-weight:400;"><?php
echo nl2br(text($prefs['postcard_top'])); ?
></textarea
>
1501 These options are for future use...
1503 <div class="divTableRow">
1504 <div class="divTableCell divTableHeading"><?php echo xlt('Combine Reminders'); ?></div>
1505 <div class="divTableCell indent20">
1507 <label for="combine_time" class="input-helper input-helper--checkbox" data-toggle='tooltip' data-placement='auto' title='If a patient has two or more future appointments scheduled within X days, combine reminders. eg. If you indicate "7" for this value, for a yearly physical with two appointments 3 days apart, or a surgical appointment with a follow-up 6 days post-op, these appointment reminds will be combined into one message, because they are less than "7" days apart.'>
1508 for appts within <input type="text" class="flow_time update" name="combine_time" id="combine_time" value="<?php echo xla($prefs['combine_time']); ?>" /> <?php echo xlt('days of each other'); ?></label>
1514 <input type
="hidden" name
="ME_username" id
="ME_username" value
="<?php echo attr($prefs['ME_username']);?>" />
1515 <input type
="hidden" name
="ME_api_key" id
="ME_api_key" value
="<?php echo attr($prefs['ME_api_key']);?>" />
1519 <div
class="col-sm-5 div-center" id
="daform2">
1520 <div
class="divTable2">
1521 <div
class="divTableBody prefs">
1522 <?php
if (count($logged_in['products']['ordered']) > '0') { ?
>
1523 <div
class="divTableRow">
1524 <div
class="divTableCell divTableHeading"><?php
echo xlt('Enabled Services'); ?
></div
>
1525 <div
class="divTableCell">
1528 foreach ($logged_in['products']['ordered'] as $service) {
1529 ?
><li
><a href
="<?php echo $service['view']; ?>" target
="_medex"><?php
echo $service['model']; ?
> </a
></li
>
1531 if ($service['product_id'] =='54') {
1533 <div style
="margin-left:10px;">Appointment Reminders
<br
/>Patient Recalls
<br
/>SMS Bot
<br
/>Go Green Messages
</div
>
1541 if (!empty($logged_in['products']['not_ordered'])) {
1543 <div
class="divTableRow">
1544 <div
class="divTableCell divTableHeading"><?php
echo xlt('Available Services'); ?
></div
>
1545 <div
class="divTableCell">
1548 foreach ($logged_in['products']['not_ordered'] as $service) {
1549 ?
><li
><a href
="<?php echo $service['view']; ?>" target
="_medex"><?php
echo $service['model']; ?
> </a
></li
>
1551 if ($service['product_id'] =='54') {
1553 <div style
="margin-left:10px;">Appointment Reminders
<br
/>Patient Recalls
<br
/>SMS Bot
<br
/>Go Green Messages
</div
>
1564 <div
class="col-sm-1"></div
>
1566 <div style
="clear:both;text-align:center;" id
="msg bottom"><br
/>
1574 public function display_recalls($logged_in)
1577 global $rcb_selectors;
1578 global $rcb_facility;
1579 global $rcb_provider;
1581 //let's get all the recalls the user requests, or if no dates set use defaults
1582 $from_date = !is_null($_REQUEST['form_from_date']) ?
DateToYYYYMMDD($_REQUEST['form_from_date']) : date('Y-m-d', strtotime('-6 months'));
1583 //limit date range for initial Board to keep us sane and not tax the server too much
1585 if (substr($GLOBALS['ptkr_end_date'], 0, 1) == 'Y') {
1586 $ptkr_time = substr($GLOBALS['ptkr_end_date'], 1, 1);
1587 $ptkr_future_time = mktime(0, 0, 0, date('m'), date('d'), date('Y')+
$ptkr_time);
1588 } elseif (substr($GLOBALS['ptkr_end_date'], 0, 1) == 'M') {
1589 $ptkr_time = substr($GLOBALS['ptkr_end_date'], 1, 1);
1590 $ptkr_future_time = mktime(0, 0, 0, date('m')+
$ptkr_time, date('d'), date('Y'));
1591 } elseif (substr($GLOBALS['ptkr_end_date'], 0, 1) == 'D') {
1592 $ptkr_time = substr($GLOBALS['ptkr_end_date'], 1, 1);
1593 $ptkr_future_time = mktime(0, 0, 0, date('m'), date('d')+
$ptkr_time, date('Y'));
1595 $to_date = date('Y-m-d', $ptkr_future_time);
1596 //prevSetting to_date?
1598 $to_date = !is_null($_REQUEST['form_to_date']) ?
DateToYYYYMMDD($_REQUEST['form_to_date']) : $to_date;
1600 $recalls = $this->get_recalls($from_date, $to_date);
1602 // if all we don't use MedEx, there is no need to display the progress tabs, all recall processing is manual.
1604 $reminder_bar = "nodisplay";
1607 $results = $MedEx->campaign
->events($logged_in['token']);
1608 $events = $results['events'];
1609 $reminder_bar = "indent20";
1611 $processed = $this->recall_board_process($logged_in, $recalls, $events);
1615 <div
class="container-fluid">
1616 <div
class="row-fluid" id
="rcb_selectors" style
="display:<?php echo attr($rcb_selectors); ?>">
1617 <div
class="col-sm-12">
1618 <div
class="showRFlow text-center" id
="show_recalls_params" style
="margin: 20px auto;">
1619 <div
class="title"><?php
echo xlt('Recall Board'); ?
></div
>
1620 <div id
="div_response"><?php
echo xlt('Persons needing a recall, no appt scheduled yet'); ?
>.</div
>
1622 if ($GLOBALS['medex_enable'] == '1') {
1626 $last_col_width="nodisplay";
1630 <form name
="rcb" id
="rcb" method
="post">
1631 <input type
="hidden" name
="go" value
="Recalls">
1632 <div
class=" text-center row divTable" style
="width: 85%;float:unset;margin: 0 auto;">
1634 <div
class="col-sm-<?php echo $col_width; ?> text-center" style
="margin-top:15px;">
1635 <input placeholder
="<?php echo attr('Patient ID'); ?>"
1636 class="form-control input-sm"
1637 type
="text" id
="form_patient_id"
1638 name
="form_patient_id"
1639 value
="<?php echo ( $form_patient_id ) ? attr($form_patient_id) : ""; ?>"
1640 onKeyUp
="show_this();">
1643 placeholder
="<?php echo attr('Patient Name'); ?>"
1644 class="form-control input-sm" id
="form_patient_name"
1645 name
="form_patient_name"
1646 value
="<?php echo ( $form_patient_name ) ? attr($form_patient_name) : ""; ?>"
1647 onKeyUp
="show_this();">
1650 <div
class="col-sm-<?php echo $col_width; ?> text-center" style
="margin-top:15px;">
1651 <select
class="form-group ui-selectmenu-button ui-button ui-widget ui-selectmenu-button-closed ui-corner-all" id
="form_facility" name
="form_facility"
1653 $fac_sql = sqlStatement("SELECT * FROM facility ORDER BY id");
1654 while ($fac = sqlFetchArray($fac_sql)) {
1655 $true = ($fac['id'] == $rcb_facility) ?
"selected=true" : '';
1656 $select_facs .= "<option value=".attr($fac['id'])." ".$true.">".text($fac['name'])."</option>\n";
1659 if ($count_facs <'1') {
1662 ?
> onchange
="show_this();">
1663 <option value
=""><?php
echo xlt('All Facilities'); ?
></option
>
1664 <?php
echo $select_facs; ?
>
1667 # Build a drop-down list of providers.
1668 $query = "SELECT id, lname, fname FROM users WHERE ".
1669 "authorized = 1 AND active = 1 ORDER BY lname, fname"; #(CHEMED) facility filter
1670 $ures = sqlStatement($query);
1671 //a year ago @matrix-amiel Adding filters to flow board and counting of statuses
1672 $count_provs = count(sqlFetchArray($ures));
1674 <select
class="form-group ui-selectmenu-button ui-button ui-widget ui-selectmenu-button-closed ui-corner-all" id
="form_provider" name
="form_provider" <?php
1675 if ($count_provs <'2') {
1678 ?
> onchange
="show_this();">
1679 <option value
="" selected
><?php
echo xlt('All Providers'); ?
></option
>
1682 // Build a drop-down list of ACTIVE providers.
1683 $query = "SELECT id, lname, fname FROM users WHERE ".
1684 "authorized = 1 AND active = 1 ORDER BY lname, fname"; #(CHEMED) facility filter
1686 $ures = sqlStatement($query);
1687 //a year ago @matrix-amiel Adding filters to flow board and counting of statuses
1688 while ($urow = sqlFetchArray($ures)) {
1689 $provid = $urow['id'];
1690 echo " <option value='" . attr($provid) . "'";
1691 if (isset($rcb_provider) && $provid == $_POST['form_provider']) {
1693 } elseif (!isset($_POST['form_provider'])&& $_SESSION['userauthorized'] && $provid == $_SESSION['authUserID']) {
1696 echo ">" . text($urow['lname']) . ", " . text($urow['fname']) . "\n";
1701 <div
class="col-sm-<?php echo $col_width; ?>">
1702 <div style
="margin: 0 auto;" class="input-append">
1703 <table
class="table-hover table-condensed" style
="margin:0 auto;">
1704 <tr
><td
class="text-right" style
="vertical-align:bottom;">
1705 <label
for="flow_from"><?php
echo xlt('From'); ?
>:</label
></td
><td
>
1706 <input id
="form_from_date" name
="form_from_date"
1707 class="datepicker form-control input-sm text-center"
1708 value
="<?php echo attr(oeFormatShortDate($from_date)); ?>"
1709 style
="max-width:140px;min-width:85px;">
1712 <tr
><td
class="text-right" style
="vertical-align:bottom;">
1713 <label
for="flow_to"> 
; 
;<?php
echo xlt('To'); ?
>:</label
></td
><td
>
1714 <input id
="form_to_date" name
="form_to_date"
1715 class="datepicker form-control input-sm text-center"
1716 value
="<?php echo attr(oeFormatShortDate($to_date)); ?>"
1717 style
="max-width:140px;min-width:85px;">
1720 <tr
><td
class="text-center" colspan
="2">
1721 <input href
="#" class="btn btn-primary" type
="submit" id
="filter_submit" value
="<?php echo xla('Filter'); ?>">
1727 <div
class="col-sm-<?php echo $col_width." ".$last_col_width; ?> text-center" >
1729 if ($GLOBALS['medex_enable'] == '1') {
1731 foreach ($results['events'] as $event) {
1732 if ($event['M_group'] != 'RECALL') {
1735 $icon = $this->get_icon($event['M_type'], 'SCHEDULED');
1736 if ($event['E_timing'] =='1') {
1739 if ($event['E_timing'] =='2') {
1740 $action = "before (PM)";
1742 if ($event['E_timing'] =='3') {
1745 if ($event['E_timing'] =='4') {
1746 $action = "after (PM)";
1748 $current_events .= $icon." ".$event['E_fire_time']." ".xlt('days')." ".xlt($action)."<br />";
1752 <a
class="fa fw fa-plus-square-o" data
-toggle
="tooltip" data
-placement
="auto" title
="<?php echo xla('Add a New Recall'); ?>" id
="BUTTON_new_recall_menu" href
="<?php echo $GLOBALS['web_root']; ?>/interface/main/messages/messages.php?go=addRecall"></a
>
1753 <b
><u
>MedEx
<?php
echo xlt('Recall Schedule'); ?
></u
></b
><br
/>
1754 <a href
="https://medexbank.com/cart/upload/index.php?route=information/campaigns&g=rec" target
="_medex">
1756 <?php
echo $current_events; ?
>
1763 <div name
="message" id
="message" class="warning"></div
>
1771 <div
class="row-fluid">
1772 <div
class="col-sm-12 text-center">
1773 <div
class="showRecalls" id
="show_recalls" style
="margin:0 auto;">
1774 <div name
="message" id
="message" class="warning"></div
>
1775 <span
class="text-right fa-stack fa-lg pull_right small" id
="rcb_caret" onclick
="toggleRcbSelectors();" data
-toggle
="tooltip" data
-placement
="auto" title
="Show/Hide the Filters"
1776 style
="color:<?php echo $color = ($setting_selectors=='none') ? 'red' : 'black'; ?>;position:relative;float:right;right:0;top:0;">
1777 <i
class="fa fa-square-o fa-stack-2x"></i
>
1778 <i id
="print_caret" class='fa fa-caret-<?php echo $caret = ($rcb_selectors==='none
') ? 'down
' : 'up
'; ?> fa-stack-1x'></i
>
1780 <ul
class="nav nav-tabs <?php echo attr($reminder_bar); ?>">
1781 <li
class="active whitish"><a onclick
="show_this();" data
-toggle
="tab"><?php
echo xlt('All'); ?
></a
></li
>
1782 <li
class="whitish"><a onclick
="show_this('whitish');" data
-toggle
="tab"><?php
echo xlt('Events Scheduled'); ?
></a
></li
>
1783 <li
class="yellowish"><a onclick
="show_this('yellowish');" data
-toggle
="tab"><?php
echo xlt('In-process'); ?
></a
></li
>
1784 <li
class="reddish"><a onclick
="show_this('reddish');" data
-toggle
="tab"><?php
echo xlt('Manual Processing Required'); ?
></a
></li
>
1785 <li
class="greenish"><a onclick
="show_this('greenish');" data
-toggle
="tab"><?php
echo xlt('Recently Completed'); ?
></a
></li
>
1788 <div
class="tab-content">
1790 <div
class="tab-pane active" id
="tab-all">
1792 $this->recall_board_top();
1793 echo $processed['ALL'];
1794 $this->recall_board_bot();
1803 //we need to respect facility and provider requests if submitted.
1804 // 1.Retrieve everything for a given date range.
1805 // 2.Refine results by facility and provider using jquery on cached results
1806 // ie. further requests to view facility/provider within page can be done fast through javascript, no page reload needed.
1809 function toggleRcbSelectors() {
1810 if ($
("#rcb_selectors").css('display') === 'none') {
1811 $
.post( "<?php echo $GLOBALS['webroot']."/interface/main
/messages
/messages
.php
"; ?>", {
1812 'rcb_selectors' : 'block',
1813 success
: function (data
) {
1814 $
("#rcb_selectors").slideToggle();
1815 $
("#rcb_caret").css('color','#000');
1819 $
.post( "<?php echo $GLOBALS['webroot']."/interface/main
/messages
/messages
.php
"; ?>", {
1820 'rcb_selectors' : 'none',
1821 success
: function (data
) {
1822 $
("#rcb_selectors").slideToggle();
1823 $
("#rcb_caret").css('color','red');
1827 $
("#print_caret").toggleClass('fa-caret-up').toggleClass('fa-caret-down');
1833 function SMS_bot(pid
) {
1834 top
.restoreSession();
1835 pid
= pid
.replace('recall_','');
1836 window
.open('<?php echo $GLOBALS['webroot
']; ?>/interface/main/messages/messages.php?nomenu=1&go=SMS_bot&pid=' + pid
,'SMS_bot', 'width=370,height=600,resizable=0');
1839 $
(document
).ready(function() {
1842 $
('.datepicker').datetimepicker({
1843 <?php
$datetimepicker_timepicker = false; ?
>
1844 <?php
$datetimepicker_showseconds = false; ?
>
1845 <?php
$datetimepicker_formatInput = true; ?
>
1846 <?php
require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?
>
1847 <?php
// can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
1852 $content = ob_get_clean();
1855 public function get_recalls($from_date = '', $to_date = '')
1857 // Recalls are requests to schedule a future appointment.
1858 // Thus there is no r_appt_time (NULL) but there is a DATE set.
1860 $query = "SELECT * FROM medex_recalls,patient_data AS pat
1861 WHERE pat.pid=medex_recalls.r_pid AND
1862 r_eventDate >= ? AND
1863 r_eventDate <= ? AND
1864 IFNULL(pat.deceased_date,0) = 0
1865 ORDER BY r_eventDate ASC";
1866 $result = sqlStatement($query, array($from_date,$to_date));
1867 while ($recall= sqlFetchArray($result)) {
1872 private function recall_board_process($logged_in, $recalls, $events = '')
1876 if (empty($recalls)) {
1879 $fac_sql = sqlStatement("SELECT id, name FROM facility WHERE service_location != 0");
1880 while ($facrow = sqlFetchArray($fac_sql)) {
1881 $facility[$facrow['id']] = $facrow['name'];
1882 $count_facilities++
;
1884 $prov_sql = sqlStatement("SELECT * FROM users WHERE authorized != 0 AND active = 1 ORDER BY lname, fname");
1885 while ($prov = sqlFetchArray($prov_sql)) {
1886 $provider[$prov['id']] = $prov['fname'][0]." ".$prov['lname'];
1887 if (!empty($prov['suffix'])) {
1888 $provider[$prov['id']] .= ', '.$prov['suffix'];
1892 foreach ($recalls as $recall) {
1893 $show = $this->show_progress_recall($recall, $events);
1894 if (!empty($show['DONE'])) {
1897 if (empty($show['status'])) {
1898 $show['status'] = 'whitish';
1901 echo '<div class="divTableRow ALL '.attr($show['status']).'"
1902 data-status="'.attr($show['status']).'"
1903 data-plan="'.attr($show['plan']).'"
1904 data-facility="'.attr($recall['r_facility']).'"
1905 data-provider="'.attr($recall['r_provider']).'"
1906 data-pname="'.attr($recall['fname']." ".$recall['lname']).'"
1907 data-pid="'.attr($recall['pid']).'"
1908 id="recall_'.attr($recall['pid']).'" style="display:none;">';
1910 $query = "SELECT cal.pc_eventDate,pat.DOB FROM openemr_postcalendar_events AS cal JOIN patient_data AS pat ON cal.pc_pid=pat.pid WHERE cal.pc_pid =? ORDER BY cal.pc_eventDate DESC LIMIT 1";
1911 $result2 = sqlQuery($query, array( $recall['pid'] ));
1912 $last_visit = $result2['pc_eventDate'];
1913 $DOB = oeFormatShortDate($result2['DOB']);
1914 $age = $MedEx->events
->getAge($result2['DOB']);
1915 echo '<div class="divTableCell text-center"><a href="#" onclick="show_patient(\''.attr($recall['pid']).'\');"> '.text($recall['fname']).' '.text($recall['lname']).'</a>';
1916 if ($GLOBALS['ptkr_show_pid']) {
1917 echo '<br /><span data-toggle="tooltip" data-placement="auto" title="'.xla("Patient ID").'" class="small">'. xlt('PID').': '.text($recall['pid']).'</span>';
1919 echo '<br /><span data-toggle="tooltip" data-placement="auto" title="'.xla("Most recent visit").'" class="small">' . xlt("Last Visit") . ': '.text(oeFormatShortDate($last_visit)).'</span>';
1920 echo '<br /><span class="small" data-toggle="tooltip" data-placement="auto" title="'.xla("Date of Birth and Age").'">'. xlt('DOB').': '.text($DOB).' ('.$age.')</span>';
1923 echo '<div class="divTableCell appt_date">'.text(oeFormatShortDate($recall['r_eventDate']));
1924 if ($recall['r_reason']>'') {
1925 echo '<br />'.text($recall['r_reason']);
1927 if (strlen($provider[$recall['r_provider']]) > 14) {
1928 $provider[$recall['r_provider']] = substr($provider[$recall['r_provider']], 0, 14)."...";
1930 if (strlen($facility[$recall['r_facility']]) > 20) {
1931 $facility[$recall['r_facility']] = substr($facility[$recall['r_facility']], 0, 17)."...";
1934 if ($count_providers > '1') {
1935 echo "<br /><span data-toggle='tooltip' data-placement='auto' title='".xla('Provider')."'>".text($provider[$recall['r_provider']])."</span>";
1937 if (( $count_facilities > '1' ) && ( $_REQUEST['form_facility'] =='' )) {
1938 echo "<br /><span data-toggle='tooltip' data-placement='auto' title='".xla('Facility')."'>".text($facility[$recall['r_facility']])."</span><br />";
1942 echo '<div class="divTableCell phones">';
1943 if ($recall['phone_cell'] >'') {
1944 echo 'C: '.text($recall['phone_cell'])."<br />";
1945 // echo 'C:'.substr($recall['phone_cell'], 0, 2).'-XXX-XXXX<br />';
1947 if ($recall['phone_home'] >'') {
1948 echo 'H: '.text($recall['phone_home'])."<br />";
1949 //echo 'H:'.substr($recall['phone_home'], 0, 2).'-XXX-XXXX<br />';
1951 if ($recall['email'] >'') {
1952 $mailto = $recall['email'];
1953 if (strlen($recall['email']) > 15) {
1954 $recall['email'] = substr($recall['email'], 0, 12)."...";
1956 echo 'E: <a data-toggle="tooltip" data-placement="auto" title="'.xla('Send an email to ').attr($mailto).'" href="mailto:'.attr($mailto).'">'.text($recall['email']).'</a><br />';
1959 $pat = $this->possibleModalities($recall);
1960 echo $pat['SMS'].$pat['AVM'].$pat['EMAIL'];//escape/translation done in possibleModalities.
1964 if ($show['postcard'] > '') {
1965 echo '<div class="divTableCell text-center postcards">'.text($show['postcard']).'</div>';
1967 echo '<div class="divTableCell text-center postcards"><input type="checkbox" name="postcards" id="postcards[]" value="'.attr($recall['pid']).'"></div>';
1970 if ($show['label'] > '') {
1971 echo '<div class="divTableCell text-center labels">'.text($show['label']).'</div>';
1973 echo '<div class="divTableCell text-center labels"><input type="checkbox" name="labels" id="labels[]" value="'.attr($recall['pid']).'"></div>';
1975 echo ' <div class="divTableCell text-center msg_manual"><span class="fa fa-fw spaced_icon" >
1976 <input type="checkbox" name="msg_phone" id="msg_phone_'.attr($recall['pid']).'" onclick="process_this(\'phone\',\''.attr($recall['pid']).'\',\''.attr($recall['r_ID']).'\')" />
1978 echo ' <span data-toggle="tooltip" data-placement="auto" title="'.xla('Scheduling').'" class="fa fa-calendar-check-o fa-fw" onclick="newEvt(\''.attr($recall['pid']).'\',\'\');">
1982 echo ' <div class="divTableCell text-left msg_resp">';
1983 // if phone call made show each in progress
1984 echo '<textarea onblur="process_this(\'notes\',\''.attr($recall['pid']).'\',\''.attr($recall['r_ID']).'\');" name="msg_notes" id="msg_notes_'.attr($recall['pid']).'" style="width:90%;height:30px;">'.nl2br(text($recall['NOTES'])).'</textarea>';
1986 echo ' <div class="divTableCell text-left msg_resp">
1987 <i class="top_right_corner fa fa-times" onclick="delete_Recall(\''.attr($recall['pid']).'\',\''.attr($recall['r_ID']).'\')"></i> ';
1988 echo $show['progression'];
1990 if ($show['appt']) {
1991 echo "<span onclick=\"newEvt('".attr($prog['pid'])."','".attr($show['pc_eid'])."');\" class='btn btn-danger text-center' data-toggle='tooltip' data-placement='auto' title='".xla('Appointment made by')." ".attr($prog['who'])." ".xla('on')." ".attr($prog['when'])."'><b>".xlt('Appt{{Abbreviation for appointment}}').":</b> ".text($show['appt'])."<br />";
1995 $content = ob_get_clean();
1996 $process['ALL'] .= $content;
2002 * This function looks at a single recall and assesses its status.
2003 * Has it been worked on yet? Any phone calls made, labels printed or postcards printed?
2004 * If they are a MedEx subscriber, do they have any scheduled Recall Campaign Events and if so when?
2005 * Have any of these MedEx events happened? Given all the variables, what is the status of this recall at this moment?
2006 * We also use color coding in the Recall Board -
2007 * -- whitish for pending, nothing happed yet.
2008 * -- yellowish for in process, something happened but no appointment was made yet!
2009 * -- reddish for manual processing needed.
2010 * -- greenish for completed and an appointment was made.
2011 * In the ideal workflow, the secretary would be going through the Recall Board line by line once per day/week/month (per practice needs).
2012 * They can then just delete the Recall by clicking the X on the right. Done move on to the next.
2013 * However a patient may have called in randomly to make this appointment - the secretary may be unaware that the Recall even exists.
2014 * In this work-flow, the secretary does not have to open the Recall Board, nor should they waste their time when we can do it for them...
2015 * Let the Recall Board look to see if there is an appointment for this patient in the future.
2016 * If there is, and it was made more than 16 hours ago, it deletes the Recall altogether. It is never displayed again.
2017 * If an appointment was created less than 16 hours ago, it turns the row greenish, signaling anyone who looks that it was just taken care of,
2018 * and display in the "progress" column what has transpired.
2019 * Thanks, move on, nothing to see here.
2020 * This also allows anyone (management?) a tool to see what Recall work was performed over the last 16 hours.
2022 * @param string $events
2024 * @internal param string $possibleModalities
2026 public function show_progress_recall($recall, $events = '')
2029 //Two scenarios: First, appt is made as recall asks. Second, appt is made not for recall reason - recall still needed.
2030 //We can either require all recalls to be manually deleted or do some automatically... If manual only,
2031 //the secretary looking at the board will need to know when they were last seen at least and when next appt is
2032 //to know if they can/should delete the recall. If semi-automatic, we'll use an artificial time horizon of 3 months.
2033 //If an appt is made through any means, and it is within 3 months of the recall date, assume it wipes out the recall.
2034 //If the appt was just made today, let the board show it as "green", ie. completed. Gives us a sense of accomplishment,
2035 //that we got some work done today...
2036 //So, if appt was made more than 16 hours ago, and it is within 3 months of the recall date, auto-delete the recall from the board.
2037 //ie. appts added in for problem visits won't auto-delete an official recall unless they are close in time to the recall...
2038 //Adjust according to your needs and work flows. This function is run by the Recall Board and with cron MedEx calls.
2039 $show['EMAIL']['text']='';
2040 $show['SMS']['text']='';
2041 $show['AVM']['text']='';
2042 $show['progression']='';
2044 $query = "SELECT * FROM openemr_postcalendar_events WHERE
2045 pc_eventDate >= CURDATE() AND pc_pid =? AND pc_eventDate > (? - INTERVAL 90 DAY) AND pc_time > (CURDATE()- INTERVAL 16 HOUR)";
2046 $count = sqlFetchArray(sqlStatement($query, array($recall['r_pid'],$recall['r_eventDate'])));
2049 $sqlDELETE = "DELETE FROM medex_outgoing WHERE msg_pc_eid = ?";
2050 sqlStatement($sqlDELETE, array('recall_'.$recall['pid']));
2051 $sqlDELETE = "DELETE FROM medex_recalls WHERE r_pid = ?";
2052 sqlStatement($sqlDELETE, array($recall['pid']));
2053 //log this action "Recall for $pid deleted now()"?
2054 $show['DONE'] ='1';//tells recall board to move on.
2055 $show['status'] ='greenish'; //tells MedEx to move on, don't process this recall - delete it from their servers.
2057 // Just cleaning up the Recall Board for you. Move along, nothing to see.
2058 // If you need to look at the track of action, look in the log?
2061 // Did anything happen yet?
2062 // Table medex_outgoing is our log for current activities.
2063 // It includes records of local manual things your office did and the MedEx reports.
2064 // For non-MedEx subscribers, the local functionality will still work just fine...
2065 // Unless the RECALL is completed (appt made) more than 16 hours ago, the RECALL's data will be present.
2067 // We need to output the correct text and icon to visually display the appt status
2069 $sql ="SELECT * FROM medex_outgoing WHERE msg_pc_eid = ? ORDER BY msg_date ASC";
2070 $result = sqlStatement($sql, array('recall_'.$recall['pid']));
2071 $something_happened='';
2073 while ($progress = sqlFetchArray($result)) {
2074 $i = $progress['campaign_uid'];//if this is a manual entry, this ==0.
2076 $phpdate = strtotime($progress['msg_date']);
2077 $when = oeFormatShortDate(date('Y-m-d', $phpdate))." @ ".date('g:iA', $phpdate);
2079 if (is_numeric($progress['msg_reply'])) { // it was manually added by id
2080 $sql2 = "SELECT * FROM users WHERE id =?";
2082 $who = sqlQuery($sql2, array($progress['msg_reply']));
2083 $who_name = $who['fname']." ".$who['lname'];
2084 //Manually generated actions
2085 if ($progress['msg_type'] == 'phone') { //ie. a manual phone call, not an AVM
2086 $show['progression'] .= "<span class='left' data-toggle='tooltip' data-placement='auto' title='".xla('Phone call made by')." ".text($who_name)."'><b>".xlt('Phone')."</b> ".text($when)."</span></br />\n";
2087 } elseif ($progress['msg_type'] == 'notes') {
2088 $show['progression'] .= "<span class='left' data-toggle='tooltip' data-placement='auto' title='".xla('Notes by')." ".text($who_name)." on ".text($when)."'><b>".xlt('Note').":</b> ".text($progress['msg_extra_text'])."</span></br />\n";
2089 } elseif ($progress['msg_type'] == 'postcards') {
2090 $show['progression'] .= "<span class='left' data-toggle='tooltip' data-placement='auto' title='".xla('Postcard printed by')." ".text($who_name)."'><b>".xlt('Postcard').":</b> ".text($when)."</span></br />\n";
2091 } elseif ($progress['msg_type'] == 'labels') {
2092 $show['progression'] .= "<span class='left' data-toggle='tooltip' data-placement='auto' title='".xla('Label printed by')." ".text($who)."'><b>".xlt('Label').":</b> ".text($when)."</span></br />";
2095 $who_name = "MedEx";
2096 // MedEx related actions
2097 // Recalls can't be confirmed through MedEx - we don't make appointments (yet)...
2098 // They disappear 16 hours after an appt is made (they glow green for those 16 hours).
2099 if (($progress['msg_reply'] == "READ")||
($show[$progress['msg_type']]['stage']=="READ")) {
2100 $show[$progress['msg_type']]['stage'] = "READ";
2101 $icon = $this->get_icon($progress['msg_type'], "READ");
2102 $show[$progress['msg_type']]['text'] = "<span class='left'>".$icon." ".text($when)."</span><br />";
2103 if ($progress['msg_type'] == 'AVM') {
2104 $show['campaign'][$i]['status']="reddish";
2106 } elseif (($progress['msg_reply'] == "SENT")||
($show[$progress['msg_type']]['stage']=="SENT")) {
2107 if ($show[$progress['msg_type']]['stage']!="READ") {
2108 $show[$progress['msg_type']]['stage'] = "SENT";
2109 $icon = $this->get_icon($progress['msg_type'], "SENT");
2110 $show[$progress['msg_type']]['text'] = "<span class='left'>".$icon." ".text($when)."</span><br />";
2112 } elseif (($progress['msg_reply'] == "To Send")||
($show[$progress['msg_type']]['stage']=="QUEUED")) {
2113 if (($show[$progress['msg_type']]['stage']!="READ")&&($show[$progress['msg_type']]['stage']!="SENT")) {
2114 $show[$progress['msg_type']]['stage'] = "QUEUED";
2115 $icon = $this->get_icon($progress['msg_type'], $progress['msg_reply']);
2118 if ($progress['msg_reply'] == "CALL") {
2119 $icon = $this->get_icon($progress['msg_type'], "CALL");
2120 $show['progression'] .= "<span class='left'>".$icon." ".text($progress['msg_type'])."@".text($when)."</span><br />";
2121 } elseif ($progress['msg_reply'] == "STOP") {
2122 $icon = $this->get_icon($progress['msg_type'], "STOP");
2123 $show['progression'] .= "<span class='left'>".$icon." ".text($when)."</span><br />";
2124 } elseif ($progress['msg_reply'] == "EXTRA") {
2125 $icon = $this->get_icon($progress['msg_type'], "EXTRA");
2126 $show['progression'] .= "<span class='left'>".$icon." ".text($when)."</span><br />";
2127 } elseif ($progress['msg_reply'] == "FAILED") {
2128 $icon = $this->get_icon($progress['msg_type'], "FAILED");
2129 $show['progression'] .= "<span class='left'>".$icon." ".text($when)."</span><br />";
2130 $show['campaign'][$i]['status']=1;
2132 $show['campaign'][$i]['icon'] = $icon;
2135 $something_happened=true;
2137 $show['progression'] .= $show['EMAIL']['text'].$show['SMS']['text'].$show['AVM']['text'];
2140 // Let's look at the MedEx events:
2141 // Show the DATE when a Campaign event will be run for a given patient
2143 * E_fire_tire = number of days before/after recall date that a MedEx campaign event will run
2144 * MedEx E_timing options:
2146 * 2 = days before PM
2151 /** @var TYPE_NAME $events */
2152 foreach ($events as $event) {
2153 if ($event['M_group'] != "RECALL") {
2156 $pat = $this->possibleModalities($recall);
2157 if ($pat['ALLOWED'][$event['M_type']] == 'NO') {
2158 continue; //it can't happen
2160 if ($pat['facility']['status']!= 'ok') {
2161 continue; //it can't happen
2163 if ($pat['provider']['status']!= 'ok') {
2164 continue; //it can't happen
2167 if ($show['campaign'][$event['C_UID']]['status']) {
2168 continue; //it is done
2170 $camps++
; //there is still work to be done
2171 if ($show['campaign'][$event['C_UID']]['icon']) {
2172 continue; //but something has happened since it was scheduled.
2175 ($event['E_timing'] < '3') ?
($interval ='-') : ($interval ='+');//this is only scheduled, 3 and 4 are for past appointments...
2176 $show['campaign'][$event['C_UID']] = $event;
2177 $show['campaign'][$event['C_UID']]['icon'] = $this->get_icon($event['M_type'], "SCHEDULED");
2179 $recall_date = date("Y-m-d", strtotime($interval.$event['E_fire_time']." days", strtotime($recall['r_eventDate'])));
2180 $date1 = date('Y-m-d');
2181 $date_diff=strtotime($date1) - strtotime($recall['r_eventDate']);
2182 if ($date_diff >= '-1') { //if it is sched for tomorrow or earlier, queue it up
2183 $show['campaign'][$event['C_UID']]['executed'] = "QUEUED";
2184 $show['status'] = "whitish";
2186 $execute = oeFormatShortDate($recall_date);
2187 $show['campaign'][$event['C_UID']]['executed'] = $execute;
2189 $show['progression'] .= "<a href='https://medexbank.com/cart/upload/index.php?route=information/campaigns' class='nowrap text-left' target='_MedEx'>".
2190 $show['campaign'][$event['C_UID']]['icon']." ".text($show['campaign'][$event['C_UID']]['executed'])."</a><br />";
2195 // Show recall row status via background color.
2196 // If an appt was made < 16hrs ago, make it green(completed) and $show['DONE'] = 1
2197 // o/w yellow(in progress) if something happened or Campaign fired
2198 // o/w red (manual needed) if no more MedEx Recall Events are scheduled to be done and no appt was made yet.
2199 // ie. we struck out and need to process this manually
2200 // or write it off or delete it or do soemthing else? Have to know what to do to write that. ;)
2201 $query = "SELECT * FROM openemr_postcalendar_events WHERE pc_eventDate > CURDATE() AND pc_pid =? AND pc_time > CURDATE()- INTERVAL 16 HOUR";
2202 $result = sqlFetchArray(sqlStatement($query, array($recall['pid'])));
2204 if ($something_happened||
$result) {
2206 $show['status'] = "greenish"; //appt made, move on
2207 $phpdate = strtotime($result['pc_eventDate']." ".$result['pc_startTime']);
2208 $show['pc_eid'] = $result['pc_eid'];
2209 $show['appt'] = oeFormatShortDate(date('Y-m-d', $phpdate))." @ ".date('g:iA', $phpdate);
2210 $show['DONE'] = '1';
2211 } elseif ($GLOBALS['medex_enable'] == '1') {
2214 $show['status'] = "reddish"; //hey, nothing automatic left to do - manual processing required.
2216 $show['status'] = "yellowish"; //no appt yet but something happened!
2220 $show['status'] = "whitish";
2222 } elseif (($GLOBALS['medex_enable'] == '1') && ($camps =='0')) {
2223 $show['status'] = "reddish"; //hey, nothing automatic left to do - manual processing required.
2225 $show['status'] = "whitish";
2228 $show['progression'] = '<div onclick="SMS_bot(\'recall_'.$recall['pid'].'\');">'.$show['progression'].'</div>';
2232 private function get_icon($event_type, $status = 'SCHEDULED')
2234 $sqlQuery = "SELECT * FROM medex_icons";
2235 $result = sqlStatement($sqlQuery);
2236 while ($icons = sqlFetchArray($result)) {
2237 if (($icons['msg_type'] == $event_type)&&($icons['msg_status'] == $status)) {
2238 return $icons['i_html'];
2243 public function possibleModalities($appt)
2246 $sqlQuery = "SELECT * FROM medex_icons";
2247 $result = sqlStatement($sqlQuery);
2248 //the text fields in DB are set by user and sql sscripts. Is translation/escaping needed here? How?
2249 while ($icons = sqlFetchArray($result)) {
2250 $icon[$icons['msg_type']][$icons['msg_status']] = $icons['i_html'];
2252 //if the patient is dead, should we really be sending them a message?
2253 //Maybe we would need to customize this for a pathologist but for the rest, the answer is no...
2254 if (empty($appt['phone_cell']) ||
($appt["hipaa_allowsms"]=="NO")) {
2255 $pat['SMS'] = $icon['SMS']['NotAllowed'];
2256 $pat['ALLOWED']['SMS'] = 'NO';
2258 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
2259 $pat['SMS'] = $icon['SMS']['ALLOWED']; // It is allowed and they have a cell phone
2261 if ((empty($appt["phone_home"]) && (empty($appt["phone_cell"])) ||
($appt["hipaa_voice"]=="NO"))) {
2262 $pat['AVM'] = $icon['AVM']['NotAllowed'];
2263 $pat['ALLOWED']['AVM'] = 'NO';
2265 if (!empty($appt["phone_cell"])) {
2266 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
2268 $phone = preg_replace("/[^0-9]/", "", $appt["phone_home"]);
2270 $pat['AVM'] = $icon['AVM']['ALLOWED']; //We have a phone to call and permission!
2272 if (($appt["email"]=="")||
($appt["hipaa_allowemail"]=="NO")) {
2273 $pat['EMAIL'] = $icon['EMAIL']['NotAllowed'];
2274 $pat['ALLOWED']['EMAIL'] = 'NO';
2276 $pat['EMAIL'] = $icon['EMAIL']['ALLOWED'];
2278 // If the practice is a MedEx practice, is this facility and/or this provider signed up for MedEx?
2279 // In this scenario, not all providers or locations for this practice are enrolled in MedEx.
2280 // Don't report that something MedEx-related is going to happen for these folks, cause it shouldn't.
2281 $sql = "SELECT * FROM medex_prefs";
2282 $prefs = sqlFetchArray(sqlStatement($sql));
2283 $facs = explode('|', $prefs['ME_facilities']);
2284 foreach ($facs as $place) {
2285 if (isset($appt['r_facility']) && ($appt['r_facility']==$place)) {
2286 $pat['facility']['status'] = 'ok';
2289 $providers = explode('|', $prefs['ME_providers']);
2290 foreach ($providers as $provider) {
2291 if (isset($appt['r_provider']) && ($appt['r_provider']==$provider)) {
2292 $pat['provider']['status'] = 'ok';
2297 private function recall_board_top()
2300 <div
class="divTable text-center" id
="rcb_table" style
="margin:0 auto 30px;width:100%;">
2301 <div
class="sticky divTableRow divTableHeading">
2302 <div
class="divTableCell text-center" style
="width:10%;"><?php
echo xlt('Name'); ?
></div
>
2303 <div
class="divTableCell text-center" style
="width:10%;"><?php
echo xlt('Recall'); ?
></div
>
2305 <div
class="divTableCell text-center phones" style
="width:10%;"><?php
echo xlt('Contacts'); ?
></div
>
2306 <div
class="divTableCell text-center msg_resp"><?php
echo xlt('Postcards'); ?
><br
/>
2307 <span onclick
="top.restoreSession();checkAll('postcards',true);" class="fa fa-square-o fa-lg" id
="chk_postcards"></span
>
2309 <span onclick
="process_this('postcards');" class="fa fa-print fa-lg"></span
>
2311 <div
class="divTableCell text-center msg_resp"><?php
echo xlt('Labels'); ?
><br
/>
2312 <span onclick
="checkAll('labels',true);" class="fa fa-square-o fa-lg" id
="chk_labels"></span
>
2314 <span onclick
="process_this('labels');" class="fa fa-print fa-lg"></span
>
2316 <div
class="divTableCell text-center msg_resp"><?php
echo xlt('Office').": ".xlt('Phone'); ?
></div
>
2317 <div
class="divTableCell text-center msg_notes"><?php
echo xlt('Notes'); ?
></div
>
2318 <div
class="divTableCell text-center"><?php
echo xlt('Progress'); ?
>
2322 <div
class="divTableBody">
2325 private function recall_board_bot()
2332 public function display_add_recall($pid = 'new')
2336 <div
class="container-fluid">
2337 <div
class="row-fluid showReminders clear text-center">
2338 <div id
="add_recall" class="col-sm-12">
2339 <div
class="title"><?php
echo xlt('New Recall'); ?
></div
>
2340 <div name
="div_response" id
="div_response"><?php
echo xlt('Create a reminder to schedule a future visit'); ?
> .</div
>
2343 <div
class="row-fluid divTable float_center">
2344 <form name
="addRecall" id
="addRecall" class="form-inline" >
2345 <input type
="hidden" name
="go" id
="go" value
="addRecall">
2346 <input type
="hidden" name
="action" id
="go" value
="addRecall">
2347 <div
class="col-sm-6 text-right form-group form-group-sm">
2348 <div
class="divTableBody pull-right">
2349 <div
class="divTableRow">
2350 <div
class="divTableCell divTableHeading"><?php
echo xlt('Name'); ?
></div
>
2351 <div
class="divTableCell recall_name">
2352 <input type
="text" name
="new_recall_name" id
="new_recall_name" class="form-control"
2353 onclick
="recall_name_click(this)"
2354 value
="" style
="width:225px;">
2355 <input type
="hidden" name
="new_pid" id
="new_pid" value
="">
2358 <div
class="divTableRow">
2359 <div
class="divTableCell divTableHeading"><?php
echo xlt('Recall When'); ?
></div
>
2360 <div
class="divTableCell indent20">
2361 <span
class="bold"><?php
echo xlt('Last Visit'); ?
>: </span
><input type
="text" value
="" name
="DOLV" id
="DOLV" class="form-control">
2363 <!-- Feel free to add in any dates you would like to show here
...
2364 <input type
="radio" name
="new_recall_when" id
="new_recall_when_6mos" value
="180">
2365 <label
for="new_recall_when_6mos" class="input-helper input-helper--checkbox">+
6 <?php
echo xlt('months'); ?
></label
><br
/>
2367 <label
for="new_recall_when_1yr" class="indent20 input-helper input-helper--checkbox"><input type
="radio" name
="new_recall_when" id
="new_recall_when_1yr" value
="365" class="form-control">
2368 <?php
echo xlt('plus 1 year'); ?
></label
><br
/>
2369 <label
for="new_recall_when_2yr" class="indent20 input-helper input-helper--checkbox"><input type
="radio" name
="new_recall_when" id
="new_recall_when_2yr" value
="730" class="form-control">
2370 <?php
echo xlt('plus 2 years'); ?
></label
><br
/>
2371 <label
for="new_recall_when_3yr" class="indent20 input-helper input-helper--checkbox"><input type
="radio" name
="new_recall_when" id
="new_recall_when_3yr" value
="1095" class="form-control">
2372 <?php
echo xlt('plus 3 years'); ?
></label
><br
/>
2373 <span
class="bold"> <?php
echo xlt('Date'); ?
>:</span
> <input
class="datepicker form-control input-sm text-center" type
="text" id
="form_recall_date" name
="form_recall_date" value
="">
2376 <div
class="divTableRow">
2377 <div
class="divTableCell divTableHeading"><?php
echo xlt('Recall Reason'); ?
></div
>
2378 <div
class="divTableCell">
2379 <input
class="form-control" type
="text" style
="width:225px;" name
="new_reason" id
="new_reason" value
="">
2382 <div
class="divTableRow">
2383 <div
class="divTableCell divTableHeading"><?php
echo xlt('Provider'); ?
></div
>
2384 <div
class="divTableCell">
2386 $ures = sqlStatement("SELECT id, username, fname, lname FROM users WHERE authorized != 0 AND active = 1 ORDER BY lname, fname");
2387 //This is an internal practice function so ignore the suffix as extraneous information. We know who we are.
2388 $defaultProvider = $_SESSION['authUserID'];
2389 // or, if we have chosen a provider in the calendar, default to them
2390 // choose the first one if multiple have been selected
2391 if (count($_SESSION['pc_username']) >= 1) {
2392 // get the numeric ID of the first provider in the array
2393 $pc_username = $_SESSION['pc_username'];
2394 $firstProvider = sqlFetchArray(sqlStatement("SELECT id FROM users WHERE username=?", array($pc_username[0])));
2395 $defaultProvider = $firstProvider['id'];
2397 // if we clicked on a provider's schedule to add the event, use THAT.
2399 $defaultProvider = $userid;
2402 echo "<select class='form-control' name='new_provider' id='new_provider' style='width:95%;'>";
2403 while ($urow = sqlFetchArray($ures)) {
2404 echo " <option value='" . attr($urow['id']) . "'";
2405 if ($urow['id'] == $defaultProvider) {
2408 echo ">" . text($urow['lname']);
2409 if ($urow['fname']) {
2410 echo ", " . text($urow['fname']);
2418 <div
class="divTableRow">
2419 <div
class="divTableCell divTableHeading"><?php
echo xlt('Facility'); ?
></div
>
2420 <div
class="divTableCell">
2421 <select
class="form-control ui-selectmenu-button ui-button ui-widget ui-selectmenu-button-closed ui-corner-all" name
="new_facility" id
="new_facility" style
="width:95%;">
2423 $qsql = sqlStatement("SELECT id, name, primary_business_entity FROM facility WHERE service_location != 0");
2424 while ($facrow = sqlFetchArray($qsql)) {
2425 if ($facrow['primary_business_entity'] == '1') {
2426 $selected = 'selected="selected"';
2427 echo "<option value='" . attr($facrow['id']) . "' $selected>" . text($facrow['name']) . "</option>";
2430 echo "<option value='" . attr($facrow['id']) . "' $selected>" . text($facrow['name']) . "</option>";
2441 <div
class="col-sm-6 text-center form-group form-group-sm">
2442 <div
class="divTableBody">
2443 <div
class="divTableRow news">
2444 <div
class="divTableCell divTableHeading"><?php
echo xlt('DOB'); ?
></div
>
2445 <div
class="divTableCell"> 
; 
;
2447 <span name
="new_DOB" id
="new_DOB" style
="width:90px;"></span
> -
2448 <span id
="new_age" name
="new_age"></span
></div
>
2450 <div
class="divTableRow news">
2451 <div
class="divTableCell divTableHeading"><?php
echo xlt('Address'); ?
></div
>
2452 <div
class="divTableCell">
2453 <input type
="text" class="form-control" name
="new_address" id
="new_address" style
="width:240px;" value
=""><br
/>
2454 <input type
="text" class="form-control" name
="new_city" id
="new_city" style
="width:100px;" value
="">
2455 <input type
="text" class="form-control" name
="new_state" id
="new_state" style
="width:40px;" value
="">
2456 <input type
="text" class="form-control" name
="new_postal_code" id
="new_postal_code" style
="width:65px;" value
=""></div
>
2458 <div
class="divTableRow news">
2459 <div
class="divTableCell divTableHeading phone_home"><?php
echo xlt('Home Phone'); ?
></div
>
2460 <div
class="divTableCell"><input type
="text" name
="new_phone_home" id
="new_phone_home" class="form-control" value
=""></div
>
2462 <div
class="divTableRow news">
2463 <div
class="divTableCell divTableHeading phone_cell"><?php
echo xlt('Mobile Phone'); ?
></div
>
2464 <div
class="divTableCell"><input type
="text" name
="new_phone_cell" id
="new_phone_cell" class="form-control" value
=""></div
>
2466 <div
class="divTableRow news">
2467 <div
class="divTableCell divTableHeading msg_sent" data
-placement
="auto" title
="<?php echo xla('Text Message permission'); ?>"><?php
echo xlt('SMS OK'); ?
></div
>
2469 <div
class="divTableCell indent20">
2470 <input type
="radio" class="form-control" name
="new_allowsms" id
="new_allowsms_yes" value
="YES"> <label
for="new_allowsms_yes"><?php
echo xlt('YES'); ?
></label
>
2472 <input type
="radio" class="form-control" name
="new_allowsms" id
="new_allowsms_no" value
="NO"> <label
for="new_allowsms_no"><?php
echo xlt('NO'); ?
></label
>
2475 <div
class="divTableRow indent20">
2476 <div
class="divTableCell divTableHeading msg_how" data
-placement
="auto" title
="<?php echo xla('Automated Voice Message permission'); ?>"><?php
echo xlt('AVM OK'); ?
></div
>
2477 <div
class="divTableCell indent20">
2478 <input type
="radio" class="form-control" name
="new_voice" id
="new_voice_yes" value
="YES"> <label
for="new_voice_yes"><?php
echo xlt('YES'); ?
></label
>
2480 <input type
="radio" class="form-control" name
="new_voice" id
="new_voice_no" value
="NO"> <label
for="new_voice_no"><?php
echo xlt('NO'); ?
></label
>
2483 <div
class="divTableRow news">
2484 <div
class="divTableCell divTableHeading phone_cell"><?php
echo xlt('E-Mail'); ?
></div
>
2485 <div
class="divTableCell"><input type
="email" name
="new_email" id
="new_email" class="form-control" style
="width:225px;" value
=""></div
>
2488 <div
class="divTableRow news">
2489 <div
class="divTableCell divTableHeading msg_when"><?php
echo xlt('E-mail OK'); ?
></div
>
2490 <div
class="divTableCell indent20">
2491 <input type
="radio" class="form-control" name
="new_email_allow" id
="new_email_yes" value
="YES"> <label
for="new_email_yes"><?php
echo xlt('YES'); ?
></label
>
2493 <input type
="radio" class="form-control" name
="new_email_allow" id
="new_email_no" value
="NO"> <label
for="new_email_no"><?php
echo xlt('NO'); ?
></label
>
2500 <div
class="row-fluid text-center">
2501 <input
class="btn btn-primary" onclick
="add_this_recall();" value
="<?php echo xla('Add Recall'); ?>" id
="add_new" name
="add_new">
2503 <em
class="small text-muted">* <?php
echo xlt('N.B.{{Nota bene}}')." ".xlt('Demographic changes made here are recorded system-wide'); ?
>.</em
>
2509 $
(document
).ready(function () {
2510 $
('.datepicker').datetimepicker({
2511 <?php
$datetimepicker_timepicker = false; ?
>
2512 <?php
$datetimepicker_showseconds = false; ?
>
2513 <?php
$datetimepicker_formatInput = true; ?
>
2514 <?php
require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?
>
2515 <?php
// can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
2520 if ($_SESSION['pid']>'') {
2522 setpatient('<?php echo text($_SESSION['pid
']); ?>');
2526 var xljs_NOTE
= '<?php echo xl("NOTE"); ?>';
2527 var xljs_PthsApSched
= '<?php echo xl("This patient already has an appointment scheduled for"); ?>';
2532 public function icon_template()
2535 <!-- icon rubric
-->
2536 <div style
="position:relative;margin:30px auto;vertical-align:middle;">
2538 $sqlQuery = "SELECT * FROM medex_icons ORDER BY msg_type";
2539 $result = sqlStatement($sqlQuery);
2541 while ($urow = sqlFetchArray($result)) {
2542 $icons['msg_type']['description'] = $urow['i_description'];
2543 $icons[$urow['msg_type']][$urow['msg_status']] = $urow['i_html'];
2545 <div
class="divTable" style
="margin:30px auto;width:100%;">
2546 <div
class="divTableBody">
2547 <div
class="divTableRow divTableHeading">
2548 <div
class="divTableCell text-center"><?php
echo xlt('Message'); ?
></div
>
2549 <div
class="divTableCell text-center"><?php
echo xlt('Possible'); ?
></div
>
2550 <div
class="divTableCell text-center"><?php
echo xlt('Not Possible'); ?
></div
>
2551 <div
class="divTableCell text-center"><?php
echo xlt('Scheduled'); ?
></div
>
2552 <div
class="divTableCell text-center"><?php
echo xlt('Sent')."<br />".xlt('In-process'); ?
></div
>
2553 <div
class="divTableCell text-center"><?php
echo xlt('Read')."<br />".xlt('Delivered');
2555 <div
class="divTableCell text-center"><?php
echo xlt('Confirmed'); ?
></div
>
2556 <div
class="divTableCell text-center"><?php
echo xlt('Callback'); ?
></div
>
2557 <div
class="divTableCell text-center"><?php
echo xlt('Failure'); ?
></div
>
2558 <div
class="divTableCell text-center"><?php
echo xlt('Replies'); ?
></div
>
2559 <div
class="divTableCell text-center"><?php
echo xlt('STOP'); ?
></div
>
2561 <div
class="divTableRow">
2562 <div
class="divTableCell text-center"><?php
echo xlt('EMAIL'); ?
></div
>
2563 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['ALLOWED']; ?
></div
>
2564 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['NotAllowed']; ?
></div
>
2565 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['SCHEDULED']; ?
></div
>
2566 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['SENT']; ?
></div
>
2567 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['READ']; ?
></div
>
2568 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['CONFIRMED']; ?
></div
>
2569 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['CALL']; ?
></div
>
2570 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['FAILED']; ?
></div
>
2571 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['EXTRA']; ?
></div
>
2572 <div
class="divTableCell text-center"><?php
echo $icons['EMAIL']['STOP']; ?
></div
>
2574 <div
class="divTableRow">
2575 <div
class="divTableCell text-center"><?php
echo xlt('SMS'); ?
></div
>
2576 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['ALLOWED']; ?
></div
>
2577 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['NotAllowed']; ?
></div
>
2578 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['SCHEDULED']; ?
></div
>
2579 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['SENT']; ?
></div
>
2580 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['READ']; ?
></div
>
2581 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['CONFIRMED']; ?
></div
>
2582 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['CALL']; ?
></div
>
2583 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['FAILED']; ?
></div
>
2584 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['EXTRA']; ?
></div
>
2585 <div
class="divTableCell text-center"><?php
echo $icons['SMS']['STOP']; ?
></div
>
2587 <div
class="divTableRow">
2588 <div
class="divTableCell text-center"><?php
echo xlt('AVM'); ?
></div
>
2589 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['ALLOWED']; ?
></div
>
2590 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['NotAllowed']; ?
></div
>
2591 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['SCHEDULED']; ?
></div
>
2592 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['SENT']; ?
></div
>
2593 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['READ']; ?
></div
>
2594 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['CONFIRMED']; ?
></div
>
2595 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['CALL']; ?
></div
>
2596 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['FAILED']; ?
></div
>
2597 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['EXTRA']; ?
></div
>
2598 <div
class="divTableCell text-center"><?php
echo $icons['AVM']['STOP']; ?
></div
>
2601 //When we have added PostCards to MedEx, we can display this.
2602 //Until then this would just add confusion.
2604 <div class="divTableRow">
2605 <div class="divTableCell text-center"><?php echo xlt('POSTCARD'); ?></div>
2606 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['ALLOWED']; ?></div>
2607 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['NotAllowed']; ?></div>
2608 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['SCHEDULED']; ?></div>
2609 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['SENT']; ?></div>
2610 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['READ']; ?></div>
2611 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['CONFIRMED']; ?></div>
2612 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['CALL']; ?></div>
2613 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['FAILURE']; ?></div>
2614 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['EXTRA']; ?></div>
2615 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['STOP']; ?></div>
2627 * This function displays a bootstrap responsive pop-up window containing an image of a phone with a record of our messaging activity.
2628 * It is fired from the Flow board.
2629 * It enables two-way SMS texting between logged_in users and patients, if desired.
2630 * It may also end up on the Recall Board.
2631 * It may also allow playback of AVM audio files.
2632 * It may also do other things that haven't been written yet.
2633 * It may open to a messaging status board on large screens.
2638 public function SMS_bot($logged_in)
2641 $fields = $_REQUEST;
2642 if (!empty($_REQUEST['pid']) && $_REQUEST['pid'] != 'find') {
2643 $responseA = $this->syncPat($_REQUEST['pid'], $logged_in);
2644 } else if ($_REQUEST['show']=='pat_list') {
2645 $responseA = $this->syncPat($_REQUEST['show'], $logged_in);
2646 $fields['pid_list'] = $responseA['pid_list'];
2647 $fields['list_hits'] = $responseA['list_hits'];
2649 $this->curl
->setUrl($this->MedEx
->getUrl('custom/SMS_bot&token='.$logged_in['token']));
2650 $this->curl
->setData($fields);
2651 $this->curl
->makeRequest();
2652 $response = $this->curl
->getResponse();
2654 if (isset($response['success'])) {
2655 echo $response['success'];
2656 } else if (isset($response['error'])) {
2657 $this->lastError
= $response['error'];
2661 public function syncPat($pid, $logged_in)
2663 if ($pid == 'pat_list') {
2665 $values = $_REQUEST['outpatient'];
2666 $sqlSync = "SELECT * FROM patient_data WHERE fname LIKE ? OR lname LIKE ? LIMIT 20";
2667 $datas = sqlStatement($sqlSync, array("%".$values."%","%".$values."%"));
2668 while ($hit = sqlFetchArray($datas)) {
2669 $data['list'][] = $hit;
2670 $pid_list[] = $hit['pid'];
2672 $this->curl
->setUrl($this->MedEx
->getUrl('custom/syncPat&token='.$logged_in['token']));
2673 $this->curl
->setData($data);
2674 $this->curl
->makeRequest();
2675 $response = $this->curl
->getResponse();
2676 $response['pid_list'] = $pid_list;
2677 $response['list_hits'] = $data['list'];
2679 $sqlSync = "SELECT * FROM patient_data WHERE pid=?";
2680 $data = sqlQuery($sqlSync, array($pid));
2681 $this->curl
->setUrl($this->MedEx
->getUrl('custom/syncPat&token='.$logged_in['token']));
2682 $this->curl
->setData($data);
2683 $this->curl
->makeRequest();
2684 $response = $this->curl
->getResponse();
2686 if (isset($response['success'])) {
2688 } else if (isset($response['error'])) {
2689 $this->lastError
= $response['error'];
2691 return $this->lastError
;
2695 class Setup
extends Base
2697 public function MedExBank($stage)
2702 <div
class="col-sm-10 text-center col-xs-offset-1">
2704 <div
class="title">MedEx
</div
>
2705 <div
class="row showReminders ">
2706 <div
class="col-sm-10 text-center col-xs-offset-1">
2708 <?php
echo xlt('Using technology to improve productivity'); ?
>.
2712 <div
class="row showReminders ">
2713 <div
class="col-sm-5 col-xs-offset-1 text-center">
2714 <h3
class="title"><?php
echo xlt('Targets'); ?
>:</h3
>
2715 <ul
class="text-left" style
="margin-left:125px;">
2716 <li
> <?php
echo xlt('Appointment Reminders'); ?
></li
>
2717 <li
> <?php
echo xlt('Patient Recalls'); ?
></li
>
2718 <li
> <?php
echo xlt('Office Announcements'); ?
></li
>
2719 <li
> <?php
echo xlt('Patient Surveys'); ?
></li
>
2722 <div
class="col-sm-4 col-xs-offset-1 text-center">
2723 <h3
class="title"><?php
echo xlt('Channels'); ?
>:</h3
>
2724 <ul
class="text-left" style
="margin-left:75px;">
2725 <li
> <?php
echo xlt('SMS Messages'); ?
></li
>
2726 <li
> <?php
echo xlt('Voice Messages'); ?
></li
>
2727 <li
> <?php
echo xlt('E-mail Messaging'); ?
></li
>
2728 <li
> <?php
echo xlt('Postcards'); ?
></li
>
2729 <li
> <?php
echo xlt('Address Labels'); ?
></li
>
2733 <div
class="text-center row showReminders">
2734 <input value
="<?php echo xla('Sign-up'); ?>" onclick
="goReminderRecall('setup&stage=2');" class="btn btn-primary">
2742 } else if ($stage =='2') {
2745 <form name
="medex_start" id
="medex_start">
2746 <div
class="col-sm-10 col-sm-offset-1 text-center">
2747 <div id
="setup_1" class="showReminders borderShadow">
2748 <div
class="title row fa"><?php
echo xlt('Register'); ?
>: MedEx Bank
</div
>
2749 <div
class="row showReminders">
2750 <div
class="fa col-sm-10 col-sm-offset-1 text-center">
2751 <div
class="divTable4" id
="answer" name
="answer">
2752 <div
class="divTableBody">
2753 <div
class="divTableRow">
2754 <div
class="divTableCell divTableHeading">
2755 <?php
echo xlt('E-mail'); ?
>
2757 <div
class="divTableCell">
2758 <i id
="email_check" name
="email_check" class="top_right_corner nodisplay red fa fa-check"></i
>
2759 <input type
="text" data
-rule
-email
="true" class="form-control" id
="new_email" name
="new_email" value
="<?php echo attr($GLOBALS['user_data']['email']); ?>" placeholder
="<?php echo xla('your email address'); ?>" required
>
2760 <div
class="signup_help nodisplay" id
="email_help" name
="email_help"><?php
echo xlt('Please provide a valid e-mail address to proceed'); ?
>...</div
>
2764 <div
class="divTableRow">
2765 <div
class="divTableCell divTableHeading">
2766 <?php
echo xlt('Password'); ?
>
2768 <div
class="divTableCell"><i id
="pwd_check" name
="pwd_check" class="top_right_corner nodisplay red fa fa-check"></i
>
2769 <i
class="fa top_right_corner fa-question" id
="pwd_ico_help" aria
-hidden
="true" onclick
="$('#pwd_help').toggleClass('nodisplay');"></i
>
2770 <input type
="password" placeholder
="<?php xla('Password'); ?>" id
="new_password" name
="new_password" class="form-control" required
>
2771 <div id
="pwd_help" class="nodisplay signup_help"><?php
echo xlt('Secure Password Required').": ". xlt('8-12 characters long, including at least one upper case letter, one lower case letter, one number, one special character and no common strings'); ?
>...</div
>
2774 <div
class="divTableRow">
2775 <div
class="divTableCell divTableHeading">
2776 <?php
echo xlt('Repeat'); ?
>
2778 <div
class="divTableCell"><i id
="pwd_rcheck" name
="pwd_rcheck" class="top_right_corner nodisplay red fa fa-check"></i
>
2779 <input type
="password" placeholder
="<?php echo xla('Repeat password'); ?>" id
="new_rpassword" name
="new_rpassword" class="form-control" required
>
2780 <div id
="pwd_rhelp" class="nodisplay signup_help" style
=""><?php
echo xlt('Passwords do not match.'); ?
></div
>
2785 <div id
="ihvread" name
="ihvread" class="fa text-left">
2786 <input type
="checkbox" class="updated required" name
="TERMS_yes" id
="TERMS_yes" required
>
2787 <label
for="TERMS_yes" class="input-helper input-helper--checkbox" data
-toggle
="tooltip" data
-placement
="auto" title
="Terms and Conditions"><?php
echo xlt('I have read and my practice agrees to the'); ?
>
2788 <a href
="#" onclick
="cascwin('https://medexbank.com/cart/upload/index.php?route=information/information&information_id=5','TERMS',800, 600);">MedEx
<?php
echo xlt('Terms and Conditions'); ?
></a
></label
><br
/>
2789 <input type
="checkbox" class="updated required" name
="BusAgree_yes" id
="BusAgree_yes" required
>
2790 <label
for="BusAgree_yes" class="input-helper input-helper--checkbox" data
-toggle
="tooltip" data
-placement
="auto" title
="BAA"><?php
echo xlt('I have read and accept the'); ?
>
2791 <a href
="#" onclick
="cascwin('https://medexbank.com/cart/upload/index.php?route=information/information&information_id=8','Bus Assoc Agree',800, 600);">MedEx
<?php
echo xlt('Business Associate Agreement'); ?
></a
></label
>
2793 <div
class="align-center row showReminders">
2794 <input id
="Register" class="btn btn-primary" value
="<?php echo xla('Register'); ?>" />
2797 <div id
="myModal" class="modal fade" role
="dialog">
2798 <div
class="modal-dialog">
2800 <!-- Modal content
-->
2801 <div
class="modal-content">
2802 <div
class="modal-header" style
="background-color: #0d4867;color: #fff;font-weight: 700;">
2803 <button type
="button" class="close" data
-dismiss
="modal" style
="color:#fff;opacity:1;box-shadow:unset !important;">×
;</button
>
2804 <h2
class="modal-title" style
="font-weight:600;">Sign
-Up Confirmation
</h2
>
2806 <div
class="modal-body" style
="padding: 10px 45px;">
2807 <p
>You are opening a secure connection to MedExBank
.com
. During this step your EHR will synchronize with the MedEx servers
. <br
/>
2809 Re
-enter your
username (e
-mail
) and password in the MedExBank
.com login window to
:
2810 <ul style
="text-align: left;width: 90%;margin: 0 auto;">
2811 <li
> confirm your practice
and providers
' information</li>
2812 <li> choose your service options</li>
2813 <li> update and activate your messages </li>
2817 <div class="modal-footer">
2818 <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
2819 <button type="button" class="btn btn-default" onlick="actualSignUp();" id="actualSignUp">Proceed</button>
2834 var email = $("#new_email").val();
2835 if (!validateEmail(email)) return alert('<?php
echo xlt('Please provide a valid e-mail address to proceed'); ?
>...');
2836 var password = $("#new_password").val();
2837 var passed = check_Password(password);
2838 if (!passed) return alert('<?php
echo xlt('Passwords must be 8-12 characters long and include one capital letter, one lower case letter and one special character'); ?
> ... ');
2839 if ($("#new_rpassword").val() !== password) return alert('<?php
echo xlt('Passwords do not match'); ?
>!');
2840 if (!$("#TERMS_yes").is(':checked
')) return alert('<?php
echo xlt('You must agree to the Terms & Conditions before signing up');?
>... ');
2841 if (!$("#BusAgree_yes").is(':checked
')) return alert('<?php
echo xlt('You must agree to the HIPAA Business Associate Agreement');?
>... ');
2842 $("#myModal").modal();
2846 function validateEmail(email) {
2847 var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
2848 return re.test(email);
2850 function check_Password(password) {
2851 var passed = validatePassword(password, {
2852 length: [8, Infinity],
2857 badWords: ["password", "qwerty", "12345"],
2858 badSequenceLength: 4
2862 function validatePassword (pw, options) {
2863 // default options (allows any password)
2867 alpha: 0, /* lower + upper */
2870 length: [0, Infinity],
2871 custom: [ /* regexes and/or functions */ ],
2873 badSequenceLength: 0,
2874 noQwertySequences: false,
2878 for (var property in options)
2879 o[property] = options[property];
2890 // enforce min/max length
2891 if (pw.length < o.length[0] || pw.length > o.length[1])
2894 // enforce lower/upper/alpha/numeric/special rules
2896 if ((pw.match(re[rule]) || []).length < o[rule])
2900 // enforce word ban (case insensitive)
2901 for (i = 0; i < o.badWords.length; i++) {
2902 if (pw.toLowerCase().indexOf(o.badWords[i].toLowerCase()) > -1)
2906 // enforce the no sequential, identical characters rule
2907 if (o.noSequential && /([\S\s])\1/.test(pw))
2910 // enforce alphanumeric/qwerty sequence ban rules
2911 if (o.badSequenceLength) {
2912 var lower = "abcdefghijklmnopqrstuvwxyz",
2913 upper = lower.toUpperCase(),
2914 numbers = "0123456789",
2915 qwerty = "qwertyuiopasdfghjklzxcvbnm",
2916 start = o.badSequenceLength - 1,
2917 seq = "_" + pw.slice(0, start);
2918 for (i = start; i < pw.length; i++) {
2919 seq = seq.slice(1) + pw.charAt(i);
2921 lower.indexOf(seq) > -1 ||
2922 upper.indexOf(seq) > -1 ||
2923 numbers.indexOf(seq) > -1 ||
2924 (o.noQwertySequences && qwerty.indexOf(seq) > -1)
2931 // enforce custom regex/function rules
2932 for (i = 0; i < o.custom.length; i++) {
2934 if (rule instanceof RegExp) {
2937 } else if (rule instanceof Function) {
2946 $(document).ready(function() {
2947 $("#Register").click(function() {
2950 $("#actualSignUp").click(function() {
2951 var url = "save.php?MedEx=start";
2952 var email = $("#new_email").val();
2953 $("#actualSignUp").html('<i
class="fa fa-spinner fa-pulse fa-fw"></i
><span
class="sr-only">Loading
...</span
>');
2954 formData = $("form#medex_start").serialize();
2955 top.restoreSession();
2961 .done(function(result) {
2962 obj = JSON.parse(result);
2963 $("#answer").html(obj.show);
2964 $("#ihvread").addClass('nodisplay
');
2965 $('#myModal').modal('toggle');
2967 url
="https://www.medexbank.com/login/"+email
;
2968 window
.open(url
, 'clinical', 'resizable=1,scrollbars=1');
2973 $
("#new_email").blur(function(e
) {
2975 var email
= $
("#new_email").val();
2976 if (validateEmail(email
)) {
2977 $
("#email_help").addClass('nodisplay');
2978 $
("#email_check").removeClass('nodisplay');
2980 $
("#email_help").removeClass('nodisplay');
2981 $
("#email_check").addClass('nodisplay');
2984 $
("#new_password,#new_rpassword").keyup(function(e
) {
2986 var pwd
= $
("#new_password").val();
2987 if (check_Password(pwd
)) {
2988 $
('#pwd_help').addClass('nodisplay');
2989 $
("#pwd_ico_help").addClass('nodisplay');
2990 $
("#pwd_check").removeClass('nodisplay');
2992 $
("#pwd_help").removeClass('nodisplay');
2993 $
("#pwd_ico_help").removeClass('nodisplay');
2994 $
("#pwd_check").addClass('nodisplay');
2996 if (this
.id
=== "new_rpassword") {
2997 var pwd1
= $
("#new_password").val();
2998 var pwd2
= $
("#new_rpassword").val();
2999 if (pwd1
=== pwd2
) {
3000 $
('#pwd_rhelp').addClass('nodisplay');
3001 $
("#pwd_rcheck").removeClass('nodisplay');
3003 $
("#pwd_rhelp").removeClass('nodisplay');
3004 $
("#pwd_rcheck").addClass('nodisplay');
3013 public function autoReg($data)
3016 return false; //throw new InvalidDataException("We need to actually send some data...");
3018 $this->curl
->setUrl($this->MedEx
->getUrl('custom/signUp'));
3019 $this->curl
->setData($data);
3020 $this->curl
->makeRequest();
3021 $response = $this->curl
->getResponse();
3022 if (isset($response['success'])) {
3024 } else if (isset($response['error'])) {
3025 $this->lastError
= $response['error'];
3033 public $lastError = '';
3045 public function __construct($url, $sessionFile = 'cookiejar_MedExAPI')
3049 if ($sessionFile == 'cookiejar_MedExAPI') {
3050 $sessionFile = $GLOBALS['temporary_files_dir'].'/cookiejar_MedExAPI';
3052 $this->url
= rtrim('https://'.preg_replace('/^https?\:\/\//', '', $url), '/') . '/cart/upload/index.php?route=api/';
3053 $this->curl
= new CurlRequest($sessionFile);
3054 $this->practice
= new Practice($this);
3055 $this->campaign
= new Campaign($this);
3056 $this->events
= new Events($this);
3057 $this->callback
= new Callback($this);
3058 $this->logging
= new Logging($this);
3059 $this->display
= new Display($this);
3060 $this->setup
= new Setup($this);
3063 public function getCookie()
3065 return $this->cookie
; }
3067 public function getLastError()
3069 return $this->lastError
; }
3071 public function login()
3074 $result = sqlStatement("SHOW TABLES LIKE 'medex_prefs'");
3075 $table2Exists = sqlFetchArray($result);
3076 if (!$table2Exists) {
3080 $query = "SELECT * FROM medex_prefs";
3081 $info = sqlFetchArray(sqlStatement($query));
3082 $username = $info['ME_username'];
3083 $key = $info['ME_api_key'];
3084 $UID = $info['MedEx_id'];
3085 $callback_key = $_POST['callback_key'];
3086 if (empty($username) ||
empty($key) ||
empty($UID)) {
3087 return false;//throw new InvalidCredentialsException("API Credentials are incomplete.");
3089 $this->curl
->setUrl($this->getUrl('login'));
3090 $this->curl
->setData(array(
3091 'username' => $username,
3094 'MedEx' => 'openEMR',
3095 'callback_key' => $callback_key
3097 $log = "/tmp/medex.log" ;
3098 $stdlog = fopen($log, 'a');
3099 $timed = date(DATE_RFC2822
);
3100 fputs($stdlog, "\n IN API.php::login ".$timed."\n");
3102 $this->curl
->makeRequest();
3103 $response = $this->curl
->getResponse();
3104 fputs($stdlog, "\n ".$this->curl
->getRawResponse()."\n");
3106 if (isset($response['success']) && isset($response['token'])) {
3108 } else if (isset($response['error'])) {
3109 $this->lastError
= $response['error'];
3110 sqlQuery("UPDATE `background_services` SET `active`='0' WHERE `name`='MedEx'");
3115 public function getUrl($method)
3117 return $this->url
. $method; }
3119 public function checkModality($event, $appt)
3121 $sqlQuery = "SELECT * FROM medex_icons";
3122 $result = sqlStatement($sqlQuery);
3123 while ($icons = sqlFetchArray($result)) {
3124 //substitute data-toggle="tooltip" data-placement="auto" title="..." with data-toggle="tooltip" data-placement="auto" title="'.xla('...').'" in $icons['i_html']
3125 $title = preg_match('/title=\"(.*)\"/', $icons['i_html']);
3126 $xl_title = xla($title);
3127 $icons['i_html'] = str_replace($title, $xl_title, $icons['i_html']);
3128 $icon[$icons['msg_type']][$icons['msg_status']] = $icons['i_html'];
3130 if ($event['M_type'] =="SMS") {
3131 if (empty($appt['phone_cell']) ||
($appt["hipaa_allowsms"]=="NO")) {
3132 return array($icon['SMS']['NotAllowed'],false);
3134 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
3135 return array($icon['SMS']['ALLOWED'],$phone); // It is allowed and they have a cell phone
3137 } else if ($event['M_type'] =="AVM") {
3138 if ((empty($appt["phone_home"]) && (empty($appt["phone_cell"])) ||
($appt["hipaa_voice"]=="NO"))) {
3139 return array($icon['AVM']['NotAllowed'],false);
3141 if (!empty($appt["phone_cell"])) {
3142 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
3144 $phone = preg_replace("/[^0-9]/", "", $appt["phone_home"]);
3146 return array($icon['AVM']['ALLOWED'],$phone); //We have a phone to call and permission!
3148 } else if ($event['M_type'] =="EMAIL") {
3149 if (($appt["email"]=="")||
($appt["hipaa_allowemail"]=="NO")) {
3150 return array($icon['EMAIL']['NotAllowed'],false);
3152 //need to make sure this is a valid email too eh?
3153 return array($icon['EMAIL']['ALLOWED'],$appt["email"]);
3155 //need to add in check for address to send postcards? - when we add in postcards...
3157 return array(false,false);
3162 class InvalidDataException
extends \Exception