3 * /library/MedEx/API.php
6 * @author MedEx <support@MedExBank.com>
7 * @link http://www.MedExBank.com
8 * @copyright Copyright (c) 2018 MedEx <support@MedExBank.com>
9 * @license https://www.gnu.org/licenses/agpl-3.0.en.html GNU Affero General Public License 3
19 private $postData = array();
20 private $cookies = array();
21 private $response = '';
25 public function __construct($sessionFile)
27 $this->sessionFile
= $sessionFile;
28 $this->restoreSession();
31 private function restoreSession()
33 if (file_exists($this->sessionFile
)) {
34 $this->cookies
= json_decode(file_get_contents($this->sessionFile
), true);
38 public function makeRequest()
40 $this->handle
= curl_init($this->url
);
42 curl_setopt($this->handle
, CURLOPT_VERBOSE
, 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
);
69 private function getCookies()
72 foreach ($this->cookies
as $name => $value) {
73 $cookies[] = $name . '=' . $value;
75 return implode('; ', $cookies);
78 private function saveSession()
80 if (empty($this->sessionFile
)) {
84 if (!file_exists(dirname($this->sessionFile
))) {
85 /** @noinspection PhpMethodParametersCountMismatchInspection */mkdir(dirname($this->sessionFile
, 0755, true));
88 file_put_contents($this->sessionFile
, json_encode($this->cookies
));
91 public function setUrl($url)
95 public function setData($postData)
97 $this->postData
= $postData; }
98 public function getResponse()
100 return json_decode($this->response
, true); }
101 public function getRawResponse()
103 return $this->response
; }
111 public function __construct($MedEx)
113 $this->MedEx
= $MedEx;
114 $this->curl
= $MedEx->curl
;
118 class Practice
extends Base
120 public function sync($token)
125 $callback = "https://".$GLOBALS['_SERVER']['SERVER_NAME'].$GLOBALS['_SERVER']['PHP_SELF'];
126 $callback = str_replace('ajax/execute_background_services.php', 'MedEx/MedEx.php', $callback);
127 $fields2['callback_url'] = $callback;
128 //get the providers list:
129 $sqlQuery = "SELECT * FROM medex_prefs";
130 $my_status = sqlQuery($sqlQuery);
131 $providers = explode('|', $my_status['ME_providers']);
132 foreach ($providers as $provider) {
133 $runQuery = "SELECT * FROM users WHERE id=?";
134 $ures = sqlStatement($runQuery, array($provider));
135 while ($urow = sqlFetchArray($ures)) {
136 $fields2['providers'][] = $urow;
139 //get the facilities list and flag which we do messaging for (checkboxes in Preferences)
140 $facilities = explode('|', $my_status['ME_facilities']);
141 $runQuery = "SELECT * FROM facility WHERE service_location='1'";
142 $ures = sqlStatement($runQuery);
143 while ($urow = sqlFetchArray($ures)) {
144 if (in_array($urow['id'], $facilities)) {
145 $urow['messages_active'] = '1';
147 $fields2['facilities'][] = $urow;
149 //get the categories list:
150 $runQuery = "SELECT pc_catid, pc_catname, pc_catdesc, pc_catcolor, pc_seq
151 FROM openemr_postcalendar_categories WHERE pc_active = 1 AND pc_cattype='0' ORDER BY pc_catid";
152 $ures = sqlStatement($runQuery);
153 while ($urow = sqlFetchArray($ures)) {
154 $fields2['categories'][] = $urow;
157 $runQuery = "SELECT * FROM `list_options` WHERE `list_id` LIKE 'apptstat' AND activity='1'";
158 $ures = sqlStatement($runQuery);
159 while ($urow = sqlFetchArray($ures)) {
160 $fields2['apptstats'][] = $urow;
162 //get definition for "Checked Out"
163 $runQuery = "SELECT option_id FROM list_options WHERE toggle_setting_2='1' AND list_id='apptstat' AND activity='1'";
164 $ures = sqlStatement($runQuery);
165 while ($urow = sqlFetchArray($ures)) {
166 $fields2['checkedOut'][] = $urow;
168 //get clinical reminders for practice
169 $sql = "SELECT * FROM `clinical_rules`,`list_options`,`rule_action`,`rule_action_item`
171 `clinical_rules`.`pid`=0 AND
172 `clinical_rules`.`patient_reminder_flag` = 1 AND
173 `clinical_rules`.id = `list_options`.option_id AND
174 `clinical_rules`.id = `rule_action`.id AND
175 `list_options`.option_id=`clinical_rules`.id AND
176 `rule_action`.category =`rule_action_item`.category AND
177 `rule_action`.item =`rule_action_item`.item ";
179 $ures = sqlStatementCdrEngine($sql);
180 while ($urow = sqlFetchArray($ures)) {
181 $fields2['clinical_reminders'][] = $urow;
184 $data = array($fields2);
185 if (!is_array($data)) {
186 // return false; //throw new InvalidProductException('Invalid practice information');
188 $this->curl
->setUrl($this->MedEx
->getUrl('custom/addpractice&token='.$token));
189 $this->curl
->setData($fields2);
190 $this->curl
->makeRequest();
191 $response = $this->curl
->getResponse();
193 //Practice: Since the last MedEx login, who has responded to one of our messages and do we know about these responses?
194 //Have we deleted, cancelled or changed an appointment that we thought we were supposed to send, and now don't want to?
195 //1. Check to see if anything pending was cancelled/changed.
196 //2. Make sure any recalls don't have appointments just scheduled. If so no Recall message needed.
197 //3. Sync any responses received on MedEx that we didn't see already.
198 // Send and Receive everything since last timestamp/update noted in medex_prefs and check.
199 //Finally we may have manually made an appointment (which deletes a Recall) or manually confirmed an appt too.
200 //4. We need to send this data to MedEx so it stops processing events that are confirmed/completed.
202 //1. Check to see if anything pending was cancelled/changed.
203 //for appts, we are just looking for appts that are flagged as 'To Send' BUT
204 // were confirmed, cancelled, moved by the staff since the appt was added to table medex_outgoing...
205 $sql = "SELECT * FROM medex_outgoing WHERE msg_pc_eid != 'recall_%' AND msg_reply LIKE 'To Send'";
206 $test = sqlStatement($sql);
207 while ($result1 = sqlFetchArray($test)) {
208 $query = "SELECT * FROM openemr_postcalendar_events WHERE pc_eid = ?";
209 $test2 = sqlStatement($query, array($result1['msg_pc_eid']));
210 $result2 = sqlFetchArray($test2);
211 //for custom installs, insert custom apptstatus here that mean appt is not happening/changed
212 if ($result2['pc_apptstatus'] =='*' ||
//confirmed
213 $result2['pc_apptstatus'] =='%' ||
//cancelled < 24hour
214 $result2['pc_apptstatus'] =='x' ) { //cancelled
216 $sqlUPDATE = "UPDATE medex_outgoing SET msg_reply = 'DONE',msg_extra_text=? WHERE msg_uid = ?";
217 sqlQuery($sqlUPDATE, array($result2['pc_apptstatus'],$result2['msg_uid']));
218 //we need to update MedEx regarding actions to try to cancel
219 $tell_MedEx['DELETE_MSG'][] = $result1['msg_pc_eid'];
223 // 2. Make sure any recalls don't have appointments, ie. just a recall is scheduled
224 // Go through each recall event in medex_outgoing and check to see if it is scheduled. If so tell MedEx.
225 // We do this when we load the Recall Board........ and now when running a cron job/background_service
226 $sql = "SELECT * FROM medex_outgoing WHERE msg_pc_eid LIKE 'recall_%' GROUP BY msg_pc_eid";
227 $result = sqlStatement($sql);
228 while ($row = sqlFetchArray($result)) {
229 $pid = trim($row['msg_pc_eid'], "recall_");
230 //if there is a future appointment now in calendar, stop this recall message from going out
231 $query = "SELECT pc_eid FROM openemr_postcalendar_events WHERE (pc_eventDate > CURDATE()) AND pc_pid=?";
232 $test3 = sqlStatement($query, array($pid));
233 $result3 = sqlFetchArray($test3);
235 $sqlUPDATE = "UPDATE medex_outgoing SET msg_reply = 'SCHEDULED', msg_extra_text=? WHERE msg_uid = ?";
236 sqlQuery($sqlUPDATE, array($result3['pc_eid'],$result2['msg_uid']));
237 //we need to update MedEx regarding actions to try to cancel
238 $tell_MedEx['DELETE_MSG'][] = $row['msg_pc_eid'];
242 //3. Sync any responses received on MedEx that we didn't see already.
244 $sqlQuery = "SELECT * FROM medex_prefs";
245 $my_status = sqlStatement($sqlQuery);
246 while ($urow = sqlFetchArray($my_status)) {
247 $fields3['MedEx_lastupdated'] = $urow['MedEx_lastupdated'];
248 $fields3['ME_providers'] = $urow['ME_providers'];
251 $this->curl
->setUrl($this->MedEx
->getUrl('custom/sync_responses&token='.$token));
252 $this->curl
->setData($fields3);
253 $this->curl
->makeRequest();
254 $responses = $this->curl
->getResponse();
256 foreach ($responses['messages'] as $data) {
257 //check to see if this response is present already
258 $data['msg_extra'] = $data['msg_extra']?
:'';
259 $sqlQuery ="SELECT * FROM medex_outgoing WHERE medex_uid=?";
260 $checker = sqlStatement($sqlQuery, array($data['msg_uid']));
261 if (sqlNumRows($checker)=='0') {
262 $this->MedEx
->callback
->receive($data);
265 $sqlUPDATE = "UPDATE medex_prefs SET MedEx_lastupdated=utc_timestamp()";
266 sqlStatement($sqlUPDATE);
268 if ($tell_MedEx['DELETE_MSG']) {
269 $this->curl
->setUrl($this->MedEx
->getUrl('custom/remMessaging&token='.$token));
270 $this->curl
->setData($tell_MedEx['DELETE_MSG']);
271 $this->curl
->makeRequest();
272 $response = $this->curl
->getResponse();
274 if (!empty($response['found_replies'])) {
275 $response['success']['message'] = xlt("Replies retrieved").": ".$response['found_replies'];
277 $response['success']['message'] = xlt("No new messages on")." MedEx.";
280 if (isset($response['success'])) {
282 } else if (isset($response['error'])) {
283 $this->lastError
= $response['error'];
289 class Campaign
extends Base
291 public function events($token)
293 $this->curl
->setUrl($this->MedEx
->getUrl('custom/showEvents&token='.$token));
294 $this->curl
->makeRequest();
295 $response = $this->curl
->getResponse();
297 if (isset($response['success'])) {
299 } else if (isset($response['error'])) {
300 $this->lastError
= $response['error'];
306 class Events
extends Base
309 * In this function we are generating all appts that match scheduled campaign events so MedEx can conduct the campaign events.
311 * This is run via MedEx_background.php or MedEx.php to find appointments that match our Campaign events and rules.
312 * Messaging Campaigns are categorized by function:
313 * There are RECALLs, REMINDERs, CLINICAL_REMINDERs, SURVEYs and ANNOUNCEments (and later whatever comes down the pike).
314 * To meet a campaign's goals, we schedule campaign events.
315 * REMINDER and RECALL campaigns each have their own events which the user creates and personalizes.
316 * There is only one REMINDER Campaign and one RECALL Campaign, each with unlimited events.
318 * SURVEYs and ANNOUNCEments can have unlimited campaigns, each with their own events...
319 * You can ANNOUNCE a weather related closure (campaign) by SMS and AVM (events) going out now/ASAP
321 * at the same time also ANNOUNCE that in two months Dr. X will be out of the office and patient needs to reschedule (campaign)
322 * using SMS and email and voice (events) with messages going out 2 days from now, spread out over 6 business days...
324 * So SURVEY AND ANNOUNCE events have parent Campaigns they are attached to
325 * but RECALLS and REMINDERS do not (they are a RECALL or a REMINDER).
326 * Clinical Reminders can be handled locally in full, some parts local, some on MedEx or all on MedEx.
327 * ie. some practices may want to send emails themselves,
328 * but have MedEx handle the SMS and phone arms of the campaign, etc.
329 * Whatever is decided, MedEx events are created on MedEx and we are notified it exists in $MedEx->campaign->events($token).
330 * We are taking those events to build our list of recipients, they are logged in local medex_outgoing table,
331 * and are sent to MedEx to process the event.
336 public function generate($token, $events)
338 if (empty($events)) {
339 return false; //throw new InvalidDataException("You have no Campaign Events on MedEx at this time.");
340 //You have no campaign events on MedEx!
345 $count_recurrents = 0;
346 $count_announcements = 0;
348 $count_clinical_reminders = 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) {
358 $escapedArr = []; //will hold prepared statement items for query.
359 if ($event['M_group'] == 'REMINDER') {
360 if ($event['time_order'] > '0') {
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 ='-'
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.
391 if (($today =="Sunday")||
($today =="Saturday")) {
394 if ($today == "Friday") {
395 $timing2 = ($timing +
3).":0:1"; //this is + 3 day, 0 hour and 1 minute...
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
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
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;
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;
467 } else if ($event['M_group'] == 'RECALL') {
468 if ($event['time_order'] > '0') {
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;
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.
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)) {
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;
548 } else if ($event['M_group'] == 'ANNOUNCE') {
549 if (empty($event['start_date'])) {
552 $today = strtotime(date('Y-m-d'));
553 $start = strtotime($event['appts_start']);
555 if ($today < $start) {
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'];
565 cal.pc_eventDate >= ? AND
566 cal.pc_eventDate <= ?
570 cal.pc_eventDate <= ? AND
571 cal.pc_endDate >= ? AND
575 $escapedArr[]=$event['appts_start'];
576 $escapedArr[]=$event['appts_end'];
577 $escapedArr[]=$event['appts_end'];
578 $escapedArr[]=$event['appts_start'];
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'])) {
592 $appt_stats = explode('|', $event['appt_stats']);
593 foreach ($appt_stats as $appt_stat) {
595 $escapedArr[]=$appt_stat;
597 $prepare_me = rtrim($prepare_me, ",");
598 $appt_status = " AND cal.pc_apptstatus in (".$prepare_me.") ";
603 if (!empty($event['providers'])) {
605 $providers = explode('|', $event['providers']);
606 foreach ($providers as $provider) {
608 $escapedArr[]=$provider;
610 $prepare_me = rtrim($prepare_me, ",");
611 $providers = " AND cal.pc_aid in (".$prepare_me.") ";
616 if (!empty($event['facilities'])) {
618 $facilities = explode('|', $event['facilities']);
619 foreach ($facilities as $facility) {
621 $escapedArr[]=$facility;
623 $prepare_me = rtrim($prepare_me, ",");
624 $places = " AND cal.pc_facility in (".$prepare_me.") ";
629 if (!empty($event['visit_types'])) {
631 $visit_types = explode('|', $event['visit_types']);
632 foreach ($visit_types as $visit_type) {
634 $escapedArr[]=$visit_type;
636 $prepare_me = rtrim($prepare_me, ",");
637 $visit_types = " AND cal.pc_catid in (".$prepare_me.") ";
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."
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;
662 $count_announcements++
;
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;
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'] .= "?,";
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))) {
733 $query = "SELECT * FROM openemr_postcalendar_events AS cal
734 LEFT JOIN patient_data AS pat ON cal.pc_pid=pat.pid
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' ".
743 AND cal.pc_aid IN (?)
745 AND hipaa_allowemail NOT LIKE 'NO'
747 ORDER BY pc_eventDate,pc_startTime
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
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;
793 } else if ($event['M_group'] == 'CLINICAL_REMINDER') {
794 $sql = "SELECT * FROM `patient_reminders`,`patient_data`
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);
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') {
814 if (!empty($event['appt_stats'])) {
816 $appt_stats = explode('|', $event['appt_stats']);
817 foreach ($appt_stats as $appt_stat) {
819 $escapedArr[]=$appt_stat;
821 $prepare_me = rtrim($prepare_me, ",");
822 $appt_status = " AND cal.pc_apptstatus in (".$prepare_me.") ";
827 if (!empty($event['providers'])) {
829 $providers = explode('|', $event['providers']);
830 foreach ($providers as $provider) {
832 $escapedArr[]=$provider;
834 $prepare_me = rtrim($prepare_me, ",");
835 $providers = " AND cal.pc_aid in (".$prepare_me.") ";
840 if (!empty($event['facilities'])) {
842 $facilities = explode('|', $event['facilities']);
843 foreach ($facilities as $facility) {
845 $escapedArr[]=$facility;
847 $prepare_me = rtrim($prepare_me, ",");
848 $places = " AND cal.pc_facility in (".$prepare_me.") ";
853 if (!empty($event['visit_types'])) {
855 $visit_types = explode('|', $event['visit_types']);
856 foreach ($visit_types as $visit_type) {
858 $escapedArr[]=$visit_type;
860 $prepare_me = rtrim($prepare_me, ",");
861 $visit_types = " AND cal.pc_catid in (".$prepare_me.") ";
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'];
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
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'];
892 if ($event['E_timing'] == '5') {
893 $target_dates = " cal.pc_eventDate > curdate() ";
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
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);
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'
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
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;
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;
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;
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.
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'));
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;
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",
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" => "",
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, ".
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");
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) {
1142 if ($needle===$value or (is_array($value) && $this->recursive_array_search($needle, $value))) {
1143 return true; //$current_key;
1150 * This function deletes Recalls from MedEx when they are completed and no further processing is
1151 * needed. They are in an array = $data.
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'])) {
1165 } else if (isset($response['error'])) {
1166 $this->lastError
= $response['error'];
1172 * This function processes appointments/recalls that meet the timimg requirements for a MedEx Campaign Event
1177 private function process($token, $appts)
1179 if (empty($appts)) {
1180 throw new InvalidDataException("You have no appointments that need processing at this time.");
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();
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'])) {
1206 } else if (isset($response['error'])) {
1207 $this->lastError
= $response['error'];
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 ///////////////////////////////////////////////////////////////////////
1221 $this->MedEx
->logging
->log_this("hello in calculateEvents");
1222 $this->MedEx
->logging
->log_this($event['pc_recurrtype']);
1223 switch ($event['pc_recurrtype']) {
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) {
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) {
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);
1269 $event_recurrspec = @unserialize
($event['pc_recurrspec']);
1271 if (checkEvent($event['pc_recurrtype'], $event_recurrspec)) {
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']) {
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) {
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) {
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) {
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";
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....
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)) {
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]) {
1369 } elseif ($nextWorkDOW == $GLOBALS['weekend_days'][1]) {
1372 } elseif (count($GLOBALS['weekend_days']) === 1 && $nextWorkDOW === $GLOBALS['weekend_days'][0]) {
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)
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=?
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);
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 = '')
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])) {
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'));
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);
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 = '')
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'])) {
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");
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))
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";
1540 $response['success'] = "completed";
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;)
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");
1563 fputs($std_log, "\nDATA= ".$data. "\n");
1570 class Display
extends base
1572 public function navigation($logged_in)
1574 global $setting_bootstrap_submenu;
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';
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');
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');
1609 <i
class="fa fa-caret-<?php
1610 if ($setting_bootstrap_submenu == 'hide') {
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
>
1629 <div
class="navbar-collapse collapse" id
="oer-navbar-collapse-1">
1630 <ul
class="navbar-nav">
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">
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
>
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
>
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
>
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">
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
>
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
>
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
>
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
>
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
>
1716 if (!empty($logged_in['products']['ordered'])) {
1717 foreach ($logged_in['products']['ordered'] as $ordered) {
1718 echo $ordered['menu'];
1724 </div
><!-- /.navbar
-collapse
-->
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;">
1742 public function preferences($prefs = '')
1745 if (empty($prefs)) {
1746 $prefs = sqlFetchArray(sqlStatement("SELECT * FROM medex_prefs"));
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
/>
1755 <form action
="#" name
="save_prefs" id
="save_prefs">
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']; ?
>
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"
1772 if ($prefs['ME_hipaa_default_override']=='1') {
1773 echo 'checked ="checked"';
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'); ?
>
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
>
1789 <div
class="divTableRow">
1790 <div
class="divTableCell divTableHeading"><?php
echo xlt('Enable Facility'); ?
></div
>
1791 <div
class="divTableCell indent20">
1794 $query = "SELECT * FROM facility";
1795 $result = sqlStatement($query);
1796 while ($fac = sqlFetchArray($result)) {
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
1813 <div
class="divTableRow">
1814 <div
class="divTableCell divTableHeading"><?php
echo xlt('Included Providers'); ?
></div
>
1815 <div
class="divTableCell indent20">
1818 $ures = sqlStatement("SELECT * FROM users WHERE authorized != 0 AND active = 1 ORDER BY lname, fname");
1819 while ($prov = sqlFetchArray($ures)) {
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
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
>
1868 <div
class="divTableRow">
1869 <div
class="divTableCell divTableHeading"><?php
echo xlt('Postcards'); ?
></div
>
1870 <div
class="divTableCell indent20">
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
>
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>
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']);?>" />
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">
1912 foreach ($logged_in['products']['ordered'] as $service) {
1913 ?
><li
><a href
="<?php echo $service['view']; ?>" target
="_medex"><?php
echo $service['model']; ?
> </a
></li
>
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
>
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">
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
>
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
>
1948 <div
class="col-sm-1"></div
>
1950 <div style
="clear:both;text-align:center;" id
="msg bottom"><br
/>
1958 public function display_recalls($logged_in)
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.
1988 $reminder_bar = "nodisplay";
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);
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
>
2006 if ($GLOBALS['medex_enable'] == '1') {
2010 $last_col_width="nodisplay";
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();">
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();">
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"
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";
2043 if ($count_facs <'1') {
2046 ?
> onchange
="show_this();">
2047 <option value
=""><?php
echo xlt('All Facilities'); ?
></option
>
2048 <?php
echo $select_facs; ?
>
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') {
2062 ?
> onchange
="show_this();">
2063 <option value
="" selected
><?php
echo xlt('All Providers'); ?
></option
>
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']) {
2077 } elseif (!isset($_POST['form_provider'])&& $_SESSION['userauthorized'] && $provid == $_SESSION['authUserID']) {
2080 echo ">" . text($urow['lname']) . ", " . text($urow['fname']) . "\n";
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;">
2096 <tr
><td
class="text-right" style
="vertical-align:bottom;">
2097 <label
for="flow_to"> 
; 
;<?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;">
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
></>
2113 <div
class="col-sm-<?php echo $col_width." ".$last_col_width; ?> text-center" >
2115 if ($GLOBALS['medex_enable'] == '1') {
2117 foreach ($results['events'] as $event) {
2118 if ($event['M_group'] != 'RECALL') {
2121 $icon = $this->get_icon($event['M_type'], 'SCHEDULED');
2122 if ($event['E_timing'] =='1') {
2125 if ($event['E_timing'] =='2') {
2126 $action = "before (PM)";
2128 if ($event['E_timing'] =='3') {
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&g=rec" target
="_medex">
2142 <?php
echo $current_events; ?
>
2149 <div name
="message" id
="message" class="warning"></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
>
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
>
2174 <div
class="tab-content">
2176 <div
class="tab-pane active" id
="tab-all">
2178 $this->recall_board_top();
2179 echo $processed['ALL'];
2180 $this->recall_board_bot();
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.
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');
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');
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');
2225 $
(document
).ready(function() {
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 ?>
2238 $content = ob_get_clean();
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)) {
2258 private function recall_board_process($logged_in, $recalls, $events = '')
2262 if (empty($recalls)) {
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'];
2278 foreach ($recalls as $recall) {
2279 $show = $this->show_progress_recall($recall, $events);
2280 if (!empty($show['DONE'])) {
2283 if (empty($show['status'])) {
2284 $show['status'] = 'whitish';
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>';
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 />";
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 />';
2345 $pat = $this->possibleModalities($recall);
2346 echo $pat['SMS'].$pat['AVM'].$pat['EMAIL'];//escape/translation done in possibleModalities.
2350 if ($show['postcard'] > '') {
2351 echo '<div class="divTableCell text-center postcards">'.text($show['postcard']).'</div>';
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>';
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']).'\')" />
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']).'\',\'\');">
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>';
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 />";
2381 $content = ob_get_clean();
2382 $process['ALL'] .= $content;
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.
2408 * @param string $events
2410 * @internal param string $possibleModalities
2412 public function show_progress_recall($recall, $events = '')
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']='';
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'])));
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.
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 />";
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:
2532 * 2 = days before PM
2537 /** @var TYPE_NAME $events */
2538 foreach ($events as $event) {
2539 if ($event['M_group'] != "RECALL") {
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";
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) {
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') {
2600 $show['status'] = "reddish"; //hey, nothing automatic left to do - manual processing required.
2602 $show['status'] = "yellowish"; //no appt yet but something happened!
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.
2611 $show['status'] = "whitish";
2614 $show['progression'] = '<div onclick="SMS_bot(\'recall_'.$recall['pid'].'\');">'.$show['progression'].'</div>';
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'];
2629 public function possibleModalities($appt)
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';
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';
2651 if (!empty($appt["phone_cell"])) {
2652 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
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';
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';
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
>
2695 <span onclick
="process_this('postcards');" class="fa fa-print fa-lg"></span
>
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
>
2700 <span onclick
="process_this('labels');" class="fa fa-print fa-lg"></span
>
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'); ?
>
2708 <div
class="divTableBody">
2711 private function recall_board_bot()
2718 public function display_add_recall($pid = 'new')
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
>
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']); ?>">
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">
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
="">
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']))); } ?>">
2770 <div
class="divTableRow">
2771 <div
class="divTableCell divTableHeading"><?php
echo xlt('Provider'); ?
></div
>
2772 <div
class="divTableCell">
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.
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) {
2796 echo ">" . text($urow['lname']);
2797 if ($urow['fname']) {
2798 echo ", " . text($urow['fname']);
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%;">
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>";
2818 echo "<option value='" . attr($facrow['id']) . "' $selected>" . text($facrow['name']) . "</option>";
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"> 
; 
;
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
>
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
>
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
>
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
>
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
>
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
>
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
>
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
>
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
>
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
>
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
>
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
>
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 ?>
2910 if ($_SESSION['pid']>'') {
2912 setpatient('<?php echo text($_SESSION['pid
']); ?>');
2916 var xljs_NOTE
= '<?php echo xl("NOTE"); ?>';
2917 var xljs_PthsApSched
= '<?php echo xl("This patient already has an appointment scheduled for"); ?>';
2922 public function icon_template()
2925 <!-- icon rubric
-->
2926 <div style
="position:relative;margin:30px auto;vertical-align:middle;">
2928 $sqlQuery = "SELECT * FROM medex_icons ORDER BY msg_type";
2929 $result = sqlStatement($sqlQuery);
2931 while ($urow = sqlFetchArray($result)) {
2932 $icons['msg_type']['description'] = $urow['i_description'];
2933 $icons[$urow['msg_type']][$urow['msg_status']] = $urow['i_html'];
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');
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
>
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
>
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
>
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
>
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>
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.
3028 public function SMS_bot($logged_in)
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'];
3051 public function syncPat($pid, $logged_in)
3053 if ($pid == 'pat_list') {
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'];
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'])) {
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)
3092 <div
class="col-sm-10 text-center col-xs-offset-1">
3094 <div
class="title">MedEx
</div
>
3095 <div
class="row showReminders ">
3096 <div
class="col-sm-10 text-center col-xs-offset-1">
3098 <?php
echo xlt('Using technology to improve productivity'); ?
>.
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
>
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
>
3123 <div
class="text-center row showReminders">
3124 <input value
="<?php echo xla('Sign-up'); ?>" onclick
="goReminderRecall('setup&stage=2');" class="btn btn-primary">
3132 } else if ($stage =='2') {
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'); ?
>
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
>
3154 <div
class="divTableRow">
3155 <div
class="divTableCell divTableHeading">
3156 <?php
echo xlt('Password'); ?
>
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
>
3164 <div
class="divTableRow">
3165 <div
class="divTableCell divTableHeading">
3166 <?php
echo xlt('Repeat'); ?
>
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
>
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
>
3183 <div
class="align-center row showReminders">
3184 <input id
="Register" class="btn btn-primary" value
="<?php echo xla('Register'); ?>" />
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;">×
;</button
>
3194 <h2
class="modal-title" style
="font-weight:600;">Sign
-Up Confirmation
</h2
>
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
/>
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>
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>
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();
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],
3247 badWords: ["password", "qwerty", "12345"],
3248 badSequenceLength: 4
3252 function validatePassword (pw, options) {
3253 // default options (allows any password)
3257 alpha: 0, /* lower + upper */
3260 length: [0, Infinity],
3261 custom: [ /* regexes and/or functions */ ],
3263 badSequenceLength: 0,
3264 noQwertySequences: false,
3268 for (var property in options)
3269 o[property] = options[property];
3280 // enforce min/max length
3281 if (pw.length < o.length[0] || pw.length > o.length[1])
3284 // enforce lower/upper/alpha/numeric/special rules
3286 if ((pw.match(re[rule]) || []).length < o[rule])
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)
3296 // enforce the no sequential, identical characters rule
3297 if (o.noSequential && /([\S\s])\1/.test(pw))
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);
3311 lower.indexOf(seq) > -1 ||
3312 upper.indexOf(seq) > -1 ||
3313 numbers.indexOf(seq) > -1 ||
3314 (o.noQwertySequences && qwerty.indexOf(seq) > -1)
3321 // enforce custom regex/function rules
3322 for (i = 0; i < o.custom.length; i++) {
3324 if (rule instanceof RegExp) {
3327 } else if (rule instanceof Function) {
3336 $(document).ready(function() {
3337 $("#Register").click(function() {
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();
3351 .done(function(result) {
3352 obj = JSON.parse(result);
3353 $("#answer").html(obj.show);
3354 $("#ihvread").addClass('nodisplay
');
3355 $('#myModal').modal('toggle');
3357 url
="https://www.medexbank.com/login/"+email
;
3358 window
.open(url
, 'clinical', 'resizable=1,scrollbars=1');
3363 $
("#new_email").blur(function(e
) {
3365 var email
= $
("#new_email").val();
3366 if (validateEmail(email
)) {
3367 $
("#email_help").addClass('nodisplay');
3368 $
("#email_check").removeClass('nodisplay');
3370 $
("#email_help").removeClass('nodisplay');
3371 $
("#email_check").addClass('nodisplay');
3374 $
("#new_password,#new_rpassword").keyup(function(e
) {
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');
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');
3393 $
("#pwd_rhelp").removeClass('nodisplay');
3394 $
("#pwd_rcheck").addClass('nodisplay');
3403 public function autoReg($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'])) {
3414 } else if (isset($response['error'])) {
3415 $this->lastError
= $response['error'];
3423 public $lastError = '';
3435 public function __construct($url, $sessionFile = 'cookiejar_MedExAPI')
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)
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']));
3491 public function login($force = '')
3494 $query = "SELECT * FROM medex_prefs";
3495 $info = sqlFetchArray(sqlStatement($query));
3497 empty($info['ME_username']) ||
3498 empty($info['ME_api_key']) ||
3499 empty($info['MedEx_id'])) {
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) {
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'])) {
3519 } else if (isset($info['error'])) {
3520 $this->lastError
= $info['error'];
3521 sqlQuery("UPDATE `background_services` SET `active`='0' WHERE `name`='MedEx'");
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);
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);
3553 if (!empty($appt["phone_cell"])) {
3554 $phone = preg_replace("/[^0-9]/", "", $appt["phone_cell"]);
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);
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...
3569 return array(false,false);
3574 class InvalidDataException
extends \Exception