bug fix #445
[openemr.git] / interface / main / calendar / find_appt_popup.php
blobc8c9df59845a16a142972371e81f57493b1c7dc8
1 <?php
2 /**
4 * Script to find open appointmnent slots
6 * Copyright (C) 2005-2013 Rod Roark <rod@sunsetsystems.com>
8 * LICENSE: This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
19 * @package OpenEMR
20 * @author Rod Roark <rod@sunsetsystems.com>
21 * @author Ian Jardine ( github.com/epsdky )
22 * @author Roberto Vasquez <robertogagliotta@gmail.com>
23 * @link http://www.open-emr.org
26 $fake_register_globals=false;
27 $sanitize_all_escapes=true;
29 include_once("../../globals.php");
30 include_once("$srcdir/patient.inc");
31 require_once(dirname(__FILE__)."/../../../library/appointments.inc.php");
32 require_once($GLOBALS['incdir']."/main/holidays/Holidays_Controller.php");
35 <script type="text/javascript" src="<?php echo $webroot ?>/interface/main/tabs/js/include_opener.js"></script>
36 <?php
37 // check access controls
38 if (!acl_check('patients','appt','',array('write','wsome') ))
39 die(xlt('Access not allowed'));
41 // If the caller is updating an existing event, then get its ID so
42 // we don't count it as a reserved time slot.
43 $eid = empty($_REQUEST['eid']) ? 0 : 0 + $_REQUEST['eid'];
45 $input_catid = $_REQUEST['catid'];
47 // Record an event into the slots array for a specified day.
48 function doOneDay($catid, $udate, $starttime, $duration, $prefcatid) {
49 global $slots, $slotsecs, $slotstime, $slotbase, $slotcount, $input_catid;
50 $udate = strtotime($starttime, $udate);
51 if ($udate < $slotstime) return;
52 $i = (int) ($udate / $slotsecs) - $slotbase;
53 $iend = (int) (($duration + $slotsecs - 1) / $slotsecs) + $i;
54 if ($iend > $slotcount) $iend = $slotcount;
55 if ($iend <= $i) $iend = $i + 1;
56 for (; $i < $iend; ++$i) {
57 if ($catid == 2) { // in office
58 // If a category ID was specified when this popup was invoked, then select
59 // only IN events with a matching preferred category or with no preferred
60 // category; other IN events are to be treated as OUT events.
61 if ($input_catid) {
62 if ($prefcatid == $input_catid || !$prefcatid)
63 $slots[$i] |= 1;
64 else
65 $slots[$i] |= 2;
66 } else {
67 $slots[$i] |= 1;
69 break; // ignore any positive duration for IN
70 } else if ($catid == 3) { // out of office
71 $slots[$i] |= 2;
72 break; // ignore any positive duration for OUT
73 } else { // all other events reserve time
74 $slots[$i] |= 4;
79 // seconds per time slot
80 $slotsecs = $GLOBALS['calendar_interval'] * 60;
83 $catslots = 1;
84 if ($input_catid) {
85 $srow = sqlQuery("SELECT pc_duration FROM openemr_postcalendar_categories WHERE pc_catid = ?", array($input_catid) );
86 if ($srow['pc_duration']) $catslots = ceil($srow['pc_duration'] / $slotsecs);
89 $info_msg = "";
91 $searchdays = 7; // default to a 1-week lookahead
92 if ($_REQUEST['searchdays']) $searchdays = $_REQUEST['searchdays'];
94 // Get a start date.
95 if ($_REQUEST['startdate'] && preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/",
96 $_REQUEST['startdate'], $matches))
98 $sdate = $matches[1] . '-' . $matches[2] . '-' . $matches[3];
99 } else {
100 $sdate = date("Y-m-d");
103 // Get an end date - actually the date after the end date.
104 preg_match("/(\d\d\d\d)\D*(\d\d)\D*(\d\d)/", $sdate, $matches);
105 $edate = date("Y-m-d",
106 mktime(0, 0, 0, $matches[2], $matches[3] + $searchdays, $matches[1]));
108 // compute starting time slot number and number of slots.
109 $slotstime = strtotime("$sdate 00:00:00");
110 $slotetime = strtotime("$edate 00:00:00");
111 $slotbase = (int) ($slotstime / $slotsecs);
112 $slotcount = (int) ($slotetime / $slotsecs) - $slotbase;
114 if ($slotcount <= 0 || $slotcount > 100000) die(xlt("Invalid date range"));
116 $slotsperday = (int) (60 * 60 * 24 / $slotsecs);
118 // Compute the number of time slots for the given event duration, or if
119 // none is given then assume the default category duration.
120 $evslots = $catslots;
121 if (isset($_REQUEST['evdur'])) {
123 // bug fix #445 -- Craig Bezuidenhout 09 Aug 2016
124 // if the event duration is less than or equal to zero, use the global calander interval
125 // if the global calendar interval is less than or equal to zero, use 10 mins
126 if(intval($_REQUEST['evdur']) <= 0){
127 if(intval($GLOBALS['calendar_interval']) <= 0){
128 $_REQUEST['evdur'] = 10;
129 }else{
130 $_REQUEST['evdur'] = intval($GLOBALS['calendar_interval']);
133 $evslots = 60 * $_REQUEST['evdur'];
134 $evslots = (int) (($evslots + $slotsecs - 1) / $slotsecs);
137 // If we have a provider, search.
139 if ($_REQUEST['providerid']) {
140 $providerid = $_REQUEST['providerid'];
142 // Create and initialize the slot array. Values are bit-mapped:
143 // bit 0 = in-office occurs here
144 // bit 1 = out-of-office occurs here
145 // bit 2 = reserved
146 // So, values may range from 0 to 7.
148 $slots = array_pad(array(), $slotcount, 0);
150 $sqlBindArray = array();
152 // Note there is no need to sort the query results.
153 $query = "SELECT pc_eventDate, pc_endDate, pc_startTime, pc_duration, " .
154 "pc_recurrtype, pc_recurrspec, pc_alldayevent, pc_catid, pc_prefcatid " .
155 "FROM openemr_postcalendar_events " .
156 "WHERE pc_aid = ? AND " .
157 "pc_eid != ? AND " .
158 "((pc_endDate >= ? AND pc_eventDate < ? ) OR " .
159 "(pc_endDate = '0000-00-00' AND pc_eventDate >= ? AND pc_eventDate < ?))";
161 array_push($sqlBindArray, $providerid, $eid, $sdate, $edate, $sdate, $edate);
163 // phyaura whimmel facility filtering
164 if ($_REQUEST['facility'] > 0 ) {
165 $facility = $_REQUEST['facility'];
166 $query .= " AND pc_facility = ?";
167 array_push($sqlBindArray, $facility);
169 // end facility filtering whimmel 29apr08
171 //////
172 $events2 = fetchEvents($sdate, $edate, null, null, false, 0, $sqlBindArray, $query);
173 foreach($events2 as $row) {
174 $thistime = strtotime($row['pc_eventDate'] . " 00:00:00");
175 doOneDay($row['pc_catid'], $thistime, $row['pc_startTime'],
176 $row['pc_duration'], $row['pc_prefcatid']);
178 //////
180 // Mark all slots reserved where the provider is not in-office.
181 // Actually we could do this in the display loop instead.
182 $inoffice = false;
183 for ($i = 0; $i < $slotcount; ++$i) {
184 if (($i % $slotsperday) == 0) $inoffice = false;
185 if ($slots[$i] & 1) $inoffice = true;
186 if ($slots[$i] & 2) $inoffice = false;
187 if (! $inoffice) { $slots[$i] |= 4; $prov[$i] = $i; }
190 $ckavail = true;
191 // If the requested date is a holiday/closed date we need to alert the user about it and let him choose if he wants to proceed
192 //////
193 $is_holiday=false;
194 $holidays_controller = new Holidays_Controller();
195 $holidays = $holidays_controller->get_holidays_by_date_range($sdate,$edate);
196 if(in_array($sdate,$holidays)){
197 $is_holiday=true;
198 $ckavail=true;
200 //////
204 // The cktime parameter is a number of minutes into the starting day of a
205 // tentative appointment that is to be checked. If it is present then we are
206 // being asked to check if this indicated slot is available, and to submit
207 // the opener and go away quietly if it is. If it's not then we have more
208 // work to do.
210 if (isset($_REQUEST['cktime'])) {
211 $cktime = 0 + $_REQUEST['cktime'];
212 $ckindex = (int) ($cktime * 60 / $slotsecs);
213 for ($j = $ckindex; $j < $ckindex + $evslots; ++$j) {
214 if ($slots[$j] >= 4) {
215 $ckavail = false;
216 $isProv = FALSE;
217 if(isset($prov[$j])){
218 $isProv = 'TRUE';
223 if ($ckavail) {
224 // The chosen appointment time is available.
225 echo "<html>"
226 . "<script language='JavaScript'>\n";
227 echo "function mytimeout() {\n";
228 echo " opener.top.restoreSession();\n";
229 echo " opener.document.forms[0].submit();\n";
230 echo " window.close();\n";
231 echo "}\n";
232 echo "</script></head><body onload='setTimeout(\"mytimeout()\",250);'>" .
233 xlt('Time slot is open, saving event') . "...</body></html>";
234 exit();
236 // The appointment slot is not available. A message will be displayed
237 // after this page is loaded.
240 <html>
241 <head>
242 <?php html_header_show(); ?>
243 <title><?php echo xlt('Find Available Appointments'); ?></title>
244 <link rel="stylesheet" href='<?php echo $css_header ?>' type='text/css'>
246 <!-- for the pop up calendar -->
247 <style type="text/css">@import url(../../../library/dynarch_calendar.css);</style>
248 <script type="text/javascript" src="../../../library/dynarch_calendar.js"></script>
249 <?php include_once("{$GLOBALS['srcdir']}/dynarch_calendar_en.inc.php"); ?>
250 <script type="text/javascript" src="../../../library/dynarch_calendar_setup.js"></script>
252 <!-- for ajax-y stuff -->
253 <script type="text/javascript" src="<?php echo $GLOBALS['webroot'] ?>/library/js/jquery-1.2.2.min.js"></script>
255 <script language="JavaScript">
257 function setappt(year,mon,mday,hours,minutes) {
258 if (opener.closed || ! opener.setappt)
259 alert('<?php echo xls('The destination form was closed; I cannot act on your selection.'); ?>');
260 else
261 opener.setappt(year,mon,mday,hours,minutes);
262 window.close();
263 return false;
266 </script>
268 <style>
269 form {
270 /* this eliminates the padding normally around a FORM tag */
271 padding: 0px;
272 margin: 0px;
274 #searchCriteria {
275 text-align: center;
276 width: 100%;
277 font-size: 0.8em;
278 background-color: #ddddff;
279 font-weight: bold;
280 padding: 3px;
282 #searchResultsHeader {
283 width: 100%;
284 background-color: lightgrey;
286 #searchResultsHeader table {
287 width: 96%; /* not 100% because the 'searchResults' table has a scrollbar */
288 border-collapse: collapse;
290 #searchResultsHeader th {
291 font-size: 0.7em;
293 #searchResults {
294 width: 100%;
295 height: 350px;
296 overflow: auto;
299 .srDate { width: 20%; }
300 .srTimes { width: 80%; }
302 #searchResults table {
303 width: 100%;
304 border-collapse: collapse;
305 background-color: white;
307 #searchResults td {
308 font-size: 0.7em;
309 border-bottom: 1px solid gray;
310 padding: 1px 5px 1px 5px;
312 .highlight { background-color: #ff9; }
313 .blue_highlight { background-color: #336699; color: white; }
314 #am {
315 border-bottom: 1px solid lightgrey;
316 color: #00c;
318 #pm { color: #c00; }
319 #pm a { color: #c00; }
320 </style>
322 </head>
324 <body class="body_top">
326 <div id="searchCriteria">
327 <form method='post' name='theform' action='find_appt_popup.php?providerid=<?php echo attr($providerid) ?>&catid=<?php echo attr($input_catid) ?>'>
328 <?php echo xlt('Start date:'); ?>
329 <input type='text' name='startdate' id='startdate' size='10' value='<?php echo attr($sdate) ?>'
330 title='<?php echo xla('yyyy-mm-dd starting date for search'); ?> '/>
331 <img src='../../pic/show_calendar.gif' align='absbottom' width='24' height='22'
332 id='img_date' border='0' alt='[?]' style='cursor:pointer'
333 title='<?php echo xla('Click here to choose a date'); ?>'>
334 <?php echo xlt('for'); ?>
335 <input type='text' name='searchdays' size='3' value='<?php echo attr($searchdays) ?>'
336 title='<?php echo xla('Number of days to search from the start date'); ?>' />
337 <?php echo xlt('days'); ?>&nbsp;
338 <input type='submit' value='<?php echo xla('Search'); ?>'>
339 </div>
341 <?php if (!empty($slots)) : ?>
343 <div id="searchResultsHeader">
344 <table>
345 <tr>
346 <th class="srDate"><?php echo xlt('Day'); ?></th>
347 <th class="srTimes"><?php echo xlt('Available Times'); ?></th>
348 </tr>
349 </table>
350 </div>
352 <div id="searchResults">
353 <table>
354 <?php
355 $lastdate = "";
356 $ampmFlag = "am"; // establish an AM-PM line break flag
357 for ($i = 0; $i < $slotcount; ++$i) {
359 $available = true;
360 for ($j = $i; $j < $i + $evslots; ++$j) {
361 if ($slots[$j] >= 4) $available = false;
363 if (!$available) continue; // skip reserved slots
365 $utime = ($slotbase + $i) * $slotsecs;
366 $thisdate = date("Y-m-d", $utime);
367 if ($thisdate != $lastdate) {
368 // if a new day, start a new row
369 if ($lastdate) {
370 echo "</div>";
371 echo "</td>\n";
372 echo " </tr>\n";
374 $lastdate = $thisdate;
375 echo " <tr class='oneresult'>\n";
376 echo " <td class='srDate'>" . date("l", $utime)."<br>".date("Y-m-d", $utime) . "</td>\n";
377 echo " <td class='srTimes'>";
378 echo "<div id='am'>AM ";
379 $ampmFlag = "am"; // reset the AMPM flag
382 $ampm = date('a', $utime);
383 if ($ampmFlag != $ampm) { echo "</div><div id='pm'>PM "; }
384 $ampmFlag = $ampm;
386 $atitle = "Choose ".date("h:i a", $utime);
387 $adate = getdate($utime);
388 $anchor = "<a href='' onclick='return setappt(" .
389 $adate['year'] . "," .
390 $adate['mon'] . "," .
391 $adate['mday'] . "," .
392 $adate['hours'] . "," .
393 $adate['minutes'] . ")'".
394 " title='$atitle' alt='$atitle'".
395 ">";
396 echo (strlen(date('g',$utime)) < 2 ? "<span style='visibility:hidden'>0</span>" : "") .
397 $anchor . date("g:i", $utime) . "</a> ";
399 // If the duration is more than 1 slot, increment $i appropriately.
400 // This is to avoid reporting available times on undesirable boundaries.
401 $i += $evslots - 1;
403 if ($lastdate) {
404 echo "</td>\n";
405 echo " </tr>\n";
406 } else {
407 echo " <tr><td colspan='2'> " . xlt('No openings were found for this period.') . "</td></tr>\n";
410 </table>
411 </div>
412 </div>
413 <?php endif; ?>
415 </form>
416 </body>
418 <!-- for the pop up calendar -->
419 <script language='JavaScript'>
420 Calendar.setup({inputField:"startdate", ifFormat:"%Y-%m-%d", button:"img_date"});
422 // jQuery stuff to make the page a little easier to use
424 $(document).ready(function(){
425 $(".oneresult").mouseover(function() { $(this).toggleClass("highlight"); });
426 $(".oneresult").mouseout(function() { $(this).toggleClass("highlight"); });
427 $(".oneresult a").mouseover(function () { $(this).toggleClass("blue_highlight"); $(this).children().toggleClass("blue_highlight"); });
428 $(".oneresult a").mouseout(function() { $(this).toggleClass("blue_highlight"); $(this).children().toggleClass("blue_highlight"); });
429 //$(".event").dblclick(function() { EditEvent(this); });
433 <?php if (!$ckavail) { ?>
434 <?php if (acl_check('patients','appt','','write')) {
435 if($is_holiday){?>
436 if (confirm('<?php echo xls('On this date there is a holiday, use it anyway?'); ?>')) {
437 opener.top.restoreSession();
438 opener.document.forms[0].submit();
439 window.close();
441 <?php }else{
442 if($isProv): ?>
443 if (confirm('<?php echo xls('Provider not available, use it anyway?'); ?>')) {
444 <?php else: ?>
445 if (confirm('<?php echo xls('This appointment slot is already used, use it anyway?'); ?>')) {
446 <?php endif; ?>
447 opener.top.restoreSession();
448 opener.document.forms[0].submit();
449 window.close();
451 <?php } ?>
452 <?php } else {
453 if($is_holiday){?>
454 alert('<?php echo xls('On this date there is a holiday, use it anyway?'); ?>');
455 <?php }else{
456 if($isProv): ?>
457 alert('<?php echo xls('Provider not available, please choose another.'); ?>');
458 <?php else: ?>
459 alert('<?php echo xls('This appointment slot is already used, please choose another.'); ?>');
460 <?php endif; ?>
461 <?php } ?>//close if is holiday
462 <?php } ?>
463 <?php } ?>
466 </script>
468 </html>