Fee sheet and Codes revenue code (#7415)
[openemr.git] / interface / patient_file / summary / demographics.php
blobae2b0229d1e6da540f9cc9835e9633ae89ee9b7f
1 <?php
3 /**
5 * Patient summary screen.
7 * @package OpenEMR
8 * @link http://www.open-emr.org
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @author Sharon Cohen <sharonco@matrix.co.il>
11 * @author Stephen Waite <stephen.waite@cmsvt.com>
12 * @author Ranganath Pathak <pathak@scrs1.org>
13 * @author Tyler Wrenn <tyler@tylerwrenn.com>
14 * @author Robert Down <robertdown@live.com>
15 * @author Stephen Nielson <snielson@discoverandchange.com>
16 * @copyright Copyright (c) 2017-2020 Brady Miller <brady.g.miller@gmail.com>
17 * @copyright Copyright (c) 2017 Sharon Cohen <sharonco@matrix.co.il>
18 * @copyright Copyright (c) 2018-2020 Stephen Waite <stephen.waite@cmsvt.com>
19 * @copyright Copyright (c) 2018 Ranganath Pathak <pathak@scrs1.org>
20 * @copyright Copyright (c) 2020 Tyler Wrenn <tyler@tylerwrenn.com>
21 * @copyright Copyright (c) 2021-2022 Robert Down <robertdown@live.com
22 * @copyright Copyright (c) 2024 Care Management Solutions, Inc. <stephen.waite@cmsvt.com>
23 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
26 require_once("../../globals.php");
27 require_once("$srcdir/lists.inc.php");
28 require_once("$srcdir/patient.inc.php");
29 require_once("$srcdir/options.inc.php");
30 require_once("../history/history.inc.php");
31 require_once("$srcdir/clinical_rules.php");
32 require_once("$srcdir/group.inc.php");
33 require_once(__DIR__ . "/../../../library/appointments.inc.php");
35 use OpenEMR\Billing\EDI270;
36 use OpenEMR\Common\Acl\AclMain;
37 use OpenEMR\Common\Csrf\CsrfUtils;
38 use OpenEMR\Common\Session\SessionUtil;
39 use OpenEMR\Common\Twig\TwigContainer;
40 use OpenEMR\Core\Header;
41 use OpenEMR\Events\Patient\Summary\Card\RenderEvent as CardRenderEvent;
42 use OpenEMR\Events\Patient\Summary\Card\SectionEvent;
43 use OpenEMR\Events\Patient\Summary\Card\RenderModel;
44 use OpenEMR\Events\Patient\Summary\Card\CardInterface;
45 use OpenEMR\Events\PatientDemographics\ViewEvent;
46 use OpenEMR\Events\PatientDemographics\RenderEvent;
47 use OpenEMR\FHIR\SMART\SmartLaunchController;
48 use OpenEMR\Menu\PatientMenuRole;
49 use OpenEMR\OeUI\OemrUI;
50 use OpenEMR\Patient\Cards\PortalCard;
51 use OpenEMR\Reminder\BirthdayReminder;
52 use OpenEMR\Services\AllergyIntoleranceService;
53 use OpenEMR\Services\ConditionService;
54 use OpenEMR\Services\ImmunizationService;
55 use OpenEMR\Services\PatientIssuesService;
56 use OpenEMR\Services\PatientService;
57 use Symfony\Component\EventDispatcher\EventDispatcher;
58 use OpenEMR\Patient\Cards\InsuranceViewCard;
59 use OpenEMR\Patient\Cards\BillingViewCard;
60 use OpenEMR\Patient\Cards\DemographicsViewCard;
62 $twig = new TwigContainer(null, $GLOBALS['kernel']);
64 // Set session for pid (via setpid). Also set session for encounter (if applicable)
65 if (isset($_GET['set_pid'])) {
66 require_once("$srcdir/pid.inc.php");
67 setpid($_GET['set_pid']);
68 $ptService = new PatientService();
69 $newPatient = $ptService->findByPid($pid);
70 $ptService->touchRecentPatientList($newPatient);
71 if (isset($_GET['set_encounterid']) && ((int)$_GET['set_encounterid'] > 0)) {
72 $encounter = (int)$_GET['set_encounterid'];
73 SessionUtil::setSession('encounter', $encounter);
77 // Note: it would eventually be a good idea to move this into
78 // it's own module that people can remove / add if they don't
79 // want smart support in their system.
80 $smartLaunchController = new SMARTLaunchController($GLOBALS["kernel"]->getEventDispatcher());
81 $smartLaunchController->registerContextEvents();
83 /**
84 * @var EventDispatcher
86 $ed = $GLOBALS['kernel']->getEventDispatcher();
88 $active_reminders = false;
89 $all_allergy_alerts = false;
90 if ($GLOBALS['enable_cdr']) {
91 //CDR Engine stuff
92 if ($GLOBALS['enable_allergy_check'] && $GLOBALS['enable_alert_log']) {
93 //Check for new allergies conflicts and throw popup if any exist(note need alert logging to support this)
94 $new_allergy_alerts = allergy_conflict($pid, 'new', $_SESSION['authUser']);
95 if (!empty($new_allergy_alerts)) {
96 $pod_warnings = '';
97 foreach ($new_allergy_alerts as $new_allergy_alert) {
98 $pod_warnings .= js_escape($new_allergy_alert) . ' + "\n"';
100 $allergyWarningMessage = '<script>alert(' . xlj('WARNING - FOLLOWING ACTIVE MEDICATIONS ARE ALLERGIES') . ' + "\n" + ' . $pod_warnings . ')</script>';
104 if ((empty($_SESSION['alert_notify_pid']) || ($_SESSION['alert_notify_pid'] != $pid)) && isset($_GET['set_pid']) && $GLOBALS['enable_cdr_crp']) {
105 // showing a new patient, so check for active reminders and allergy conflicts, which use in active reminder popup
106 $active_reminders = active_alert_summary($pid, "reminders-due", '', 'default', $_SESSION['authUser'], true);
107 if ($GLOBALS['enable_allergy_check']) {
108 $all_allergy_alerts = allergy_conflict($pid, 'all', $_SESSION['authUser'], true);
111 SessionUtil::setSession('alert_notify_pid', $pid);
112 // can not output html until after above setSession call
113 if (!empty($allergyWarningMessage)) {
114 echo $allergyWarningMessage;
117 //Check to see is only one insurance is allowed
118 if ($GLOBALS['insurance_only_one']) {
119 $insurance_array = array('primary');
120 } else {
121 $insurance_array = array('primary', 'secondary', 'tertiary');
124 function print_as_money($money)
126 preg_match("/(\d*)\.?(\d*)/", $money, $moneymatches);
127 $tmp = wordwrap(strrev($moneymatches[1]), 3, ",", 1);
128 $ccheck = strrev($tmp);
129 if ($ccheck[0] == ",") {
130 $tmp = substr($ccheck, 1, strlen($ccheck) - 1);
133 if ($moneymatches[2] != "") {
134 return "$ " . strrev($tmp) . "." . $moneymatches[2];
135 } else {
136 return "$ " . strrev($tmp);
140 // get an array from Photos category
141 function pic_array($pid, $picture_directory)
143 $pics = array();
144 $sql_query = "select documents.id from documents join categories_to_documents " .
145 "on documents.id = categories_to_documents.document_id " .
146 "join categories on categories.id = categories_to_documents.category_id " .
147 "where categories.name like ? and documents.foreign_id = ? and documents.deleted = 0";
148 if ($query = sqlStatement($sql_query, array($picture_directory, $pid))) {
149 while ($results = sqlFetchArray($query)) {
150 array_push($pics, $results['id']);
154 return ($pics);
157 // Get the document ID's in a specific catg.
158 // this is only used in one place, here for id photos
159 function get_document_by_catg($pid, $doc_catg, $limit = 1)
161 $results = null;
163 if ($pid and $doc_catg) {
164 $query = sqlStatement("SELECT d.id, d.date, d.url
165 FROM documents AS d, categories_to_documents AS cd, categories AS c
166 WHERE d.foreign_id = ?
167 AND cd.document_id = d.id
168 AND c.id = cd.category_id
169 AND c.name LIKE ?
170 ORDER BY d.date DESC LIMIT " . escape_limit($limit), array($pid, $doc_catg));
172 while ($result = sqlFetchArray($query)) {
173 $results[] = $result['id'];
175 return ($results ?? false);
178 function isPortalEnabled(): bool
180 if (
181 !$GLOBALS['portal_onsite_two_enable']
183 return false;
186 return true;
189 function isPortalSiteAddressValid(): bool
191 if (
192 // maybe can use filter_var() someday but the default value in GLOBALS
193 // fails with FILTER_VALIDATE_URL
194 !isset($GLOBALS['portal_onsite_two_address'])
196 return false;
199 return true;
202 function isPortalAllowed($pid): bool
204 $return = false;
206 $portalStatus = sqlQuery("SELECT allow_patient_portal FROM patient_data WHERE pid = ?", [$pid]);
207 if ($portalStatus['allow_patient_portal'] == 'YES') {
208 $return = true;
210 return $return;
213 function isApiAllowed($pid): bool
215 $return = false;
217 $apiStatus = sqlQuery("SELECT prevent_portal_apps FROM patient_data WHERE pid = ?", [$pid]);
218 if (strtoupper($apiStatus['prevent_portal_apps'] ?? '') != 'YES') {
219 $return = true;
221 return $return;
224 function areCredentialsCreated($pid): bool
226 $return = false;
227 $credentialsCreated = sqlQuery("SELECT date_created FROM `patient_access_onsite` WHERE `pid`=?", [$pid]);
228 if ($credentialsCreated['date_created'] ?? null) {
229 $return = true;
232 return $return;
235 function isContactEmail($pid): bool
237 $return = false;
239 $email = sqlQuery("SELECT email, email_direct FROM patient_data WHERE pid = ?", [$pid]);
240 if (!empty($email['email']) || !empty($email['email_direct'])) {
241 $return = true;
243 return $return;
246 function isEnforceSigninEmailPortal(): bool
248 if (
249 $GLOBALS['enforce_signin_email']
251 return true;
254 return false;
257 function deceasedDays($days_deceased)
259 $deceased_days = intval($days_deceased['days_deceased'] ?? '');
260 if ($deceased_days == 0) {
261 $num_of_days = xl("Today");
262 } elseif ($deceased_days == 1) {
263 $num_of_days = $deceased_days . " " . xl("day ago");
264 } elseif ($deceased_days > 1 && $deceased_days < 90) {
265 $num_of_days = $deceased_days . " " . xl("days ago");
266 } elseif ($deceased_days >= 90 && $deceased_days < 731) {
267 $num_of_days = "~" . round($deceased_days / 30) . " " . xl("months ago"); // function intdiv available only in php7
268 } elseif ($deceased_days >= 731) {
269 $num_of_days = xl("More than") . " " . round($deceased_days / 365) . " " . xl("years ago");
272 if (strlen($days_deceased['date_deceased'] ?? '') > 10 && $GLOBALS['date_display_format'] < 1) {
273 $deceased_date = substr($days_deceased['date_deceased'], 0, 10);
274 } else {
275 $deceased_date = oeFormatShortDate($days_deceased['date_deceased'] ?? '');
278 return xlt("Deceased") . " - " . text($deceased_date) . " (" . text($num_of_days) . ")";
281 $deceased = is_patient_deceased($pid);
284 // Display image in 'widget style'
285 function image_widget($doc_id, $doc_catg)
287 global $pid, $web_root;
288 $docobj = new Document($doc_id);
289 $image_file = $docobj->get_url_file();
290 $image_file_name = $docobj->get_name();
291 $image_width = $GLOBALS['generate_doc_thumb'] == 1 ? '' : 'width=100';
292 $extension = substr($image_file_name, strrpos($image_file_name, "."));
293 $viewable_types = array('.png', '.jpg', '.jpeg', '.png', '.bmp', '.PNG', '.JPG', '.JPEG', '.PNG', '.BMP');
294 if (in_array($extension, $viewable_types)) { // extension matches list
295 $to_url = "<td> <a href = '$web_root" .
296 "/controller.php?document&retrieve&patient_id=" . attr_url($pid) . "&document_id=" . attr_url($doc_id) . "&as_file=false&original_file=true&disable_exit=false&show_original=true'" .
297 " onclick='top.restoreSession();' class='image_modal'>" .
298 " <img src = '$web_root" .
299 "/controller.php?document&retrieve&patient_id=" . attr_url($pid) . "&document_id=" . attr_url($doc_id) . "&as_file=false'" .
300 " $image_width alt='" . attr($doc_catg) . ":" . attr($image_file_name) . "'> </a> </td> <td class='align-middle'>" .
301 text($doc_catg) . '<br />&nbsp;' . text($image_file_name) . "</td>";
302 } else {
303 $to_url = "<td> <a href='" . $web_root . "/controller.php?document&retrieve" .
304 "&patient_id=" . attr_url($pid) . "&document_id=" . attr_url($doc_id) . "'" .
305 " onclick='top.restoreSession()' class='btn btn-primary btn-sm'>" .
306 "<span>" .
307 xlt("View") . "</a> &nbsp;" .
308 text("$doc_catg - $image_file_name") .
309 "</span> </td>";
312 echo "<table><tr>";
313 echo $to_url;
314 echo "</tr></table>";
317 // Determine if the Vitals form is in use for this site.
318 $tmp = sqlQuery("SELECT count(*) AS count FROM registry WHERE directory = 'vitals' AND state = 1");
319 $vitals_is_registered = $tmp['count'];
321 // Get patient/employer/insurance information.
323 $result = getPatientData($pid, "*, DATE_FORMAT(DOB,'%Y-%m-%d') as DOB_YMD");
324 $result2 = getEmployerData($pid);
325 $result3 = getInsuranceData(
326 $pid,
327 "primary",
328 "copay,
329 provider,
330 DATE_FORMAT(`date`,'%Y-%m-%d') as effdate,
331 DATE_FORMAT(`date_end`,'%Y-%m-%d') as effdate_end"
333 $insco_name = "";
334 if (!empty($result3['provider'])) { // Use provider in case there is an ins record w/ unassigned insco
335 $insco_name = getInsuranceProvider($result3['provider']);
338 $arrOeUiSettings = array(
339 'page_id' => 'core.mrd',
340 'heading_title' => xl('Medical Record Dashboard'),
341 'include_patient_name' => true,
342 'expandable' => true,
343 'expandable_files' => array('demographics_xpd'), //all file names need suffix _xpd
344 'action' => "", //conceal, reveal, search, reset, link or back
345 'action_title' => "",
346 'action_href' => "", //only for actions - reset, link or back
347 'show_help_icon' => true,
348 'help_file_name' => "medical_dashboard_help.php"
350 $oemr_ui = new OemrUI($arrOeUiSettings);
352 <!DOCTYPE html>
353 <html>
355 <head>
356 <?php
357 Header::setupHeader(['common','utility']);
358 require_once("$srcdir/options.js.php");
360 <script>
361 // Process click on diagnosis for referential cds popup.
362 function referentialCdsClick(codetype, codevalue) {
363 top.restoreSession();
364 // Force a new window instead of iframe to address cross site scripting potential
365 dlgopen('../education.php?type=' + encodeURIComponent(codetype) + '&code=' + encodeURIComponent(codevalue), '_blank', 1024, 750,true);
368 function oldEvt(apptdate, eventid) {
369 let title = <?php echo xlj('Appointments'); ?>;
370 dlgopen('../../main/calendar/add_edit_event.php?date=' + encodeURIComponent(apptdate) + '&eid=' + encodeURIComponent(eventid), '_blank', 800, 500, '', title);
373 function advdirconfigure() {
374 dlgopen('advancedirectives.php', '_blank', 400, 500);
377 function refreshme() {
378 top.restoreSession();
379 location.reload();
382 // Process click on Delete link.
383 function deleteme() { // @todo don't think this is used any longer!!
384 dlgopen('../deleter.php?patient=' + <?php echo js_url($pid); ?> + '&csrf_token_form=' + <?php echo js_url(CsrfUtils::collectCsrfToken()); ?>, '_blank', 500, 450, '', '', {
385 allowResize: false,
386 allowDrag: false,
387 dialogId: 'patdel',
388 type: 'iframe'
390 return false;
393 // Called by the deleteme.php window on a successful delete.
394 function imdeleted() {
395 top.clearPatient();
398 function newEvt() {
399 let title = <?php echo xlj('Appointments'); ?>;
400 let url = '../../main/calendar/add_edit_event.php?patientid=' + <?php echo js_url($pid); ?>;
401 dlgopen(url, '_blank', 800, 500, '', title);
402 return false;
405 function toggleIndicator(target, div) {
406 // <i id="show_hide" class="fa fa-lg small fa-eye-slash" title="Click to Hide"></i>
407 $mode = $(target).find(".indicator").text();
408 if ($mode == <?php echo xlj('collapse'); ?>) {
409 $(target).find(".indicator").text(<?php echo xlj('expand'); ?>);
410 $("#" + div).hide();
411 $.post("../../../library/ajax/user_settings.php", {
412 target: div,
413 mode: 0,
414 csrf_token_form: <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>
416 } else {
417 $(target).find(".indicator").text(<?php echo xlj('collapse'); ?>);
418 $("#" + div).show();
419 $.post("../../../library/ajax/user_settings.php", {
420 target: div,
421 mode: 1,
422 csrf_token_form: <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>
427 // edit prescriptions dialog.
428 // called from stats.php.
430 function editScripts(url) {
431 var AddScript = function() {
432 var __this = $(this);
433 __this.find("#clearButton").css("display", "");
434 __this.find("#backButton").css("display", "");
435 __this.find("#addButton").css("display", "none");
437 var iam = top.frames.editScripts;
438 iam.location.href = '<?php echo $GLOBALS['webroot'] ?>/controller.php?prescription&edit&id=0&pid=' + <?php echo js_url($pid); ?>;
440 var ListScripts = function() {
441 var __this = $(this);
442 __this.find("#clearButton").css("display", "none");
443 __this.find("#backButton").css("display", "none");
444 __this.find("#addButton").css("display", "");
445 var iam = top.frames.editScripts
446 iam.location.href = '<?php echo $GLOBALS['webroot'] ?>/controller.php?prescription&list&id=' + <?php echo js_url($pid); ?>;
449 let title = <?php echo xlj('Prescriptions'); ?>;
450 let w = 960; // for weno width
452 dlgopen(url, 'editScripts', w, 400, '', '', {
453 buttons: [{
454 text: <?php echo xlj('Add'); ?>,
455 close: false,
456 id: 'addButton',
457 class: 'btn-primary btn-sm',
458 click: AddScript
461 text: <?php echo xlj('Clear'); ?>,
462 close: false,
463 id: 'clearButton',
464 style: 'display:none;',
465 class: 'btn-primary btn-sm',
466 click: AddScript
469 text: <?php echo xlj('Back'); ?>,
470 close: false,
471 id: 'backButton',
472 style: 'display:none;',
473 class: 'btn-primary btn-sm',
474 click: ListScripts
477 text: <?php echo xlj('Quit'); ?>,
478 close: true,
479 id: 'doneButton',
480 class: 'btn-secondary btn-sm'
483 onClosed: 'refreshme',
484 allowResize: true,
485 allowDrag: true,
486 dialogId: 'editscripts',
487 type: 'iframe'
489 return false;
493 * async function fetchHtml(...)
495 * @param {*} url
496 * @param {boolean} embedded
497 * @param {boolean} sessionRestore
498 * @returns {text}
500 async function fetchHtml(url, embedded = false, sessionRestore = false) {
501 if (sessionRestore === true) {
502 // restore cookie before fetch.
503 top.restoreSession();
505 let csrf = new FormData;
506 // a security given.
507 csrf.append("csrf_token_form", <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>);
508 if (embedded === true) {
509 // special formatting in certain widgets.
510 csrf.append("embeddedScreen", true);
513 const response = await fetch(url, {
514 method: 'POST',
515 credentials: 'same-origin',
516 body: csrf
518 return await response.text();
522 * async function placeHtml(...) will await fetch of html then place in divId.
523 * This function will return a promise for use to init various items regarding
524 * inserted HTML if needed.
525 * If divId does not exist, then will skip.
526 * Example
528 * @param {*} url
529 * @param {string} divId id
530 * @param {boolean} embedded
531 * @param {boolean} sessionRestore
532 * @returns {object} promise
534 async function placeHtml(url, divId, embedded = false, sessionRestore = false) {
535 const contentDiv = document.getElementById(divId);
536 if (contentDiv) {
537 await fetchHtml(url, embedded, sessionRestore).then(fragment => {
538 contentDiv.innerHTML = fragment;
543 if (typeof load_location === 'undefined') {
544 function load_location(location) {
545 top.restoreSession();
546 document.location = location;
550 $(function() {
551 var msg_updation = '';
552 <?php
553 if ($GLOBALS['erx_enable']) {
554 $soap_status = sqlStatement("select soap_import_status,pid from patient_data where pid=? and soap_import_status in ('1','3')", array($pid));
555 while ($row_soapstatus = sqlFetchArray($soap_status)) { ?>
556 top.restoreSession();
557 let reloadRequired = false;
558 $.ajax({
559 type: "POST",
560 url: "../../soap_functions/soap_patientfullmedication.php",
561 dataType: "html",
562 data: {
563 patient: <?php echo js_escape($row_soapstatus['pid']); ?>,
565 async: false,
566 success: function(thedata) {
567 if (!thedata.includes("Nothing")) {
568 reloadRequired = true;
570 msg_updation += thedata;
572 error: function() {
573 alert('ajax error');
577 top.restoreSession();
578 $.ajax({
579 type: "POST",
580 url: "../../soap_functions/soap_allergy.php",
581 dataType: "html",
582 data: {
583 patient: <?php echo js_escape($row_soapstatus['pid']); ?>,
585 async: false,
586 success: function(thedata) {
587 if (!thedata.includes("Nothing")) {
588 reloadRequired = true;
590 msg_updation += "\n" + thedata;
592 error: function() {
593 alert('ajax error');
597 if (reloadRequired) {
598 document.location.reload();
601 <?php
602 if ($GLOBALS['erx_import_status_message']) { ?>
603 if (msg_updation)
604 alert(msg_updation);
605 <?php
611 // load divs
612 placeHtml("stats.php", "stats_div", true).then(() => {
613 $('[data-toggle="collapse"]').on('click', function (e) {
614 updateUserVisibilitySetting(e);
617 placeHtml("pnotes_fragment.php", 'pnotes_ps_expand').then(() => {
618 // must be delegated event!
619 $(this).on("click", ".complete_btn", function() {
620 let btn = $(this);
621 let csrf = new FormData;
622 csrf.append("csrf_token_form", <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>);
623 fetch("pnotes_fragment.php?docUpdateId=" + encodeURIComponent(btn.attr('data-id')), {
624 method: "POST",
625 credentials: 'same-origin',
626 body: csrf
628 .then(function() {
629 placeHtml("pnotes_fragment.php", 'pnotes_ps_expand');
633 placeHtml("disc_fragment.php", "disclosures_ps_expand");
634 placeHtml("labdata_fragment.php", "labdata_ps_expand");
635 placeHtml("track_anything_fragment.php", "track_anything_ps_expand");
636 <?php if ($vitals_is_registered && AclMain::aclCheckCore('patients', 'med')) { ?>
637 // Initialize the Vitals form if it is registered and user is authorized.
638 placeHtml("vitals_fragment.php", "vitals_ps_expand");
639 <?php } ?>
641 <?php if ($GLOBALS['enable_cdr'] && $GLOBALS['enable_cdr_crw']) { ?>
642 placeHtml("clinical_reminders_fragment.php", "clinical_reminders_ps_expand", true, true).then(() => {
643 // (note need to place javascript code here also to get the dynamic link to work)
644 $(".medium_modal").on('click', function(e) {
645 e.preventDefault();
646 e.stopPropagation();
647 dlgopen('', '', 800, 200, '', '', {
648 buttons: [{
649 text: <?php echo xlj('Close'); ?>,
650 close: true,
651 style: 'secondary btn-sm'
653 onClosed: 'refreshme',
654 allowResize: false,
655 allowDrag: true,
656 dialogId: 'demreminder',
657 type: 'iframe',
658 url: $(this).attr('href')
662 <?php } // end crw
665 <?php if ($GLOBALS['enable_cdr'] && $GLOBALS['enable_cdr_prw']) { ?>
666 placeHtml("patient_reminders_fragment.php", "patient_reminders_ps_expand", false, true);
667 <?php } // end prw
670 <?php
671 // Initialize for each applicable LBF form.
672 $gfres = sqlStatement("SELECT grp_form_id
673 FROM layout_group_properties
674 WHERE grp_form_id LIKE 'LBF%'
675 AND grp_group_id = ''
676 AND grp_repeats > 0
677 AND grp_activity = 1
678 ORDER BY grp_seq, grp_title");
679 while ($gfrow = sqlFetchArray($gfres)) { ?>
680 $(<?php echo js_escape("#" . $gfrow['grp_form_id'] . "_ps_expand"); ?>).load("lbf_fragment.php?formname=" + <?php echo js_url($gfrow['grp_form_id']); ?>, {
681 csrf_token_form: <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>
683 <?php } ?>
684 tabbify();
686 // modal for dialog boxes
687 $(".large_modal").on('click', function(e) {
688 e.preventDefault();
689 e.stopPropagation();
690 dlgopen('', '', 1000, 600, '', '', {
691 buttons: [{
692 text: <?php echo xlj('Close'); ?>,
693 close: true,
694 style: 'secondary btn-sm'
696 allowResize: true,
697 allowDrag: true,
698 dialogId: '',
699 type: 'iframe',
700 url: $(this).attr('href')
704 $(".rx_modal").on('click', function(e) {
705 e.preventDefault();
706 e.stopPropagation();
707 var title = <?php echo xlj('Amendments'); ?>;
708 dlgopen('', 'editAmendments', 800, 300, '', title, {
709 onClosed: 'refreshme',
710 allowResize: true,
711 allowDrag: true,
712 dialogId: '',
713 type: 'iframe',
714 url: $(this).attr('href')
718 // modal for image viewer
719 $(".image_modal").on('click', function(e) {
720 e.preventDefault();
721 e.stopPropagation();
722 dlgopen('', '', 400, 300, '', <?php echo xlj('Patient Images'); ?>, {
723 allowResize: true,
724 allowDrag: true,
725 dialogId: '',
726 type: 'iframe',
727 url: $(this).attr('href')
731 $(".deleter").on('click', function(e) {
732 e.preventDefault();
733 e.stopPropagation();
734 dlgopen('', '', 600, 360, '', '', {
735 buttons: [{
736 text: <?php echo xlj('Close'); ?>,
737 close: true,
738 style: 'secondary btn-sm'
740 //onClosed: 'imdeleted',
741 allowResize: false,
742 allowDrag: false,
743 dialogId: 'patdel',
744 type: 'iframe',
745 url: $(this).attr('href')
749 $(".iframe1").on('click', function(e) {
750 e.preventDefault();
751 e.stopPropagation();
752 dlgopen('', '', 350, 300, '', '', {
753 buttons: [{
754 text: <?php echo xlj('Close'); ?>,
755 close: true,
756 style: 'secondary btn-sm'
758 allowResize: true,
759 allowDrag: true,
760 dialogId: '',
761 type: 'iframe',
762 url: $(this).attr('href')
765 // for patient portal
766 $(".small_modal").on('click', function(e) {
767 e.preventDefault();
768 e.stopPropagation();
769 dlgopen('', '', 550, 550, '', '', {
770 buttons: [{
771 text: <?php echo xlj('Close'); ?>,
772 close: true,
773 style: 'secondary btn-sm'
775 allowResize: true,
776 allowDrag: true,
777 dialogId: '',
778 type: 'iframe',
779 url: $(this).attr('href')
783 function openReminderPopup() {
784 top.restoreSession()
785 dlgopen('', 'reminders', 500, 250, '', '', {
786 buttons: [{
787 text: <?php echo xlj('Close'); ?>,
788 close: true,
789 style: 'secondary btn-sm'
791 allowResize: true,
792 allowDrag: true,
793 dialogId: '',
794 type: 'iframe',
795 url: $("#reminder_popup_link").attr('href')
799 <?php if ($GLOBALS['patient_birthday_alert']) {
800 // To display the birthday alert:
801 // 1. The patient is not deceased
802 // 2. The birthday is today (or in the past depending on global selection)
803 // 3. The notification has not been turned off (or shown depending on global selection) for this year
804 $birthdayAlert = new BirthdayReminder($pid, $_SESSION['authUserID']);
805 if ($birthdayAlert->isDisplayBirthdayAlert()) {
807 // show the active reminder modal
808 dlgopen('', 'bdayreminder', 300, 170, '', false, {
809 allowResize: false,
810 allowDrag: true,
811 dialogId: '',
812 type: 'iframe',
813 url: $("#birthday_popup").attr('href')
816 <?php } elseif ($active_reminders || $all_allergy_alerts) { ?>
817 openReminderPopup();
818 <?php } ?>
819 <?php } elseif ($active_reminders || $all_allergy_alerts) { ?>
820 openReminderPopup();
821 <?php } ?>
825 * Change the preference to expand/collapse a given card.
827 * For the given e element, find the corresponding card body, determine if it is collapsed
828 * or shown, and then save the state to the user preferences via an async fetch call POST'ing
829 * the updated setting.
831 * @var e element The Button that was clicked to collapse/expand the card
833 async function updateUserVisibilitySetting(e) {
834 const targetID = e.target.getAttribute("data-target");
835 const target = document.querySelector(targetID);
836 const targetStr = targetID.substring(1);
837 // test ensure at least an element we want.
838 if (target.classList.contains("collapse")) {
839 // who is icon. Easier to catch BS event than create one specific for this decision..
840 // Should always be icon target
841 let iconTarget = e.target.children[0] || e.target;
842 // toggle
843 if (iconTarget.classList.contains("fa-expand")) {
844 iconTarget.classList.remove('fa-expand');
845 iconTarget.classList.add('fa-compress');
847 else {
848 iconTarget.classList.remove('fa-compress');
849 iconTarget.classList.add('fa-expand');
852 let formData = new FormData();
853 formData.append("csrf_token_form", <?php echo js_escape(CsrfUtils::collectCsrfToken()); ?>);
854 formData.append("target", targetStr);
855 formData.append("mode", (target.classList.contains("show")) ? 0 : 1);
856 top.restoreSession();
857 const response = await fetch("../../../library/ajax/user_settings.php", {
858 method: "POST",
859 credentials: 'same-origin',
860 body: formData,
863 return await response.text();
866 // JavaScript stuff to do when a new patient is set.
868 function setMyPatient() {
869 <?php
870 if (isset($_GET['set_pid'])) {
871 $date_of_death = is_patient_deceased($pid);
872 if (!empty($date_of_death)) {
873 $date_of_death = $date_of_death['date_deceased'];
876 parent.left_nav.setPatient(<?php echo js_escape($result['fname'] . " " . $result['lname']) .
877 "," . js_escape($pid) . "," . js_escape($result['pubpid']) . ",'',";
878 if (empty($date_of_death)) {
879 echo js_escape(" " . xl('DOB') . ": " . oeFormatShortDate($result['DOB_YMD']) . " " . xl('Age') . ": " . getPatientAgeDisplay($result['DOB_YMD']));
880 } else {
881 echo js_escape(" " . xl('DOB') . ": " . oeFormatShortDate($result['DOB_YMD']) . " " . xl('Age at death') . ": " . oeFormatAge($result['DOB_YMD'], $date_of_death));
882 } ?>);
883 var EncounterDateArray = [];
884 var CalendarCategoryArray = new Array;
885 var EncounterIdArray = new Array;
886 var Count = 0;
887 <?php
888 //Encounter details are stored to javacript as array.
889 $result4 = sqlStatement("SELECT fe.encounter,fe.date,openemr_postcalendar_categories.pc_catname FROM form_encounter AS fe " .
890 " left join openemr_postcalendar_categories on fe.pc_catid=openemr_postcalendar_categories.pc_catid WHERE fe.pid = ? order by fe.date desc", array($pid));
891 if (sqlNumRows($result4) > 0) {
892 while ($rowresult4 = sqlFetchArray($result4)) { ?>
893 EncounterIdArray[Count] = <?php echo js_escape($rowresult4['encounter']); ?>;
894 EncounterDateArray[Count] = <?php echo js_escape(oeFormatShortDate(date("Y-m-d", strtotime($rowresult4['date'])))); ?>;
895 CalendarCategoryArray[Count] = <?php echo js_escape(xl_appt_category($rowresult4['pc_catname'])); ?>;
896 Count++;
897 <?php
901 parent.left_nav.setPatientEncounter(EncounterIdArray, EncounterDateArray, CalendarCategoryArray);
902 <?php
903 } // end setting new pid
905 parent.left_nav.syncRadios();
906 <?php if ((isset($_GET['set_pid'])) && (isset($_GET['set_encounterid'])) && (intval($_GET['set_encounterid']) > 0)) {
907 $query_result = sqlQuery("SELECT `date` FROM `form_encounter` WHERE `encounter` = ?", array($encounter)); ?>
908 encurl = 'encounter/encounter_top.php?set_encounter=' + <?php echo js_url($encounter); ?> + '&pid=' + <?php echo js_url($pid); ?>;
909 parent.left_nav.setEncounter(<?php echo js_escape(oeFormatShortDate(date("Y-m-d", strtotime($query_result['date'])))); ?>, <?php echo js_escape($encounter); ?>, 'enc');
910 top.restoreSession();
911 parent.left_nav.loadFrame('enc2', 'enc', 'patient_file/' + encurl);
912 <?php } // end setting new encounter id (only if new pid is also set)
916 $(window).on('load', function() {
917 setMyPatient();
919 </script>
921 <style>
922 /* Bad practice to override here, will get moved to base style theme */
923 .card {
924 box-shadow: 1px 1px 1px hsl(0 0% 0% / .2);
925 border-radius: 0;
928 /* Short term fix. This ensures the problem list, allergies, medications, and immunization cards handle long lists without interuppting
929 the UI. This should be configurable and should go in a more appropriate place
930 .pami-list {
931 max-height: 200px;
932 overflow-y: scroll;
933 } */
935 <?php
936 if (!empty($GLOBALS['right_justify_labels_demographics']) && ($_SESSION['language_direction'] == 'ltr')) { ?>
937 div.tab td.label_custom, div.label_custom {
938 text-align: right !important;
941 div.tab td.data, div.data {
942 padding-left: 0.5em;
943 padding-right: 2em;
945 <?php
946 } ?>
948 <?php
949 // This is for layout font size override.
950 $grparr = array();
951 getLayoutProperties('DEM', $grparr, 'grp_size');
952 if (!empty($grparr['']['grp_size'])) {
953 $FONTSIZE = round($grparr['']['grp_size'] * 1.333333);
954 $FONTSIZE = round($FONTSIZE * 0.0625, 2);
957 /* Override font sizes in the theme. */
958 #DEM .groupname {
959 font-size: <?php echo attr($FONTSIZE); ?>rem;
962 #DEM .label {
963 font-size: <?php echo attr($FONTSIZE); ?>rem;
966 #DEM .data {
967 font-size: <?php echo attr($FONTSIZE); ?>rem;
970 #DEM .data td {
971 font-size: <?php echo attr($FONTSIZE); ?>rem;
974 <?php } ?> :root {
975 --white: #fff;
976 --bg: hsl(0 0% 90%);
979 body {
980 background: var(--bg) !important;
983 section {
984 background: var(--white);
985 margin-top: .25em;
986 padding: .25em;
989 .section-header-dynamic {
990 border-bottom: none;
992 </style>
993 <title><?php echo xlt("Dashboard{{patient file}}"); ?></title>
994 </head>
996 <body class="mt-1 patient-demographic bg-light">
998 <?php
999 // Create and fire the patient demographics view event
1000 $viewEvent = new ViewEvent($pid);
1001 $viewEvent = $GLOBALS["kernel"]->getEventDispatcher()->dispatch($viewEvent, ViewEvent::EVENT_HANDLE, 10);
1002 $thisauth = AclMain::aclCheckCore('patients', 'demo');
1004 if (!$thisauth || !$viewEvent->authorized()) {
1005 echo $twig->getTwig()->render('core/unauthorized-partial.html.twig', ['pageTitle' => xl("Medical Dashboard")]);
1006 exit();
1010 <div id="container_div" class="<?php echo $oemr_ui->oeContainer(); ?> mb-2">
1011 <a href='../reminder/active_reminder_popup.php' id='reminder_popup_link' style='display: none' onclick='top.restoreSession()'></a>
1012 <a href='../birthday_alert/birthday_pop.php?pid=<?php echo attr_url($pid); ?>&user_id=<?php echo attr_url($_SESSION['authUserID']); ?>' id='birthday_popup' style='display: none;' onclick='top.restoreSession()'></a>
1013 <?php
1015 if ($thisauth) {
1016 if ($result['squad'] && !AclMain::aclCheckCore('squads', $result['squad'])) {
1017 $thisauth = 0;
1021 if ($thisauth) :
1022 require_once("$include_root/patient_file/summary/dashboard_header.php");
1023 endif;
1025 $list_id = "dashboard"; // to indicate nav item is active, count and give correct id
1026 // Collect the patient menu then build it
1027 $menuPatient = new PatientMenuRole($twig);
1028 $menuPatient->displayHorizNavBarMenu();
1029 // Get the document ID of the patient ID card if access to it is wanted here.
1030 $idcard_doc_id = false;
1031 if ($GLOBALS['patient_id_category_name']) {
1032 $idcard_doc_id = get_document_by_catg($pid, $GLOBALS['patient_id_category_name'], 3);
1035 <div class="main mb-1">
1036 <!-- start main content div -->
1037 <div class="form-row">
1038 <?php
1039 $t = $twig->getTwig();
1041 $allergy = (AclMain::aclCheckIssue('allergy')) ? 1 : 0;
1042 $pl = (AclMain::aclCheckIssue('medical_problem')) ? 1 : 0;
1043 $meds = (AclMain::aclCheckIssue('medication')) ? 1 : 0;
1044 $rx = (!$GLOBALS['disable_prescriptions'] && AclMain::aclCheckCore('patients', 'rx')) ? 1 : 0;
1045 $cards = max(1, ($allergy + $pl + $meds));
1046 $col = "p-1 ";
1047 $colInt = 12 / $cards;
1048 $col .= "col-md-" . $colInt;
1051 * Helper function to return only issues with an outcome not equal to resolved
1053 * @param array $i An array of issues
1054 * @return array
1056 function filterActiveIssues(array $i): array
1058 return array_filter($i, function ($_i) {
1059 return ($_i['outcome'] != 1) && (empty($_i['enddate']) || (strtotime($_i['enddate']) > strtotime('now')));
1063 // ALLERGY CARD
1064 if ($allergy === 1) {
1065 $allergyService = new AllergyIntoleranceService();
1066 $_rawAllergies = filterActiveIssues($allergyService->getAll(['lists.pid' => $pid])->getData());
1067 $id = 'allergy_ps_expand';
1068 $viewArgs = [
1069 'title' => xl('Allergies'),
1070 'card_container_class_list' => ['flex-fill', 'mx-1', 'card'],
1071 'id' => $id,
1072 'forceAlwaysOpen' => false,
1073 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1074 'linkMethod' => "javascript",
1075 'list' => $_rawAllergies,
1076 'listTouched' => (!empty(getListTouch($pid, 'allergy'))) ? true : false,
1077 'auth' => true,
1078 'btnLabel' => 'Edit',
1079 'btnLink' => "return load_location('{$GLOBALS['webroot']}/interface/patient_file/summary/stats_full.php?active=all&category=allergy')"
1081 echo "<div class=\"$col\">";
1082 echo $t->render('patient/card/allergies.html.twig', $viewArgs);
1083 echo "</div>";
1086 $patIssueService = new PatientIssuesService();
1088 // MEDICAL PROBLEMS CARD
1089 if ($pl === 1) {
1090 $_rawPL = $patIssueService->search(['lists.pid' => $pid, 'lists.type' => 'medical_problem'])->getData();
1091 $id = 'medical_problem_ps_expand';
1092 $viewArgs = [
1093 'title' => xl('Medical Problems'),
1094 'card_container_class_list' => ['flex-fill', 'mx-1', 'card'],
1095 'id' => $id,
1096 'forceAlwaysOpen' => false,
1097 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1098 'linkMethod' => "javascript",
1099 'list' => filterActiveIssues($_rawPL),
1100 'listTouched' => (!empty(getListTouch($pid, 'medical_problem'))) ? true : false,
1101 'auth' => true,
1102 'btnLabel' => 'Edit',
1103 'btnLink' => "return load_location('{$GLOBALS['webroot']}/interface/patient_file/summary/stats_full.php?active=all&category=medical_problem')"
1105 echo "<div class=\"$col\">";
1106 echo $t->render('patient/card/medical_problems.html.twig', $viewArgs);
1107 echo "</div>";
1110 // MEDICATION CARD
1111 if ($meds === 1) {
1112 $_rawMedList = $patIssueService->search(['lists.pid' => $pid, 'lists.type' => 'medication'])->getData();
1113 $id = 'medication_ps_expand';
1114 $viewArgs = [
1115 'title' => xl('Medications'),
1116 'card_container_class_list' => ['flex-fill', 'mx-1', 'card'],
1117 'id' => $id,
1118 'forceAlwaysOpen' => false,
1119 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1120 'linkMethod' => "javascript",
1121 'list' => filterActiveIssues($_rawMedList),
1122 'listTouched' => (!empty(getListTouch($pid, 'medication'))) ? true : false,
1123 'auth' => true,
1124 'btnLabel' => 'Edit',
1125 'btnLink' => "return load_location('{$GLOBALS['webroot']}/interface/patient_file/summary/stats_full.php?active=all&category=medication')"
1127 echo "<div class=\"$col\">";
1128 echo $t->render('patient/card/medication.html.twig', $viewArgs);
1129 echo "</div>";
1132 // Render the Prescriptions card if turned on
1133 if ($rx === 1) :
1134 if ($GLOBALS['erx_enable'] && $display_current_medications_below == 1) {
1135 $sql = "SELECT * FROM prescriptions WHERE patient_id = ? AND active = '1'";
1136 $res = sqlStatement($sql, [$pid]);
1138 $rxArr = [];
1139 while ($row = sqlFetchArray($res)) {
1140 $row['unit'] = generate_display_field(array('data_type' => '1', 'list_id' => 'drug_units'), $row['unit']);
1141 $row['form'] = generate_display_field(array('data_type' => '1', 'list_id' => 'drug_form'), $row['form']);
1142 $row['route'] = generate_display_field(array('data_type' => '1', 'list_id' => 'drug_route'), $row['route']);
1143 $row['interval'] = generate_display_field(array('data_type' => '1', 'list_id' => 'drug_interval'), $row['interval']);
1144 $rxArr[] = $row;
1146 $id = "current_prescriptions_ps_expand";
1147 $viewArgs = [
1148 'title' => xl('Current Medications'),
1149 'id' => $id,
1150 'forceAlwaysOpen' => false,
1151 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1152 'auth' => false,
1153 'rxList' => $rxArr,
1156 echo $t->render('patient/card/erx.html.twig', $viewArgs);
1159 $id = "prescriptions_ps_expand";
1160 $viewArgs = [
1161 'title' => xl("Prescriptions"),
1162 'card_container_class_list' => ['flex-fill', 'mx-1', 'card'],
1163 'id' => $id,
1164 'forceAlwaysOpen' => false,
1165 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1166 'btnLabel' => "Edit",
1167 'auth' => AclMain::aclCheckCore('patients', 'rx', '', ['write', 'addonly']),
1170 if ($GLOBALS['erx_enable']) {
1171 $viewArgs['title'] = 'Prescription History';
1172 $viewArgs['btnLabel'] = 'Add';
1173 $viewArgs['btnLink'] = "{$GLOBALS['webroot']}/interface/eRx.php?page=compose";
1174 } else {
1175 $viewArgs['btnLink'] = "editScripts('{$GLOBALS['webroot']}/controller.php?prescription&list&id=" . attr_url($pid) . "')";
1176 $viewArgs['linkMethod'] = "javascript";
1177 $viewArgs['btnClass'] = "iframe";
1180 $cwd = getcwd();
1181 chdir("../../../");
1182 $c = new Controller();
1183 // This is a hacky way to get a Smarty template from the controller and injecting it into
1184 // a Twig template. This reduces the amount of refactoring that is required but ideally the
1185 // Smarty template should be upgraded to Twig
1186 ob_start();
1187 echo $c->act(['prescription' => '', 'fragment' => '', 'patient_id' => $pid]);
1188 $viewArgs['content'] = ob_get_contents();
1189 ob_end_clean();
1191 echo "<div class=\"col\">";
1192 echo $t->render('patient/card/rx.html.twig', $viewArgs);
1193 echo "</div>";
1194 endif;
1196 </div>
1197 <div class="row">
1198 <div class="col-md-8">
1199 <?php
1200 if ($deceased > 0) :
1201 echo $twig->getTwig()->render('patient/partials/deceased.html.twig', [
1202 'deceasedDays' => deceasedDays($deceased),
1204 endif;
1206 $sectionRenderEvents = $ed->dispatch(new SectionEvent('primary'), SectionEvent::EVENT_HANDLE);
1207 $sectionRenderEvents->addCard(new DemographicsViewCard($result, $result2, ['dispatcher' => $ed]));
1208 if (!$GLOBALS['hide_billing_widget']) {
1209 $sectionRenderEvents->addCard(new BillingViewCard($pid, $insco_name, $result['billing_note'], $result3, ['dispatcher' => $ed]));
1211 $sectionRenderEvents->addCard(new InsuranceViewCard($pid, ['dispatcher' => $ed]));
1212 $sectionCards = $sectionRenderEvents->getCards();
1214 // if anyone wants to render anything before the patient demographic list
1215 $GLOBALS["kernel"]->getEventDispatcher()->dispatch(new RenderEvent($pid), RenderEvent::EVENT_SECTION_LIST_RENDER_BEFORE, 10);
1217 foreach ($sectionCards as $card) {
1218 $_auth = $card->getAcl();
1219 if (!empty($_auth) && !AclMain::aclCheckCore($_auth[0], $_auth[1])) {
1220 continue;
1223 $btnLabel = false;
1224 if ($card->canAdd()) {
1225 $btnLabel = 'Add';
1226 } elseif ($card->canEdit()) {
1227 $btnLabel = 'Edit';
1230 $viewArgs = [
1231 'title' => $card->getTitle(),
1232 'id' => $card->getIdentifier(),
1233 'initiallyCollapsed' => $card->isInitiallyCollapsed(),
1234 'card_bg_color' => $card->getBackgroundColorClass(),
1235 'card_text_color' => $card->getTextColorClass(),
1236 'forceAlwaysOpen' => !$card->canCollapse(),
1237 'btnLabel' => $btnLabel,
1238 'btnLink' => 'test',
1241 echo $t->render($card->getTemplateFile(), array_merge($viewArgs, $card->getTemplateVariables()));
1244 if (AclMain::aclCheckCore('patients', 'notes')) :
1245 $dispatchResult = $ed->dispatch(new CardRenderEvent('note'), CardRenderEvent::EVENT_HANDLE);
1246 // Notes expand collapse widget
1247 $id = "pnotes_ps_expand";
1248 $viewArgs = [
1249 'title' => xl("Messages"),
1250 'id' => $id,
1251 'btnLabel' => "Edit",
1252 'btnLink' => "pnotes_full.php?form_active=1",
1253 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1254 'linkMethod' => "html",
1255 'bodyClass' => "notab",
1256 'auth' => AclMain::aclCheckCore('patients', 'notes', '', 'write'),
1257 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1258 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1260 echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs);
1261 endif; // end if notes authorized
1263 if (AclMain::aclCheckCore('patients', 'reminder') && $GLOBALS['enable_cdr'] && $GLOBALS['enable_cdr_prw']) :
1264 // patient reminders collapse widget
1265 $dispatchResult = $ed->dispatch(new CardRenderEvent('reminder'), CardRenderEvent::EVENT_HANDLE);
1266 $id = "patient_reminders_ps_expand";
1267 $viewArgs = [
1268 'title' => xl('Patient Reminders'),
1269 'id' => $id,
1270 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1271 'btnLabel' => 'Edit',
1272 'btnLink' => '../reminder/patient_reminders.php?mode=simple&patient_id=' . attr_url($pid),
1273 'linkMethod' => 'html',
1274 'bodyClass' => 'notab collapse show',
1275 'auth' => AclMain::aclCheckCore('patients', 'reminder', '', 'write'),
1276 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1277 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1279 echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs);
1280 endif; //end if prw is activated
1282 if (AclMain::aclCheckCore('patients', 'disclosure')) :
1283 $authWriteDisclosure = AclMain::aclCheckCore('patients', 'disclosure', '', 'write');
1284 $authAddonlyDisclosure = AclMain::aclCheckCore('patients', 'disclosure', '', 'addonly');
1285 $dispatchResult = $ed->dispatch(new CardRenderEvent('disclosure'), CardRenderEvent::EVENT_HANDLE);
1286 // disclosures expand collapse widget
1287 $id = "disclosures_ps_expand";
1288 $viewArgs = [
1289 'title' => xl('Disclosures'),
1290 'id' => $id,
1291 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1292 'btnLabel' => 'Edit',
1293 'btnLink' => 'disclosure_full.php',
1294 'linkMethod' => 'html',
1295 'bodyClass' => 'notab collapse show',
1296 'auth' => ($authWriteDisclosure || $authAddonlyDisclosure),
1297 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1298 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1300 echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs);
1301 endif; // end if disclosures authorized
1303 if ($GLOBALS['amendments'] && AclMain::aclCheckCore('patients', 'amendment')) :
1304 $dispatchResult = $ed->dispatch(new CardRenderEvent('amendment'), CardRenderEvent::EVENT_HANDLE);
1305 // Amendments widget
1306 $sql = "SELECT * FROM amendments WHERE pid = ? ORDER BY amendment_date DESC";
1307 $result = sqlStatement($sql, [$pid]);
1308 $amendments = [];
1309 while ($row = sqlFetchArray($result)) {
1310 $amendments[] = $row;
1313 $id = "amendments_ps_expand";
1314 $viewArgs = [
1315 'title' => xl('Amendments'),
1316 'id' => $id,
1317 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1318 'btnLabel' => 'Edit',
1319 'btnLink' => $GLOBALS['webroot'] . "/interface/patient_file/summary/list_amendments.php?id=" . attr_url($pid),
1320 'btnCLass' => '',
1321 'linkMethod' => 'html',
1322 'bodyClass' => 'notab collapse show',
1323 'auth' => AclMain::aclCheckCore('patients', 'amendment', '', ['write', 'addonly']),
1324 'amendments' => $amendments,
1325 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1326 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1328 echo $twig->getTwig()->render('patient/card/amendments.html.twig', $viewArgs);
1329 endif; // end amendments authorized
1331 if (AclMain::aclCheckCore('patients', 'lab')) :
1332 $dispatchResult = $ed->dispatch(new CardRenderEvent('lab'), CardRenderEvent::EVENT_HANDLE);
1333 // labdata expand collapse widget
1334 // check to see if any labdata exist
1335 $spruch = "SELECT procedure_report.date_collected AS date
1336 FROM procedure_report
1337 JOIN procedure_order ON procedure_report.procedure_order_id = procedure_order.procedure_order_id
1338 WHERE procedure_order.patient_id = ?
1339 ORDER BY procedure_report.date_collected DESC";
1340 $existLabdata = sqlQuery($spruch, array($pid));
1341 $widgetAuth = ($existLabdata) ? true : false;
1343 $id = "labdata_ps_expand";
1344 $viewArgs = [
1345 'title' => xl('Labs'),
1346 'id' => $id,
1347 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1348 'btnLabel' => 'Trend',
1349 'btnLink' => "../summary/labdata.php",
1350 'linkMethod' => 'html',
1351 'bodyClass' => 'collapse show',
1352 'auth' => $widgetAuth,
1353 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1354 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1356 echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs);
1357 endif; // end labs authorized
1359 if ($vitals_is_registered && AclMain::aclCheckCore('patients', 'med')) :
1360 $dispatchResult = $ed->dispatch(new CardRenderEvent('vital_sign'), CardRenderEvent::EVENT_HANDLE);
1361 // vitals expand collapse widget
1362 // check to see if any vitals exist
1363 $existVitals = sqlQuery("SELECT * FROM form_vitals WHERE pid=?", array($pid));
1364 $widgetAuth = ($existVitals) ? true : false;
1366 $id = "vitals_ps_expand";
1367 $viewArgs = [
1368 'title' => xl('Vitals'),
1369 'id' => $id,
1370 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1371 'btnLabel' => 'Trend',
1372 'btnLink' => "../encounter/trend_form.php?formname=vitals&context=dashboard",
1373 'linkMethod' => 'html',
1374 'bodyClass' => 'collapse show',
1375 'auth' => $widgetAuth,
1376 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1377 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1379 echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs);
1380 endif; // end vitals
1382 // if anyone wants to render anything after the patient demographic list
1383 $GLOBALS["kernel"]->getEventDispatcher()->dispatch(new RenderEvent($pid), RenderEvent::EVENT_SECTION_LIST_RENDER_AFTER, 10);
1385 // This generates a section similar to Vitals for each LBF form that
1386 // supports charting. The form ID is used as the "widget label".
1387 $gfres = sqlStatement("SELECT grp_form_id AS option_id, grp_title AS title, grp_aco_spec
1388 FROM layout_group_properties
1389 WHERE grp_form_id LIKE 'LBF%'
1390 AND grp_group_id = ''
1391 AND grp_repeats > 0
1392 AND grp_activity = 1
1393 ORDER BY grp_seq, grp_title");
1395 while ($gfrow = sqlFetchArray($gfres)) :
1396 // $jobj = json_decode($gfrow['notes'], true);
1397 $LBF_ACO = empty($gfrow['grp_aco_spec']) ? false : explode('|', $gfrow['grp_aco_spec']);
1398 if ($LBF_ACO && !AclMain::aclCheckCore($LBF_ACO[0], $LBF_ACO[1])) {
1399 continue;
1402 // vitals expand collapse widget
1403 $widgetAuth = false;
1404 if (!$LBF_ACO || AclMain::aclCheckCore($LBF_ACO[0], $LBF_ACO[1], '', 'write')) {
1405 // check to see if any instances exist for this patient
1406 $existVitals = sqlQuery("SELECT * FROM forms WHERE pid = ? AND formdir = ? AND deleted = 0", [$pid, $vitals_form_id]);
1407 $widgetAuth = $existVitals;
1410 $dispatchResult = $ed->dispatch(new CardRenderEvent($gfrow['title']), CardRenderEvent::EVENT_HANDLE);
1411 $viewArgs = [
1412 'title' => xl($gfrow['title']),
1413 'id' => $vitals_form_id,
1414 'initiallyCollapsed' => (getUserSetting($vitals_form_id) == 0) ? true : false,
1415 'btnLabel' => 'Trend',
1416 'btnLink' => "../encounter/trend_form.php?formname=vitals&context=dashboard",
1417 'linkMethod' => 'html',
1418 'bodyClass' => 'notab collapse show',
1419 'auth' => $widgetAuth,
1420 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1421 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1423 echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs);
1424 endwhile; // end while
1426 </div> <!-- end left column div -->
1427 <div class="col-md-4">
1428 <!-- start right column div -->
1429 <?php
1430 $_extAccess = [
1431 $GLOBALS['portal_onsite_two_enable'],
1432 $GLOBALS['rest_fhir_api'],
1433 $GLOBALS['rest_api'],
1434 $GLOBALS['rest_portal_api'],
1436 foreach ($_extAccess as $_) {
1437 if ($_) {
1438 $portalCard = new PortalCard($GLOBALS);
1439 break;
1443 $sectionRenderEvents = $ed->dispatch(new SectionEvent('secondary'), SectionEvent::EVENT_HANDLE);
1444 $sectionCards = $sectionRenderEvents->getCards();
1446 $t = $twig->getTwig();
1448 foreach ($sectionCards as $card) {
1449 $_auth = $card->getAcl();
1450 $auth = AclMain::aclCheckCore($_auth[0], $_auth[1]);
1451 if (!$auth) {
1452 continue;
1455 $btnLabel = false;
1456 if ($card->canAdd()) {
1457 $btnLabel = 'Add';
1458 } elseif ($card->canEdit()) {
1459 $btnLabel = 'Edit';
1462 $viewArgs = [
1463 'card' => $card,
1464 'title' => $card->getTitle(),
1465 'id' => $card->getIdentifier() . "_expand",
1466 'auth' => $auth,
1467 'linkMethod' => 'html',
1468 'initiallyCollapsed' => $card->isInitiallyCollapsed(),
1469 'card_bg_color' => $card->getBackgroundColorClass(),
1470 'card_text_color' => $card->getTextColorClass(),
1471 'forceAlwaysOpen' => !$card->canCollapse(),
1472 'btnLabel' => $btnLabel,
1473 'btnLink' => "javascript:$('#patient_portal').collapse('toggle')",
1476 echo $t->render($card->getTemplateFile(), array_merge($viewArgs, $card->getTemplateVariables()));
1479 if ($GLOBALS['erx_enable']) :
1480 $dispatchResult = $ed->dispatch(new CardRenderEvent('demographics'), CardRenderEvent::EVENT_HANDLE);
1481 echo $twig->getTwig()->render('patient/partials/erx.html.twig', [
1482 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1483 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1485 endif;
1487 // If there is an ID Card or any Photos show the widget
1488 $photos = pic_array($pid, $GLOBALS['patient_photo_category_name']);
1489 if ($photos or $idcard_doc_id) {
1490 $id = "photos_ps_expand";
1491 $dispatchResult = $ed->dispatch(new CardRenderEvent('patient_photo'), CardRenderEvent::EVENT_HANDLE);
1492 $viewArgs = [
1493 'title' => xl("ID Card / Photos"),
1494 'id' => $id,
1495 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1496 'btnLabel' => 'Edit',
1497 'linkMethod' => "javascript",
1498 'bodyClass' => 'collapse show',
1499 'auth' => false,
1500 'patientIDCategoryID' => $GLOBALS['patient_id_category_name'],
1501 'patientPhotoCategoryName' => $GLOBALS['patient_photo_category_name'],
1502 'photos' => $photos,
1503 'idCardDocID' => $idcard_doc_id,
1504 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1505 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1507 echo $twig->getTwig()->render('patient/card/photo.html.twig', $viewArgs);
1510 // Advance Directives
1511 if ($GLOBALS['advance_directives_warning']) {
1512 // advance directives expand collapse widget
1514 $counterFlag = false; //flag to record whether any categories contain ad records
1515 $query = "SELECT id FROM categories WHERE name='Advance Directive'";
1516 $myrow2 = sqlQuery($query);
1517 $advDirArr = [];
1518 if ($myrow2) {
1519 $parentId = $myrow2['id'];
1520 $query = "SELECT id, name FROM categories WHERE parent=?";
1521 $resNew1 = sqlStatement($query, array($parentId));
1522 while ($myrows3 = sqlFetchArray($resNew1)) {
1523 $categoryId = $myrows3['id'];
1524 $nameDoc = $myrows3['name'];
1525 $query = "SELECT documents.date, documents.id
1526 FROM documents
1527 INNER JOIN categories_to_documents ON categories_to_documents.document_id=documents.id
1528 WHERE categories_to_documents.category_id=?
1529 AND documents.foreign_id=?
1530 AND documents.deleted = 0
1531 ORDER BY documents.date DESC";
1532 $resNew2 = sqlStatement($query, array($categoryId, $pid));
1533 $limitCounter = 0; // limit to one entry per category
1534 while (($myrows4 = sqlFetchArray($resNew2)) && ($limitCounter == 0)) {
1535 $dateTimeDoc = $myrows4['date'];
1536 // remove time from datetime stamp
1537 $tempParse = explode(" ", $dateTimeDoc);
1538 $dateDoc = $tempParse[0];
1539 $idDoc = $myrows4['id'];
1540 $tmp = [
1541 'pid' => $pid,
1542 'docID' => $idDoc,
1543 'docName' => $nameDoc,
1544 'docDate' => $dateDoc,
1546 $advDirArr[] = $tmp;
1547 $limitCounter = $limitCounter + 1;
1548 $counterFlag = true;
1552 $id = "adv_directives_ps_expand";
1554 $dispatchResult = $ed->dispatch(new CardRenderEvent('advance_directive'), CardRenderEvent::EVENT_HANDLE);
1555 $viewArgs = [
1556 'title' => xl("Advance Directives"),
1557 'id' => $id,
1558 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1559 'btnLabel' => 'Edit',
1560 'linkMethod' => "javascript",
1561 'btnLink' => "return advdirconfigure();",
1562 'bodyClass' => 'collapse show',
1563 'auth' => true,
1564 'advDirArr' => $advDirArr,
1565 'counterFlag' => $counterFlag,
1566 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1567 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1569 echo $twig->getTwig()->render('patient/card/adv_dir.html.twig', $viewArgs);
1571 } // close advanced dir block
1573 // Show Clinical Reminders for any user that has rules that are permitted.
1574 $clin_rem_check = resolve_rules_sql('', '0', true, '', $_SESSION['authUser']);
1575 $cdr = $GLOBALS['enable_cdr'];
1576 $cdr_crw = $GLOBALS['enable_cdr_crw'];
1577 if (!empty($clin_rem_check) && $cdr && $cdr_crw && AclMain::aclCheckCore('patients', 'alert')) {
1578 // clinical summary expand collapse widget
1579 $id = "clinical_reminders_ps_expand";
1580 $dispatchResult = $ed->dispatch(new CardRenderEvent('clinical_reminders'), CardRenderEvent::EVENT_HANDLE);
1581 $viewArgs = [
1582 'title' => xl("Clinical Reminders"),
1583 'id' => $id,
1584 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1585 'btnLabel' => "Edit",
1586 'btnLink' => "../reminder/clinical_reminders.php?patient_id=" . attr_url($pid),
1587 'linkMethod' => "html",
1588 'auth' => AclMain::aclCheckCore('patients', 'alert', '', 'write'),
1589 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1590 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1592 echo $twig->getTwig()->render('patient/card/loader.html.twig', $viewArgs);
1593 } // end if crw
1595 $displayAppts = false;
1596 $displayRecurrAppts = false;
1597 $displayPastAppts = false;
1599 // Show current and upcoming appointments.
1600 // Recurring appointment support and Appointment Display Sets
1601 // added to Appointments by Ian Jardine ( epsdky ).
1602 if (isset($pid) && !$GLOBALS['disable_calendar'] && AclMain::aclCheckCore('patients', 'appt')) {
1603 $displayAppts = true;
1604 $current_date2 = date('Y-m-d');
1605 $events = array();
1606 $apptNum = (int) $GLOBALS['number_of_appts_to_show'];
1607 $apptNum2 = ($apptNum != 0) ? abs($apptNum) : 10;
1609 $mode1 = !$GLOBALS['appt_display_sets_option'];
1610 $colorSet1 = $GLOBALS['appt_display_sets_color_1'];
1611 $colorSet2 = $GLOBALS['appt_display_sets_color_2'];
1612 $colorSet3 = $GLOBALS['appt_display_sets_color_3'];
1613 $colorSet4 = $GLOBALS['appt_display_sets_color_4'];
1614 $extraAppts = ($mode1) ? 1 : 6;
1615 $extraApptDate = '';
1617 $past_appts = [];
1618 $recallArr = [];
1620 $events = fetchNextXAppts($current_date2, $pid, $apptNum2 + $extraAppts, true);
1622 if ($events) {
1623 $selectNum = 0;
1624 $apptNumber = count($events);
1626 if ($apptNumber <= $apptNum2) {
1627 $extraApptDate = '';
1629 } elseif ($mode1 && $apptNumber == $apptNum2 + 1) {
1630 $extraApptDate = $events[$apptNumber - 1]['pc_eventDate'];
1631 array_pop($events);
1632 --$apptNumber;
1633 $selectNum = 1;
1635 } elseif ($apptNumber == $apptNum2 + 6) {
1636 $extraApptDate = $events[$apptNumber - 1]['pc_eventDate'];
1637 array_pop($events);
1638 --$apptNumber;
1639 $selectNum = 2;
1641 } else { // mode 2 - $apptNum2 < $apptNumber < $apptNum2 + 6
1642 $extraApptDate = '';
1643 $selectNum = 2;
1647 $limitApptIndx = $apptNum2 - 1;
1648 $limitApptDate = $events[$limitApptIndx]['pc_eventDate'] ?? '';
1650 switch ($selectNum) {
1651 case 2:
1652 $lastApptIndx = $apptNumber - 1;
1653 $thisNumber = $lastApptIndx - $limitApptIndx;
1654 for ($i = 1; $i <= $thisNumber; ++$i) {
1655 if ($events[$limitApptIndx + $i]['pc_eventDate'] != $limitApptDate) {
1656 $extraApptDate = $events[$limitApptIndx + $i]['pc_eventDate'];
1657 $events = array_slice($events, 0, $limitApptIndx + $i);
1658 break;
1661 // Break in the loop to improve performance
1662 case 1:
1663 $firstApptIndx = 0;
1664 for ($i = 1; $i <= $limitApptIndx; ++$i) {
1665 if ($events[$limitApptIndx - $i]['pc_eventDate'] != $limitApptDate) {
1666 $firstApptIndx = $apptNum2 - $i;
1667 break;
1670 // Break in the loop to improve performance
1673 if ($extraApptDate) {
1674 if ($extraApptDate != $limitApptDate) {
1675 $apptStyle2 = " style='background-color:" . attr($colorSet3) . ";'";
1676 } else {
1677 $apptStyle2 = " style='background-color:" . attr($colorSet4) . ";'";
1682 $count = 0;
1683 $toggleSet = true;
1684 $priorDate = "";
1685 $therapyGroupCategories = array();
1686 $query = sqlStatement("SELECT pc_catid FROM openemr_postcalendar_categories WHERE pc_cattype = 3 AND pc_active = 1");
1687 while ($result = sqlFetchArray($query)) {
1688 $therapyGroupCategories[] = $result['pc_catid'];
1691 // Build the UI Loop
1692 $appts = [];
1693 foreach ($events as $row) {
1694 $count++;
1695 $dayname = date("D", strtotime($row['pc_eventDate']));
1696 $displayMeridiem = ($GLOBALS['time_display_format'] == 0) ? "" : "am";
1697 $disphour = substr($row['pc_startTime'], 0, 2) + 0;
1698 $dispmin = substr($row['pc_startTime'], 3, 2);
1699 if ($disphour >= 12 && $GLOBALS['time_display_format'] == 1) {
1700 $displayMeridiem = "pm";
1701 if ($disphour > 12) {
1702 $disphour -= 12;
1706 // Note the translaution occurs here instead of in teh Twig file for some specific concatenation needs
1707 $etitle = xl('(Click to edit)');
1708 if ($row['pc_hometext'] != "") {
1709 $etitle = xl('Comments') . ": " . ($row['pc_hometext']) . "\r\n" . $etitle;
1712 $row['etitle'] = $etitle;
1714 if ($extraApptDate && $count > $firstApptIndx) {
1715 $apptStyle = $apptStyle2;
1716 } else {
1717 if ($row['pc_eventDate'] != $priorDate) {
1718 $priorDate = $row['pc_eventDate'];
1719 $toggleSet = !$toggleSet;
1722 $bgColor = ($toggleSet) ? $colorSet2 : $colorSet1;
1725 $row['pc_eventTime'] = sprintf("%02d", $disphour) . ":{$dispmin}";
1726 $row['pc_status'] = generate_display_field(array('data_type' => '1', 'list_id' => 'apptstat'), $row['pc_apptstatus']);
1727 if ($row['pc_status'] == 'None') {
1728 $row['pc_status'] = 'Scheduled';
1731 if (in_array($row['pc_catid'], $therapyGroupCategories)) {
1732 $row['groupName'] = getGroup($row['pc_gid'])['group_name'];
1735 $row['uname'] = text($row['ufname'] . " " . $row['ulname']);
1736 $row['bgColor'] = $bgColor;
1737 $row['dayName'] = $dayname;
1738 $row['displayMeridiem'] = $displayMeridiem;
1739 $row['jsEvent'] = attr_js(preg_replace("/-/", "", $row['pc_eventDate'])) . ', ' . attr_js($row['pc_eid']);
1740 $appts[] = $row;
1743 if ($resNotNull) {
1744 // Show Recall if one exists
1745 $query = sqlStatement("SELECT * FROM `medex_recalls` WHERE `r_pid` = ?", [(int)$pid]);
1746 $recallArr = [];
1747 $count2 = 0;
1748 while ($result2 = sqlFetchArray($query)) {
1749 //tabYourIt('recall', 'main/messages/messages.php?go=' + choice);
1750 //parent.left_nav.loadFrame('1', tabNAME, url);
1751 $recallArr[] = [
1752 'date' => $result2['r_eventDate'],
1753 'reason' => $result2['r_reason'],
1755 $count2++;
1757 $id = "recall_ps_expand";
1758 $dispatchResult = $ed->dispatch(new CardRenderEvent('recall'), CardRenderEvent::EVENT_HANDLE);
1759 echo $twig->getTwig()->render('patient/card/recall.html.twig', [
1760 'title' => xl('Recall'),
1761 'id' => $id,
1762 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1763 'recalls' => $recallArr,
1764 'recallsAvailable' => ($count < 1 && empty($count2)) ? false : true,
1765 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1766 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1769 } // End of Appointments Widget.
1771 /* Widget that shows recurrences for appointments. */
1772 $recurr = [];
1773 if (isset($pid) && !$GLOBALS['disable_calendar'] && $GLOBALS['appt_recurrences_widget'] && AclMain::aclCheckCore('patients', 'appt')) {
1774 $displayRecurrAppts = true;
1775 $count = 0;
1776 $toggleSet = true;
1777 $priorDate = "";
1779 //Fetch patient's recurrences. Function returns array with recurrence appointments' category, recurrence pattern (interpreted), and end date.
1780 $recurrences = fetchRecurrences($pid);
1781 if (!empty($recurrences)) {
1782 foreach ($recurrences as $row) {
1783 if (!recurrence_is_current($row['pc_endDate'])) {
1784 continue;
1787 if (ends_in_a_week($row['pc_endDate'])) {
1788 $row['close_to_end'] = true;
1790 $recurr[] = $row;
1794 /* End of recurrence widget */
1796 // Show PAST appointments.
1797 // added by Terry Hill to allow reverse sorting of the appointments
1798 $direction = '1';
1799 if ($GLOBALS['num_past_appointments_to_show'] < 0) {
1800 $direction = '2';
1801 ($showpast = -1 * $GLOBALS['num_past_appointments_to_show']);
1802 } else {
1803 $showpast = $GLOBALS['num_past_appointments_to_show'];
1806 if (isset($pid) && !$GLOBALS['disable_calendar'] && $showpast > 0 && AclMain::aclCheckCore('patients', 'appt')) {
1807 $displayPastAppts = true;
1809 $pastAppts = fetchXPastAppts($pid, $showpast, $direction); // This line added by epsdky
1811 $count = 0;
1813 foreach ($pastAppts as $row) {
1814 $count++;
1815 $dayname = date("D", strtotime($row['pc_eventDate']));
1816 $displayMeridiem = ($GLOBALS['time_display_format'] == 0) ? "" : "am";
1817 $disphour = substr($row['pc_startTime'], 0, 2) + 0;
1818 $dispmin = substr($row['pc_startTime'], 3, 2);
1819 if ($disphour >= 12) {
1820 $displayMeridiem = "pm";
1821 if ($disphour > 12 && $GLOBALS['time_display_format'] == 1) {
1822 $disphour -= 12;
1826 $petitle = xl('(Click to edit)');
1827 if ($row['pc_hometext'] != "") {
1828 $petitle = xl('Comments') . ": " . ($row['pc_hometext']) . "\r\n" . $petitle;
1830 $row['etitle'] = $petitle;
1832 $row['pc_status'] = generate_display_field(array('data_type' => '1', 'list_id' => 'apptstat'), $row['pc_apptstatus']);
1834 $row['dayName'] = $dayname;
1835 $row['displayMeridiem'] = $displayMeridiem;
1836 $row['pc_eventTime'] = sprintf("%02d", $disphour) . ":{$dispmin}";
1837 $row['uname'] = text($row['ufname'] . " " . $row['ulname']);
1838 $row['jsEvent'] = attr_js(preg_replace("/-/", "", $row['pc_eventDate'])) . ', ' . attr_js($row['pc_eid']);
1839 $past_appts[] = $row;
1842 // END of past appointments
1844 // Display the Appt card
1845 $id = "appointments_ps_expand";
1846 $dispatchResult = $ed->dispatch(new CardRenderEvent('appointment'), CardRenderEvent::EVENT_HANDLE);
1847 echo $twig->getTwig()->render('patient/card/appointments.html.twig', [
1848 'title' => xl("Appointments"),
1849 'id' => $id,
1850 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1851 'btnLabel' => "Add",
1852 'btnLink' => "return newEvt()",
1853 'linkMethod' => "javascript",
1854 'appts' => $appts,
1855 'recurrAppts' => $recurr,
1856 'pastAppts' => $past_appts,
1857 'displayAppts' => $displayAppts,
1858 'displayRecurrAppts' => $displayRecurrAppts,
1859 'displayPastAppts' => $displayPastAppts,
1860 'extraApptDate' => $extraApptDate,
1861 'therapyGroupCategories' => $therapyGroupCategories,
1862 'auth' => $resNotNull && (AclMain::aclCheckCore('patients', 'appt', '', 'write') || AclMain::aclCheckCore('patients', 'appt', '', 'addonly')),
1863 'resNotNull' => $resNotNull,
1864 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1865 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1868 echo "<div id=\"stats_div\"></div>";
1870 // TRACK ANYTHING
1871 // Determine if track_anything form is in use for this site.
1872 $tmp = sqlQuery("SELECT count(*) AS count FROM registry WHERE directory = 'track_anything' AND state = 1");
1873 $track_is_registered = $tmp['count'];
1874 if ($track_is_registered) {
1875 $spruch = "SELECT id FROM forms WHERE pid = ? AND formdir = ?";
1876 $existTracks = sqlQuery($spruch, array($pid, "track_anything"));
1877 $id = "track_anything_ps_expand";
1878 $dispatchResult = $ed->dispatch(new CardRenderEvent('track_anything'), CardRenderEvent::EVENT_HANDLE);
1879 echo $twig->getTwig()->render('patient/card/loader.html.twig', [
1880 'title' => xl("Tracks"),
1881 'id' => $id,
1882 'initiallyCollapsed' => (getUserSetting($id) == 0) ? true : false,
1883 'btnLink' => "../../forms/track_anything/create.php",
1884 'linkMethod' => "html",
1885 'prependedInjection' => $dispatchResult->getPrependedInjection(),
1886 'appendedInjection' => $dispatchResult->getAppendedInjection(),
1888 } // end track_anything
1890 if ($thisauth) :
1891 echo $twig->getTwig()->render('patient/partials/delete.html.twig', [
1892 'isAdmin' => AclMain::aclCheckCore('admin', 'super'),
1893 'allowPatientDelete' => $GLOBALS['allow_pat_delete'],
1894 'csrf' => CsrfUtils::collectCsrfToken(),
1895 'pid' => $pid
1897 endif;
1899 </div> <!-- end right column div -->
1900 </div> <!-- end div.main > row:first -->
1901 </div> <!-- end main content div -->
1902 </div><!-- end container div -->
1903 <?php $oemr_ui->oeBelowContainerDiv(); ?>
1904 <script>
1905 // Array of skip conditions for the checkSkipConditions() function.
1906 var skipArray = [
1907 <?php echo ($condition_str ?? ''); ?>
1909 checkSkipConditions();
1913 var isPost = <?php echo js_escape($showEligibility ?? false); ?>;
1914 var listId = '#' + <?php echo js_escape($list_id); ?>;
1915 $(function() {
1916 $(listId).addClass("active");
1917 if (isPost === true) {
1918 $("#eligibility").click();
1919 $("#eligibility").get(0).scrollIntoView();
1922 </script>
1923 </body>
1924 <?php $ed->dispatch(new RenderEvent($pid), RenderEvent::EVENT_RENDER_POST_PAGELOAD, 10); ?>
1925 </html>