7 * @link http://www.open-emr.org
8 * @author Rod Roark <rod@sunsetsystems.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @copyright Copyright (c) 2005-2017 Rod Roark <rod@sunsetsystems.com>
11 * @copyright Copyright (c) 2018 Brady Miller <brady.g.miller@gmail.com>
12 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
15 require_once('../../globals.php');
16 require_once($GLOBALS['srcdir'] . '/lists.inc.php');
17 require_once($GLOBALS['fileroot'] . '/custom/code_types.inc.php');
18 require_once($GLOBALS['srcdir'] . '/options.inc.php');
20 use OpenEMR\Common\Acl\AclMain
;
21 use OpenEMR\Common\Csrf\CsrfUtils
;
22 use OpenEMR\Common\Twig\TwigContainer
;
23 use OpenEMR\Core\Header
;
24 use OpenEMR\Menu\PatientMenuRole
;
25 use OpenEMR\OeUI\OemrUI
;
26 use OpenEMR\Services\ListService
;
28 // Check if user has permission for any issue type.
30 foreach ($ISSUE_TYPES as $type => $dummy) {
31 if (AclMain
::aclCheckIssue($type)) {
38 $tmp = getPatientData($pid, "squad");
39 if ($tmp['squad'] && ! AclMain
::aclCheckCore('squads', $tmp['squad'])) {
40 die(xlt('Not authorized'));
43 echo (new TwigContainer(null, $GLOBALS['kernel']))->getTwig()->render('core/unauthorized.html.twig', ['pageTitle' => xl("Patient Issues")]);
47 // Collect parameter(s)
48 $category = empty($_REQUEST['category']) ?
'' : $_REQUEST['category'];
50 // Get patient's preferred language for the patient education URL.
51 $tmp = getPatientData($pid, 'language');
52 $language = $tmp['language'];
56 <?php Header
::setupHeader('popper'); ?
>
57 <title
><?php
echo xlt('Patient Issues'); ?
></title
>
60 // callback from add_edit_issue.php:
61 function refreshIssue(issue
, title
) {
63 window
.location
=window
.location
;
66 function dopclick(id
, category
) {
68 category
= (category
== 0) ?
'' : category
;
69 let dlg_url
= 'add_edit_issue.php?issue=' +
encodeURIComponent(id
) +
'&thistype=' +
encodeURIComponent(category
);
70 dlgopen(dlg_url
, '_blank', 1280, 900, '', <?php
echo xlj("Add/Edit Issue"); ?
>);
73 // Process click on number of encounters.
74 function doeclick(id
) {
76 dlgopen('../problem_encounter.php?issue=' +
encodeURIComponent(id
), '_blank', 700, 400);
79 function getSelectionCheckBoxes(issueType
) {
80 let issue
= (issueType
instanceof HTMLElement
) ? issueType
: document
.getElementById(issueType
)
81 return Array.from(issue
.getElementsByClassName("selection-check"));
84 function rowSelectionChanged(issue
) {
85 var deleteBtn
= document
.getElementById(issue +
"-delete");
87 deleteBtn
.disabled
= !getSelectionCheckBoxes(issue
).some(e
=> e
.checked
);
91 function headerSelectionChanged(groupBox
, issueType
) {
92 let issueElm
= document
.getElementById(issueType
);
93 for (const c of
getSelectionCheckBoxes(issueElm
)) {
94 c
.checked
= groupBox
.checked
;
96 rowSelectionChanged(issueType
);
99 function deleteSelectedIssues(tableName
) {
100 var selBoxes
= getSelectionCheckBoxes(tableName
);
103 for (var i
= 0; i
< selBoxes
.length
; i++
) {
104 if (selBoxes
[i
].checked
) {
108 ids +
= selBoxes
[i
].id
.split("_")[1]
113 dlgopen('../deleter.php?issue=' + ids +
'&csrf_token_form=' +
<?php
echo js_url(CsrfUtils
::collectCsrfToken()); ?
>, '_blank', 500, 450);
116 // Called by the deleter.php window on a successful delete.
117 function imdeleted() {
121 // Process click on diagnosis for patient education popup.
122 function educlick(codetype
, codevalue
) {
123 top
.restoreSession();
124 dlgopen('../education.php?type=' +
encodeURIComponent(codetype
) +
125 '&code=' +
encodeURIComponent(codevalue
) +
126 '&language=' +
<?php
echo js_url($language); ?
>,
127 '_blank', 1024, 750,true); // Force a new window instead of iframe to address cross site scripting potential
130 // Add Encounter button is clicked.
131 function newEncounter() {
132 var f
= document
.forms
[0];
133 top
.restoreSession();
134 location
.href
='../../forms/newpatient/new.php?autoloaded=1&calenc=';
140 require_once("$include_root/patient_file/erx_patient_portal_js.php"); // jQuery for popups for eRx and patient portal
144 $arrOeUiSettings = array(
145 'heading_title' => xl('Medical Issues'),
146 'include_patient_name' => true,
147 'expandable' => true,
148 'expandable_files' => array("stats_full_patient_xpd", "external_data_patient_xpd", "patient_ledger_patient_xpd"),//all file names need suffix _xpd
149 'action' => "",//conceal, reveal, search, reset, link or back
150 'action_title' => "",
151 'action_href' => "",//only for actions - reset, link or back
152 'show_help_icon' => true,
153 'help_file_name' => "issues_dashboard_help.php"
155 $oemr_ui = new OemrUI($arrOeUiSettings);
168 <body
class="patient-medical-issues">
169 <div id
="container_div" class="<?php echo $oemr_ui->oeContainer();?>">
171 <div
class="col-sm-12">
172 <?php
require_once("$include_root/patient_file/summary/dashboard_header.php") ?
>
176 $list_id = "issues"; // to indicate nav item is active, count and give correct id
177 // Collect the patient menu then build it
178 $menuPatient = new PatientMenuRole();
179 $menuPatient->displayHorizNavBarMenu();
182 <div id
='patient_stats'>
183 <form method
='post' action
='stats_full.php' onsubmit
='return top.restoreSession()'>
184 <?php
foreach ($ISSUE_TYPES as $t => $focustitles) : ?
>
186 if (!AclMain
::aclCheckIssue($t)) {
190 if ($category && ($t !== $category)) {
194 $btnDelete = AclMain
::aclCheckCore('admin', 'super');
195 $canSelect = $btnDelete;
197 if (AclMain
::aclCheckIssue($t, '', ['write', 'addonly'])) {
198 if (in_array($t, ['allergy', 'medications']) && $GLOBALS['erx_enable']) {
200 'href' => '../../eRx.php?page=medentry',
201 'onclick' => 'top.restoreSession()',
206 'onclick' => 'dopclick(0, ' . attr_js($t) . ')'
211 $condition = ($GLOBALS['erx_enable'] && $GLOBALS['erx_medication_display'] && $t == 'medication') ?
"AND erx_uploaded != '1'" : '';
212 $pres = sqlStatement("SELECT * FROM lists WHERE pid = ? AND type = ? $condition ORDER BY begdate", [$pid, $t]);
214 $nothingRecorded = false;
216 // if no issues (will place a 'None' text vs. toggle algorithm here)
217 if (sqlNumRows($pres) < 1) {
218 if (getListTouch($pid, $t)) {
219 // Data entry has happened to this type, so can display an explicit None.
222 // Data entry has not happened to this type, so can show the none selection option.
223 $nothingRecorded = true;
228 <div
class="bg-light w-100 p-3 d-flex sticky-top justify-content-between align-items-center">
229 <div
class="d-flex align-items-center">
230 <?php
if (!($noIssues ||
$nothingRecorded)) : ?
>
231 <input type
="checkbox" class="selection-check mr-1" onclick
="headerSelectionChanged(this, <?php echo attr_js($t);?>);"/>
232 <button type
="button" class="btn btn-text px-2" data
-issue
-type
="<?php echo attr($t); ?>" data
-action
="toggle" data
-expanded
="false" aria
-label
="<?php echo xla("Expand
or collapse all items in section
"); ?>"><span
class="fa fa-fw fa-expand" aria
-hidden
="true"></span
></button
>
234 <h4
class="d-inline-block p-0 m-0"><?php
echo text($focustitles[0]); ?
></h4
>
237 <div
class="btn-group" role
="group">
239 <?php
if ($btnAdd) : ?
>
240 <a href
="<?php echo attr($btnAdd['href']); ?>" class="btn btn-sm btn-text" onclick
='<?php echo $btnAdd['onclick
']; ?>'><span
class="fa fa-fw fa-plus"></span
> 
;<?php
echo xlt('Add'); ?
></a
>
243 <?php
if ($btnDelete) : ?
>
244 <button type
="button" id
="<?php echo attr($t); ?>-delete" class="btn btn-sm btn-text" disabled onclick
="deleteSelectedIssues(<?php echo attr_js($t); ?>)"><span
class="fa fa-fw fa-trash-can"></span
> 
;<?php
echo xlt('Delete'); ?
></a
>
248 <div
class="list-group list-group-flush" id
="<?php echo attr($t); ?>">
249 <?php
if ($noIssues && !$nothingRecorded) : ?
>
250 <div
class="list-group-item"><?php
echo xlt("None{{Issue}}"); ?
></div
>
251 <?php
elseif (!$noIssues && $nothingRecorded) : ?
>
252 <div
class="list-group-item">
253 <div
class="form-check">
254 <input
class="form-check-input noneCheck" value
="none" <?php
echo (!AclMain
::aclCheckIssue($t, '', 'write')) ?
" disabled" : ""; ?
> type
="checkbox" name
="<?php echo attr($t); ?>" id
="<?php echo attr($t); ?>">
255 <label
class="form-check-label" for="<?php echo attr($t); ?>"><?php
echo xlt("None{{Issue}}"); ?
></label
>
262 while ($row = sqlFetchArray($pres)) :
265 $disptitle = trim($row['title']) ?
$row['title'] : "[Missing Title]";
267 $ierow = sqlQuery("SELECT count(*) AS count FROM issue_encounter WHERE list_id = ?", array($rowid));
269 // encount is used to toggle the color of the table-row output below
271 $bgclass = (($encount & 1) ?
"bg1" : "bg2");
273 $colorstyle = !empty($row['enddate']) ?
"text-muted" : "";
275 // look up the diag codes
277 if ($row['diagnosis'] != "") {
278 $diags = explode(";", $row['diagnosis']);
279 foreach ($diags as $diag) {
280 $codedesc = lookup_code_descriptions($diag);
281 list($codetype, $code) = explode(':', $diag);
286 $codetext .= "<a href='javascript:educlick(" . attr_js($codetype) . "," . attr_js($code) . ")' class='" . $colorstyle . "'>" .
287 text($diag . " (" . $codedesc . ")") . "</a>";
291 // calculate the status
293 if ($row['outcome'] == "1" && $row['enddate'] != null) {
296 $statusCompute = generate_display_field(array('data_type' => '1','list_id' => 'outcome'), $row['outcome']);
297 } elseif ($row['enddate'] == null) {
298 $statusCompute = xlt("Active");
300 // MU3 criteria, show medical problem's with end dates as a status of Completed.
301 $statusCompute = ($t == 'medical_problem') ?
xlt("Completed") : xlt("Inactive");
302 $resolved = ($t == "medical_problems") ?
true : false;
305 $click_class = 'statrow';
306 if ($row['erx_source'] == 1 && $t == 'allergy') {
308 } elseif ($row['erx_uploaded'] == 1 && $t == 'medication') {
312 $shortBegDate = trim(oeFormatShortDate($row['begdate']));
313 $shortEndDate = trim(oeFormatShortDate($row['enddate']));
314 $fullBegDate = trim(oeFormatDateTime($row['begdate']));
315 $fullEndDate = trim(oeFormatDateTime($row['enddate']));
316 $shortModDate = trim(oeFormatShortDate($row['modifydate']));
317 $fullModDate = trim(oeFormatDateTime($row['modifydate']));
319 $outcome = ($row['outcome']) ?
generate_display_field(['data_type' => 1, 'list_id' => 'outcome'], $row['outcome']) : false;
321 <div
class="list-group-item p-1">
322 <div
class="summary m-0 p-0 d-flex w-100 justify-content-end align-content-center">
323 <?php
if ($canSelect) : ?
>
324 <input type
="checkbox" class="selection-check mt-1 mr-2" data
-issue
="<?php echo attr($t); ?>" name
="sel_<?php echo attr($rowid); ?>" id
="sel_<?php echo attr($rowid); ?>">
326 <div
class="flex-fill pl-2">
327 <div
class="btn-group" role
="group">
328 <button type
="button" class="btn btn-outline-text btn-sm collapsed" data
-toggle
="collapse" data
-target
="#details_<?php echo attr($row['id']); ?>" aria
-expanded
="false" aria
-controls
="details_<?php echo attr($row['id']); ?>"><span aria
-hidden
="true" class="fa fa-fw fa-chevron-right"></span
></button
>
329 <button type
="button" class="btn btn-outline-text btn-sm editenc" data
-issue
-id
="<?php echo attr($row['id']); ?>"><span aria
-hidden
="true" class="fa fa-fw fa-link"></span
></button
>
331 <a href
="#" data
-issue
-id
="<?php echo attr($row['id']); ?>" class="font-weight-bold issue_title" data
-toggle
="tooltip" data
-placement
="right" title
="<?php echo text($diag . ": " . $codedesc); ?>">
332 <?php
echo text($disptitle); ?
>
333 </a
> 
;(<?php
echo $statusCompute; ?
><?php
echo (!$resolved && $outcome) ?
", $outcome" : ""; ?
>)
335 if ($focustitles[0] == "Allergies") :
336 echo generate_display_field(array('data_type' => '1','list_id' => 'reaction'), $row['reaction']);
340 <?php
if ($focustitles[0] == "Allergies") :
341 $l = new ListService();
342 $sev = $l->getListOption('severity_ccda', $row['severity_al']);
343 $hgl = (in_array($row['severity_al'], ['severe', 'life_threatening_severity', 'fatal'])) ?
'bg-warning font-weight-bold px-1' : '';
345 <span
class="mr-3 <?php echo attr($hgl); ?>">
346 <?php
echo text($sev['title']); ?
>
349 <div
class="text-right">
350 <span
class="font-weight-bold d-inline"><?php
echo xlt("Occurrence"); ?
></span
>
351 <span
><?php
echo generate_display_field(['data_type' => '1', 'list_id' => 'occurrence'], $row['occurrence']); ?
></span
>
354 <div id
="details_<?php echo attr($row['id']); ?>" class="collapse">
355 <div
class="d-flex flex-column w-100 my-3">
356 <div
class="d-flex w-100">
358 <div
class="font-weight-bold"><?php
echo xlt("Last Modified"); ?
></div
>
359 <div
class="pl-1" title
="<?php echo attr($fullModDate); ?>"><?php
echo text($shortModDate); ?
></div
>
361 <?php
if ($row['begdate']) : ?
>
363 <div
class="font-weight-bold "><?php
echo xlt("Start Date"); ?
></div
>
364 <div
class="" title
="<?php echo text($fullBegDate); ?>"><?php
echo text($shortBegDate); ?
></div
>
367 <?php
if ($row['enddate']) : ?
>
369 <div
class="font-weight-bold "><?php
echo xlt("End Date"); ?
></div
>
370 <div title
="<?php echo attr($fullEndDate); ?>"><?php
echo text($shortEndDate); ?
></div
>
373 <?php
if ($t == "allergy" ||
$t == "medical_problem") : ?
>
375 <div
class="font-weight-bold"><?php
echo xlt("Verification"); ?
></div
>
378 $codeListName = (!empty($thistype) && ($thistype == 'medical_problem')) ?
'condition-verification' : 'allergyintolerance-verification';
379 echo generate_display_field(array('data_type' => '1','list_id' => $codeListName), $row['verification']);
384 <?php
if ($row['referredby']) : ?
>
386 <div
class="font-weight-bold"><?php
echo xlt("Referred By"); ?
></div
>
387 <div
><?php
echo text($row['referredby']); ?
></div
>
390 <?php
if ($row['comments']) : ?
>
391 <div
class="flex-fill">
392 <div
class="font-weight-bold"><?php
echo xlt("Comments"); ?
></div
>
393 <div
><?php
echo text($row['comments']); ?
></div
>
397 <div
class="d-flex w-100 mt-2">
398 <?php
echo $codetext; ?
>
407 </div
> <!-- end patient_stats
-->
408 </div
><!--end of container div
-->
409 <?php
$oemr_ui->oeBelowContainerDiv();?
>
413 $
("[data-toggle='collapse']").click(function() {
414 $
(this
).children("span").toggleClass(["fa-chevron-right", "fa-chevron-down"]);
416 $
(".selection-check").on('change', function(e
) {
417 rowSelectionChanged(this
.getAttribute('data-issue'));
419 $
('[data-action="toggle"]').on('click', function(e
) {
420 let type
= this
.getAttribute('data-issue-type');
421 let isExp
= this
.getAttribute('data-expanded');
422 let selector
= (isExp
=== "false") ?
"[data-toggle='collapse'].collapsed" : "[data-toggle='collapse']";
423 console
.debug(selector
);
424 $
("#" + type +
" " + selector
).trigger('click');
425 $
(this
).children(".fa").toggleClass(["fa-compress", 'fa-expand']);
426 this
.setAttribute('data-expanded', (isExp
=== "false" ?
"true" : "false"));
428 $
(".issue_title").click(function() { dopclick($
(this
).data('issue-id'),0); });
429 $
(".editenc").click(function(event
) { doeclick($
(this
).data('issue-id'),0); });
430 $
("#newencounter").click(function() { newEncounter(); });
431 $
("#history").click(function() { GotoHistory(); });
432 $
("#back").click(function() { GoBack(); });
434 $
(".noneCheck").click(function() {
435 top
.restoreSession();
436 $
.post( "../../../library/ajax/lists_touch.php",
439 patient_id
: <?php
echo js_escape($pid); ?
>,
440 csrf_token_form
: <?php
echo js_escape(CsrfUtils
::collectCsrfToken()); ?
>
447 var GotoHistory
= function() {
448 top
.restoreSession();
449 location
.href
='../history/history_full.php';
452 var GoBack
= function () {
453 top
.restoreSession();
454 location
.href
='demographics.php';