New Http Rest Client (#2023)
[openemr.git] / library / MedEx / API.php
blobd12ff5b121fdbcdadae126b6489dd264f31200b7
1 <?php
2 /**
3 * /library/MedEx/API.php
5 * @package MedEx
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
12 namespace MedExApi;
14 error_reporting(0);
16 class CurlRequest
18 private $url;
19 private $postData = array();
20 private $cookies = array();
21 private $response = '';
22 private $handle;
23 private $sessionFile;
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, 0);
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);
66 $this->saveSession();
69 private function getCookies()
71 $cookies = array();
72 foreach ($this->cookies as $name => $value) {
73 $cookies[] = $name . '=' . $value;
75 return implode('; ', $cookies);
78 private function saveSession()
80 if (empty($this->sessionFile)) {
81 return;
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)
93 $this->url = $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; }
106 class Base
108 protected $MedEx;
109 protected $curl;
111 public function __construct($MedEx)
113 $this->MedEx = $MedEx;
114 $this->curl = $MedEx->curl;
118 class Practice extends Base
120 public function sync($token)
122 global $GLOBALS;
123 $fields2 = array();
124 $fields3 = array();
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;
156 //get apptstats
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`
170 WHERE
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);
234 if ($result3) {
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.
243 // get last update
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'];
250 // send notes
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);
267 //4. sync notes
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'];
276 } else {
277 $response['success']['message'] = xlt("No new messages on")." MedEx.";
280 if (isset($response['success'])) {
281 return $response;
282 } else if (isset($response['error'])) {
283 $this->lastError = $response['error'];
285 return false;
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'])) {
298 return $response;
299 } else if (isset($response['error'])) {
300 $this->lastError = $response['error'];
302 return false;
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
320 * AND
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.
332 * @param $token
333 * @param $events
334 * @return mixed
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!
342 $appt3 = array();
343 $count_appts = 0;
344 $count_recalls = 0;
345 $count_recurrents = 0;
346 $count_announcements = 0;
347 $count_surveys = 0;
348 $count_clinical_reminders = 0;
349 $count_gogreen = 0;
351 // For future appts, we don't want to run anything on the weekend so do them Friday.
352 // There is a GLOBALS value for weekend days, maybe use that LTR.
353 // -->If Friday, send all appts matching campaign fire_Time + 2 days
354 // For past appts, we also don't want to send messages on the weekend, so do them Monday.
355 $this->MedEx->logging->log_this($events);
356 foreach ($events as $event) {
357 $escClause=[];
358 $escapedArr = []; //will hold prepared statement items for query.
359 if ($event['M_group'] == 'REMINDER') {
360 if ($event['time_order'] > '0') {
361 $interval ="+";
362 //NOTE IF you have customized the pc_appstatus flags, you need to adjust them here too.
363 if ($event['E_instructions'] == "stop") { // ie. don't send this if it has been confirmed.
364 $appt_status = " and pc_apptstatus='-'";//we only look at future appts w/ apptstatus == NONE ='-'
365 // OR send anyway - unless appstatus is not cancelled, then it is no longer an appointment to confirm...
366 } elseif ($event['E_instructions'] == "always") { //send anyway
367 $appt_status = " and pc_apptstatus != '%'
368 and pc_apptstatus != 'x' ";
369 } else { //reminders are always or stop, that's it
370 $event['E_instructions'] ='stop';
371 $appt_status = " and pc_apptstatus='-'";//we only look at future appts w/ apptstatus == NONE ='-'
373 } else {
374 // Past appts -> appts that are completed.
375 // Need to exclude appts that were cancelled or noshowed
376 // Granular Functionality - select out via appt_stats/Visit Types/Doc/Fac is in Go Green Messaging
377 // Use the flag in the list_options to note that the appointment is completed
378 $interval ='-';// appts completed - this is defined by list_option->toggle_setting2=1 for Flow Board
379 $appt_status = " and pc_apptstatus in (SELECT option_id from list_options where toggle_setting_2='1' and list_id='apptstat')
380 and pc_apptstatus != '%'
381 and pc_apptstatus != 'x' ";
383 $timing = (int)$event['E_fire_time']-1;//the minus one is a critical change
384 // if it is Friday do stuff as scheduled + 2 days more
385 // for future appts, widen the net to get Timing2 = $timing +2.":1:1";
386 // eg an event/message is scheduled to go out 2 days in advance - reminder SMS.
387 // It is Friday. 2 days ahead is Sunday, but Monday's would run on Saturday and Tuesday's on Sunday.
388 // We should run them all on Friday... So load them that way now.
389 $today=date("l");
391 if (($today =="Sunday")||($today =="Saturday")) {
392 //continue;
394 if ($today == "Friday") {
395 $timing2 = ($timing + 3).":0:1"; //this is + 3 day, 0 hour and 1 minute...
396 } else {
397 $timing2 = ($timing + 1).":1:1"; //this is + 1 day, 1 hour and 1 minute...
399 $sql2= "SELECT ME_facilities FROM medex_prefs";
400 $pref_facilities = sqlQuery($sql2);
402 if (!empty($pref_facilities['ME_facilities'])) {
403 $places = str_replace("|", ",", $pref_facilities['ME_facilities']);
404 $query = "SELECT * FROM openemr_postcalendar_events AS cal
405 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
406 WHERE
409 pc_eventDate > CURDATE() ".$interval." INTERVAL ".$timing." DAY AND
410 pc_eventDate < CURDATE() ".$interval." INTERVAL '".$timing2."' DAY_MINUTE
414 pc_eventDate <= CURDATE() ".$interval." INTERVAL '".$timing2."' DAY_MINUTE AND
415 pc_endDate >= curdate() ".$interval." INTERVAL ".$timing." DAY AND
416 pc_recurrtype >'0'
419 ". $appt_status."
420 AND pc_facility IN (".$places.")
421 AND pat.pid=cal.pc_pid ORDER BY pc_eventDate,pc_startTime";
422 $result = sqlStatement($query, $escapedArr);
423 while ($appt= sqlFetchArray($result)) {
424 list($response,$results) = $this->MedEx->checkModality($event, $appt);
425 if ($results==false) {
426 continue; //not happening - either not allowed or not possible
428 if (($appt['pc_recurrtype'] !='0') && ($interval =="+")) {
429 $recurrents = $this->addRecurrent($appt, $interval, $timing, $timing2);
430 $count_recurrents += $recurrents;
431 continue;
433 $count_appts++;
435 $appt2 = [];
436 $appt2['pc_pid'] = $appt['pc_pid'];
437 $appt2['pc_eventDate'] = $appt['pc_eventDate'];
438 $appt2['pc_startTime'] = $appt['pc_startTime'];
439 $appt2['pc_eid'] = $appt['pc_eid'];
440 $appt2['pc_aid'] = $appt['pc_aid'];
441 $appt2['e_reason'] = (!empty($appt['e_reason']))?:'';
442 $appt2['e_is_subEvent_of']= (!empty($appt['e_is_subEvent_of']))?:"0";
443 $appt2['language'] = $appt['language'];
444 $appt2['pc_facility'] = $appt['pc_facility'];
445 $appt2['fname'] = $appt['fname'];
446 $appt2['lname'] = $appt['lname'];
447 $appt2['mname'] = $appt['mname'];
448 $appt2['street'] = $appt['street'];
449 $appt2['postal_code'] = $appt['postal_code'];
450 $appt2['city'] = $appt['city'];
451 $appt2['state'] = $appt['state'];
452 $appt2['country_code'] = $appt['country_code'];
453 $appt2['phone_home'] = $appt['phone_home'];
454 $appt2['phone_cell'] = $appt['phone_cell'];
455 $appt2['email'] = $appt['email'];
456 $appt2['pc_apptstatus'] = $appt['pc_apptstatus'];
458 $appt2['C_UID'] = $event['C_UID'];
459 $appt2['reply'] = "To Send";
460 $appt2['extra'] = "QUEUED";
461 $appt2['status'] = "SENT";
463 $appt2['to'] = $results;
464 $appt3[] = $appt2;
467 } else if ($event['M_group'] == 'RECALL') {
468 if ($event['time_order'] > '0') {
469 $interval ="+";
470 } else {
471 $interval ='-';
473 $timing = $event['E_fire_time'];
475 $query = "SELECT * FROM medex_recalls AS recall
476 LEFT JOIN patient_data AS pat ON recall.r_pid=pat.pid
477 WHERE (recall.r_eventDate < CURDATE() ".$interval." INTERVAL ".$timing." DAY)
478 ORDER BY recall.r_eventDate";
479 $result = sqlStatement($query);
481 while ($recall = sqlFetchArray($result)) {
482 list($response,$results) = $this->MedEx->checkModality($event, $recall);
483 if ($results==false) {
484 continue; //not happening - either not allowed or not possible
486 $show = $this->MedEx->display->show_progress_recall($recall, $event);
487 if ($show['DONE'] == '1') {
488 // Appointment was made -the Recall about to be deleted, so don't process this RECALL
489 // MedEx needs to delete this RECALL!
490 $RECALLS_completed[] = $recall;
491 continue;
494 if ($show['status']!=="reddish") {
495 // OK there is status for this recall. Something happened. Maybe something was sent/postcard/phone call
496 // But despite what transpired there has been no appointment yet (yellowish) or it was just
497 // made today (greenish)? Either way, we don't want to regenerate this - don't add it to our Appt list.
498 continue;
501 // OK the list of recalls may include "older than today" recalls, they should only be included once...
502 // Otherwise you'll be sending messages every day to recalls that have passed. Not the intended work flow...
503 // If a recall date has passed but no MedEx messages have been sent yet, we want to do something.
504 // ie. loading recalls for the first time CAN include recalls that are already due, not just upcoming.
505 // If there is more than one campaign event that could fire for these "old recalls",
506 // we are only going to do the first one. Generate one thing for MedEx to do for this event.
507 // Maybe the practice schedules 2 RECALL Campaign Event Messages for the same day? We would want to run both right?
508 // Yes, and for new ones, ones just due today, we will follow that directive.
509 // However we will limit recalls that are in the past to one event!
510 // Note: This only is an issue the first time loading a recall whose due date has already passed.
511 // If a new recall is added for this patient eg. two years from now we need a new one,
512 // the old one will be deleted from memory.
513 if (strtotime($recall['r_eventDate']) < mktime(0, 0, 0)) {
514 if ($this->recursive_array_search("recall_".$recall['r_pid'], $appt3)) {
515 continue;
519 $count_recalls++;
520 $recall2 = array();
521 $recall2['pc_pid'] = $recall['r_pid'];
522 $recall2['pc_eventDate'] = $recall['r_eventDate'];
523 $recall2['pc_startTime'] = '10:42:00';
524 $recall2['pc_eid'] = "recall_".$recall['r_pid'];
525 $recall2['pc_aid'] = $recall['r_provider'];
526 $recall2['e_is_subEvent_of']= "0";
527 $recall2['language'] = $recall['language'];
528 $recall2['pc_facility'] = $recall['r_facility'];
529 $recall2['fname'] = $recall['fname'];
530 $recall2['lname'] = $recall['lname'];
531 $recall2['mname'] = $recall['mname'];
532 $recall2['street'] = $recall['street'];
533 $recall2['postal_code'] = $recall['postal_code'];
534 $recall2['city'] = $recall['city'];
535 $recall2['state'] = $recall['state'];
536 $recall2['country_code'] = $recall['country_code'];
537 $recall2['phone_home'] = $recall['phone_home'];
538 $recall2['phone_cell'] = $recall['phone_cell'];
539 $recall2['email'] = $recall['email'];
540 $recall2['C_UID'] = $event['C_UID'];
541 $recall2['reply'] = "To Send";
542 $recall2['extra'] = "QUEUED";
543 $recall2['status'] = "SENT";
544 $recall2['to'] = $results;
546 $appt3[] = $recall2;
548 } else if ($event['M_group'] == 'ANNOUNCE') {
549 if (empty($event['start_date'])) {
550 continue;
552 $today = strtotime(date('Y-m-d'));
553 $start = strtotime($event['appts_start']);
555 if ($today < $start) {
556 continue;
558 if ($start >= $today) {
559 //we need recurrents because it is a future date or today
560 if (empty($event['appts_end'])) {
561 $event['appts_end'] = $event['appts_start'];
563 $target_dates = "(
565 cal.pc_eventDate >= ? AND
566 cal.pc_eventDate <= ?
570 cal.pc_eventDate <= ? AND
571 cal.pc_endDate >= ? AND
572 pc_recurrtype >'0'
574 ) ";
575 $escapedArr[]=$event['appts_start'];
576 $escapedArr[]=$event['appts_end'];
577 $escapedArr[]=$event['appts_end'];
578 $escapedArr[]=$event['appts_start'];
579 } else {
580 //it is a past date or today so we do not need recurrents
581 if (empty($event['appts_end'])) { //a single date
582 $target_dates = "pc_eventDate = ?";
583 $escapedArr[]=$event['appts_start'];
584 } else { //a date range, start and end
585 $target_dates = "(pc_eventDate >= ? and pc_eventDate <= ?)";
586 $escapedArr[]=$event['appts_start'];
587 $escapedArr[]=$event['appts_end'];
590 if (!empty($event['appt_stats'])) {
591 $prepare_me ='';
592 $appt_stats = explode('|', $event['appt_stats']);
593 foreach ($appt_stats as $appt_stat) {
594 $prepare_me .= "?,";
595 $escapedArr[]=$appt_stat;
597 $prepare_me = rtrim($prepare_me, ",");
598 $appt_status = " AND cal.pc_apptstatus in (".$prepare_me.") ";
599 } else {
600 $appt_status ='';
603 if (!empty($event['providers'])) {
604 $prepare_me ='';
605 $providers = explode('|', $event['providers']);
606 foreach ($providers as $provider) {
607 $prepare_me .= "?,";
608 $escapedArr[]=$provider;
610 $prepare_me = rtrim($prepare_me, ",");
611 $providers = " AND cal.pc_aid in (".$prepare_me.") ";
612 } else {
613 $providers ='';
616 if (!empty($event['facilities'])) {
617 $prepare_me ='';
618 $facilities = explode('|', $event['facilities']);
619 foreach ($facilities as $facility) {
620 $prepare_me .= "?,";
621 $escapedArr[]=$facility;
623 $prepare_me = rtrim($prepare_me, ",");
624 $places = " AND cal.pc_facility in (".$prepare_me.") ";
625 } else {
626 $places ='';
629 if (!empty($event['visit_types'])) {
630 $prepare_me ='';
631 $visit_types = explode('|', $event['visit_types']);
632 foreach ($visit_types as $visit_type) {
633 $prepare_me .= "?,";
634 $escapedArr[]=$visit_type;
636 $prepare_me = rtrim($prepare_me, ",");
637 $visit_types = " AND cal.pc_catid in (".$prepare_me.") ";
638 } else {
639 $visit_types ='';
642 $sql_ANNOUNCE = "SELECT * FROM openemr_postcalendar_events AS cal
643 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
644 WHERE ".$target_dates."
645 ".$appt_status."
646 ".$providers."
647 ".$places."
648 ".$visit_types."
649 ORDER BY pc_eventDate,pc_startTime";
650 $result = sqlStatement($sql_ANNOUNCE, $escapedArr);
652 while ($appt= sqlFetchArray($result)) {
653 list($response,$results) = $this->MedEx->checkModality($event, $appt);
654 if ($results==false) {
655 continue; //not happening - either not allowed or not possible
657 if ($appt['pc_recurrtype'] !='0') {
658 $recurrents = $this->addRecurrent($appt, "+", $event['appts_start'], $event['appts_end'], "ANNOUNCE");
659 $count_recurrents += $recurrents;
660 continue;
662 $count_announcements++;
664 $appt2 = array();
665 $appt2['pc_pid'] = $appt['pc_pid'];
666 $appt2['pc_eventDate'] = $appt['pc_eventDate'];
667 $appt2['pc_startTime'] = $appt['pc_startTime'];
668 $appt2['pc_eid'] = $event['C_UID'].'_'.$appt['pc_eid'];
669 $appt2['pc_aid'] = $appt['pc_aid'];
670 $appt2['e_reason'] = (!empty($appt['e_reason']))?:'';
671 $appt2['e_is_subEvent_of']= (!empty($appt['e_is_subEvent_of']))?:"0";
672 $appt2['language'] = $appt['language'];
673 $appt2['pc_facility'] = $appt['pc_facility'];
674 $appt2['fname'] = $appt['fname'];
675 $appt2['lname'] = $appt['lname'];
676 $appt2['mname'] = $appt['mname'];
677 $appt2['street'] = $appt['street'];
678 $appt2['postal_code'] = $appt['postal_code'];
679 $appt2['city'] = $appt['city'];
680 $appt2['state'] = $appt['state'];
681 $appt2['country_code'] = $appt['country_code'];
682 $appt2['phone_home'] = $appt['phone_home'];
683 $appt2['phone_cell'] = $appt['phone_cell'];
684 $appt2['email'] = $appt['email'];
685 $appt2['e_apptstatus'] = $appt['pc_apptstatus'];
686 $appt2['C_UID'] = $event['C_UID'];
688 $appt2['reply'] = "To Send";
689 $appt2['extra'] = "QUEUED";
690 $appt2['status'] = "SENT";
692 $appt2['to'] = $results;
693 $appt3[] = $appt2;
695 } else if ($event['M_group'] == 'SURVEY') {
696 // Look at appts on a per-provider basis for now...
697 //and Retrospectively only - so no recurrents
698 if (empty($event['timing'])) {
699 $event['timing'] = "180";
701 // appts completed - this is defined by list_option->toggle_setting2=1 for Flow Board
702 $appt_status = " and pc_apptstatus in (SELECT option_id from list_options where toggle_setting_2='1' and list_id='apptstat') ";
703 //if we are to refine further, to not limit by completed status but with a specific appt_stats
704 // eg survey people who left without appt or
705 //T_appt_stats = array -> list of appstat(s) to restrict event to in a ',' separated list
706 if (!empty($event['T_appt_stats'])) {
707 foreach ($event['T_appt_stats'] as $stat) {
708 $escapedArr[] = $stat;
709 $escClause['Stat'] .= "?,";
711 rtrim($escStat, ",");
712 $appt_status = " and pc_appstatus in (".$escClause['Stat'].") ";
715 $sql2= "SELECT * FROM medex_prefs";
716 $pref = sqlQuery($sql2);
717 //if we are refining survey by facility
718 $facility_clause = '';
719 if (!empty($event['T_facilities'])) {
720 foreach ($event['T_facilities'] as $fac) {
721 $escapedArr[] = $fac;
722 $escClause['Fac'] .= "?,";
724 rtrim($escFac, ",");
725 $facility_clause = " AND cal.pc_facility in (".$escClause['Fac'].") ";
727 $all_providers = explode('|', $pref['ME_providers']);
728 foreach ($event['survey'] as $k => $v) {
729 if (($v <= 0) || (empty($event['providers'])) || (!in_array($k, $all_providers))) {
730 continue;
732 $escapedArr[] = $k;
733 $query = "SELECT * FROM openemr_postcalendar_events AS cal
734 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
735 WHERE (
736 cal.pc_eventDate > CURDATE() - INTERVAL ".$event['timing']." DAY AND
737 cal.pc_eventDate < CURDATE() - INTERVAL 3 DAY) AND
738 pat.pid=cal.pc_pid AND
739 pc_apptstatus !='%' AND
740 pc_apptstatus != 'x' ".
741 $appt_status.
742 $facility_clause."
743 AND cal.pc_aid IN (?)
744 AND email > ''
745 AND hipaa_allowemail NOT LIKE 'NO'
746 GROUP BY pc_pid
747 ORDER BY pc_eventDate,pc_startTime
748 LIMIT ".$v;
749 //Survey opt-out option should feed into here also but there is no field for it in patient_data
750 //MedEx tracks opt-out requests however so unless there are a lot, it won't matter here.
751 $result = sqlStatement($query, $escapedArr);
752 while ($appt= sqlFetchArray($result)) {
753 list($response,$results) = $this->MedEx->checkModality($event, $appt);
754 if ($results==false) {
755 continue; //not happening - either not allowed or not possible
757 $appt2 = array();
758 $appt2['pc_pid'] = $appt['pc_pid'];
759 $appt2['pc_eventDate'] = $appt['pc_eventDate'];
760 $appt2['pc_startTime'] = $appt['pc_startTime'];
761 $appt2['pc_eid'] = $appt['pc_eid'];
762 $appt2['pc_aid'] = $appt['pc_aid'];
763 $appt2['e_reason'] = (!empty($appt['e_reason']))?:'';
764 $appt2['e_is_subEvent_of']= (!empty($appt['e_is_subEvent_of']))?:"0";
765 $appt2['language'] = $appt['language'];
766 $appt2['pc_facility'] = $appt['pc_facility'];
767 $appt2['fname'] = $appt['fname'];
768 $appt2['lname'] = $appt['lname'];
769 $appt2['mname'] = $appt['mname'];
770 $appt2['street'] = $appt['street'];
771 $appt2['postal_code'] = $appt['postal_code'];
772 $appt2['city'] = $appt['city'];
773 $appt2['state'] = $appt['state'];
774 $appt2['country_code'] = $appt['country_code'];
775 $appt2['phone_home'] = $appt['phone_home'];
776 $appt2['phone_cell'] = $appt['phone_cell'];
777 $appt2['email'] = $appt['email'];
778 $appt2['pc_apptstatus'] = $appt['pc_apptstatus'];
780 $appt2['C_UID'] = $event['C_UID'];
781 $appt2['E_fire_time'] = $event['E_fire_time'];
782 $appt2['time_order'] = $event['time_order'];
783 $appt2['M_type'] = $event['M_type'];
784 $appt2['reply'] = "To Send";
785 $appt2['extra'] = "QUEUED";
786 $appt2['status'] = "SENT";
788 $appt2['to'] = $results;
789 $appt3[] = $appt2;
790 $count_surveys++;
793 } else if ($event['M_group'] == 'CLINICAL_REMINDER') {
794 $sql = "SELECT * FROM `patient_reminders`,`patient_data`
795 WHERE
796 `patient_reminders`.pid ='".$event['PID']."' AND
797 `patient_reminders`.active='1' AND
798 `patient_reminders`.date_sent IS NULL AND
799 `patient_reminders`.pid=`patient_data`.pid
800 ORDER BY `due_status`, `date_created`";
801 $ures = sqlStatementCdrEngine($sql);
802 $returnval=array();
803 while ($urow = sqlFetchArray($ures)) {
804 list($response,$results) = $this->MedEx->checkModality($event, $urow);
805 if ($results==false) {
806 continue; //not happening - either not allowed or not possible
808 $fields2['clinical_reminders'][] = $urow;
809 $count_clinical_reminders++;
811 } else if ($event['M_group'] == 'GOGREEN') {
812 $escapedArr=[];
814 if (!empty($event['appt_stats'])) {
815 $prepare_me ='';
816 $appt_stats = explode('|', $event['appt_stats']);
817 foreach ($appt_stats as $appt_stat) {
818 $prepare_me .= "?,";
819 $escapedArr[]=$appt_stat;
821 $prepare_me = rtrim($prepare_me, ",");
822 $appt_status = " AND cal.pc_apptstatus in (".$prepare_me.") ";
823 } else {
824 $appt_status ='';
827 if (!empty($event['providers'])) {
828 $prepare_me ='';
829 $providers = explode('|', $event['providers']);
830 foreach ($providers as $provider) {
831 $prepare_me .= "?,";
832 $escapedArr[]=$provider;
834 $prepare_me = rtrim($prepare_me, ",");
835 $providers = " AND cal.pc_aid in (".$prepare_me.") ";
836 } else {
837 $providers ='';
840 if (!empty($event['facilities'])) {
841 $prepare_me ='';
842 $facilities = explode('|', $event['facilities']);
843 foreach ($facilities as $facility) {
844 $prepare_me .= "?,";
845 $escapedArr[]=$facility;
847 $prepare_me = rtrim($prepare_me, ",");
848 $places = " AND cal.pc_facility in (".$prepare_me.") ";
849 } else {
850 $places ='';
853 if (!empty($event['visit_types'])) {
854 $prepare_me ='';
855 $visit_types = explode('|', $event['visit_types']);
856 foreach ($visit_types as $visit_type) {
857 $prepare_me .= "?,";
858 $escapedArr[]=$visit_type;
860 $prepare_me = rtrim($prepare_me, ",");
861 $visit_types = " AND cal.pc_catid in (".$prepare_me.") ";
862 } else {
863 $visit_types ='';
866 $frequency ='';
867 if ($event['E_instructions'] == 'once') {
868 //once - this patient only gets one
869 $frequency = " AND cal.pc_pid NOT in (
870 SELECT msg_pid from medex_outgoing where
871 campaign_uid =? and msg_date >= curdate() )";
872 $escapedArr[] = (int)$event['C_UID'];
873 } else {
874 if ($event['E_instructions'] == 'yearly') {
875 //yearly - this patient only gets this once a year
876 $frequency = " AND cal.pc_pid NOT in (
877 SELECT msg_pid from medex_outgoing where
878 campaign_uid =? and
879 msg_date > curdate() - interval 1 year )";
880 $escapedArr[] = (int)$event['C_UID'];
883 //otherwise Campaign was set to send with each appointment...
885 //make sure this only gets sent once for this campaign
886 $no_dupes = " AND cal.pc_eid NOT IN (
887 SELECT msg_pc_eid from medex_outgoing where
888 campaign_uid =? and msg_date >= curdate() ) ";
889 $escapedArr[] = (int)$event['C_UID'];
891 $target_dates ='';
892 if ($event['E_timing'] == '5') {
893 $target_dates = " cal.pc_eventDate > curdate() ";
894 } else {
895 if (!is_numeric($event['E_fire_time'])) { //this would be an error in building the event
896 $event['E_fire_time'] ='0';
898 $timing = (int)$event['E_fire_time'];
899 if (($event['E_timing'] == '1') || ($event['E_timing'] == '2')) {
900 //then this is X days prior to appt
901 //we need to include recurrents
902 $target_dates = "(
904 cal.pc_eventDate = CURDATE() + INTERVAL ".$timing." DAY
908 cal.pc_eventDate <= CURDATE() + INTERVAL ".$timing." DAY AND
909 cal.pc_endDate >= CURDATE() + INTERVAL ".$timing." DAY AND
910 cal.pc_recurrtype >'0'
915 if ($today == "Friday") {
916 $timing2 = ($timing + 2);
917 $target_dates = "(
919 cal.pc_eventDate >= (CURDATE() + INTERVAL ".$timing." DAY AND
920 cal.pc_eventDate <= (CURDATE() + INTERVAL ".$timing2." DAY)
924 cal.pc_eventDate <= CURDATE() + INTERVAL ".$timing2." DAY AND
925 cal.pc_endDate >= CURDATE() + INTERVAL ".$timing." DAY AND
926 cal.pc_recurrtype >'0'
930 } else {
931 if (($event['E_timing'] == '3') || ($event['E_timing'] == '4')) {
932 //then this is X days post appt
933 //no recurrent needed
934 $target_dates = "cal.pc_eventDate = curdate() - interval ".$timing." day";
935 if ($today == "Monday") {
936 $timing2 = ($timing + 3);
937 $target_dates .= "cal.pc_eventDate <= curdate() - INTERVAL ".$timing." DAY AND cal.pc_eventDate > (curdate() - INTERVAL '".$timing2."' DAY) ";
942 $sql_GOGREEN = "SELECT * FROM openemr_postcalendar_events AS cal
943 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
944 WHERE
945 ".$target_dates."
946 ".$appt_status."
947 ".$providers."
948 ".$places."
949 ".$visit_types."
950 ".$frequency."
951 ".$no_dupes."
952 ORDER BY cal.pc_eventDate,cal.pc_startTime";
953 $result = sqlStatement($sql_GOGREEN, $escapedArr);
955 while ($appt = sqlFetchArray($result)) {
956 list($response,$results) = $this->MedEx->checkModality($event, $appt);
957 if ($results==false) {
958 continue; //not happening - either not allowed or not possible
960 if ($appt['pc_recurrtype'] !='0') {
961 $recurrents = $this->addRecurrent($appt, "+", $start_date, $end_date, "GOGREEN");
962 $count_recurrents += $recurrents;
963 continue;
965 $count_gogreen++;
966 $appt2 = array();
967 $appt2['pc_pid'] = $appt['pc_pid'];
968 $appt2['pc_eventDate'] = $appt['pc_eventDate'];
969 $appt2['pc_startTime'] = $appt['pc_startTime'];
970 $appt2['pc_eid'] = $appt['pc_eid'];
971 $appt2['pc_aid'] = $appt['pc_aid'];
972 $appt2['e_reason'] = (!empty($appt['e_reason']))?:'';
973 $appt2['e_is_subEvent_of']= (!empty($appt['e_is_subEvent_of']))?:"0";
974 $appt2['language'] = $appt['language'];
975 $appt2['pc_facility'] = $appt['pc_facility'];
976 $appt2['fname'] = $appt['fname'];
977 $appt2['lname'] = $appt['lname'];
978 $appt2['mname'] = $appt['mname'];
979 $appt2['street'] = $appt['street'];
980 $appt2['postal_code'] = $appt['postal_code'];
981 $appt2['city'] = $appt['city'];
982 $appt2['state'] = $appt['state'];
983 $appt2['country_code'] = $appt['country_code'];
984 $appt2['phone_home'] = $appt['phone_home'];
985 $appt2['phone_cell'] = $appt['phone_cell'];
986 $appt2['email'] = $appt['email'];
987 $appt2['pc_apptstatus'] = $appt['pc_apptstatus'];
989 $appt2['C_UID'] = $event['C_UID'];
990 $appt2['reply'] = "To Send";
991 $appt2['extra'] = "QUEUED";
992 $appt2['status'] = "SENT";
994 $appt2['to'] = $results;
995 $appt3[] = $appt2;
998 $this->MedEx->logging->log_this("Stop");
1002 if (!empty($RECALLS_completed)) {
1003 $deletes = $this->process_deletes($token, $RECALLS_completed);
1006 if (!empty($appt3)) {
1007 $this->process($token, $appt3);
1009 $responses['deletes'] = $deletes;
1010 $responses['count_appts'] = $count_appts;
1011 $responses['count_recalls'] = $count_recalls;
1012 $responses['count_recurrents'] = $count_recurrents;
1013 $responses['count_announcements'] = $count_announcements;
1014 $responses['count_surveys'] = $count_surveys;
1015 $responses['count_clinical_reminders'] = $count_clinical_reminders;
1016 $responses['count_gogreen'] = $count_gogreen;
1018 return $responses;
1022 * This function will check recurring appt entries in calendar.
1023 * REMINDERS, GOGREEN, ANNOUNCEMENTS and SURVEYS run off the calendar.
1024 * Only REMINDERS however will alter the entry in postcalendar_events
1025 * If there is a match, it will check to see if the postCalendar_events table already has an entry for this appt.
1026 * If a recurrent appointment exists but nothing has been done to it yet, there will not be an entry for it in this table.
1027 * Then we need to create an entry into the postcalendar_events table and return this appt.
1028 * If something has happened, eg set appt status, there will already be an entry and this recurring
1029 * appointment will list this as "exclude_date" in pc_recurrspec serialized field.
1030 * Either way, for appts, return false. Next time this runs the appts will be seen as appts
1031 * and not part of a recurring event - so they will be generated on their own.
1032 * For the non-REMINDERS, if there is an appointment match, just return true.
1034 * @param $appt
1035 * @param $result
1036 * @return array|bool
1038 private function addRecurrent($appt, $interval, $timing, $timing2, $M_group = "REMINDER")
1040 // We have a row in postcalendar_events that pc_recurrtype > 0
1041 // Check the dates that match
1042 // Make postcalendar_events entry for them so medex_outgoing can update them
1043 // and exclude them from recurring
1044 // and add an entry into patient_tracker so Flow Board knows it's here
1046 $this->MedEx->logging->log_this("Checking RECURRENT: ");
1047 //get dates in this request
1048 if ($M_group=="REMINDER") {
1049 $start = explode(':', $timing);
1050 $end = explode(':', $timing2);
1051 $start_date = date('Y-m-d', strtotime($interval.$start[0] . ' day'));
1052 $stop_date = date('Y-m-d', strtotime($interval.$end[0] . ' day'));
1053 } else {
1054 $start_date = $timing;
1055 $stop_date = $timing2;
1057 $this->MedEx->logging->log_this("start: ".$start_date." -- stop: ".$stop_date);
1058 //foreach date between curdate + timing and curdate + timing2 excluding dates excluded in recurring
1059 $hits = $this->MedEx->events->calculateEvents($appt, $start_date, $stop_date);
1060 $this->MedEx->logging->log_this($hits);
1061 $this->MedEx->logging->log_this("above are the hits for pc_eid: " . $appt['pc_eid']."\n\n
1062 &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n
1063 Now Convert them to real appt slots for MedEx\n");
1065 //any dates that match need to be spawned from recurrent and made to live on their own.
1066 $oldRecurrspec = unserialize($appt['pc_recurrspec']);
1068 foreach ($hits as $selected_date) {
1069 $exclude = str_replace("-", "", $selected_date);
1071 if ($oldRecurrspec['exdate'] != "") {
1072 $oldRecurrspec['exdate'] .= ",".$exclude;
1073 } else {
1074 $oldRecurrspec['exdate'] .= $exclude;
1076 $this->MedEx->logging->log_this($oldRecurrspec['exdate']." --- ".$selected_date." - ".$exclude." - ".$appt['pc_eid']);
1078 // mod original event recur specs to exclude this date
1079 sqlStatement("UPDATE openemr_postcalendar_events SET pc_recurrspec = ? WHERE pc_eid = ?", array(serialize($oldRecurrspec),$appt['pc_eid']));
1080 // specify some special variables needed for the INSERT
1081 // no recurr specs, this is used for adding a new non-recurring event
1082 $noRecurrspec = array("event_repeat_freq" => "",
1083 "event_repeat_freq_type" => "",
1084 "event_repeat_on_num" => "1",
1085 "event_repeat_on_day" => "0",
1086 "event_repeat_on_freq" => "0",
1087 "exdate" => ""
1089 // Useless garbage that we must save. Anon
1090 // - ok but why is it useless? RM 2018-11-05
1091 $locationspecs = array("event_location" => "",
1092 "event_street1" => "",
1093 "event_street2" => "",
1094 "event_city" => "",
1095 "event_state" => "",
1096 "event_postal" => ""
1098 $locationspec = serialize($locationspecs);
1099 $args['duration'] = $appt['duration'];
1100 // this event is forced to NOT REPEAT
1101 $args['form_repeat'] = "0";
1102 $args['recurrspec'] = $noRecurrspec;
1103 $args['form_enddate'] = "0000-00-00";
1104 //$args['prefcatid'] = (int)$appt['prefcatid'];
1106 $sql= "INSERT INTO openemr_postcalendar_events ( " .
1107 "pc_catid, pc_multiple, pc_aid, pc_pid, pc_gid, pc_title, ".
1108 "pc_time, ".
1109 "pc_hometext, pc_informant, pc_eventDate, pc_endDate, pc_duration, pc_recurrtype, " .
1110 "pc_recurrspec, pc_startTime, pc_endTime, pc_alldayevent, " .
1111 "pc_apptstatus, pc_prefcatid, pc_location, pc_eventstatus, pc_sharing, pc_facility,".
1112 "pc_billing_location,pc_room " .
1113 ") VALUES (?,?,?,?,?,?,NOW(),?,?,?,?,?,?,?,?,?,?,?,?,?,1,1,?,?,?)";
1114 $this->MedEx->logging->log_this($sql);
1116 $pc_eid = sqlInsert($sql, array($appt['pc_catid'], $appt['pc_multiple'], $appt['pc_aid'], $appt['pc_pid'], $appt['pc_gid'], $appt['pc_title'],
1117 $appt['pc_hometext'], $appt['pc_informant'], $selected_date, $args['form_enddate'], $appt['pc_duration'], '0',
1118 serialize($noRecurrspec), $appt['pc_startTime'], $appt['pc_endTime'], $appt['pc_alldayevent'],
1119 $appt['pc_apptstatus'], $appt['pc_prefcatid'], $locationspec, (int)$appt['pc_facility'],
1120 (int)$appt['pc_billing_facility'], $appt['pc_room']));
1121 $this->MedEx->logging->log_this($appt['pc_eid']."-".$pc_eid);
1123 #Add a new tracker item for this appt.
1124 $datetime = date("Y-m-d H:i:s");
1125 sqlInsert(
1126 "INSERT INTO `patient_tracker` " .
1127 "(`date`, `apptdate`, `appttime`, `eid`, `pid`, `original_user`, `encounter`, `lastseq`) " .
1128 "VALUES (?,?,?,?,?,'MedEx','0','1')",
1129 array($datetime, $selected_date, $appt['pc_startTime'], $pc_eid, $appt['pc_pid'])
1133 $this->MedEx->logging->log_this("That was the answer for RECURRENT....\n\n ");
1135 return count($hits);
1138 private function recursive_array_search($needle, $haystack)
1140 foreach ($haystack as $key => $value) {
1141 $current_key=$key;
1142 if ($needle===$value or (is_array($value) && $this->recursive_array_search($needle, $value))) {
1143 return true; //$current_key;
1146 return false;
1150 * This function deletes Recalls from MedEx when they are completed and no further processing is
1151 * needed. They are in an array = $data.
1152 * @param $token
1153 * @param $data
1154 * @return bool
1156 private function process_deletes($token, $data)
1158 $this->curl->setUrl($this->MedEx->getUrl('custom/remRecalls&token='.$token));
1159 $this->curl->setData($data);
1160 $this->curl->makeRequest();
1161 $response = $this->curl->getResponse();
1163 if (isset($response['success'])) {
1164 return $response;
1165 } else if (isset($response['error'])) {
1166 $this->lastError = $response['error'];
1168 return false;
1172 * This function processes appointments/recalls that meet the timimg requirements for a MedEx Campaign Event
1173 * @param $token
1174 * @param $appts
1175 * @return bool
1177 private function process($token, $appts)
1179 if (empty($appts)) {
1180 throw new InvalidDataException("You have no appointments that need processing at this time.");
1182 $data= array();
1184 foreach ($appts as $appt) {
1185 $data['appts'][] = $appt;
1186 $sqlUPDATE = "UPDATE medex_outgoing SET msg_reply=?, msg_extra_text=?, msg_date=NOW()
1187 WHERE msg_pc_eid=? AND campaign_uid=? AND msg_type=? AND msg_reply='To Send'";
1188 sqlQuery($sqlUPDATE, array($appt['reply'],$appt['extra'],$appt['pc_eid'],$appt['C_UID'], $appt['M_type']));
1189 if (count($data['appts'])>'100') {
1190 $this->curl->setUrl($this->MedEx->getUrl('custom/loadAppts&token='.$token));
1191 $this->curl->setData($data);
1192 $this->curl->makeRequest();
1193 $response = $this->curl->getResponse();
1194 $data = array();
1195 sleep(1);
1198 //finish those $data < 100.
1199 $this->curl->setUrl($this->MedEx->getUrl('custom/loadAppts&token='.$token));
1200 $this->curl->setData($data);
1201 $this->curl->makeRequest();
1202 $response = $this->curl->getResponse();
1204 if (isset($response['success'])) {
1205 return $response;
1206 } else if (isset($response['error'])) {
1207 $this->lastError = $response['error'];
1209 return false;
1212 public function calculateEvents($event, $start_date, $stop_date)
1215 ///////////////////////////////////////////////////////////////////////
1216 // The following code is from the calculateEvents function in the //
1217 // PostCalendar Module modified by epsdky and inserted here, //
1218 // and modified some more for MedEx. //
1219 ///////////////////////////////////////////////////////////////////////
1220 $data = array();
1221 $this->MedEx->logging->log_this("hello in calculateEvents");
1222 $this->MedEx->logging->log_this($event['pc_recurrtype']);
1223 switch ($event['pc_recurrtype']) {
1224 //not recurrent
1225 case '0':
1226 $data[] = $event;
1227 break;
1228 case '1':
1229 case '3':
1230 $event_recurrspec = @unserialize($event['pc_recurrspec']);
1232 $rfreq = $event_recurrspec['event_repeat_freq'];
1233 $rtype = $event_recurrspec['event_repeat_freq_type'];
1234 $exdate = $event_recurrspec['exdate'];
1235 list($ny,$nm,$nd) = explode('-', $event['pc_eventDate']);
1236 $occurence = $event['pc_eventDate'];
1238 // prep work to start cooking...
1239 // ignore dates less than start_date
1240 while (strtotime($occurence) < strtotime($start_date)) {
1241 // if the start date is later than the recur date start
1242 // just go up a unit at a time until we hit start_date
1243 $occurence =& $this->MedEx->events->__increment($nd, $nm, $ny, $rfreq, $rtype);
1244 list($ny,$nm,$nd) = explode('-', $occurence);
1246 //now we are cooking...
1247 while ($occurence <= $stop_date) {
1248 $excluded = false;
1249 if (isset($exdate)) {
1250 foreach (explode(",", $exdate) as $exception) {
1251 // occurrence format == yyyy-mm-dd
1252 // exception format == yyyymmdd
1253 if (preg_replace("/-/", "", $occurence) == $exception) {
1254 $excluded = true;
1259 if ($excluded == false) {
1260 $data[] = $occurence;
1262 $occurence =& $this->MedEx->events->__increment($nd, $nm, $ny, $rfreq, $rtype);
1263 list($ny,$nm,$nd) = explode('-', $occurence);
1265 break;
1267 //////
1268 case '2':
1269 $event_recurrspec = @unserialize($event['pc_recurrspec']);
1271 if (checkEvent($event['pc_recurrtype'], $event_recurrspec)) {
1272 break; }
1274 $rfreq = $event_recurrspec['event_repeat_on_freq'];
1275 $rnum = $event_recurrspec['event_repeat_on_num'];
1276 $rday = $event_recurrspec['event_repeat_on_day'];
1277 $exdate = $event_recurrspec['exdate'];
1279 list($ny,$nm,$nd) = explode('-', $event['pc_eventDate']);
1281 if (isset($event_recurrspec['rt2_pf_flag']) && $event_recurrspec['rt2_pf_flag']) {
1282 $nd = 1;
1285 $occurenceYm = "$ny-$nm"; // YYYY-mm
1286 $from_dateYm = substr($start_date, 0, 7); // YYYY-mm
1287 $stop_dateYm = substr($stop_date, 0, 7); // YYYY-mm
1289 // $nd will sometimes be 29, 30 or 31 and if used in the mktime functions below
1290 // a problem with overflow will occur so it is set to 1 to avoid this (for rt2
1291 // appointments set prior to fix $nd remains unchanged). This can be done since
1292 // $nd has no influence past the mktime functions.
1293 while ($occurenceYm < $from_dateYm) {
1294 $occurenceYmX = date('Y-m-d', mktime(0, 0, 0, $nm+$rfreq, $nd, $ny));
1295 list($ny,$nm,$nd) = explode('-', $occurenceYmX);
1296 $occurenceYm = "$ny-$nm";
1299 while ($occurenceYm <= $stop_dateYm) {
1300 // (YYYY-mm)-dd
1301 $dnum = $rnum;
1302 do {
1303 $occurence = Date_Calc::NWeekdayOfMonth($dnum--, $rday, $nm, $ny, $format = "%Y-%m-%d");
1304 } while ($occurence === -1);
1306 if ($occurence >= $from_date && $occurence <= $stop_date) {
1307 $excluded = false;
1308 if (isset($exdate)) {
1309 foreach (explode(",", $exdate) as $exception) {
1310 // occurrence format == yyyy-mm-dd
1311 // exception format == yyyymmdd
1312 if (preg_replace("/-/", "", $occurence) == $exception) {
1313 $excluded = true;
1318 if ($excluded == false) {
1319 $event['pc_eventDate'] = $occurence;
1320 $event['pc_endDate'] = '0000-00-00';
1321 $events2[] = $event;
1322 $data[] = $event['pc_eventDate'];
1326 $occurenceYmX = date('Y-m-d', mktime(0, 0, 0, $nm+$rfreq, $nd, $ny));
1327 list($ny,$nm,$nd) = explode('-', $occurenceYmX);
1328 $occurenceYm = "$ny-$nm";
1330 break;
1332 return $data;
1335 private function &__increment($d, $m, $y, $f, $t)
1337 define('REPEAT_EVERY_DAY', 0);
1338 define('REPEAT_EVERY_WEEK', 1);
1339 define('REPEAT_EVERY_MONTH', 2);
1340 define('REPEAT_EVERY_YEAR', 3);
1341 define('REPEAT_EVERY_WORK_DAY', 4);
1342 define('REPEAT_DAYS_EVERY_WEEK', 6);
1344 if ($t == REPEAT_EVERY_DAY) {
1345 $this->MedEx->logging->log_this(date('Y-m-d', mktime(0, 0, 0, $m, ($d+$f), $y)));
1346 return date('Y-m-d', mktime(0, 0, 0, $m, ($d+$f), $y));
1347 } elseif ($t == REPEAT_EVERY_WORK_DAY) {
1348 // a workday is defined as Mon,Tue,Wed,Thu,Fri
1349 // repeating on every or Nth work day means to not include
1350 // weekends (Sat/Sun) in the increment... tricky
1352 // ugh, a day-by-day loop seems necessary here, something where
1353 // we can check to see if the day is a Sat/Sun and increment
1354 // the frequency count so as to ignore the weekend. hmmmm....
1355 $orig_freq = $f;
1356 for ($daycount=1; $daycount<=$orig_freq; $daycount++) {
1357 $nextWorkDOW = date('w', mktime(0, 0, 0, $m, ($d+$daycount), $y));
1358 if (is_weekend_day($nextWorkDOW)) {
1359 $f++;
1363 // and finally make sure we haven't landed on a end week days
1364 // adjust as necessary
1365 $nextWorkDOW = date('w', mktime(0, 0, 0, $m, ($d+$f), $y));
1366 if (count($GLOBALS['weekend_days']) === 2) {
1367 if ($nextWorkDOW == $GLOBALS['weekend_days'][0]) {
1368 $f+=2;
1369 } elseif ($nextWorkDOW == $GLOBALS['weekend_days'][1]) {
1370 $f++;
1372 } elseif (count($GLOBALS['weekend_days']) === 1 && $nextWorkDOW === $GLOBALS['weekend_days'][0]) {
1373 $f++;
1375 return date('Y-m-d', mktime(0, 0, 0, $m, ($d+$f), $y));
1376 } elseif ($t == REPEAT_EVERY_WEEK) {
1377 return date('Y-m-d', mktime(0, 0, 0, $m, ($d+(7*$f)), $y));
1378 } elseif ($t == REPEAT_EVERY_MONTH) {
1379 return date('Y-m-d', mktime(0, 0, 0, ($m+$f), $d, $y));
1380 } elseif ($t == REPEAT_EVERY_YEAR) {
1381 return date('Y-m-d', mktime(0, 0, 0, $m, $d, ($y+$f)));
1382 } elseif ($t == REPEAT_DAYS_EVERY_WEEK) {
1383 $old_appointment_date = date('Y-m-d', mktime(0, 0, 0, $m, $d, $y));
1384 $next_appointment_date = getTheNextAppointment($old_appointment_date, $f);
1385 return $next_appointment_date;
1389 public function save_recall($saved)
1391 $this->delete_Recall();
1392 $mysqldate = DateToYYYYMMDD($_REQUEST['form_recall_date']);
1393 $queryINS = "INSERT INTO medex_recalls (r_pid,r_reason,r_eventDate,r_provider,r_facility)
1394 VALUES (?,?,?,?,?)
1395 ON DUPLICATE KEY
1396 UPDATE r_reason=?, r_eventDate=?, r_provider=?,r_facility=?";
1397 $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']));
1398 $query = "UPDATE patient_data
1399 SET phone_home=?,phone_cell=?,email=?,
1400 hipaa_allowemail=?,hipaa_voice=?,hipaa_allowsms=?,
1401 street=?,postal_code=?,city=?,state=?
1402 WHERE pid=?";
1403 $sqlValues = array($_REQUEST['new_phone_home'],$_REQUEST['new_phone_cell'],$_REQUEST['new_email'],
1404 $_REQUEST['new_email_allow'],$_REQUEST['new_voice'],$_REQUEST['new_allowsms'],
1405 $_REQUEST['new_address'],$_REQUEST['new_postal_code'],$_REQUEST['new_city'],$_REQUEST['new_state'],
1406 $_REQUEST['new_pid']);
1407 $result = sqlStatement($query, $sqlValues);
1408 return;
1411 public function delete_Recall()
1413 $sqlQuery = "DELETE FROM medex_recalls WHERE r_pid=? OR r_ID=?";
1414 sqlStatement($sqlQuery, array($_POST['pid'],$_POST['r_ID']));
1416 $sqlDELETE = "DELETE FROM medex_outgoing WHERE msg_pc_eid = ?";
1417 sqlStatement($sqlDELETE, array('recall_'.$_POST['pid']));
1420 public function getAge($dob, $asof = '')
1422 if (empty($asof)) {
1423 $asof = date('Y-m-d');
1425 $a1 = explode('-', substr($dob, 0, 10));
1426 $a2 = explode('-', substr($asof, 0, 10));
1427 $age = $a2[0] - $a1[0];
1428 if ($a2[1] < $a1[1] || ($a2[1] == $a1[1] && $a2[2] < $a1[2])) {
1429 --$age;
1431 return $age;
1434 private function getDatesInRecurring($appt, $interval, $start_days = '', $end_days = '')
1436 $start = date('Y-m-d', strtotime($interval.$start_days . ' day'));
1437 $end = date('Y-m-d', strtotime($interval.$end_days . ' day'));
1438 $aryRange=array();
1440 $iDateFrom=mktime(1, 0, 0, substr($start, 5, 2), substr($start, 8, 2), substr($start, 0, 4));
1441 $iDateTo=mktime(1, 0, 0, substr($end, 5, 2), substr($end, 8, 2), substr($end, 0, 4));
1443 if ($iDateTo>=$iDateFrom) {
1444 array_push($aryRange, date('Y-m-d', $iDateFrom)); // first entry
1445 while ($iDateFrom<$iDateTo) {
1446 $iDateFrom+=86400; // add 24 hours
1447 array_push($aryRange, date('Y-m-d', $iDateFrom));
1450 $this->MedEx->logging->log_this($aryRange);
1451 return $aryRange;
1456 * Process updates and message replies received from MedEx.
1457 * Lets MedEx know if we did anything manually to a queued event.
1459 class Callback extends Base
1461 public function receive($data = '')
1463 if ($data=='') {
1464 $data = $_POST;
1466 if (empty($data['campaign_uid'])) {
1467 // throw new InvalidDataException("There must be a Campaign to update...");
1468 $response['success'] = "No campaigns to process.";
1470 //why aren't they the same??
1471 if (!$data['patient_id']) { //process AVM responses??
1472 if ($data['e_pid']) {
1473 $data['patient_id'] = $data['e_pid'];
1474 } else if ($data['pc_eid']) { //process responses from callback into MedEx.php - no pid just pc_eid
1475 $query = "SELECT * FROM openemr_postcalendar_events WHERE pc_eid=?"; //assume one patient per appointment pc_eid/slot...
1476 $patient = sqlFetchArray(sqlStatement($query, array($data['pc_eid']))); //otherwise this will need to be a loop
1477 $data['patient_id'] = $patient['pid'];
1480 if ($data['patient_id']) {
1481 //Store responses in TABLE medex_outgoing
1482 $sqlINSERT = "INSERT INTO medex_outgoing (msg_pc_eid, msg_pid, campaign_uid, msg_type, msg_reply, msg_extra_text, msg_date, medex_uid)
1483 VALUES (?,?,?,?,?,?,utc_timestamp(),?)";
1484 if (!$data['M_type']) {
1485 $data['M_type'] ='pending'; }
1486 sqlQuery($sqlINSERT, array($data['pc_eid'],$data['patient_id'], $data['campaign_uid'], $data['M_type'],$data['msg_reply'],$data['msg_extra'],$data['msg_uid']));
1488 if ($data['msg_reply']=="CONFIRMED") {
1489 $sqlUPDATE = "UPDATE openemr_postcalendar_events SET pc_apptstatus = ? WHERE pc_eid=?";
1490 sqlStatement($sqlUPDATE, array($data['msg_type'],$data['pc_eid']));
1491 //need to insert this into patient_tracker
1492 $query = "SELECT * FROM patient_tracker WHERE eid=?";
1493 $tracker = sqlFetchArray(sqlStatement($query, array($data['pc_eid']))); //otherwise this will need to be a loop
1494 #Update lastseq in tracker.
1495 if (!empty($tracker['id'])) {
1496 sqlStatement(
1497 "UPDATE `patient_tracker` SET `lastseq` = ? WHERE eid=?",
1498 array(($tracker['lastseq']+1),$data['pc_eid'])
1500 #Add a tracker item.
1501 $datetime = date("Y-m-d H:i:s");
1502 sqlInsert(
1503 "INSERT INTO `patient_tracker_element` " .
1504 "(`pt_tracker_id`, `start_datetime`, `user`, `status`, `seq`) " .
1505 "VALUES (?,?,?,?,?)",
1506 array($tracker['id'],$datetime,'MedEx',$data['msg_type'],($tracker['lastseq']+1))
1508 } else {
1509 $this->MedEx->logging->log_this("Tracker ID is missing for pc_eid=".$data['pc_eid']."!!!!!!");
1510 $this->MedEx->logging->log_this($tracker);
1512 } elseif ($data['msg_reply']=="CALL") {
1513 $sqlUPDATE = "UPDATE openemr_postcalendar_events SET pc_apptstatus = 'CALL' WHERE pc_eid=?";
1514 $test = sqlQuery($sqlUPDATE, array($data['pc_eid']));
1515 //this requires attention. Send up the FLAG!
1516 //$this->MedEx->logging->new_message($data);
1517 } elseif (($data['msg_type']=="AVM") && ($data['msg_reply']=="STOP")) {
1518 //if reply = "STOP" update patient demographics to disallow this mode of communication
1519 $sqlUPDATE = "UPDATE patient_data SET hipaa_voice = 'NO' WHERE pid=?";
1520 sqlQuery($sqlUPDATE, array($data['patient_id']));
1521 } elseif (($data['msg_type']=="SMS") && ($data['msg_reply']=="STOP")) {
1522 $sqlUPDATE = "UPDATE patient_data SET hipaa_allowsms = 'NO' WHERE pid=?";
1523 sqlQuery($sqlUPDATE, array($data['patient_id']));
1524 } elseif (($data['msg_type']=="EMAIL") && ($data['msg_reply']=="STOP")) {
1525 $sqlUPDATE = "UPDATE patient_data SET hipaa_allowemail = 'NO' WHERE pid=?";
1526 sqlQuery($sqlUPDATE, array($data['patient_id']));
1528 if (($data['msg_type']=="SMS")&&($data['msg_reply']=="Other")) {
1529 //ideally we would be incrementing the "new Message Icon", perhaps using:
1530 //$this->MedEx->logging->new_message($data);
1532 if (($data['msg_reply']=="SENT")||($data['msg_reply']=="READ")) {
1533 $sqlDELETE = "DELETE FROM medex_outgoing WHERE msg_pc_eid=? AND msg_reply='To Send'";
1534 sqlQuery($sqlDELETE, array($data['pc_eid']));
1536 $response['comments'] = $data['pc_eid']." - ".$data['campaign_uid']." - ".$data['msg_type']." - ".$data['reply']." - ".$data['extra'];
1537 $response['pid'] = $data['patient_id'];
1538 $response['success'] = $data['msg_type']." reply";
1539 } else {
1540 $response['success'] = "completed";
1542 return $response;
1546 class Logging extends base
1548 public function log_this($data)
1550 //truly a debug function, that we will probably find handy to keep on end users' servers;)
1551 return;
1552 $log = "/tmp/medex.log" ;
1553 $std_log = fopen($log, 'a');// or die(print_r(error_get_last(), true));
1554 $timed = date(DATE_RFC2822);
1555 fputs($std_log, "**********************\nlibrary/MedEx/API.php fn log_this(data): ".$timed."\n");
1556 if (is_array($data)) {
1557 $dumper = print_r($data, true);
1558 fputs($std_log, $dumper);
1559 foreach ($data as $key => $value) {
1560 fputs($stdlog, $key.": ".$value."\n");
1562 } else {
1563 fputs($std_log, "\nDATA= ".$data. "\n");
1565 fclose($std_log);
1566 return true;
1570 class Display extends base
1572 public function navigation($logged_in)
1574 global $setting_bootstrap_submenu;
1577 <script>
1578 function toggle_menu() {
1579 var x = document.getElementById('hide_nav');
1580 if (x.style.display === 'none') {
1581 $.post( "<?php echo $GLOBALS['webroot']."/interface/main/messages/messages.php"; ?>", {
1582 'setting_bootstrap_submenu' : 'show',
1583 success: function (data) {
1584 x.style.display = 'block';
1588 } else {
1589 $.post( "<?php echo $GLOBALS['webroot']."/interface/main/messages/messages.php"; ?>", {
1590 'setting_bootstrap_submenu' : 'hide',
1591 success: function (data) {
1592 x.style.display = 'none';
1596 $("#patient_caret").toggleClass('fa-caret-up').toggleClass('fa-caret-down');
1600 * @return {boolean}
1602 function SMS_bot_list() {
1603 top.restoreSession();
1604 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');
1605 myWindow.focus();
1606 return false;
1608 </script>
1609 <i class="fa fa-caret-<?php
1610 if ($setting_bootstrap_submenu == 'hide') {
1611 echo 'down';
1612 } else {
1613 echo 'up';
1614 } ?> menu_arrow" style="position:fixed;left:5px;top:5px;z-index:1099;" id="patient_caret" onclick='toggle_menu();' aria-hidden="true"></i>
1615 <div id="hide_nav" style="<?php if ($setting_bootstrap_submenu == 'hide') {
1616 echo "display:none;"; } ?>">
1617 <nav id="navbar_oe" class="bgcolor2 navbar-fixed-top navbar-custom navbar-bright navbar-inner" name="kiosk_hide"
1618 data-role="page banner navigation">
1619 <!-- Brand and toggle get grouped for better mobile display -->
1620 <div class="container-fluid">
1621 <div class="navbar-header brand">
1622 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#oer-navbar-collapse-1">
1623 <span class="sr-only"><?php echo xlt("Toggle navigation"); ?></span>
1624 <span class="icon-bar"></span>
1625 <span class="icon-bar"></span>
1626 <span class="icon-bar"></span>
1627 </button>
1628 </div>
1629 <div class="navbar-collapse collapse" id="oer-navbar-collapse-1">
1630 <ul class="navbar-nav">
1631 <?php
1632 if ($GLOBALS['medex_enable'] == '1') {
1634 <li class="dropdown">
1635 <a class="dropdown-toggle" data-toggle="dropdown" id="menu_dropdown_file" role="button" aria-expanded="true"><?php echo xlt("File"); ?> </a>
1636 <ul class="bgcolor2 dropdown-menu" role="menu">
1637 <?php
1638 if ($logged_in) {
1640 <li id="menu_PREFERENCES" name="menu_PREFERENCES" class=""><a onclick="tabYourIt('prefs','main/messages/messages.php?go=Preferences');"><?php echo xlt("Preferences"); ?></a></li>
1641 <li id="icons" name="icons"><a onclick="doRecallclick_edit('icons');"><?php echo xlt('Icon Legend'); ?></a></li>
1642 <?php
1643 } else {
1645 <li id="menu_PREFERENCES" name="menu_PREFERENCES" class="">
1646 <a href="<?php echo $GLOBALS['web_root']; ?>/interface/main/messages/messages.php?go=setup&stage=1"><?php echo xlt("Setup MedEx"); ?></a></li>
1647 <?php
1650 </ul>
1651 </li>
1652 <?php
1656 <li class="dropdown">
1657 <a class="dropdown-toggle" data-toggle="dropdown" id="menu_dropdown_msg" role="button" aria-expanded="true"><?php echo xlt("Messages"); ?> </a>
1658 <ul class="bgcolor2 dropdown-menu" role="menu">
1659 <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>
1661 <li class="divider"><hr /></li>
1663 <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>
1664 <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>
1666 <li class="divider"><hr /></li>
1668 <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>
1669 <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>
1670 <li id="menu_log_msg"> <a onclick="openLogScreen();" > <?php echo xlt("Message Log"); ?></a></li>
1671 </ul>
1672 </li>
1673 <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>
1674 <ul class="bgcolor2 dropdown-menu" role="menu">
1675 <?php
1676 if ($GLOBALS['disable_calendar'] != '1') { ?>
1677 <li><a id="BUTTON_ApRem_menu" onclick="tabYourIt('cal','main/main_info.php');"> <?php echo xlt("Calendar"); ?></a></li>
1678 <li class="divider"><hr /></li>
1679 <?php
1681 if ($GLOBALS['disable_pat_trkr'] != '1') {
1683 <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>
1684 <?php }
1685 if ($logged_in) {
1687 <li class="divider"><hr /></li>
1688 <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>
1689 <?php
1692 </ul>
1693 </li>
1694 <?php
1696 if ($GLOBALS['disable_rcb'] != '1') { ?>
1697 <li class="dropdown">
1698 <a class="dropdown-toggle" data-toggle="dropdown" id="menu_dropdown_recalls" role="button" aria-expanded="true"><?php echo xlt("Patient Recalls"); ?> </a>
1699 <ul class="bgcolor2 dropdown-menu" role="menu">
1700 <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>
1701 <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>
1702 <?php
1703 if ($logged_in) {
1705 <li class="divider"><hr /></li>
1706 <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>
1707 <?php
1710 </ul>
1711 </li>
1712 <?php
1715 if ($logged_in) {
1716 if (!empty($logged_in['products']['ordered'])) {
1717 foreach ($logged_in['products']['ordered'] as $ordered) {
1718 echo $ordered['menu'];
1723 </ul>
1724 </div><!-- /.navbar-collapse -->
1725 </div>
1726 </nav>
1727 </div>
1728 <?php
1729 if ($GLOBALS['medex_enable'] == '1') {
1730 $error=$this->MedEx->getLastError();
1731 if (!empty($error['ip'])) {
1733 <div class="alert alert-danger" style="width:50%;margin:30px auto 5px;font-size:0.9em;text-align:center;">
1734 <?php
1735 echo $error['ip'];
1737 </div>
1738 <?php
1742 public function preferences($prefs = '')
1744 global $logged_in;
1745 if (empty($prefs)) {
1746 $prefs = sqlFetchArray(sqlStatement("SELECT * FROM medex_prefs"));
1749 <div class="row">
1750 <div class="col-sm-12 text-center">
1751 <div class="showRecalls" id="show_recalls">
1752 <div class="title">MedEx <?php echo xlt('Preferences'); ?></div>
1753 <div name="div_response" id="div_response" class="form-inline"><br />
1754 </div>
1755 <form action="#" name="save_prefs" id="save_prefs">
1756 <div class="row">
1757 <input type="hidden" name="go" id="go" value="Preferences">
1758 <div class="col-sm-5 div-center col-sm-offset-1" id="daform2">
1759 <div class="divTable2">
1760 <div class="divTableBody prefs">
1761 <div class="divTableRow">
1762 <div class="divTableCell divTableHeading">MedEx <?php echo xlt('Username'); ?></div>
1763 <div class="divTableCell indent20">
1764 <?php echo $prefs['ME_username']; ?>
1765 </div>
1766 </div>
1767 <div class="divTableRow">
1768 <div class="divTableCell divTableHeading"><?php echo xlt('General'); ?></div>
1769 <div class="divTableCell indent20">
1770 <input type="checkbox" class="update" name="ME_hipaa_default_override" id="ME_hipaa_default_override" value="1"
1771 <?php
1772 if ($prefs['ME_hipaa_default_override']=='1') {
1773 echo 'checked ="checked"';
1775 ?> >
1776 <label for="ME_hipaa_default_override" class="input-helper input-helper--checkbox"
1777 data-toggle='tooltip'
1778 data-placement='auto right'
1779 title='<?php echo xla('Default'); ?>: "<?php echo xla('checked'); ?>".
1780 <?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... ?>'>
1781 <?php echo xlt('Assume patients receive HIPAA policy'); ?>
1782 </label><br />
1783 <input type="checkbox" class="update" name="MSGS_default_yes" id="MSGS_default_yes" value="1" <?php if ($prefs['MSGS_default_yes']=='1') {
1784 echo "checked='checked'";} ?>>
1785 <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\'.'); ?>">
1786 <?php echo xlt('Assume patients permit Messaging'); ?></label>
1787 </div>
1788 </div>
1789 <div class="divTableRow">
1790 <div class="divTableCell divTableHeading"><?php echo xlt('Enable Facility'); ?></div>
1791 <div class="divTableCell indent20">
1792 <?php
1793 $count="1";
1794 $query = "SELECT * FROM facility";
1795 $result = sqlStatement($query);
1796 while ($fac = sqlFetchArray($result)) {
1797 $checked ="";
1798 if ($prefs) {
1799 $facs = explode('|', $prefs['ME_facilities']);
1800 foreach ($facs as $place) {
1801 if ($place == $fac['id']) {
1802 $checked = 'checked ="checked"';
1807 <input <?php echo $checked; ?> class="update" type="checkbox" name="facilities[]" id="facility_<?php echo attr($fac['id']); ?>" value="<?php echo attr($fac['id']); ?>">
1808 <label for="facility_<?php echo attr($fac['id']); ?>"><?php echo text($fac['name']); ?></label><br /><?php
1811 </div>
1812 </div>
1813 <div class="divTableRow">
1814 <div class="divTableCell divTableHeading"><?php echo xlt('Included Providers'); ?></div>
1815 <div class="divTableCell indent20">
1816 <?php
1817 $count="1";
1818 $ures = sqlStatement("SELECT * FROM users WHERE authorized != 0 AND active = 1 ORDER BY lname, fname");
1819 while ($prov = sqlFetchArray($ures)) {
1820 $checked ="";
1821 $suffix="";
1822 if ($prefs) {
1823 $provs = explode('|', $prefs['ME_providers']);
1824 foreach ($provs as $doc) {
1825 if ($doc == $prov['id']) {
1826 $checked = 'checked ="checked"';
1830 if (!empty($prov['suffix'])) {
1831 $suffix = ', '.$prov['suffix'];
1834 <input <?php echo $checked; ?> class="update" type="checkbox" name="providers[]" id="provider_<?php echo attr($prov['id']); ?>" value="<?php echo attr($prov['id']); ?>">
1835 <label for="provider_<?php echo attr($prov['id']); ?>"><?php echo text($prov['fname'])." ".text($prov['lname']).text($suffix); ?></label><br /><?php
1838 </div>
1839 </div>
1840 <div class="divTableRow">
1841 <div class="divTableCell divTableHeading"><?php echo xlt('Labels'); ?></div>
1842 <div class="divTableCell indent20">
1843 <input type="checkbox" class="update" name="LABELS_local" id="LABELS_local" value="1" <?php if ($prefs['LABELS_local']) {
1844 echo "checked='checked'";} ?> />
1845 <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'); ?>'>
1846 <?php echo xlt('Use Avery Labels'); ?></label>
1847 <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">
1848 <option value='1' <?php if ($prefs['LABELS_choice'] == '1') {
1849 echo "selected";} ?>>5160</option>
1850 <option value='2' <?php if ($prefs['LABELS_choice'] == '2') {
1851 echo "selected";} ?>>5161</option>
1852 <option value='3' <?php if ($prefs['LABELS_choice'] == '3') {
1853 echo "selected";} ?>>5162</option>
1854 <option value='4' <?php if ($prefs['LABELS_choice'] == '4') {
1855 echo "selected";} ?>>5163</option>
1856 <option value='5' <?php if ($prefs['LABELS_choice'] == '5') {
1857 echo "selected";} ?>>5164</option>
1858 <option value='6' <?php if ($prefs['LABELS_choice'] == '6') {
1859 echo "selected";} ?>>8600</option>
1860 <option value='7' <?php if ($prefs['LABELS_choice'] == '7') {
1861 echo "selected";} ?>>L7163</option>
1862 <option value='8' <?php if ($prefs['LABELS_choice'] == '8') {
1863 echo "selected";} ?>>3422</option>
1864 </select>
1866 </div>
1867 </div>
1868 <div class="divTableRow">
1869 <div class="divTableCell divTableHeading"><?php echo xlt('Postcards'); ?></div>
1870 <div class="divTableCell indent20">
1871 <!--
1872 <input type="checkbox" class="update" name="POSTCARDS_local" id="POSTCARDS_local" value="1" <?php if ($prefs['POSTCARDS_local']) {
1873 echo "checked='checked'";} ?>" />
1874 <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 />
1875 <input type="checkbox" class="update" name="POSTCARDS_remote" id="POSTCARDS_remote" value="1" <?php if ($prefs['POSTCARDS_remote']) {
1876 echo "checked='checked'";} ?>" />
1877 <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>
1879 <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 />
1880 <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>
1881 </div>
1882 </div>
1883 <?php
1884 /* <!--
1885 These options are for future use...
1887 <div class="divTableRow">
1888 <div class="divTableCell divTableHeading"><?php echo xlt('Combine Reminders'); ?></div>
1889 <div class="divTableCell indent20">
1891 <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.'>
1892 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>
1893 </div>
1894 </div>
1898 <input type="hidden" name="ME_username" id="ME_username" value="<?php echo attr($prefs['ME_username']);?>" />
1899 <input type="hidden" name="ME_api_key" id="ME_api_key" value="<?php echo attr($prefs['ME_api_key']);?>" />
1900 </div>
1901 </div>
1902 </div>
1903 <div class="col-sm-5 div-center" id="daform2">
1904 <div class="divTable2">
1905 <div class="divTableBody prefs">
1906 <?php if (count($logged_in['products']['ordered']) > '0') { ?>
1907 <div class="divTableRow">
1908 <div class="divTableCell divTableHeading"><?php echo xlt('Enabled Services'); ?></div>
1909 <div class="divTableCell">
1910 <ul>
1911 <?php
1912 foreach ($logged_in['products']['ordered'] as $service) {
1913 ?><li><a href="<?php echo $service['view']; ?>" target="_medex"><?php echo $service['model']; ?> </a></li>
1914 <?php
1915 if ($service['product_id'] =='54') {
1917 <div style="margin-left:10px;">Appointment Reminders<br />Patient Recalls<br />SMS Bot<br />Go Green Messages</div>
1918 <?php
1920 } ?>
1921 </ul>
1922 </div>
1923 </div>
1924 <?php }
1925 if (!empty($logged_in['products']['not_ordered'])) {
1927 <div class="divTableRow">
1928 <div class="divTableCell divTableHeading"><?php echo xlt('Available Services'); ?></div>
1929 <div class="divTableCell">
1930 <ul>
1931 <?php
1932 foreach ($logged_in['products']['not_ordered'] as $service) {
1933 ?><li><a href="<?php echo $service['view']; ?>" target="_medex"><?php echo $service['model']; ?> </a></li>
1934 <?php
1935 if ($service['product_id'] =='54') {
1937 <div style="margin-left:10px;">Appointment Reminders<br />Patient Recalls<br />SMS Bot<br />Go Green Messages</div>
1938 <?php
1940 } ?>
1941 </ul>
1942 </div>
1943 </div>
1944 <?php } ?>
1945 </div>
1946 </div>
1947 </div>
1948 <div class="col-sm-1"></div>
1949 </div>
1950 <div style="clear:both;text-align:center;" id="msg bottom"><br />
1951 </div>
1952 </form>
1953 </div>
1954 </div>
1955 </div>
1956 <?php
1958 public function display_recalls($logged_in)
1960 global $MedEx;
1961 global $rcb_selectors;
1962 global $rcb_facility;
1963 global $rcb_provider;
1965 //let's get all the recalls the user requests, or if no dates set use defaults
1966 $from_date = !is_null($_REQUEST['form_from_date']) ? DateToYYYYMMDD($_REQUEST['form_from_date']) : date('Y-m-d', strtotime('-6 months'));
1967 //limit date range for initial Board to keep us sane and not tax the server too much
1969 if (substr($GLOBALS['ptkr_end_date'], 0, 1) == 'Y') {
1970 $ptkr_time = substr($GLOBALS['ptkr_end_date'], 1, 1);
1971 $ptkr_future_time = mktime(0, 0, 0, date('m'), date('d'), date('Y')+$ptkr_time);
1972 } elseif (substr($GLOBALS['ptkr_end_date'], 0, 1) == 'M') {
1973 $ptkr_time = substr($GLOBALS['ptkr_end_date'], 1, 1);
1974 $ptkr_future_time = mktime(0, 0, 0, date('m')+$ptkr_time, date('d'), date('Y'));
1975 } elseif (substr($GLOBALS['ptkr_end_date'], 0, 1) == 'D') {
1976 $ptkr_time = substr($GLOBALS['ptkr_end_date'], 1, 1);
1977 $ptkr_future_time = mktime(0, 0, 0, date('m'), date('d')+$ptkr_time, date('Y'));
1979 $to_date = date('Y-m-d', $ptkr_future_time);
1980 //prevSetting to_date?
1982 $to_date = !is_null($_REQUEST['form_to_date']) ? DateToYYYYMMDD($_REQUEST['form_to_date']) : $to_date;
1984 $recalls = $this->get_recalls($from_date, $to_date);
1986 // if all we don't use MedEx, there is no need to display the progress tabs, all recall processing is manual.
1987 if (!$logged_in) {
1988 $reminder_bar = "nodisplay";
1989 $events='';
1990 } else {
1991 $results = $MedEx->campaign->events($logged_in['token']);
1992 $events = $results['events'];
1993 $reminder_bar = "indent20";
1995 $processed = $this->recall_board_process($logged_in, $recalls, $events);
1996 ob_start();
1999 <div class="container-fluid">
2000 <div class="row-fluid" id="rcb_selectors" style="display:<?php echo attr($rcb_selectors); ?>">
2001 <div class="col-sm-12">
2002 <div class="showRFlow text-center" id="show_recalls_params" style="margin: 20px auto;">
2003 <div class="title"><?php echo xlt('Recall Board'); ?></div>
2004 <div id="div_response"><?php echo xlt('Persons needing a recall, no appt scheduled yet'); ?>.</div>
2005 <?php
2006 if ($GLOBALS['medex_enable'] == '1') {
2007 $col_width="3";
2008 } else {
2009 $col_width="4";
2010 $last_col_width="nodisplay";
2013 <br />
2014 <form name="rcb" id="rcb" method="post">
2015 <input type="hidden" name="go" value="Recalls">
2016 <div class=" text-center row divTable" style="width: 85%;float:unset;margin: 0 auto;">
2018 <div class="col-sm-<?php echo $col_width; ?> text-center" style="margin-top:15px;">
2019 <input placeholder="<?php echo attr('Patient ID'); ?>"
2020 class="form-control input-sm"
2021 type="text" id="form_patient_id"
2022 name="form_patient_id"
2023 value="<?php echo ( $form_patient_id ) ? attr($form_patient_id) : ""; ?>"
2024 onKeyUp="show_this();">
2026 <input type="text"
2027 placeholder="<?php echo attr('Patient Name'); ?>"
2028 class="form-control input-sm" id="form_patient_name"
2029 name="form_patient_name"
2030 value="<?php echo ( $form_patient_name ) ? attr($form_patient_name) : ""; ?>"
2031 onKeyUp="show_this();">
2032 </div>
2034 <div class="col-sm-<?php echo $col_width; ?> text-center" style="margin-top:15px;">
2035 <select class="form-group ui-selectmenu-button ui-button ui-widget ui-selectmenu-button-closed ui-corner-all" id="form_facility" name="form_facility"
2036 <?php
2037 $fac_sql = sqlStatement("SELECT * FROM facility ORDER BY id");
2038 while ($fac = sqlFetchArray($fac_sql)) {
2039 $true = ($fac['id'] == $rcb_facility) ? "selected=true" : '';
2040 $select_facs .= "<option value=".attr($fac['id'])." ".$true.">".text($fac['name'])."</option>\n";
2041 $count_facs++;
2043 if ($count_facs <'1') {
2044 echo "disabled";
2046 ?> onchange="show_this();">
2047 <option value=""><?php echo xlt('All Facilities'); ?></option>
2048 <?php echo $select_facs; ?>
2049 </select>
2050 <?php
2051 # Build a drop-down list of providers.
2052 $query = "SELECT id, lname, fname FROM users WHERE ".
2053 "authorized = 1 AND active = 1 ORDER BY lname, fname"; #(CHEMED) facility filter
2054 $ures = sqlStatement($query);
2055 //a year ago @matrix-amiel Adding filters to flow board and counting of statuses
2056 $count_provs = count(sqlFetchArray($ures));
2058 <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
2059 if ($count_provs <'2') {
2060 echo "disabled";
2062 ?> onchange="show_this();">
2063 <option value="" selected><?php echo xlt('All Providers'); ?></option>
2065 <?php
2066 // Build a drop-down list of ACTIVE providers.
2067 $query = "SELECT id, lname, fname FROM users WHERE ".
2068 "authorized = 1 AND active = 1 ORDER BY lname, fname"; #(CHEMED) facility filter
2070 $ures = sqlStatement($query);
2071 //a year ago @matrix-amiel Adding filters to flow board and counting of statuses
2072 while ($urow = sqlFetchArray($ures)) {
2073 $provid = $urow['id'];
2074 echo " <option value='" . attr($provid) . "'";
2075 if (isset($rcb_provider) && $provid == $_POST['form_provider']) {
2076 echo " selected";
2077 } elseif (!isset($_POST['form_provider'])&& $_SESSION['userauthorized'] && $provid == $_SESSION['authUserID']) {
2078 echo " selected";
2080 echo ">" . text($urow['lname']) . ", " . text($urow['fname']) . "\n";
2083 </select>
2084 </div>
2085 <div class="col-sm-<?php echo $col_width; ?>">
2086 <div style="margin: 0 auto;" class="input-append">
2087 <table class="table-hover table-condensed" style="margin:0 auto;">
2088 <tr><td class="text-right" style="vertical-align:bottom;">
2089 <label for="flow_from"><?php echo xlt('From'); ?>:</label></td><td>
2090 <input id="form_from_date" name="form_from_date"
2091 class="datepicker form-control input-sm text-center"
2092 value="<?php echo attr(oeFormatShortDate($from_date)); ?>"
2093 style="max-width:140px;min-width:85px;">
2095 </td></tr>
2096 <tr><td class="text-right" style="vertical-align:bottom;">
2097 <label for="flow_to">&nbsp;&nbsp;<?php echo xlt('To'); ?>:</label></td><td>
2098 <input id="form_to_date" name="form_to_date"
2099 class="datepicker form-control input-sm text-center"
2100 value="<?php echo attr(oeFormatShortDate($to_date)); ?>"
2101 style="max-width:140px;min-width:85px;">
2102 </td></tr>
2104 <tr>
2105 <td class="text-center" colspan="2">
2106 <button class="btn btn-default btn-filter" style="float:none;" type="submit" id="filter_submit" value="<?php echo xla('Filter'); ?>"><?php echo xlt('Filter'); ?></button>
2107 <button class="btn btn-default btn-add" onclick="goReminderRecall('addRecall');return false;"><span><?php echo xlt('New Recall'); ?></span></>
2108 </td>
2109 </tr>
2110 </table>
2111 </div>
2112 </div>
2113 <div class="col-sm-<?php echo $col_width." ".$last_col_width; ?> text-center" >
2114 <?php
2115 if ($GLOBALS['medex_enable'] == '1') {
2116 if ($logged_in) {
2117 foreach ($results['events'] as $event) {
2118 if ($event['M_group'] != 'RECALL') {
2119 continue;
2121 $icon = $this->get_icon($event['M_type'], 'SCHEDULED');
2122 if ($event['E_timing'] =='1') {
2123 $action = "before";
2125 if ($event['E_timing'] =='2') {
2126 $action = "before (PM)";
2128 if ($event['E_timing'] =='3') {
2129 $action = "after";
2131 if ($event['E_timing'] =='4') {
2132 $action = "after (PM)";
2134 $current_events .= $icon." ".$event['E_fire_time']." ".xlt('days')." ".xlt($action)."<br />";
2138 <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>
2139 <b><u>MedEx <?php echo xlt('Recall Schedule'); ?></u></b><br />
2140 <a href="https://medexbank.com/cart/upload/index.php?route=information/campaigns&amp;g=rec" target="_medex">
2141 <span>
2142 <?php echo $current_events; ?>
2143 </span>
2144 </a>
2145 </div>
2146 </div>
2147 <?php } ?>
2148 </div>
2149 <div name="message" id="message" class="warning"></div>
2150 </div>
2151 </div>
2152 </form>
2153 </div>
2154 </div>
2155 </div>
2157 <div class="row-fluid">
2158 <div class="col-sm-12 text-center">
2159 <div class="showRecalls" id="show_recalls" style="margin:0 auto;">
2160 <div name="message" id="message" class="warning"></div>
2161 <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"
2162 style="color:<?php echo $color = ($setting_selectors=='none') ? 'red' : 'black'; ?>;position:relative;float:right;right:0;top:0;">
2163 <i class="fa fa-square-o fa-stack-2x"></i>
2164 <i id="print_caret" class='fa fa-caret-<?php echo $caret = ($rcb_selectors==='none') ? 'down' : 'up'; ?> fa-stack-1x'></i>
2165 </span>
2166 <ul class="nav nav-tabs <?php echo attr($reminder_bar); ?>">
2167 <li class="active whitish"><a onclick="show_this();" data-toggle="tab"><?php echo xlt('All'); ?></a></li>
2168 <li class="whitish"><a onclick="show_this('whitish');" data-toggle="tab"><?php echo xlt('Events Scheduled'); ?></a></li>
2169 <li class="yellowish"><a onclick="show_this('yellowish');" data-toggle="tab"><?php echo xlt('In-process'); ?></a></li>
2170 <li class="reddish"><a onclick="show_this('reddish');" data-toggle="tab"><?php echo xlt('Manual Processing Required'); ?></a></li>
2171 <li class="greenish"><a onclick="show_this('greenish');" data-toggle="tab"><?php echo xlt('Recently Completed'); ?></a></li>
2172 </ul>
2174 <div class="tab-content">
2176 <div class="tab-pane active" id="tab-all">
2177 <?php
2178 $this->recall_board_top();
2179 echo $processed['ALL'];
2180 $this->recall_board_bot();
2182 </div>
2183 </div>
2184 </div>
2185 </div>
2186 </div>
2187 </div>
2188 <?php
2189 //we need to respect facility and provider requests if submitted.
2190 // 1.Retrieve everything for a given date range.
2191 // 2.Refine results by facility and provider using jquery on cached results
2192 // ie. further requests to view facility/provider within page can be done fast through javascript, no page reload needed.
2194 <script>
2195 function toggleRcbSelectors() {
2196 if ($("#rcb_selectors").css('display') === 'none') {
2197 $.post( "<?php echo $GLOBALS['webroot']."/interface/main/messages/messages.php"; ?>", {
2198 'rcb_selectors' : 'block',
2199 success: function (data) {
2200 $("#rcb_selectors").slideToggle();
2201 $("#rcb_caret").css('color','#000');
2204 } else {
2205 $.post( "<?php echo $GLOBALS['webroot']."/interface/main/messages/messages.php"; ?>", {
2206 'rcb_selectors' : 'none',
2207 success: function (data) {
2208 $("#rcb_selectors").slideToggle();
2209 $("#rcb_caret").css('color','red');
2213 $("#print_caret").toggleClass('fa-caret-up').toggleClass('fa-caret-down');
2217 * @return {boolean}
2219 function SMS_bot(pid) {
2220 top.restoreSession();
2221 pid = pid.replace('recall_','');
2222 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');
2223 return false;
2225 $(document).ready(function() {
2226 show_this();
2228 $('.datepicker').datetimepicker({
2229 <?php $datetimepicker_timepicker = false; ?>
2230 <?php $datetimepicker_showseconds = false; ?>
2231 <?php $datetimepicker_formatInput = true; ?>
2232 <?php require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?>
2233 <?php // can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
2236 </script>
2237 <?php
2238 $content = ob_get_clean();
2239 echo $content;
2241 public function get_recalls($from_date = '', $to_date = '')
2243 // Recalls are requests to schedule a future appointment.
2244 // Thus there is no r_appt_time (NULL) but there is a DATE set.
2246 $query = "SELECT * FROM medex_recalls,patient_data AS pat
2247 WHERE pat.pid=medex_recalls.r_pid AND
2248 r_eventDate >= ? AND
2249 r_eventDate <= ? AND
2250 IFNULL(pat.deceased_date,0) = 0
2251 ORDER BY r_eventDate ASC";
2252 $result = sqlStatement($query, array($from_date,$to_date));
2253 while ($recall= sqlFetchArray($result)) {
2254 $recalls[]=$recall;
2256 return $recalls;
2258 private function recall_board_process($logged_in, $recalls, $events = '')
2260 global $MedEx;
2261 $process = array();
2262 if (empty($recalls)) {
2263 return false;
2265 $fac_sql = sqlStatement("SELECT id, name FROM facility WHERE service_location != 0");
2266 while ($facrow = sqlFetchArray($fac_sql)) {
2267 $facility[$facrow['id']] = $facrow['name'];
2268 $count_facilities++;
2270 $prov_sql = sqlStatement("SELECT * FROM users WHERE authorized != 0 AND active = 1 ORDER BY lname, fname");
2271 while ($prov = sqlFetchArray($prov_sql)) {
2272 $provider[$prov['id']] = $prov['fname'][0]." ".$prov['lname'];
2273 if (!empty($prov['suffix'])) {
2274 $provider[$prov['id']] .= ', '.$prov['suffix'];
2276 $count_providers++;
2278 foreach ($recalls as $recall) {
2279 $show = $this->show_progress_recall($recall, $events);
2280 if (!empty($show['DONE'])) {
2281 continue;
2283 if (empty($show['status'])) {
2284 $show['status'] = 'whitish';
2286 ob_start();
2287 echo '<div class="divTableRow ALL '.attr($show['status']).'"
2288 data-status="'.attr($show['status']).'"
2289 data-plan="'.attr($show['plan']).'"
2290 data-facility="'.attr($recall['r_facility']).'"
2291 data-provider="'.attr($recall['r_provider']).'"
2292 data-pname="'.attr($recall['fname']." ".$recall['lname']).'"
2293 data-pid="'.attr($recall['pid']).'"
2294 id="recall_'.attr($recall['pid']).'" style="display:none;">';
2296 $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";
2297 $result2 = sqlQuery($query, array( $recall['pid'] ));
2298 $last_visit = $result2['pc_eventDate'];
2299 $DOB = oeFormatShortDate($result2['DOB']);
2300 $age = $MedEx->events->getAge($result2['DOB']);
2301 echo '<div class="divTableCell text-center"><a href="#" onclick="show_patient(\''.attr($recall['pid']).'\');"> '.text($recall['fname']).' '.text($recall['lname']).'</a>';
2302 if ($GLOBALS['ptkr_show_pid']) {
2303 echo '<br /><span data-toggle="tooltip" data-placement="auto" title="'.xla("Patient ID").'" class="small">'. xlt('PID').': '.text($recall['pid']).'</span>';
2305 echo '<br /><span data-toggle="tooltip" data-placement="auto" title="'.xla("Most recent visit").'" class="small">' . xlt("Last Visit") . ': '.text(oeFormatShortDate($last_visit)).'</span>';
2306 echo '<br /><span class="small" data-toggle="tooltip" data-placement="auto" title="'.xla("Date of Birth and Age").'">'. xlt('DOB').': '.text($DOB).' ('.$age.')</span>';
2307 echo '</div>';
2309 echo '<div class="divTableCell appt_date">'.text(oeFormatShortDate($recall['r_eventDate']));
2310 if ($recall['r_reason']>'') {
2311 echo '<br />'.text($recall['r_reason']);
2313 if (strlen($provider[$recall['r_provider']]) > 14) {
2314 $provider[$recall['r_provider']] = substr($provider[$recall['r_provider']], 0, 14)."...";
2316 if (strlen($facility[$recall['r_facility']]) > 20) {
2317 $facility[$recall['r_facility']] = substr($facility[$recall['r_facility']], 0, 17)."...";
2320 if ($count_providers > '1') {
2321 echo "<br /><span data-toggle='tooltip' data-placement='auto' title='".xla('Provider')."'>".text($provider[$recall['r_provider']])."</span>";
2323 if (( $count_facilities > '1' ) && ( $_REQUEST['form_facility'] =='' )) {
2324 echo "<br /><span data-toggle='tooltip' data-placement='auto' title='".xla('Facility')."'>".text($facility[$recall['r_facility']])."</span><br />";
2327 echo '</div>';
2328 echo '<div class="divTableCell phones">';
2329 if ($recall['phone_cell'] >'') {
2330 echo 'C: '.text($recall['phone_cell'])."<br />";
2331 // echo 'C:'.substr($recall['phone_cell'], 0, 2).'-XXX-XXXX<br />';
2333 if ($recall['phone_home'] >'') {
2334 echo 'H: '.text($recall['phone_home'])."<br />";
2335 //echo 'H:'.substr($recall['phone_home'], 0, 2).'-XXX-XXXX<br />';
2337 if ($recall['email'] >'') {
2338 $mailto = $recall['email'];
2339 if (strlen($recall['email']) > 15) {
2340 $recall['email'] = substr($recall['email'], 0, 12)."...";
2342 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 />';
2344 if ($logged_in) {
2345 $pat = $this->possibleModalities($recall);
2346 echo $pat['SMS'].$pat['AVM'].$pat['EMAIL'];//escape/translation done in possibleModalities.
2348 echo '</div>';
2350 if ($show['postcard'] > '') {
2351 echo '<div class="divTableCell text-center postcards">'.text($show['postcard']).'</div>';
2352 } else {
2353 echo '<div class="divTableCell text-center postcards"><input type="checkbox" name="postcards" id="postcards[]" value="'.attr($recall['pid']).'"></div>';
2356 if ($show['label'] > '') {
2357 echo '<div class="divTableCell text-center labels">'.text($show['label']).'</div>';
2358 } else {
2359 echo '<div class="divTableCell text-center labels"><input type="checkbox" name="labels" id="labels[]" value="'.attr($recall['pid']).'"></div>';
2361 echo ' <div class="divTableCell text-center msg_manual"><span class="fa fa-fw spaced_icon" >
2362 <input type="checkbox" name="msg_phone" id="msg_phone_'.attr($recall['pid']).'" onclick="process_this(\'phone\',\''.attr($recall['pid']).'\',\''.attr($recall['r_ID']).'\')" />
2363 </span>';
2364 echo ' <span data-toggle="tooltip" data-placement="auto" title="'.xla('Scheduling').'" class="fa fa-calendar-check-o fa-fw" onclick="newEvt(\''.attr($recall['pid']).'\',\'\');">
2365 </span>';
2366 echo '</div>';
2368 echo ' <div class="divTableCell text-left msg_resp">';
2369 // if phone call made show each in progress
2370 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>';
2371 echo '</div>';
2372 echo ' <div class="divTableCell text-left msg_resp">
2373 <i class="top_right_corner fa fa-times" onclick="delete_Recall(\''.attr($recall['pid']).'\',\''.attr($recall['r_ID']).'\')"></i> ';
2374 echo $show['progression'];
2376 if ($show['appt']) {
2377 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 />";
2379 echo '</div>';
2380 echo '</div>';
2381 $content = ob_get_clean();
2382 $process['ALL'] .= $content;
2384 return $process;
2388 * This function looks at a single recall and assesses its status.
2389 * Has it been worked on yet? Any phone calls made, labels printed or postcards printed?
2390 * If they are a MedEx subscriber, do they have any scheduled Recall Campaign Events and if so when?
2391 * Have any of these MedEx events happened? Given all the variables, what is the status of this recall at this moment?
2392 * We also use color coding in the Recall Board -
2393 * -- whitish for pending, nothing happed yet.
2394 * -- yellowish for in process, something happened but no appointment was made yet!
2395 * -- reddish for manual processing needed.
2396 * -- greenish for completed and an appointment was made.
2397 * In the ideal workflow, the secretary would be going through the Recall Board line by line once per day/week/month (per practice needs).
2398 * They can then just delete the Recall by clicking the X on the right. Done move on to the next.
2399 * However a patient may have called in randomly to make this appointment - the secretary may be unaware that the Recall even exists.
2400 * 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...
2401 * Let the Recall Board look to see if there is an appointment for this patient in the future.
2402 * If there is, and it was made more than 16 hours ago, it deletes the Recall altogether. It is never displayed again.
2403 * 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,
2404 * and display in the "progress" column what has transpired.
2405 * Thanks, move on, nothing to see here.
2406 * This also allows anyone (management?) a tool to see what Recall work was performed over the last 16 hours.
2407 * @param $recall
2408 * @param string $events
2409 * @return mixed
2410 * @internal param string $possibleModalities
2412 public function show_progress_recall($recall, $events = '')
2414 global $logged_in;
2415 //Two scenarios: First, appt is made as recall asks. Second, appt is made not for recall reason - recall still needed.
2416 //We can either require all recalls to be manually deleted or do some automatically... If manual only,
2417 //the secretary looking at the board will need to know when they were last seen at least and when next appt is
2418 //to know if they can/should delete the recall. If semi-automatic, we'll use an artificial time horizon of 3 months.
2419 //If an appt is made through any means, and it is within 3 months of the recall date, assume it wipes out the recall.
2420 //If the appt was just made today, let the board show it as "green", ie. completed. Gives us a sense of accomplishment,
2421 //that we got some work done today...
2422 //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.
2423 //ie. appts added in for problem visits won't auto-delete an official recall unless they are close in time to the recall...
2424 //Adjust according to your needs and work flows. This function is run by the Recall Board and with cron MedEx calls.
2425 $show['EMAIL']['text']='';
2426 $show['SMS']['text']='';
2427 $show['AVM']['text']='';
2428 $show['progression']='';
2429 $show['DONE']='';
2430 $query = "SELECT * FROM openemr_postcalendar_events WHERE
2431 pc_eventDate >= CURDATE() AND pc_pid =? AND pc_eventDate > (? - INTERVAL 90 DAY) AND pc_time > (CURDATE()- INTERVAL 16 HOUR)";
2432 $count = sqlFetchArray(sqlStatement($query, array($recall['r_pid'],$recall['r_eventDate'])));
2434 if ($count) {
2435 $sqlDELETE = "DELETE FROM medex_outgoing WHERE msg_pc_eid = ?";
2436 sqlStatement($sqlDELETE, array('recall_'.$recall['pid']));
2437 $sqlDELETE = "DELETE FROM medex_recalls WHERE r_pid = ?";
2438 sqlStatement($sqlDELETE, array($recall['pid']));
2439 //log this action "Recall for $pid deleted now()"?
2440 $show['DONE'] ='1';//tells recall board to move on.
2441 $show['status'] ='greenish'; //tells MedEx to move on, don't process this recall - delete it from their servers.
2442 return $show;
2443 // Just cleaning up the Recall Board for you. Move along, nothing to see.
2444 // If you need to look at the track of action, look in the log?
2447 // Did anything happen yet?
2448 // Table medex_outgoing is our log for current activities.
2449 // It includes records of local manual things your office did and the MedEx reports.
2450 // For non-MedEx subscribers, the local functionality will still work just fine...
2451 // Unless the RECALL is completed (appt made) more than 16 hours ago, the RECALL's data will be present.
2453 // We need to output the correct text and icon to visually display the appt status
2455 $sql ="SELECT * FROM medex_outgoing WHERE msg_pc_eid = ? ORDER BY msg_date ASC";
2456 $result = sqlStatement($sql, array('recall_'.$recall['pid']));
2457 $something_happened='';
2459 while ($progress = sqlFetchArray($result)) {
2460 $i = $progress['campaign_uid'];//if this is a manual entry, this ==0.
2462 $phpdate = strtotime($progress['msg_date']);
2463 $when = oeFormatShortDate(date('Y-m-d', $phpdate))." @ ".date('g:iA', $phpdate);
2465 if (is_numeric($progress['msg_reply'])) { // it was manually added by id
2466 $sql2 = "SELECT * FROM users WHERE id =?";
2468 $who = sqlQuery($sql2, array($progress['msg_reply']));
2469 $who_name = $who['fname']." ".$who['lname'];
2470 //Manually generated actions
2471 if ($progress['msg_type'] == 'phone') { //ie. a manual phone call, not an AVM
2472 $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";
2473 } elseif ($progress['msg_type'] == 'notes') {
2474 $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";
2475 } elseif ($progress['msg_type'] == 'postcards') {
2476 $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";
2477 } elseif ($progress['msg_type'] == 'labels') {
2478 $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 />";
2480 } else {
2481 $who_name = "MedEx";
2482 // MedEx related actions
2483 // Recalls can't be confirmed through MedEx - we don't make appointments (yet)...
2484 // They disappear 16 hours after an appt is made (they glow green for those 16 hours).
2485 if (($progress['msg_reply'] == "READ")||($show[$progress['msg_type']]['stage']=="READ")) {
2486 $show[$progress['msg_type']]['stage'] = "READ";
2487 $icon = $this->get_icon($progress['msg_type'], "READ");
2488 $show[$progress['msg_type']]['text'] = "<span class='left'>".$icon." ".text($when)."</span><br />";
2489 if ($progress['msg_type'] == 'AVM') {
2490 $show['campaign'][$i]['status']="reddish";
2492 } elseif (($progress['msg_reply'] == "SENT")||($show[$progress['msg_type']]['stage']=="SENT")) {
2493 if ($show[$progress['msg_type']]['stage']!="READ") {
2494 $show[$progress['msg_type']]['stage'] = "SENT";
2495 $icon = $this->get_icon($progress['msg_type'], "SENT");
2496 $show[$progress['msg_type']]['text'] = "<span class='left'>".$icon." ".text($when)."</span><br />";
2498 } elseif (($progress['msg_reply'] == "To Send")||($show[$progress['msg_type']]['stage']=="QUEUED")) {
2499 if (($show[$progress['msg_type']]['stage']!="READ")&&($show[$progress['msg_type']]['stage']!="SENT")) {
2500 $show[$progress['msg_type']]['stage'] = "QUEUED";
2501 $icon = $this->get_icon($progress['msg_type'], $progress['msg_reply']);
2504 if ($progress['msg_reply'] == "CALL") {
2505 $icon = $this->get_icon($progress['msg_type'], "CALL");
2506 $show['progression'] .= "<span class='left'>".$icon." ".text($progress['msg_type'])."@".text($when)."</span><br />";
2507 } elseif ($progress['msg_reply'] == "STOP") {
2508 $icon = $this->get_icon($progress['msg_type'], "STOP");
2509 $show['progression'] .= "<span class='left'>".$icon." ".text($when)."</span><br />";
2510 } elseif ($progress['msg_reply'] == "EXTRA") {
2511 $icon = $this->get_icon($progress['msg_type'], "EXTRA");
2512 $show['progression'] .= "<span class='left'>".$icon." ".text($when)."</span><br />";
2513 } elseif ($progress['msg_reply'] == "FAILED") {
2514 $icon = $this->get_icon($progress['msg_type'], "FAILED");
2515 $show['progression'] .= "<span class='left'>".$icon." ".text($when)."</span><br />";
2516 $show['campaign'][$i]['status']=1;
2518 $show['campaign'][$i]['icon'] = $icon;
2521 $something_happened=true;
2523 $show['progression'] .= $show['EMAIL']['text'].$show['SMS']['text'].$show['AVM']['text'];
2526 // Let's look at the MedEx events:
2527 // Show the DATE when a Campaign event will be run for a given patient
2529 * E_fire_tire = number of days before/after recall date that a MedEx campaign event will run
2530 * MedEx E_timing options:
2531 * 1 = days before
2532 * 2 = days before PM
2533 * 3 = days after
2534 * 4 = days after PM
2536 $camps='0';
2537 /** @var TYPE_NAME $events */
2538 foreach ($events as $event) {
2539 if ($event['M_group'] != "RECALL") {
2540 continue;
2542 $pat = $this->possibleModalities($recall);
2543 if ($pat['ALLOWED'][$event['M_type']] == 'NO') {
2544 continue; //it can't happen
2546 if ($pat['facility']['status']!= 'ok') {
2547 continue; //it can't happen
2549 if ($pat['provider']['status']!= 'ok') {
2550 continue; //it can't happen
2553 if ($show['campaign'][$event['C_UID']]['status']) {
2554 continue; //it is done
2556 $camps++; //there is still work to be done
2557 if ($show['campaign'][$event['C_UID']]['icon']) {
2558 continue; //but something has happened since it was scheduled.
2561 ($event['E_timing'] < '3') ? ($interval ='-') : ($interval ='+');//this is only scheduled, 3 and 4 are for past appointments...
2562 $show['campaign'][$event['C_UID']] = $event;
2563 $show['campaign'][$event['C_UID']]['icon'] = $this->get_icon($event['M_type'], "SCHEDULED");
2565 $recall_date = date("Y-m-d", strtotime($interval.$event['E_fire_time']." days", strtotime($recall['r_eventDate'])));
2566 $date1 = date('Y-m-d');
2567 $date_diff=strtotime($date1) - strtotime($recall['r_eventDate']);
2568 if ($date_diff >= '-1') { //if it is sched for tomorrow or earlier, queue it up
2569 $show['campaign'][$event['C_UID']]['executed'] = "QUEUED";
2570 $show['status'] = "whitish";
2571 } else {
2572 $execute = oeFormatShortDate($recall_date);
2573 $show['campaign'][$event['C_UID']]['executed'] = $execute;
2575 $show['progression'] .= "<a href='https://medexbank.com/cart/upload/index.php?route=information/campaigns' class='nowrap text-left' target='_MedEx'>".
2576 $show['campaign'][$event['C_UID']]['icon']." ".text($show['campaign'][$event['C_UID']]['executed'])."</a><br />";
2581 // Show recall row status via background color.
2582 // If an appt was made < 16hrs ago, make it green(completed) and $show['DONE'] = 1
2583 // o/w yellow(in progress) if something happened or Campaign fired
2584 // o/w red (manual needed) if no more MedEx Recall Events are scheduled to be done and no appt was made yet.
2585 // ie. we struck out and need to process this manually
2586 // or write it off or delete it or do soemthing else? Have to know what to do to write that. ;)
2587 $query = "SELECT * FROM openemr_postcalendar_events WHERE pc_eventDate > CURDATE() AND pc_pid =? AND pc_time > CURDATE()- INTERVAL 16 HOUR";
2588 $result = sqlFetchArray(sqlStatement($query, array($recall['pid'])));
2590 if ($something_happened||$result) {
2591 if ($result) {
2592 $show['status'] = "greenish"; //appt made, move on
2593 $phpdate = strtotime($result['pc_eventDate']." ".$result['pc_startTime']);
2594 $show['pc_eid'] = $result['pc_eid'];
2595 $show['appt'] = oeFormatShortDate(date('Y-m-d', $phpdate))." @ ".date('g:iA', $phpdate);
2596 $show['DONE'] = '1';
2597 } elseif ($GLOBALS['medex_enable'] == '1') {
2598 if ($logged_in) {
2599 if ($camps =='0') {
2600 $show['status'] = "reddish"; //hey, nothing automatic left to do - manual processing required.
2601 } else {
2602 $show['status'] = "yellowish"; //no appt yet but something happened!
2605 } else {
2606 $show['status'] = "whitish";
2608 } elseif (($GLOBALS['medex_enable'] == '1') && ($camps =='0')) {
2609 $show['status'] = "reddish"; //hey, nothing automatic left to do - manual processing required.
2610 } else {
2611 $show['status'] = "whitish";
2613 if ($logged_in) {
2614 $show['progression'] = '<div onclick="SMS_bot(\'recall_'.$recall['pid'].'\');">'.$show['progression'].'</div>';
2616 return $show;
2618 private function get_icon($event_type, $status = 'SCHEDULED')
2620 $sqlQuery = "SELECT * FROM medex_icons";
2621 $result = sqlStatement($sqlQuery);
2622 while ($icons = sqlFetchArray($result)) {
2623 if (($icons['msg_type'] == $event_type)&&($icons['msg_status'] == $status)) {
2624 return $icons['i_html'];
2627 return false;
2629 public function possibleModalities($appt)
2631 $pat = array();
2632 $sqlQuery = "SELECT * FROM medex_icons";
2633 $result = sqlStatement($sqlQuery);
2634 //the text fields in DB are set by user and sql sscripts. Is translation/escaping needed here? How?
2635 while ($icons = sqlFetchArray($result)) {
2636 $icon[$icons['msg_type']][$icons['msg_status']] = $icons['i_html'];
2638 //if the patient is dead, should we really be sending them a message?
2639 //Maybe we would need to customize this for a pathologist but for the rest, the answer is no...
2640 if (empty($appt['phone_cell']) || ($appt["hipaa_allowsms"]=="NO")) {
2641 $pat['SMS'] = $icon['SMS']['NotAllowed'];
2642 $pat['ALLOWED']['SMS'] = 'NO';
2643 } else {
2644 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
2645 $pat['SMS'] = $icon['SMS']['ALLOWED']; // It is allowed and they have a cell phone
2647 if ((empty($appt["phone_home"]) && (empty($appt["phone_cell"])) || ($appt["hipaa_voice"]=="NO"))) {
2648 $pat['AVM'] = $icon['AVM']['NotAllowed'];
2649 $pat['ALLOWED']['AVM'] = 'NO';
2650 } else {
2651 if (!empty($appt["phone_cell"])) {
2652 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
2653 } else {
2654 $phone = preg_replace("/[^0-9]/", "", $appt["phone_home"]);
2656 $pat['AVM'] = $icon['AVM']['ALLOWED']; //We have a phone to call and permission!
2658 if (($appt["email"]=="")||($appt["hipaa_allowemail"]=="NO")) {
2659 $pat['EMAIL'] = $icon['EMAIL']['NotAllowed'];
2660 $pat['ALLOWED']['EMAIL'] = 'NO';
2661 } else {
2662 $pat['EMAIL'] = $icon['EMAIL']['ALLOWED'];
2664 // If the practice is a MedEx practice, is this facility and/or this provider signed up for MedEx?
2665 // In this scenario, not all providers or locations for this practice are enrolled in MedEx.
2666 // Don't report that something MedEx-related is going to happen for these folks, cause it shouldn't.
2667 $sql = "SELECT * FROM medex_prefs";
2668 $prefs = sqlFetchArray(sqlStatement($sql));
2669 $facs = explode('|', $prefs['ME_facilities']);
2670 foreach ($facs as $place) {
2671 if (isset($appt['r_facility']) && ($appt['r_facility']==$place)) {
2672 $pat['facility']['status'] = 'ok';
2675 $providers = explode('|', $prefs['ME_providers']);
2676 foreach ($providers as $provider) {
2677 if (isset($appt['r_provider']) && ($appt['r_provider']==$provider)) {
2678 $pat['provider']['status'] = 'ok';
2681 return $pat;
2683 private function recall_board_top()
2686 <div class="divTable text-center" id="rcb_table" style="margin:0 auto 30px;width:100%;">
2687 <div class="sticky divTableRow divTableHeading">
2688 <div class="divTableCell text-center" style="width:10%;"><?php echo xlt('Name'); ?></div>
2689 <div class="divTableCell text-center" style="width:10%;"><?php echo xlt('Recall'); ?></div>
2691 <div class="divTableCell text-center phones" style="width:10%;"><?php echo xlt('Contacts'); ?></div>
2692 <div class="divTableCell text-center msg_resp"><?php echo xlt('Postcards'); ?><br />
2693 <span onclick="top.restoreSession();checkAll('postcards',true);" class="fa fa-square-o fa-lg" id="chk_postcards"></span>
2694 &nbsp;&nbsp;
2695 <span onclick="process_this('postcards');" class="fa fa-print fa-lg"></span>
2696 </div>
2697 <div class="divTableCell text-center msg_resp"><?php echo xlt('Labels'); ?><br />
2698 <span onclick="checkAll('labels',true);" class="fa fa-square-o fa-lg" id="chk_labels"></span>
2699 &nbsp;&nbsp;
2700 <span onclick="process_this('labels');" class="fa fa-print fa-lg"></span>
2701 </div>
2702 <div class="divTableCell text-center msg_resp"><?php echo xlt('Office').": ".xlt('Phone'); ?></div>
2703 <div class="divTableCell text-center msg_notes"><?php echo xlt('Notes'); ?></div>
2704 <div class="divTableCell text-center"><?php echo xlt('Progress'); ?>
2705 </div>
2707 </div>
2708 <div class="divTableBody">
2709 <?php
2711 private function recall_board_bot()
2713 ?> </div>
2714 </div>
2715 </div>
2716 <?php
2718 public function display_add_recall($pid = 'new')
2720 global $result_pat;
2722 <div class="container-fluid">
2723 <div class="row-fluid showReminders clear text-center">
2724 <div id="add_recall" class="col-sm-12">
2725 <div class="title"><?php echo xlt('New Recall'); ?></div>
2726 <div name="div_response" id="div_response"><?php echo xlt('Create a reminder to schedule a future visit'); ?> .</div>
2727 </div>
2728 </div>
2729 <div class="row-fluid divTable float_center">
2730 <form name="addRecall" id="addRecall" class="form-inline" >
2731 <input type="hidden" name="go" id="go" value="addRecall">
2732 <input type="hidden" name="action" id="go" value="addRecall">
2733 <div class="col-sm-6 text-right form-group form-group-sm">
2734 <div class="divTableBody pull-right">
2735 <div class="divTableRow">
2736 <div class="divTableCell divTableHeading"><?php echo xlt('Name'); ?></div>
2737 <div class="divTableCell recall_name">
2738 <input type="text" name="new_recall_name" id="new_recall_name" class="form-control"
2739 onclick="recall_name_click(this)"
2740 value="<?php echo attr($result_pat['fname'])." ".attr($result_pat['lname']); ?>" style="width:225px;">
2741 <input type="hidden" name="new_pid" id="new_pid" value="<?php echo attr($result_pat['id']); ?>">
2742 </div>
2743 </div>
2744 <div class="divTableRow">
2745 <div class="divTableCell divTableHeading"><?php echo xlt('Recall When'); ?></div>
2746 <div class="divTableCell indent20">
2747 <span class="bold"><?php echo xlt('Last Visit'); ?>: </span><input type="text" value="" name="DOLV" id="DOLV" class="form-control">
2748 <br />
2749 <!-- Feel free to add in any dates you would like to show here...
2750 <input type="radio" name="new_recall_when" id="new_recall_when_6mos" value="180">
2751 <label for="new_recall_when_6mos" class="input-helper input-helper--checkbox">+ 6 <?php echo xlt('months'); ?></label><br />
2753 <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">
2754 <?php echo xlt('plus 1 year'); ?></label><br />
2755 <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">
2756 <?php echo xlt('plus 2 years'); ?></label><br />
2757 <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">
2758 <?php echo xlt('plus 3 years'); ?></label><br />
2759 <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="">
2760 </div>
2761 </div>
2762 <div class="divTableRow">
2763 <div class="divTableCell divTableHeading"><?php echo xlt('Recall Reason'); ?></div>
2764 <div class="divTableCell">
2765 <input class="form-control" type="text" style="width:225px;" name="new_reason" id="new_reason" value="<?php
2766 if ($result_pat['PLAN'] > '') {
2767 echo attr(rtrim("|", trim($result_pat['PLAN']))); } ?>">
2768 </div>
2769 </div>
2770 <div class="divTableRow">
2771 <div class="divTableCell divTableHeading"><?php echo xlt('Provider'); ?></div>
2772 <div class="divTableCell">
2773 <?php
2774 $ures = sqlStatement("SELECT id, username, fname, lname FROM users WHERE authorized != 0 AND active = 1 ORDER BY lname, fname");
2775 //This is an internal practice function so ignore the suffix as extraneous information. We know who we are.
2776 $defaultProvider = $_SESSION['authUserID'];
2777 // or, if we have chosen a provider in the calendar, default to them
2778 // choose the first one if multiple have been selected
2779 if (count($_SESSION['pc_username']) >= 1) {
2780 // get the numeric ID of the first provider in the array
2781 $pc_username = $_SESSION['pc_username'];
2782 $firstProvider = sqlFetchArray(sqlStatement("SELECT id FROM users WHERE username=?", array($pc_username[0])));
2783 $defaultProvider = $firstProvider['id'];
2785 // if we clicked on a provider's schedule to add the event, use THAT.
2786 if ($userid) {
2787 $defaultProvider = $userid;
2790 echo "<select class='form-control' name='new_provider' id='new_provider' style='width:95%;'>";
2791 while ($urow = sqlFetchArray($ures)) {
2792 echo " <option value='" . attr($urow['id']) . "'";
2793 if ($urow['id'] == $defaultProvider) {
2794 echo " selected";
2796 echo ">" . text($urow['lname']);
2797 if ($urow['fname']) {
2798 echo ", " . text($urow['fname']);
2800 echo "</option>\n";
2802 echo "</select>";
2804 </div>
2805 </div>
2806 <div class="divTableRow">
2807 <div class="divTableCell divTableHeading"><?php echo xlt('Facility'); ?></div>
2808 <div class="divTableCell">
2809 <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%;">
2810 <?php
2811 $qsql = sqlStatement("SELECT id, name, primary_business_entity FROM facility WHERE service_location != 0");
2812 while ($facrow = sqlFetchArray($qsql)) {
2813 if ($facrow['primary_business_entity'] == '1') {
2814 $selected = 'selected="selected"';
2815 echo "<option value='" . attr($facrow['id']) . "' $selected>" . text($facrow['name']) . "</option>";
2816 } else {
2817 $selected = '';
2818 echo "<option value='" . attr($facrow['id']) . "' $selected>" . text($facrow['name']) . "</option>";
2822 </select>
2823 </div>
2824 </div>
2826 </div>
2827 </div>
2829 <div class="col-sm-6 text-center form-group form-group-sm">
2830 <div class="divTableBody">
2831 <div class="divTableRow news">
2832 <div class="divTableCell divTableHeading"><?php echo xlt('DOB'); ?></div>
2833 <div class="divTableCell">&nbsp;&nbsp;
2834 <?php
2835 $DOB = oeFormatShortDate($result_pat['DOB']);
2837 <span name="new_DOB" id="new_DOB" style="width:90px;"><?php echo text($DOB); ?></span> -
2838 <span id="new_age" name="new_age"><?php echo text($result_pat['age']); ?></span></div>
2839 </div>
2840 <div class="divTableRow news">
2841 <div class="divTableCell divTableHeading"><?php echo xlt('Address'); ?></div>
2842 <div class="divTableCell">
2843 <input type="text" class="form-control" name="new_address" id="new_address" style="width:240px;" value="<?php echo attr($result_pat['street']); ?>"><br />
2844 <input type="text" class="form-control" name="new_city" id="new_city" style="width:100px;" value="<?php echo attr($result_pat['city']); ?>">
2845 <input type="text" class="form-control" name="new_state" id="new_state" style="width:40px;" value="<?php echo attr($result_pat['state']); ?>">
2846 <input type="text" class="form-control" name="new_postal_code" id="new_postal_code" style="width:65px;" value="<?php echo attr($result_pat['postal_code']); ?>"></div>
2847 </div>
2848 <div class="divTableRow news">
2849 <div class="divTableCell divTableHeading phone_home"><?php echo xlt('Home Phone'); ?></div>
2850 <div class="divTableCell"><input type="text" name="new_phone_home" id="new_phone_home" class="form-control" value="<?php echo attr($result_pat['phone_home']); ?>"></div>
2851 </div>
2852 <div class="divTableRow news">
2853 <div class="divTableCell divTableHeading phone_cell"><?php echo xlt('Mobile Phone'); ?></div>
2854 <div class="divTableCell"><input type="text" name="new_phone_cell" id="new_phone_cell" class="form-control" value="<?php echo attr($result_pat['phone_cell']); ?>"></div>
2855 </div>
2856 <div class="divTableRow news">
2857 <div class="divTableCell divTableHeading msg_sent" data-placement="auto" title="<?php echo xla('Text Message permission'); ?>"><?php echo xlt('SMS OK'); ?></div>
2859 <div class="divTableCell indent20">
2860 <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>
2861 &nbsp;&nbsp;
2862 <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>
2863 </div>
2864 </div>
2865 <div class="divTableRow indent20">
2866 <div class="divTableCell divTableHeading msg_how" data-placement="auto" title="<?php echo xla('Automated Voice Message permission'); ?>"><?php echo xlt('AVM OK'); ?></div>
2867 <div class="divTableCell indent20">
2868 <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>
2869 &nbsp;&nbsp;
2870 <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>
2871 </div>
2872 </div>
2873 <div class="divTableRow news">
2874 <div class="divTableCell divTableHeading phone_cell"><?php echo xlt('E-Mail'); ?></div>
2875 <div class="divTableCell"><input type="email" name="new_email" id="new_email" class="form-control" style="width:225px;" value="<?php echo attr($result_pat['email']); ?>"></div>
2876 </div>
2878 <div class="divTableRow news">
2879 <div class="divTableCell divTableHeading msg_when"><?php echo xlt('E-mail OK'); ?></div>
2880 <div class="divTableCell indent20">
2881 <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>
2882 &nbsp;&nbsp;
2883 <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>
2884 </div>
2885 </div>
2886 </div>
2887 </div>
2888 </form>
2889 </div>
2890 <div class="row-fluid text-center">
2891 <button class="btn btn-default btn-add" style="float:none;" onclick="add_this_recall();" value="<?php echo xla('Add Recall'); ?>" id="add_new" name="add_new"><?php echo xlt('Add Recall'); ?></button>
2893 <em class="small text-muted">* <?php echo xlt('N.B.{{Nota bene}}')." ".xlt('Demographic changes made here are recorded system-wide'); ?>.</em>
2894 </p>
2895 </div>
2897 </div>
2898 <script>
2899 $(document).ready(function () {
2900 $('.datepicker').datetimepicker({
2901 <?php $datetimepicker_timepicker = false; ?>
2902 <?php $datetimepicker_showseconds = false; ?>
2903 <?php $datetimepicker_formatInput = true; ?>
2904 <?php require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?>
2905 <?php // can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
2909 <?php
2910 if ($_SESSION['pid']>'') {
2912 setpatient('<?php echo text($_SESSION['pid']); ?>');
2913 <?php
2916 var xljs_NOTE = '<?php echo xl("NOTE"); ?>';
2917 var xljs_PthsApSched = '<?php echo xl("This patient already has an appointment scheduled for"); ?>';
2919 </script>
2920 <?php
2922 public function icon_template()
2925 <!-- icon rubric -->
2926 <div style="position:relative;margin:30px auto;vertical-align:middle;">
2927 <?php
2928 $sqlQuery = "SELECT * FROM medex_icons ORDER BY msg_type";
2929 $result = sqlStatement($sqlQuery);
2930 $icons = array();
2931 while ($urow = sqlFetchArray($result)) {
2932 $icons['msg_type']['description'] = $urow['i_description'];
2933 $icons[$urow['msg_type']][$urow['msg_status']] = $urow['i_html'];
2934 } ?>
2935 <div class="divTable" style="margin:30px auto;width:100%;">
2936 <div class="divTableBody">
2937 <div class="divTableRow divTableHeading">
2938 <div class="divTableCell text-center"><?php echo xlt('Message'); ?></div>
2939 <div class="divTableCell text-center"><?php echo xlt('Possible'); ?></div>
2940 <div class="divTableCell text-center"><?php echo xlt('Not Possible'); ?></div>
2941 <div class="divTableCell text-center"><?php echo xlt('Scheduled'); ?></div>
2942 <div class="divTableCell text-center"><?php echo xlt('Sent')."<br />".xlt('In-process'); ?></div>
2943 <div class="divTableCell text-center"><?php echo xlt('Read')."<br />".xlt('Delivered');
2944 ; ?></div>
2945 <div class="divTableCell text-center"><?php echo xlt('Confirmed'); ?></div>
2946 <div class="divTableCell text-center"><?php echo xlt('Callback'); ?></div>
2947 <div class="divTableCell text-center"><?php echo xlt('Failure'); ?></div>
2948 <div class="divTableCell text-center"><?php echo xlt('Replies'); ?></div>
2949 <div class="divTableCell text-center"><?php echo xlt('STOP'); ?></div>
2950 </div>
2951 <div class="divTableRow">
2952 <div class="divTableCell text-center"><?php echo xlt('EMAIL'); ?></div>
2953 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['ALLOWED']; ?></div>
2954 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['NotAllowed']; ?></div>
2955 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['SCHEDULED']; ?></div>
2956 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['SENT']; ?></div>
2957 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['READ']; ?></div>
2958 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['CONFIRMED']; ?></div>
2959 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['CALL']; ?></div>
2960 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['FAILED']; ?></div>
2961 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['EXTRA']; ?></div>
2962 <div class="divTableCell text-center"><?php echo $icons['EMAIL']['STOP']; ?></div>
2963 </div>
2964 <div class="divTableRow">
2965 <div class="divTableCell text-center"><?php echo xlt('SMS'); ?></div>
2966 <div class="divTableCell text-center"><?php echo $icons['SMS']['ALLOWED']; ?></div>
2967 <div class="divTableCell text-center"><?php echo $icons['SMS']['NotAllowed']; ?></div>
2968 <div class="divTableCell text-center"><?php echo $icons['SMS']['SCHEDULED']; ?></div>
2969 <div class="divTableCell text-center"><?php echo $icons['SMS']['SENT']; ?></div>
2970 <div class="divTableCell text-center"><?php echo $icons['SMS']['READ']; ?></div>
2971 <div class="divTableCell text-center"><?php echo $icons['SMS']['CONFIRMED']; ?></div>
2972 <div class="divTableCell text-center"><?php echo $icons['SMS']['CALL']; ?></div>
2973 <div class="divTableCell text-center"><?php echo $icons['SMS']['FAILED']; ?></div>
2974 <div class="divTableCell text-center"><?php echo $icons['SMS']['EXTRA']; ?></div>
2975 <div class="divTableCell text-center"><?php echo $icons['SMS']['STOP']; ?></div>
2976 </div>
2977 <div class="divTableRow">
2978 <div class="divTableCell text-center"><?php echo xlt('AVM'); ?></div>
2979 <div class="divTableCell text-center"><?php echo $icons['AVM']['ALLOWED']; ?></div>
2980 <div class="divTableCell text-center"><?php echo $icons['AVM']['NotAllowed']; ?></div>
2981 <div class="divTableCell text-center"><?php echo $icons['AVM']['SCHEDULED']; ?></div>
2982 <div class="divTableCell text-center"><?php echo $icons['AVM']['SENT']; ?></div>
2983 <div class="divTableCell text-center"><?php echo $icons['AVM']['READ']; ?></div>
2984 <div class="divTableCell text-center"><?php echo $icons['AVM']['CONFIRMED']; ?></div>
2985 <div class="divTableCell text-center"><?php echo $icons['AVM']['CALL']; ?></div>
2986 <div class="divTableCell text-center"><?php echo $icons['AVM']['FAILED']; ?></div>
2987 <div class="divTableCell text-center"><?php echo $icons['AVM']['EXTRA']; ?></div>
2988 <div class="divTableCell text-center"><?php echo $icons['AVM']['STOP']; ?></div>
2989 </div>
2990 <?php
2991 //When we have added PostCards to MedEx, we can display this.
2992 //Until then this would just add confusion.
2994 <div class="divTableRow">
2995 <div class="divTableCell text-center"><?php echo xlt('POSTCARD'); ?></div>
2996 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['ALLOWED']; ?></div>
2997 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['NotAllowed']; ?></div>
2998 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['SCHEDULED']; ?></div>
2999 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['SENT']; ?></div>
3000 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['READ']; ?></div>
3001 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['CONFIRMED']; ?></div>
3002 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['CALL']; ?></div>
3003 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['FAILURE']; ?></div>
3004 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['EXTRA']; ?></div>
3005 <div class="divTableCell text-center"><?php echo $icons['POSTCARD']['STOP']; ?></div>
3006 </div>
3007 </div>
3010 </div>
3011 </div>
3013 <?php
3017 * This function displays a bootstrap responsive pop-up window containing an image of a phone with a record of our messaging activity.
3018 * It is fired from the Flow board.
3019 * It enables two-way SMS texting between logged_in users and patients, if desired.
3020 * It may also end up on the Recall Board.
3021 * It may also allow playback of AVM audio files.
3022 * It may also do other things that haven't been written yet.
3023 * It may open to a messaging status board on large screens.
3024 * @param $logged_in
3025 * @return bool
3028 public function SMS_bot($logged_in)
3030 $fields = array();
3031 $fields = $_REQUEST;
3032 if (!empty($_REQUEST['pid']) && $_REQUEST['pid'] != 'find') {
3033 $responseA = $this->syncPat($_REQUEST['pid'], $logged_in);
3034 } else if ($_REQUEST['show']=='pat_list') {
3035 $responseA = $this->syncPat($_REQUEST['show'], $logged_in);
3036 $fields['pid_list'] = $responseA['pid_list'];
3037 $fields['list_hits'] = $responseA['list_hits'];
3039 $this->curl->setUrl($this->MedEx->getUrl('custom/SMS_bot&token='.$logged_in['token']));
3040 $this->curl->setData($fields);
3041 $this->curl->makeRequest();
3042 $response = $this->curl->getResponse();
3044 if (isset($response['success'])) {
3045 echo $response['success'];
3046 } else if (isset($response['error'])) {
3047 $this->lastError = $response['error'];
3049 return false;
3051 public function syncPat($pid, $logged_in)
3053 if ($pid == 'pat_list') {
3054 global $data;
3055 $values = $_REQUEST['outpatient'];
3056 $sqlSync = "SELECT * FROM patient_data WHERE fname LIKE ? OR lname LIKE ? LIMIT 20";
3057 $datas = sqlStatement($sqlSync, array("%".$values."%","%".$values."%"));
3058 while ($hit = sqlFetchArray($datas)) {
3059 $data['list'][] = $hit;
3060 $pid_list[] = $hit['pid'];
3062 $this->curl->setUrl($this->MedEx->getUrl('custom/syncPat&token='.$logged_in['token']));
3063 $this->curl->setData($data);
3064 $this->curl->makeRequest();
3065 $response = $this->curl->getResponse();
3066 $response['pid_list'] = $pid_list;
3067 $response['list_hits'] = $data['list'];
3068 } else {
3069 $sqlSync = "SELECT * FROM patient_data WHERE pid=?";
3070 $data = sqlQuery($sqlSync, array($pid));
3071 $this->curl->setUrl($this->MedEx->getUrl('custom/syncPat&token='.$logged_in['token']));
3072 $this->curl->setData($data);
3073 $this->curl->makeRequest();
3074 $response = $this->curl->getResponse();
3076 if (isset($response['success'])) {
3077 return $response;
3078 } else if (isset($response['error'])) {
3079 $this->lastError = $response['error'];
3081 return $this->lastError;
3085 class Setup extends Base
3087 public function MedExBank($stage)
3089 if ($stage =='1') {
3091 <div class="row">
3092 <div class="col-sm-10 text-center col-xs-offset-1">
3093 <div id="setup_1">
3094 <div class="title">MedEx</div>
3095 <div class="row showReminders ">
3096 <div class="col-sm-10 text-center col-xs-offset-1">
3097 <em>
3098 <?php echo xlt('Using technology to improve productivity'); ?>.
3099 </em>
3100 </div>
3101 </div>
3102 <div class="row showReminders ">
3103 <div class="col-sm-5 col-xs-offset-1 text-center">
3104 <h3 class="title"><?php echo xlt('Targets'); ?>:</h3>
3105 <ul class="text-left" style="margin-left:125px;">
3106 <li> <?php echo xlt('Appointment Reminders'); ?></li>
3107 <li> <?php echo xlt('Patient Recalls'); ?></li>
3108 <li> <?php echo xlt('Office Announcements'); ?></li>
3109 <li> <?php echo xlt('Patient Surveys'); ?></li>
3110 </ul>
3111 </div>
3112 <div class="col-sm-4 col-xs-offset-1 text-center">
3113 <h3 class="title"><?php echo xlt('Channels'); ?>:</h3>
3114 <ul class="text-left" style="margin-left:75px;">
3115 <li> <?php echo xlt('SMS Messages'); ?></li>
3116 <li> <?php echo xlt('Voice Messages'); ?></li>
3117 <li> <?php echo xlt('E-mail Messaging'); ?></li>
3118 <li> <?php echo xlt('Postcards'); ?></li>
3119 <li> <?php echo xlt('Address Labels'); ?></li>
3120 </ul>
3121 </div>
3122 </div>
3123 <div class="text-center row showReminders">
3124 <input value="<?php echo xla('Sign-up'); ?>" onclick="goReminderRecall('setup&stage=2');" class="btn btn-primary">
3125 </div>
3127 </div>
3128 </div>
3129 </div>
3131 <?php
3132 } else if ($stage =='2') {
3134 <div class="row">
3135 <form name="medex_start" id="medex_start">
3136 <div class="col-sm-10 col-sm-offset-1 text-center">
3137 <div id="setup_1" class="showReminders borderShadow">
3138 <div class="title row fa"><?php echo xlt('Register'); ?>: MedEx Bank</div>
3139 <div class="row showReminders">
3140 <div class="fa col-sm-10 col-sm-offset-1 text-center">
3141 <div class="divTable4" id="answer" name="answer">
3142 <div class="divTableBody">
3143 <div class="divTableRow">
3144 <div class="divTableCell divTableHeading">
3145 <?php echo xlt('E-mail'); ?>
3146 </div>
3147 <div class="divTableCell">
3148 <i id="email_check" name="email_check" class="top_right_corner nodisplay red fa fa-check"></i>
3149 <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>
3150 <div class="signup_help nodisplay" id="email_help" name="email_help"><?php echo xlt('Please provide a valid e-mail address to proceed'); ?>...</div>
3152 </div>
3153 </div>
3154 <div class="divTableRow">
3155 <div class="divTableCell divTableHeading">
3156 <?php echo xlt('Password'); ?>
3157 </div>
3158 <div class="divTableCell"><i id="pwd_check" name="pwd_check" class="top_right_corner nodisplay red fa fa-check"></i>
3159 <i class="fa top_right_corner fa-question" id="pwd_ico_help" aria-hidden="true" onclick="$('#pwd_help').toggleClass('nodisplay');"></i>
3160 <input type="password" placeholder="<?php xla('Password'); ?>" id="new_password" name="new_password" class="form-control" required>
3161 <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>
3162 </div>
3163 </div>
3164 <div class="divTableRow">
3165 <div class="divTableCell divTableHeading">
3166 <?php echo xlt('Repeat'); ?>
3167 </div>
3168 <div class="divTableCell"><i id="pwd_rcheck" name="pwd_rcheck" class="top_right_corner nodisplay red fa fa-check"></i>
3169 <input type="password" placeholder="<?php echo xla('Repeat password'); ?>" id="new_rpassword" name="new_rpassword" class="form-control" required>
3170 <div id="pwd_rhelp" class="nodisplay signup_help" style=""><?php echo xlt('Passwords do not match.'); ?></div>
3171 </div>
3172 </div>
3173 </div>
3174 </div>
3175 <div id="ihvread" name="ihvread" class="fa text-left">
3176 <input type="checkbox" class="updated required" name="TERMS_yes" id="TERMS_yes" required>
3177 <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'); ?>
3178 <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 />
3179 <input type="checkbox" class="updated required" name="BusAgree_yes" id="BusAgree_yes" required>
3180 <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'); ?>
3181 <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>
3182 <br />
3183 <div class="align-center row showReminders">
3184 <input id="Register" class="btn btn-primary" value="<?php echo xla('Register'); ?>" />
3185 </div>
3187 <div id="myModal" class="modal fade" role="dialog">
3188 <div class="modal-dialog">
3190 <!-- Modal content-->
3191 <div class="modal-content">
3192 <div class="modal-header" style="background-color: #0d4867;color: #fff;font-weight: 700;">
3193 <button type="button" class="close" data-dismiss="modal" style="color:#fff;opacity:1;box-shadow:unset !important;">&times;</button>
3194 <h2 class="modal-title" style="font-weight:600;">Sign-Up Confirmation</h2>
3195 </div>
3196 <div class="modal-body" style="padding: 10px 45px;">
3197 <p>You are opening a secure connection to MedExBank.com. During this step your EHR will synchronize with the MedEx servers. <br />
3198 <br />
3199 Re-enter your username (e-mail) and password in the MedExBank.com login window to:
3200 <ul style="text-align: left;width: 90%;margin: 0 auto;">
3201 <li> confirm your practice and providers' information</li>
3202 <li> choose your service options</li>
3203 <li> update and activate your messages </li>
3204 </ul>
3205 </p>
3206 </div>
3207 <div class="modal-footer">
3208 <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
3209 <button type="button" class="btn btn-default" onlick="actualSignUp();" id="actualSignUp">Proceed</button>
3210 </div>
3211 </div>
3213 </div>
3214 </div>
3215 </div>
3216 </div>
3217 </div>
3218 </div>
3219 </div>
3220 </form>
3221 </div>
3222 <script>
3223 function signUp() {
3224 var email = $("#new_email").val();
3225 if (!validateEmail(email)) return alert('<?php echo xlt('Please provide a valid e-mail address to proceed'); ?>...');
3226 var password = $("#new_password").val();
3227 var passed = check_Password(password);
3228 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'); ?> ... ');
3229 if ($("#new_rpassword").val() !== password) return alert('<?php echo xlt('Passwords do not match'); ?>!');
3230 if (!$("#TERMS_yes").is(':checked')) return alert('<?php echo xlt('You must agree to the Terms & Conditions before signing up');?>... ');
3231 if (!$("#BusAgree_yes").is(':checked')) return alert('<?php echo xlt('You must agree to the HIPAA Business Associate Agreement');?>... ');
3232 $("#myModal").modal();
3233 return false;
3236 function validateEmail(email) {
3237 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,}))$/;
3238 return re.test(email);
3240 function check_Password(password) {
3241 var passed = validatePassword(password, {
3242 length: [8, Infinity],
3243 lower: 1,
3244 upper: 1,
3245 numeric: 1,
3246 special: 1,
3247 badWords: ["password", "qwerty", "12345"],
3248 badSequenceLength: 4
3250 return passed;
3252 function validatePassword (pw, options) {
3253 // default options (allows any password)
3254 var o = {
3255 lower: 0,
3256 upper: 0,
3257 alpha: 0, /* lower + upper */
3258 numeric: 0,
3259 special: 0,
3260 length: [0, Infinity],
3261 custom: [ /* regexes and/or functions */ ],
3262 badWords: [],
3263 badSequenceLength: 0,
3264 noQwertySequences: false,
3265 noSequential: false
3268 for (var property in options)
3269 o[property] = options[property];
3271 var re = {
3272 lower: /[a-z]/g,
3273 upper: /[A-Z]/g,
3274 alpha: /[A-Z]/gi,
3275 numeric: /[0-9]/g,
3276 special: /[\W_]/g
3278 rule, i;
3280 // enforce min/max length
3281 if (pw.length < o.length[0] || pw.length > o.length[1])
3282 return false;
3284 // enforce lower/upper/alpha/numeric/special rules
3285 for (rule in re) {
3286 if ((pw.match(re[rule]) || []).length < o[rule])
3287 return false;
3290 // enforce word ban (case insensitive)
3291 for (i = 0; i < o.badWords.length; i++) {
3292 if (pw.toLowerCase().indexOf(o.badWords[i].toLowerCase()) > -1)
3293 return false;
3296 // enforce the no sequential, identical characters rule
3297 if (o.noSequential && /([\S\s])\1/.test(pw))
3298 return false;
3300 // enforce alphanumeric/qwerty sequence ban rules
3301 if (o.badSequenceLength) {
3302 var lower = "abcdefghijklmnopqrstuvwxyz",
3303 upper = lower.toUpperCase(),
3304 numbers = "0123456789",
3305 qwerty = "qwertyuiopasdfghjklzxcvbnm",
3306 start = o.badSequenceLength - 1,
3307 seq = "_" + pw.slice(0, start);
3308 for (i = start; i < pw.length; i++) {
3309 seq = seq.slice(1) + pw.charAt(i);
3310 if (
3311 lower.indexOf(seq) > -1 ||
3312 upper.indexOf(seq) > -1 ||
3313 numbers.indexOf(seq) > -1 ||
3314 (o.noQwertySequences && qwerty.indexOf(seq) > -1)
3316 return false;
3321 // enforce custom regex/function rules
3322 for (i = 0; i < o.custom.length; i++) {
3323 rule = o.custom[i];
3324 if (rule instanceof RegExp) {
3325 if (!rule.test(pw))
3326 return false;
3327 } else if (rule instanceof Function) {
3328 if (!rule(pw))
3329 return false;
3333 // great success!
3334 return true;
3336 $(document).ready(function() {
3337 $("#Register").click(function() {
3338 signUp();
3340 $("#actualSignUp").click(function() {
3341 var url = "save.php?MedEx=start";
3342 var email = $("#new_email").val();
3343 $("#actualSignUp").html('<i class="fa fa-spinner fa-pulse fa-fw"></i><span class="sr-only">Loading...</span>');
3344 formData = $("form#medex_start").serialize();
3345 top.restoreSession();
3346 $.ajax({
3347 type : 'POST',
3348 url : url,
3349 data : formData
3351 .done(function(result) {
3352 obj = JSON.parse(result);
3353 $("#answer").html(obj.show);
3354 $("#ihvread").addClass('nodisplay');
3355 $('#myModal').modal('toggle');
3356 if (obj.success) {
3357 url="https://www.medexbank.com/login/"+email;
3358 window.open(url, 'clinical', 'resizable=1,scrollbars=1');
3359 refresh_me();
3363 $("#new_email").blur(function(e) {
3364 e.preventDefault();
3365 var email = $("#new_email").val();
3366 if (validateEmail(email)) {
3367 $("#email_help").addClass('nodisplay');
3368 $("#email_check").removeClass('nodisplay');
3369 } else {
3370 $("#email_help").removeClass('nodisplay');
3371 $("#email_check").addClass('nodisplay');
3374 $("#new_password,#new_rpassword").keyup(function(e) {
3375 e.preventDefault();
3376 var pwd = $("#new_password").val();
3377 if (check_Password(pwd)) {
3378 $('#pwd_help').addClass('nodisplay');
3379 $("#pwd_ico_help").addClass('nodisplay');
3380 $("#pwd_check").removeClass('nodisplay');
3381 } else {
3382 $("#pwd_help").removeClass('nodisplay');
3383 $("#pwd_ico_help").removeClass('nodisplay');
3384 $("#pwd_check").addClass('nodisplay');
3386 if (this.id === "new_rpassword") {
3387 var pwd1 = $("#new_password").val();
3388 var pwd2 = $("#new_rpassword").val();
3389 if (pwd1 === pwd2) {
3390 $('#pwd_rhelp').addClass('nodisplay');
3391 $("#pwd_rcheck").removeClass('nodisplay');
3392 } else {
3393 $("#pwd_rhelp").removeClass('nodisplay');
3394 $("#pwd_rcheck").addClass('nodisplay');
3399 </script>
3400 <?php
3403 public function autoReg($data)
3405 if (empty($data)) {
3406 return false; //throw new InvalidDataException("We need to actually send some data...");
3408 $this->curl->setUrl($this->MedEx->getUrl('custom/signUp'));
3409 $this->curl->setData($data);
3410 $this->curl->makeRequest();
3411 $response = $this->curl->getResponse();
3412 if (isset($response['success'])) {
3413 return $response;
3414 } else if (isset($response['error'])) {
3415 $this->lastError = $response['error'];
3417 return false;
3421 class MedEx
3423 public $lastError = '';
3424 public $curl;
3425 public $practice;
3426 public $campaign;
3427 public $events;
3428 public $callback;
3429 public $logging;
3430 public $display;
3431 public $setup;
3432 private $cookie;
3433 private $url;
3435 public function __construct($url, $sessionFile = 'cookiejar_MedExAPI')
3437 global $GLOBALS;
3439 if ($sessionFile == 'cookiejar_MedExAPI') {
3440 $sessionFile = $GLOBALS['temporary_files_dir'].'/cookiejar_MedExAPI';
3442 $this->url = rtrim('https://'.preg_replace('/^https?\:\/\//', '', $url), '/') . '/cart/upload/index.php?route=api/';
3443 $this->curl = new CurlRequest($sessionFile);
3444 $this->practice = new Practice($this);
3445 $this->campaign = new Campaign($this);
3446 $this->events = new Events($this);
3447 $this->callback = new Callback($this);
3448 $this->logging = new Logging($this);
3449 $this->display = new Display($this);
3450 $this->setup = new Setup($this);
3453 public function getCookie()
3455 return $this->cookie; }
3457 public function getLastError()
3459 return $this->lastError; }
3462 private function just_login($info)
3464 if (empty($info)) {
3465 return; }
3467 $this->curl->setUrl($this->getUrl('login'));
3468 $this->curl->setData(array(
3469 'username' => $info['ME_username'],
3470 'key' => $info['ME_api_key'],
3471 'UID' => $info['MedEx_id'],
3472 'MedEx' => 'openEMR',
3473 'callback_key' => $info['callback_key']
3476 $this->curl->makeRequest();
3477 $response = $this->curl->getResponse();
3478 //token, news and products returned in $response so far
3479 if (!empty($response['token'])) {
3480 $response['practice'] = $this->practice->sync($response['token']);
3481 $response['campaigns'] = $this->campaign->events($response['token']);
3482 $response['generate'] = $this->events->generate($response['token'], $response['campaigns']['events']);
3483 $response['success'] = "200";
3485 $sql = "UPDATE medex_prefs set status = ?";
3486 $info['status'] = json_encode($response);
3487 sqlQuery($sql, array($info['status']));
3488 return $info;
3491 public function login($force = '')
3493 $info= array();
3494 $query = "SELECT * FROM medex_prefs";
3495 $info = sqlFetchArray(sqlStatement($query));
3496 if (empty($info) ||
3497 empty($info['ME_username']) ||
3498 empty($info['ME_api_key']) ||
3499 empty($info['MedEx_id'])) {
3500 return false;
3503 $info['callback_key'] = $_POST['callback_key'];
3505 if (empty($force)) {
3506 $timer = strtotime($info['MedEx_lastupdated']);
3507 $utc_now = gmdate('Y-m-d H:m:s');
3508 $now = strtotime($utc_now, "-5 minutes");
3509 if ($now > $timer) {
3510 $expired ='1';
3513 if (($expired =='1') || ($force=='1')) {
3514 $info = $this->just_login($info);
3516 $info['status'] = json_decode($info['status'], true);
3517 if (isset($info['status']['success']) && isset($info['status']['token'])) {
3518 return $info;
3519 } else if (isset($info['error'])) {
3520 $this->lastError = $info['error'];
3521 sqlQuery("UPDATE `background_services` SET `active`='0' WHERE `name`='MedEx'");
3522 return $info;
3524 return false;
3527 public function getUrl($method)
3529 return $this->url . $method; }
3531 public function checkModality($event, $appt)
3533 $sqlQuery = "SELECT * FROM medex_icons";
3534 $result = sqlStatement($sqlQuery);
3535 while ($icons = sqlFetchArray($result)) {
3536 //substitute data-toggle="tooltip" data-placement="auto" title="..." with data-toggle="tooltip" data-placement="auto" title="'.xla('...').'" in $icons['i_html']
3537 $title = preg_match('/title=\"(.*)\"/', $icons['i_html']);
3538 $xl_title = xla($title);
3539 $icons['i_html'] = str_replace($title, $xl_title, $icons['i_html']);
3540 $icon[$icons['msg_type']][$icons['msg_status']] = $icons['i_html'];
3542 if ($event['M_type'] =="SMS") {
3543 if (empty($appt['phone_cell']) || ($appt["hipaa_allowsms"]=="NO")) {
3544 return array($icon['SMS']['NotAllowed'],false);
3545 } else {
3546 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
3547 return array($icon['SMS']['ALLOWED'],$phone); // It is allowed and they have a cell phone
3549 } else if ($event['M_type'] =="AVM") {
3550 if ((empty($appt["phone_home"]) && (empty($appt["phone_cell"])) || ($appt["hipaa_voice"]=="NO"))) {
3551 return array($icon['AVM']['NotAllowed'],false);
3552 } else {
3553 if (!empty($appt["phone_cell"])) {
3554 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
3555 } else {
3556 $phone = preg_replace("/[^0-9]/", "", $appt["phone_home"]);
3558 return array($icon['AVM']['ALLOWED'],$phone); //We have a phone to call and permission!
3560 } else if ($event['M_type'] =="EMAIL") {
3561 if (($appt["email"]=="")||($appt["hipaa_allowemail"]=="NO")) {
3562 return array($icon['EMAIL']['NotAllowed'],false);
3563 } else {
3564 //need to make sure this is a valid email too eh?
3565 return array($icon['EMAIL']['ALLOWED'],$appt["email"]);
3567 //need to add in check for address to send postcards? - when we add in postcards...
3568 } else {
3569 return array(false,false);
3574 class InvalidDataException extends \Exception