* @author Sherwin Gaddis * @author Stephen Waite * @author Rod Roark * @copyright Copyright (c) 2018-2019 Brady Miller * @copyright Copyright (c) 2019 Sherwin Gaddis * @copyright Copyright (c) 2018-2021 Stephen Waite * @copyright Copyright (c) 2021-2022 Rod Roark * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ use OpenEMR\Common\Uuid\UuidRegistry; use OpenEMR\Services\FacilityService; use OpenEMR\Services\PatientService; use OpenEMR\Services\SocialHistoryService; require_once(dirname(__FILE__) . "/dupscore.inc.php"); global $facilityService; $facilityService = new FacilityService(); // These are for sports team use: $PLAYER_FITNESSES = array( xl('Full Play'), xl('Full Training'), xl('Restricted Training'), xl('Injured Out'), xl('Rehabilitation'), xl('Illness'), xl('International Duty') ); $PLAYER_FITCOLORS = array('#6677ff', '#00cc00', '#ffff00', '#ff3333', '#ff8800', '#ffeecc', '#ffccaa'); // Hard-coding this array because its values and meanings are fixed by the 837p // standard and we don't want people messing with them. $policy_types = array( '' => xl('N/A'), '12' => xl('Working Aged Beneficiary or Spouse with Employer Group Health Plan'), '13' => xl('End-Stage Renal Disease Beneficiary in MCP with Employer`s Group Plan'), '14' => xl('No-fault Insurance including Auto is Primary'), '15' => xl('Worker`s Compensation'), '16' => xl('Public Health Service (PHS) or Other Federal Agency'), '41' => xl('Black Lung'), '42' => xl('Veteran`s Administration'), '43' => xl('Disabled Beneficiary Under Age 65 with Large Group Health Plan (LGHP)'), '47' => xl('Other Liability Insurance is Primary'), ); /** * Get a patient's demographic data. * * @param int $pid The PID of the patient * @param string $given an optional subsection of the patient's demographic * data to retrieve. * @return array The requested subsection of a patient's demographic data. * If no subsection was given, returns everything, with the * date of birth as the last field. */ // To prevent sql injection on this function, if a variable is used for $given parameter, then // it needs to be escaped via whitelisting prior to using this function. function getPatientData($pid, $given = "*, DATE_FORMAT(DOB,'%m/%d/%Y') as DOB_TS") { $sql = "select $given from patient_data where pid=? order by date DESC limit 0,1"; return sqlQuery($sql, array($pid)); } function getInsuranceProvider($ins_id) { $sql = "select name from insurance_companies where id=?"; $row = sqlQuery($sql, array($ins_id)); return $row['name'] ?? ''; } function getInsuranceProviders() { $returnval = array(); if (true) { $sql = "select name, id from insurance_companies where inactive != 1 order by name, id"; $rez = sqlStatement($sql); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$row['id']] = $row['name']; } } else { // Please leave this here. I have a user who wants to see zip codes and PO // box numbers listed along with the insurance company names, as many companies // have different billing addresses for different plans. -- Rod Roark $sql = "select insurance_companies.name, insurance_companies.id, " . "addresses.zip, addresses.line1 " . "from insurance_companies, addresses " . "where addresses.foreign_id = insurance_companies.id " . "order by insurance_companies.name, addresses.zip"; $rez = sqlStatement($sql); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { preg_match("/\d+/", $row['line1'], $matches); $returnval[$row['id']] = $row['name'] . " (" . $row['zip'] . "," . $matches[0] . ")"; } } return $returnval; } function getInsuranceProvidersExtra() { $returnval = array(); // add a global and if for where to allow inactive inscompanies $sql = "SELECT insurance_companies.name, insurance_companies.id, insurance_companies.cms_id, addresses.line1, addresses.line2, addresses.city, addresses.state, addresses.zip FROM insurance_companies, addresses WHERE addresses.foreign_id = insurance_companies.id AND insurance_companies.inactive != 1 ORDER BY insurance_companies.name, addresses.zip"; $rez = sqlStatement($sql); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { switch ($GLOBALS['insurance_information']) { case $GLOBALS['insurance_information'] = '0': $returnval[$row['id']] = $row['name']; break; case $GLOBALS['insurance_information'] = '1': $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ")"; break; case $GLOBALS['insurance_information'] = '2': $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['zip'] . ")"; break; case $GLOBALS['insurance_information'] = '3': $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['state'] . ")"; break; case $GLOBALS['insurance_information'] = '4': $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['state'] . ", " . $row['zip'] . ")"; break; case $GLOBALS['insurance_information'] = '5': $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['city'] . ", " . $row['state'] . ", " . $row['zip'] . ")"; break; case $GLOBALS['insurance_information'] = '6': $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['city'] . ", " . $row['state'] . ", " . $row['zip'] . ", " . $row['cms_id'] . ")"; break; case $GLOBALS['insurance_information'] = '7': preg_match("/\d+/", $row['line1'], $matches); $returnval[$row['id']] = $row['name'] . " (" . $row['zip'] . "," . $matches[0] . ")"; break; } } return $returnval; } // ---------------------------------------------------------------------------- // Get one facility row. If the ID is not specified, then get either the // "main" (billing) facility, or the default facility of the currently // logged-in user. This was created to support genFacilityTitle() but // may find additional uses. // function getFacility($facid = 0) { global $facilityService; $facility = null; if ($facid > 0) { return $facilityService->getById($facid); } if ($GLOBALS['login_into_facility']) { //facility is saved in sessions $facility = $facilityService->getById($_SESSION['facilityId']); } else { if ($facid == 0) { $facility = $facilityService->getPrimaryBillingLocation(); } else { $facility = $facilityService->getFacilityForUser($_SESSION['authUserID']); } } return $facility; } // Generate a report title including report name and facility name, address // and phone. // function genFacilityTitle($repname = '', $facid = 0, $logo = '') { $s = ''; $s .= "\n"; $s .= " \n"; if (empty($logo)) { $s .= " \n"; } else { $s .= " \n"; $s .= " \n"; } $s .= " \n"; $s .= " \n"; $s .= "
" . text($repname) . "" . $logo . "" . text($repname) . "\n"; $r = getFacility($facid); if (!empty($r)) { $s .= "" . text($r['name'] ?? '') . "\n"; if (!empty($r['street'])) { $s .= "
" . text($r['street']) . "\n"; } if (!empty($r['city']) || !empty($r['state']) || !empty($r['postal_code'])) { $s .= "
"; if ($r['city']) { $s .= text($r['city']); } if ($r['state']) { if ($r['city']) { $s .= ", \n"; } $s .= text($r['state']); } if ($r['postal_code']) { $s .= " " . text($r['postal_code']); } $s .= "\n"; } if (!empty($r['country_code'])) { $s .= "
" . text($r['country_code']) . "\n"; } if (preg_match('/[1-9]/', ($r['phone'] ?? ''))) { $s .= "
" . text($r['phone']) . "\n"; } } $s .= "
\n"; return $s; } /** GET FACILITIES returns all facilities or just the id for the first one (FACILITY FILTERING (lemonsoftware)) @param string - if 'first' return first facility ordered by id @return array | int for 'first' case */ function getFacilities($first = '') { global $facilityService; $fres = $facilityService->getAllFacility(); if ($first == 'first') { return $fres[0]['id']; } else { return $fres; } } //(CHEMED) facility filter function getProviderInfo($providerID = "%", $providers_only = true, $facility = '') { $param1 = ""; if ($providers_only === 'any') { $param1 = " AND authorized = 1 AND active = 1 "; } elseif ($providers_only) { $param1 = " AND authorized = 1 AND calendar = 1 "; } //-------------------------------- //(CHEMED) facility filter $param2 = ""; if ($facility) { if ($GLOBALS['restrict_user_facility']) { $param2 = " AND (facility_id = '" . add_escape_custom($facility) . "' OR '" . add_escape_custom($facility) . "' IN (select facility_id from users_facility where tablename = 'users' and table_id = id))"; } else { $param2 = " AND facility_id = '" . add_escape_custom($facility) . "' "; } } //-------------------------------- $command = "="; if ($providerID == "%") { $command = "like"; } // removing active from query since is checked above with $providers_only argument $query = "select distinct id, username, lname, fname, authorized, info, facility, suffix " . "from users where username != '' and id $command '" . add_escape_custom($providerID) . "' " . $param1 . $param2; // sort by last name -- JRM June 2008 $query .= " ORDER BY lname, fname "; $rez = sqlStatement($query); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } //if only one result returned take the key/value pairs in array [0] and merge them down into // the base array so that $resultval[0]['key'] is also accessible from $resultval['key'] if ($iter == 1) { $akeys = array_keys($returnval[0]); foreach ($akeys as $key) { $returnval[0][$key] = $returnval[0][$key]; } } return ($returnval ?? null); } function getProviderName($providerID, $provider_only = 'any') { $pi = getProviderInfo($providerID, $provider_only); if (!empty($pi[0]["lname"]) && (strlen($pi[0]["lname"]) > 0)) { if (!empty($pi[0]["suffix"]) && (strlen($pi[0]["suffix"]) > 0)) { $pi[0]["lname"] .= ", " . $pi[0]["suffix"]; } return $pi[0]['fname'] . " " . $pi[0]['lname']; } return ""; } function getProviderId($providerName) { $query = "select id from users where username = ?"; $rez = sqlStatement($query, array($providerName)); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } return $returnval; } // To prevent sql injection on this function, if a variable is used for $given parameter, then // it needs to be escaped via whitelisting prior to using this function; see lines 2020-2121 of // library/clinical_rules.php script for example of this. function getHistoryData($pid, $given = "*", $dateStart = '', $dateEnd = '') { $where = ''; if ($given == 'tobacco') { $where = 'tobacco is not null and'; } if ($dateStart && $dateEnd) { $res = sqlQuery("select $given from history_data where $where pid = ? and date >= ? and date <= ? order by date DESC, id DESC limit 0,1", array($pid,$dateStart,$dateEnd)); } elseif ($dateStart && !$dateEnd) { $res = sqlQuery("select $given from history_data where $where pid = ? and date >= ? order by date DESC, id DESC limit 0,1", array($pid,$dateStart)); } elseif (!$dateStart && $dateEnd) { $res = sqlQuery("select $given from history_data where $where pid = ? and date <= ? order by date DESC, id DESC limit 0,1", array($pid,$dateEnd)); } else { $res = sqlQuery("select $given from history_data where $where pid = ? order by date DESC, id DESC limit 0,1", array($pid)); } return $res; } // function getInsuranceData($pid, $type = "primary", $given = "insd.*, DATE_FORMAT(subscriber_DOB,'%m/%d/%Y') as subscriber_DOB, ic.name as provider_name") // To prevent sql injection on this function, if a variable is used for $given parameter, then // it needs to be escaped via whitelisting prior to using this function. function getInsuranceData($pid, $type = "primary", $given = "insd.*, ic.name as provider_name") { $sql = "select $given from insurance_data as insd " . "left join insurance_companies as ic on ic.id = insd.provider " . "where pid = ? and type = ? order by date DESC limit 1"; return sqlQuery($sql, array($pid, $type)); } // To prevent sql injection on this function, if a variable is used for $given parameter, then // it needs to be escaped via whitelisting prior to using this function. function getInsuranceDataByDate( $pid, $date, $type, $given = "insd.*, DATE_FORMAT(subscriber_DOB,'%m/%d/%Y') as subscriber_DOB, ic.name as provider_name" ) { // this must take the date in the following manner: YYYY-MM-DD // this function recalls the insurance value that was most recently enterred from the // given date. it will call up most recent records up to and on the date given, // but not records enterred after the given date $sql = "select $given from insurance_data as insd " . "left join insurance_companies as ic on ic.id = provider " . "where pid = ? and (date_format(date,'%Y-%m-%d') <= ? OR date IS NULL) and " . "type=? order by date DESC limit 1"; return sqlQuery($sql, array($pid,$date,$type)); } // To prevent sql injection on this function, if a variable is used for $given parameter, then // it needs to be escaped via whitelisting prior to using this function. function getEmployerData($pid, $given = "*") { $sql = "select $given from employer_data where pid=? order by date DESC limit 0,1"; return sqlQuery($sql, array($pid)); } function _set_patient_inc_count($limit, $count, $where, $whereBindArray = array()) { // When the limit is exceeded, find out what the unlimited count would be. $GLOBALS['PATIENT_INC_COUNT'] = $count; // if ($limit != "all" && $GLOBALS['PATIENT_INC_COUNT'] >= $limit) { if ($limit != "all") { $tmp = sqlQuery("SELECT count(*) AS count FROM patient_data WHERE $where", $whereBindArray); $GLOBALS['PATIENT_INC_COUNT'] = $tmp['count']; } } /** * Allow the last name to be followed by a comma and some part of a first name(can * also place middle name after the first name with a space separating them) * Allows comma alone followed by some part of a first name(can also place middle name * after the first name with a space separating them). * Allows comma alone preceded by some part of a last name. * If no comma or space, then will search both last name and first name. * If the first letter of either name is capital, searches for name starting * with given substring (the expected behavior). If it is lower case, it * searches for the substring anywhere in the name. This applies to either * last name, first name, and middle name. * Also allows first name followed by middle and/or last name when separated by spaces. * @param string $term * @param string $given * @param string $orderby * @param string $limit * @param string $start * @return array */ // To prevent sql injection on this function, if a variable is used for $given OR $orderby parameter, then // it needs to be escaped via whitelisting prior to using this function. function getPatientLnames($term = "%", $given = "pid, id, lname, fname, mname, providerID, DATE_FORMAT(DOB,'%m/%d/%Y') as DOB_TS", $orderby = "lname ASC, fname ASC", $limit = "all", $start = "0") { $names = getPatientNameSplit($term); foreach ($names as $key => $val) { if (!empty($val)) { if ((strlen($val) > 1) && ($names[$key][0] != strtoupper($names[$key][0]))) { $names[$key] = '%' . $val . '%'; } else { $names[$key] = $val . '%'; } } } // Debugging section below //if(array_key_exists('first',$names)) { // error_log("first name search term :".$names['first']); //} //if(array_key_exists('middle',$names)) { // error_log("middle name search term :".$names['middle']); //} //if(array_key_exists('last',$names)) { // error_log("last name search term :".$names['last']); //} // Debugging section above $sqlBindArray = array(); if (array_key_exists('last', $names) && $names['last'] == '') { // Do not search last name $where = "fname LIKE ? "; array_push($sqlBindArray, $names['first']); if ($names['middle'] != '') { $where .= "AND mname LIKE ? "; array_push($sqlBindArray, $names['middle']); } } elseif (array_key_exists('first', $names) && $names['first'] == '') { // Do not search first name or middle name $where = "lname LIKE ? "; array_push($sqlBindArray, $names['last']); } elseif (empty($names['first']) && !empty($names['last'])) { // Search both first name and last name with same term $names['first'] = $names['last']; $where = "lname LIKE ? OR fname LIKE ? "; array_push($sqlBindArray, $names['last'], $names['first']); } elseif ($names['middle'] != '') { $where = "lname LIKE ? AND fname LIKE ? AND mname LIKE ? "; array_push($sqlBindArray, $names['last'], $names['first'], $names['middle']); } else { $where = "lname LIKE ? AND fname LIKE ? "; array_push($sqlBindArray, $names['last'], $names['first']); } if (!empty($GLOBALS['pt_restrict_field'])) { if ($_SESSION["authUser"] != 'admin' || $GLOBALS['pt_restrict_admin']) { $where .= " AND ( patient_data." . add_escape_custom($GLOBALS['pt_restrict_field']) . " = ( SELECT facility_id FROM users WHERE username = ?) OR patient_data." . add_escape_custom($GLOBALS['pt_restrict_field']) . " = '' ) "; array_push($sqlBindArray, $_SESSION["authUser"]); } } $sql = "SELECT $given FROM patient_data WHERE $where ORDER BY $orderby"; if ($limit != "all") { $sql .= " limit " . escape_limit($start) . ", " . escape_limit($limit); } $rez = sqlStatement($sql, $sqlBindArray); $returnval = array(); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } _set_patient_inc_count($limit, count($returnval), $where, $sqlBindArray); return $returnval; } /** * Accept a string used by a search function expected to find a patient name, * then split up the string if a comma or space exists. Return an array having * from 1 to 3 elements, named first, middle, and last. * See above getPatientLnames() function for details on how the splitting occurs. * @param string $term * @return array */ function getPatientNameSplit($term) { $term = trim($term); if (strpos($term, ',') !== false) { $names = explode(',', $term); $n['last'] = $names[0]; if (strpos(trim($names[1]), ' ') !== false) { list($n['first'], $n['middle']) = explode(' ', trim($names[1])); } else { $n['first'] = $names[1]; } } elseif (strpos($term, ' ') !== false) { $names = explode(' ', $term); if (count($names) == 1) { $n['last'] = $names[0]; } elseif (count($names) == 3) { $n['first'] = $names[0]; $n['middle'] = $names[1]; $n['last'] = $names[2]; } else { // This will handle first and last name or first followed by // multiple names only using just the last of the names in the list. $n['first'] = $names[0]; $n['last'] = end($names); } } else { $n['last'] = $term; if (empty($n['last'])) { $n['last'] = '%'; } } // Trim whitespace off the names before returning foreach ($n as $key => $val) { $n[$key] = trim($val); } return $n; // associative array containing names } // To prevent sql injection on this function, if a variable is used for $given OR $orderby parameter, then // it needs to be escaped via whitelisting prior to using this function. function getPatientId($pid = "%", $given = "pid, id, lname, fname, mname, providerID, DATE_FORMAT(DOB,'%m/%d/%Y') as DOB_TS", $orderby = "lname ASC, fname ASC", $limit = "all", $start = "0") { $sqlBindArray = array(); $where = "pubpid LIKE ? "; array_push($sqlBindArray, $pid . "%"); if (!empty($GLOBALS['pt_restrict_field']) && $GLOBALS['pt_restrict_by_id']) { if ($_SESSION["authUser"] != 'admin' || $GLOBALS['pt_restrict_admin']) { $where .= "AND ( patient_data." . add_escape_custom($GLOBALS['pt_restrict_field']) . " = ( SELECT facility_id FROM users WHERE username = ?) OR patient_data." . add_escape_custom($GLOBALS['pt_restrict_field']) . " = '' ) "; array_push($sqlBindArray, $_SESSION["authUser"]); } } $sql = "SELECT $given FROM patient_data WHERE $where ORDER BY $orderby"; if ($limit != "all") { $sql .= " limit " . escape_limit($start) . ", " . escape_limit($limit); } $rez = sqlStatement($sql, $sqlBindArray); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } _set_patient_inc_count($limit, count($returnval), $where, $sqlBindArray); return $returnval; } // To prevent sql injection on this function, if a variable is used for $given OR $orderby parameter, then // it needs to be escaped via whitelisting prior to using this function. function getByPatientDemographics($searchTerm = "%", $given = "pid, id, lname, fname, mname, providerID, DATE_FORMAT(DOB,'%m/%d/%Y') as DOB_TS", $orderby = "lname ASC, fname ASC", $limit = "all", $start = "0") { $layoutCols = sqlStatement( "SELECT field_id FROM layout_options WHERE form_id = 'DEM' AND field_id not like ? AND uor !=0", array('em\_%') ); $sqlBindArray = array(); $where = ""; for ($iter = 0; $row = sqlFetchArray($layoutCols); $iter++) { if ($iter > 0) { $where .= " or "; } $where .= " " . add_escape_custom($row["field_id"]) . " like ? "; array_push($sqlBindArray, "%" . $searchTerm . "%"); } $sql = "SELECT $given FROM patient_data WHERE $where ORDER BY $orderby"; if ($limit != "all") { $sql .= " limit " . escape_limit($start) . ", " . escape_limit($limit); } $rez = sqlStatement($sql, $sqlBindArray); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } _set_patient_inc_count($limit, count($returnval), $where, $sqlBindArray); return $returnval; } // To prevent sql injection on this function, if a variable is used for $given OR $orderby parameter, then // it needs to be escaped via whitelisting prior to using this function. function getByPatientDemographicsFilter( $searchFields, $searchTerm = "%", $given = "pid, id, lname, fname, mname, providerID, DATE_FORMAT(DOB,'%m/%d/%Y') as DOB_TS", $orderby = "lname ASC, fname ASC", $limit = "all", $start = "0", $search_service_code = '' ) { $layoutCols = explode('~', $searchFields); $sqlBindArray = array(); $where = ""; $i = 0; foreach ($layoutCols as $val) { if (empty($val)) { continue; } if ($i > 0) { $where .= " or "; } if ($val == 'pid') { $where .= " " . escape_sql_column_name($val, ['patient_data']) . " = ? "; array_push($sqlBindArray, $searchTerm); } else { $where .= " " . escape_sql_column_name($val, ['patient_data']) . " like ? "; array_push($sqlBindArray, $searchTerm . "%"); } $i++; } // If no search terms, ensure valid syntax. if ($i == 0) { $where = "1 = 1"; } // If a non-empty service code was given, then restrict to patients who // have been provided that service. Since the code is used in a LIKE // clause, % and _ wildcards are supported. if ($search_service_code) { $where = "( $where ) AND " . "( SELECT COUNT(*) FROM billing AS b WHERE " . "b.pid = patient_data.pid AND " . "b.activity = 1 AND " . "b.code_type != 'COPAY' AND " . "b.code LIKE ? " . ") > 0"; array_push($sqlBindArray, $search_service_code); } $sql = "SELECT $given FROM patient_data WHERE $where ORDER BY $orderby"; if ($limit != "all") { $sql .= " limit " . escape_limit($start) . ", " . escape_limit($limit); } $rez = sqlStatement($sql, $sqlBindArray); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } _set_patient_inc_count($limit, count($returnval), $where, $sqlBindArray); return $returnval; } // return a collection of Patient PIDs // new arg style by JRM March 2008 // orig function getPatientPID($pid = "%", $given = "pid, id, lname, fname, mname, providerID, DATE_FORMAT(DOB,'%m/%d/%Y') as DOB_TS", $orderby = "lname ASC, fname ASC", $limit="all", $start="0") // To prevent sql injection on this function, if a variable is used for $given OR $orderby parameter, then // it needs to be escaped via whitelisting prior to using this function. function getPatientPID($args) { $pid = "%"; $given = "pid, id, lname, fname, mname, suffix, providerID, DATE_FORMAT(DOB,'%m/%d/%Y') as DOB_TS"; $orderby = "lname ASC, fname ASC"; $limit = "all"; $start = "0"; // alter default values if defined in the passed in args if (isset($args['pid'])) { $pid = $args['pid']; } if (isset($args['given'])) { $given = $args['given']; } if (isset($args['orderby'])) { $orderby = $args['orderby']; } if (isset($args['limit'])) { $limit = $args['limit']; } if (isset($args['start'])) { $start = $args['start']; } $command = "="; if ($pid == -1) { $pid = "%"; } elseif (empty($pid)) { $pid = "NULL"; } if (strstr($pid, "%")) { $command = "like"; } $sql = "select $given from patient_data where pid $command '" . add_escape_custom($pid) . "' order by $orderby"; if ($limit != "all") { $sql .= " limit " . escape_limit($start) . ", " . escape_limit($limit); } $rez = sqlStatement($sql); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } return $returnval; } /* return a patient's name in the format LAST, FIRST */ function getPatientName($pid) { if (empty($pid)) { return ""; } $patientData = getPatientPID(array("pid" => $pid)); if (empty($patientData[0]['lname'])) { return ""; } $patientName = $patientData[0]['lname'] . ", " . $patientData[0]['fname']; return $patientName; } /** * Get a patient's first name, middle name, last name and suffix if applicable. * * Returns a properly formatted, complete name when applicable. Example name * would be "John B Doe Jr". No additional punctuation is added. Spaces are * correctly omitted if the middle name of suffix does not apply. * * @var $pid int The Patient ID * @returns string The Full Name */ function getPatientFullNameAsString($pid): string { if (empty($pid)) { return ''; } $ptData = getPatientPID(["pid" => $pid]); $pt = $ptData[0]; if (empty($pt['lname'])) { return ""; } $name = $pt['fname']; if ($pt['mname']) { $name .= " {$pt['mname']}"; } $name .= " {$pt['lname']}"; if ($pt['suffix']) { $name .= " {$pt['suffix']}"; } return $name; } /* return a patient's name in the format FIRST LAST */ function getPatientNameFirstLast($pid) { if (empty($pid)) { return ""; } $patientData = getPatientPID(array("pid" => $pid)); if (empty($patientData[0]['lname'])) { return ""; } $patientName = $patientData[0]['fname'] . " " . $patientData[0]['lname']; return $patientName; } /* find patient data by DOB */ // To prevent sql injection on this function, if a variable is used for $given OR $orderby parameter, then // it needs to be escaped via whitelisting prior to using this function. function getPatientDOB($DOB = "%", $given = "pid, id, lname, fname, mname", $orderby = "lname ASC, fname ASC", $limit = "all", $start = "0") { $sqlBindArray = array(); $where = "DOB like ? "; array_push($sqlBindArray, $DOB . "%"); if (!empty($GLOBALS['pt_restrict_field'])) { if ($_SESSION["authUser"] != 'admin' || $GLOBALS['pt_restrict_admin']) { $where .= "AND ( patient_data." . add_escape_custom($GLOBALS['pt_restrict_field']) . " = ( SELECT facility_id FROM users WHERE username = ?) OR patient_data." . add_escape_custom($GLOBALS['pt_restrict_field']) . " = '' ) "; array_push($sqlBindArray, $_SESSION["authUser"]); } } $sql = "SELECT $given FROM patient_data WHERE $where ORDER BY $orderby"; if ($limit != "all") { $sql .= " limit " . escape_limit($start) . ", " . escape_limit($limit); } $rez = sqlStatement($sql, $sqlBindArray); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } _set_patient_inc_count($limit, count($returnval), $where, $sqlBindArray); return $returnval; } /* find patient data by SSN */ // To prevent sql injection on this function, if a variable is used for $given OR $orderby parameter, then // it needs to be escaped via whitelisting prior to using this function. function getPatientSSN($ss = "%", $given = "pid, id, lname, fname, mname, providerID", $orderby = "lname ASC, fname ASC", $limit = "all", $start = "0") { $sqlBindArray = array(); $where = "ss LIKE ?"; array_push($sqlBindArray, $ss . "%"); $sql = "SELECT $given FROM patient_data WHERE $where ORDER BY $orderby"; if ($limit != "all") { $sql .= " limit " . escape_limit($start) . ", " . escape_limit($limit); } $rez = sqlStatement($sql, $sqlBindArray); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } _set_patient_inc_count($limit, count($returnval), $where, $sqlBindArray); return $returnval; } //(CHEMED) Search by phone number // To prevent sql injection on this function, if a variable is used for $given OR $orderby parameter, then // it needs to be escaped via whitelisting prior to using this function. function getPatientPhone($phone = "%", $given = "pid, id, lname, fname, mname, providerID", $orderby = "lname ASC, fname ASC", $limit = "all", $start = "0") { $phone = preg_replace("/[[:punct:]]/", "", $phone); $sqlBindArray = array(); $where = "REPLACE(REPLACE(phone_home, '-', ''), ' ', '') REGEXP ?"; array_push($sqlBindArray, $phone); $sql = "SELECT $given FROM patient_data WHERE $where ORDER BY $orderby"; if ($limit != "all") { $sql .= " limit " . escape_limit($start) . ", " . escape_limit($limit); } $rez = sqlStatement($sql, $sqlBindArray); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { $returnval[$iter] = $row; } _set_patient_inc_count($limit, count($returnval), $where, $sqlBindArray); return $returnval; } //----------------------input functions function newPatientData( $db_id = "", $title = "", $fname = "", $lname = "", $mname = "", $sex = "", $DOB = "", $street = "", $postal_code = "", $city = "", $state = "", $country_code = "", $ss = "", $occupation = "", $phone_home = "", $phone_biz = "", $phone_contact = "", $status = "", $contact_relationship = "", $referrer = "", $referrerID = "", $email = "", $language = "", $ethnoracial = "", $interpretter = "", $migrantseasonal = "", $family_size = "", $monthly_income = "", $homeless = "", $financial_review = "", $pubpid = "", $pid = "MAX(pid)+1", $providerID = "", $genericname1 = "", $genericval1 = "", $genericname2 = "", $genericval2 = "", $billing_note = "", $phone_cell = "", $hipaa_mail = "", $hipaa_voice = "", $squad = 0, $pharmacy_id = 0, $drivers_license = "", $hipaa_notice = "", $hipaa_message = "", $regdate = "" ) { $fitness = 0; $referral_source = ''; if ($pid) { $rez = sqlQuery("select id, fitness, referral_source from patient_data where pid = ?", array($pid)); // Check for brain damage: if ($db_id != $rez['id']) { $errmsg = "Internal error: Attempt to change patient_data.id from '" . text($rez['id']) . "' to '" . text($db_id) . "' for pid '" . text($pid) . "'"; die($errmsg); } $fitness = $rez['fitness']; $referral_source = $rez['referral_source']; } // Get the default price level. $lrow = sqlQuery("SELECT option_id FROM list_options WHERE " . "list_id = 'pricelevel' AND activity = 1 ORDER BY is_default DESC, seq ASC LIMIT 1"); $pricelevel = empty($lrow['option_id']) ? '' : $lrow['option_id']; $query = ("replace into patient_data set id='" . add_escape_custom($db_id) . "', title='" . add_escape_custom($title) . "', fname='" . add_escape_custom($fname) . "', lname='" . add_escape_custom($lname) . "', mname='" . add_escape_custom($mname) . "', sex='" . add_escape_custom($sex) . "', DOB='" . add_escape_custom($DOB) . "', street='" . add_escape_custom($street) . "', postal_code='" . add_escape_custom($postal_code) . "', city='" . add_escape_custom($city) . "', state='" . add_escape_custom($state) . "', country_code='" . add_escape_custom($country_code) . "', drivers_license='" . add_escape_custom($drivers_license) . "', ss='" . add_escape_custom($ss) . "', occupation='" . add_escape_custom($occupation) . "', phone_home='" . add_escape_custom($phone_home) . "', phone_biz='" . add_escape_custom($phone_biz) . "', phone_contact='" . add_escape_custom($phone_contact) . "', status='" . add_escape_custom($status) . "', contact_relationship='" . add_escape_custom($contact_relationship) . "', referrer='" . add_escape_custom($referrer) . "', referrerID='" . add_escape_custom($referrerID) . "', email='" . add_escape_custom($email) . "', language='" . add_escape_custom($language) . "', ethnoracial='" . add_escape_custom($ethnoracial) . "', interpretter='" . add_escape_custom($interpretter) . "', migrantseasonal='" . add_escape_custom($migrantseasonal) . "', family_size='" . add_escape_custom($family_size) . "', monthly_income='" . add_escape_custom($monthly_income) . "', homeless='" . add_escape_custom($homeless) . "', financial_review='" . add_escape_custom($financial_review) . "', pubpid='" . add_escape_custom($pubpid) . "', pid= '" . add_escape_custom($pid) . "', providerID = '" . add_escape_custom($providerID) . "', genericname1 = '" . add_escape_custom($genericname1) . "', genericval1 = '" . add_escape_custom($genericval1) . "', genericname2 = '" . add_escape_custom($genericname2) . "', genericval2 = '" . add_escape_custom($genericval2) . "', billing_note= '" . add_escape_custom($billing_note) . "', phone_cell = '" . add_escape_custom($phone_cell) . "', pharmacy_id = '" . add_escape_custom($pharmacy_id) . "', hipaa_mail = '" . add_escape_custom($hipaa_mail) . "', hipaa_voice = '" . add_escape_custom($hipaa_voice) . "', hipaa_notice = '" . add_escape_custom($hipaa_notice) . "', hipaa_message = '" . add_escape_custom($hipaa_message) . "', squad = '" . add_escape_custom($squad) . "', fitness='" . add_escape_custom($fitness) . "', referral_source='" . add_escape_custom($referral_source) . "', regdate='" . add_escape_custom($regdate) . "', pricelevel='" . add_escape_custom($pricelevel) . "', date=NOW()"); $id = sqlInsert($query); if (!$db_id) { // find the last inserted id for new patient case $db_id = $id; } $foo = sqlQuery("select `pid`, `uuid` from `patient_data` where `id` = ? order by `date` limit 0,1", array($id)); // set uuid if not set yet (if this was an insert and not an update) if (empty($foo['uuid'])) { $uuid = (new UuidRegistry(['table_name' => 'patient_data']))->createUuid(); sqlStatementNoLog("UPDATE `patient_data` SET `uuid` = ? WHERE `id` = ?", [$uuid, $id]); } return $foo['pid']; } // Supported input date formats are: // mm/dd/yyyy // mm/dd/yy (assumes 20yy for yy < 10, else 19yy) // yyyy/mm/dd // also mm-dd-yyyy, etc. and mm.dd.yyyy, etc. // function fixDate($date, $default = "0000-00-00") { $fixed_date = $default; $date = trim($date); if (preg_match("'^[0-9]{1,4}[/.-][0-9]{1,2}[/.-][0-9]{1,4}$'", $date)) { $dmy = preg_split("'[/.-]'", $date); if ($dmy[0] > 99) { $fixed_date = sprintf("%04u-%02u-%02u", $dmy[0], $dmy[1], $dmy[2]); } else { if ($dmy[0] != 0 || $dmy[1] != 0 || $dmy[2] != 0) { if ($dmy[2] < 1000) { $dmy[2] += 1900; } if ($dmy[2] < 1910) { $dmy[2] += 100; } } // Determine if MDY date format is used, preferring Date Display Format from // global settings if it's not YMD, otherwise guessing from country code. $using_mdy = empty($GLOBALS['date_display_format']) ? ($GLOBALS['phone_country_code'] == 1) : ($GLOBALS['date_display_format'] == 1); if ($using_mdy) { $fixed_date = sprintf("%04u-%02u-%02u", $dmy[2], $dmy[0], $dmy[1]); } else { $fixed_date = sprintf("%04u-%02u-%02u", $dmy[2], $dmy[1], $dmy[0]); } } } return $fixed_date; } function pdValueOrNull($key, $value) { if ( ($key == 'DOB' || $key == 'regdate' || $key == 'contrastart' || substr($key, 0, 8) == 'userdate' || $key == 'deceased_date') && (empty($value) || $value == '0000-00-00') ) { return "NULL"; } else { return "'" . add_escape_custom($value) . "'"; } } /** * Create or update patient data from an array. * * This is a wrapper function for the PatientService which is now the single point * of patient creation and update. * * If successful, returns the pid of the patient * * @param $pid * @param $new * @param false $create * @return mixed */ function updatePatientData($pid, $new, $create = false) { // Create instance of patient service $patientService = new PatientService(); if ( $create === true || $pid === null ) { $result = $patientService->databaseInsert($new); updateDupScore($result['pid']); } else { $new['pid'] = $pid; $result = $patientService->databaseUpdate($new); } // From the returned patient data array // retrieve the data and return the pid $pid = $result['pid']; return $pid; } function newEmployerData( $pid, $name = "", $street = "", $postal_code = "", $city = "", $state = "", $country = "" ) { return sqlInsert("insert into employer_data set name='" . add_escape_custom($name) . "', street='" . add_escape_custom($street) . "', postal_code='" . add_escape_custom($postal_code) . "', city='" . add_escape_custom($city) . "', state='" . add_escape_custom($state) . "', country='" . add_escape_custom($country) . "', pid='" . add_escape_custom($pid) . "', date=NOW() "); } // Create or update employer data from an array. // function updateEmployerData($pid, $new, $create = false) { // used to hard code colnames array('name','street','city','state','postal_code','country'); // but now adapted for layout based $colnames = array(); foreach ($new as $key => $value) { $colnames[] = $key; } if ($create) { $set = "pid = '" . add_escape_custom($pid) . "', date = NOW()"; foreach ($colnames as $key) { $value = isset($new[$key]) ? $new[$key] : ''; $set .= ", `$key` = '" . add_escape_custom($value) . "'"; } return sqlInsert("INSERT INTO employer_data SET $set"); } else { $set = ''; $old = getEmployerData($pid); $modified = false; foreach ($colnames as $key) { $value = empty($old[$key]) ? '' : $old[$key]; if (isset($new[$key]) && strcmp($new[$key], $value) != 0) { $value = $new[$key]; $modified = true; } $set .= "`$key` = '" . add_escape_custom($value) . "', "; } if ($modified) { $set .= "pid = '" . add_escape_custom($pid) . "', date = NOW()"; return sqlInsert("INSERT INTO employer_data SET $set"); } return ($old['id'] ?? ''); } } // This updates or adds the given insurance data info, while retaining any // previously added insurance_data rows that should be preserved. // This does not directly support the maintenance of non-current insurance. // function newInsuranceData( $pid, $type = "", $provider = "", $policy_number = "", $group_number = "", $plan_name = "", $subscriber_lname = "", $subscriber_mname = "", $subscriber_fname = "", $subscriber_relationship = "", $subscriber_ss = "", $subscriber_DOB = null, $subscriber_street = "", $subscriber_postal_code = "", $subscriber_city = "", $subscriber_state = "", $subscriber_country = "", $subscriber_phone = "", $subscriber_employer = "", $subscriber_employer_street = "", $subscriber_employer_city = "", $subscriber_employer_postal_code = "", $subscriber_employer_state = "", $subscriber_employer_country = "", $copay = "", $subscriber_sex = "", $effective_date = null, $accept_assignment = "TRUE", $policy_type = "" ) { if (strlen($type) <= 0) { return false; } if (is_null($accept_assignment)) { $accept_assignment = "TRUE"; } if (is_null($policy_type)) { $policy_type = ""; } // If empty dates were passed, then null. if (empty($effective_date)) { $effective_date = null; } if (empty($subscriber_DOB)) { $subscriber_DOB = null; } $idres = sqlStatement("SELECT * FROM insurance_data WHERE " . "pid = ? AND type = ? ORDER BY date DESC", array($pid,$type)); $idrow = sqlFetchArray($idres); // Replace the most recent entry in any of the following cases: // * Its effective date is >= this effective date. // * It is the first entry and it has no (insurance) provider. // * There is no encounter that is earlier than the new effective date but // on or after the old effective date. // Otherwise insert a new entry. $replace = false; if ($idrow) { // convert date from null to "0000-00-00" for below strcmp and query $temp_idrow_date = (!empty($idrow['date'])) ? $idrow['date'] : "0000-00-00"; $temp_effective_date = (!empty($effective_date)) ? $effective_date : "0000-00-00"; if (strcmp($temp_idrow_date, $temp_effective_date) > 0) { $replace = true; } else { if (!$idrow['provider'] && !sqlFetchArray($idres)) { $replace = true; } else { $ferow = sqlQuery("SELECT count(*) AS count FROM form_encounter " . "WHERE pid = ? AND date < ? AND " . "date >= ?", array($pid, $temp_effective_date . " 00:00:00", $temp_idrow_date . " 00:00:00")); if ($ferow['count'] == 0) { $replace = true; } } } } if ($replace) { // TBD: This is a bit dangerous in that a typo in entering the effective // date can wipe out previous insurance history. So we want some data // entry validation somewhere. if ($effective_date === null) { sqlStatement("DELETE FROM insurance_data WHERE " . "pid = ? AND type = ? AND " . "id != ?", array($pid, $type, $idrow['id'])); } else { sqlStatement("DELETE FROM insurance_data WHERE " . "pid = ? AND type = ? AND date >= ? AND " . "id != ?", array($pid, $type, $effective_date, $idrow['id'])); } $data = array(); $data['type'] = $type; $data['provider'] = $provider; $data['policy_number'] = $policy_number; $data['group_number'] = $group_number; $data['plan_name'] = $plan_name; $data['subscriber_lname'] = $subscriber_lname; $data['subscriber_mname'] = $subscriber_mname; $data['subscriber_fname'] = $subscriber_fname; $data['subscriber_relationship'] = $subscriber_relationship; $data['subscriber_ss'] = $subscriber_ss; $data['subscriber_DOB'] = $subscriber_DOB; $data['subscriber_street'] = $subscriber_street; $data['subscriber_postal_code'] = $subscriber_postal_code; $data['subscriber_city'] = $subscriber_city; $data['subscriber_state'] = $subscriber_state; $data['subscriber_country'] = $subscriber_country; $data['subscriber_phone'] = $subscriber_phone; $data['subscriber_employer'] = $subscriber_employer; $data['subscriber_employer_city'] = $subscriber_employer_city; $data['subscriber_employer_street'] = $subscriber_employer_street; $data['subscriber_employer_postal_code'] = $subscriber_employer_postal_code; $data['subscriber_employer_state'] = $subscriber_employer_state; $data['subscriber_employer_country'] = $subscriber_employer_country; $data['copay'] = $copay; $data['subscriber_sex'] = $subscriber_sex; $data['pid'] = $pid; $data['date'] = $effective_date; $data['accept_assignment'] = $accept_assignment; $data['policy_type'] = $policy_type; updateInsuranceData($idrow['id'], $data); return $idrow['id']; } else { return sqlInsert( "INSERT INTO `insurance_data` SET `type` = ?, `provider` = ?, `policy_number` = ?, `group_number` = ?, `plan_name` = ?, `subscriber_lname` = ?, `subscriber_mname` = ?, `subscriber_fname` = ?, `subscriber_relationship` = ?, `subscriber_ss` = ?, `subscriber_DOB` = ?, `subscriber_street` = ?, `subscriber_postal_code` = ?, `subscriber_city` = ?, `subscriber_state` = ?, `subscriber_country` = ?, `subscriber_phone` = ?, `subscriber_employer` = ?, `subscriber_employer_city` = ?, `subscriber_employer_street` = ?, `subscriber_employer_postal_code` = ?, `subscriber_employer_state` = ?, `subscriber_employer_country` = ?, `copay` = ?, `subscriber_sex` = ?, `pid` = ?, `date` = ?, `accept_assignment` = ?, `policy_type` = ?", [ $type, $provider, $policy_number, $group_number, $plan_name, $subscriber_lname, $subscriber_mname, $subscriber_fname, $subscriber_relationship, $subscriber_ss, $subscriber_DOB, $subscriber_street, $subscriber_postal_code, $subscriber_city, $subscriber_state, $subscriber_country, $subscriber_phone, $subscriber_employer, $subscriber_employer_city, $subscriber_employer_street, $subscriber_employer_postal_code, $subscriber_employer_state, $subscriber_employer_country, $copay, $subscriber_sex, $pid, $effective_date, $accept_assignment, $policy_type ] ); } } // This is used internally only. function updateInsuranceData($id, $new) { $fields = sqlListFields("insurance_data"); $use = array(); foreach ($new as $key => $value) { if (in_array($key, $fields)) { $use[$key] = $value; } } $sqlBindArray = []; $sql = "UPDATE insurance_data SET "; foreach ($use as $key => $value) { $sql .= "`" . $key . "` = ?, "; array_push($sqlBindArray, $value); } $sql = substr($sql, 0, -2) . " WHERE id = ?"; array_push($sqlBindArray, $id); sqlStatement($sql, $sqlBindArray); } function newHistoryData($pid, $new = false) { $socialHistoryService = new SocialHistoryService(); $insertionRecord = $new; if (!is_array(($insertionRecord))) { $insertionRecord = [ 'pid' => $pid ]; } $socialHistoryService->create($insertionRecord); } function updateHistoryData($pid, $new) { $socialHistoryService = new SocialHistoryService(); return $socialHistoryService->updateHistoryDataForPatientPid($pid, $new); } // Returns Age // in months if < 2 years old // in years if > 2 years old // given YYYYMMDD from MySQL DATE_FORMAT(DOB,'%Y%m%d') // (optional) nowYMD is a date in YYYYMMDD format function getPatientAge($dobYMD, $nowYMD = null) { $patientService = new PatientService(); return $patientService->getPatientAge($dobYMD, $nowYMD); } /** * Wrapper to make sure the clinical rules dates formats corresponds to the * format expected by getPatientAgeYMD * * @param string $dob date of birth * @param string $target date to calculate age on * @return array containing * age - decimal age in years * age_in_months - decimal age in months * ageinYMD - formatted string #y #m #d */ function parseAgeInfo($dob, $target) { // Prepare dob (expected in order Y M D, remove whatever delimiters might be there $dateDOB = preg_replace("/[-\s\/]/", "", $dob); ; // Prepare target (Y-M-D H:M:S) $dateTarget = preg_replace("/[-\s\/]/", "", $target); return getPatientAgeYMD($dateDOB, $dateTarget); } /** * * @param type $dob * @param type $date * @return array containing * age - decimal age in years * age_in_months - decimal age in months * ageinYMD - formatted string #y #m #d */ function getPatientAgeYMD($dob, $date = null) { $service = new PatientService(); return $service->getPatientAgeYMD($dob, $date); } // Returns Age in days // in months if < 2 years old // in years if > 2 years old // given YYYYMMDD from MySQL DATE_FORMAT(DOB,'%Y%m%d') // (optional) nowYMD is a date in YYYYMMDD format function getPatientAgeInDays($dobYMD, $nowYMD = null) { $age = -1; // strip any dashes from the DOB $dobYMD = preg_replace("/-/", "", $dobYMD); $dobDay = substr($dobYMD, 6, 2); $dobMonth = substr($dobYMD, 4, 2); $dobYear = substr($dobYMD, 0, 4); // set the 'now' date values if ($nowYMD == null) { $nowDay = date("d"); $nowMonth = date("m"); $nowYear = date("Y"); } else { $nowDay = substr($nowYMD, 6, 2); $nowMonth = substr($nowYMD, 4, 2); $nowYear = substr($nowYMD, 0, 4); } // do the date math $dobtime = strtotime($dobYear . "-" . $dobMonth . "-" . $dobDay); $nowtime = strtotime($nowYear . "-" . $nowMonth . "-" . $nowDay); $timediff = $nowtime - $dobtime; $age = $timediff / 86400; // 24 hours * 3600 seconds/hour = 86400 seconds return $age; } /** * Returns a string to be used to display a patient's age * * @param type $dobYMD * @param type $asOfYMD * @return string suitable for displaying patient's age based on preferences */ function getPatientAgeDisplay($dobYMD, $asOfYMD = null) { $service = new PatientService(); return $service->getPatientAgeDisplay($dobYMD, $asOfYMD); } function dateToDB($date) { $date = substr($date, 6, 4) . "-" . substr($date, 3, 2) . "-" . substr($date, 0, 2); return $date; } /** * Get up to 3 insurances (primary, secondary, tertiary) that are effective * for the given patient on the given date. * * @param int The PID of the patient. * @param string Date in yyyy-mm-dd format. * @return array Array of 0-3 insurance_data rows. */ function getEffectiveInsurances($patient_id, $encdate) { $insarr = array(); foreach (array('primary','secondary','tertiary') as $instype) { $tmp = sqlQuery( "SELECT * FROM insurance_data " . "WHERE pid = ? AND type = ? " . "AND (date <= ? OR date IS NULL) ORDER BY date DESC LIMIT 1", array($patient_id, $instype, $encdate) ); if (empty($tmp['provider'])) { break; } $insarr[] = $tmp; } return $insarr; } /** * Get all requisition insurance companies * * */ function getAllinsurances($pid) { $insarr = array(); $sql = "SELECT a.type, a.provider, a.plan_name, a.policy_number, a.group_number, a.subscriber_lname, a.subscriber_fname, a.subscriber_relationship, a.subscriber_employer, b.name, c.line1, c.line2, c.city, c.state, c.zip FROM `insurance_data` AS a RIGHT JOIN insurance_companies AS b ON a.provider = b.id RIGHT JOIN addresses AS c ON a.provider = c.foreign_id WHERE a.pid = ? "; $inco = sqlStatement($sql, array($pid)); while ($icl = sqlFetchArray($inco)) { $insarr[] = $icl; } return $insarr; } /** * Get the patient's balance due. Normally this excludes amounts that are out * to insurance. If you want to include what insurance owes, set the second * parameter to true. * * @param int The PID of the patient. * @param boolean Indicates if amounts owed by insurance are to be included. * @param int Optional encounter id. If value is passed, will fetch only bills from specified encounter. * @return number The balance. */ function get_patient_balance($pid, $with_insurance = false, $eid = false) { $balance = 0; $bindarray = array($pid); $sqlstatement = "SELECT date, encounter, last_level_billed, " . "last_level_closed, stmt_count " . "FROM form_encounter WHERE pid = ?"; if ($eid) { $sqlstatement .= " AND encounter = ?"; array_push($bindarray, $eid); } $feres = sqlStatement($sqlstatement, $bindarray); while ($ferow = sqlFetchArray($feres)) { $encounter = $ferow['encounter']; $dos = substr($ferow['date'], 0, 10); $insarr = getEffectiveInsurances($pid, $dos); $inscount = count($insarr); if (!$with_insurance && $ferow['last_level_closed'] < $inscount && $ferow['stmt_count'] == 0) { // It's out to insurance so only the co-pay might be due. $brow = sqlQuery( "SELECT SUM(fee) AS amount FROM billing WHERE " . "pid = ? AND encounter = ? AND " . "code_type = 'copay' AND activity = 1", array($pid, $encounter) ); $drow = sqlQuery( "SELECT SUM(pay_amount) AS payments " . "FROM ar_activity WHERE " . "deleted IS NULL AND pid = ? AND encounter = ? AND payer_type = 0", array($pid, $encounter) ); // going to comment this out for now since computing future copays doesn't // equate to cash in hand, which shows in the Billing widget in dashboard 4-23-21 // $copay = !empty($insarr[0]['copay']) ? $insarr[0]['copay'] * 1 : 0; $copay = 0; $amt = !empty($brow['amount']) ? $brow['amount'] * 1 : 0; $pay = !empty($drow['payments']) ? $drow['payments'] * 1 : 0; $ptbal = $copay + $amt - $pay; if ($ptbal) { // @TODO check if we want to show patient payment credits. $balance += $ptbal; } } else { // Including insurance or not out to insurance, everything is due. $brow = sqlQuery("SELECT SUM(fee) AS amount FROM billing WHERE " . "pid = ? AND encounter = ? AND " . "activity = 1", array($pid, $encounter)); $drow = sqlQuery("SELECT SUM(pay_amount) AS payments, " . "SUM(adj_amount) AS adjustments FROM ar_activity WHERE " . "deleted IS NULL AND pid = ? AND encounter = ?", array($pid, $encounter)); $srow = sqlQuery("SELECT SUM(fee) AS amount FROM drug_sales WHERE " . "pid = ? AND encounter = ?", array($pid, $encounter)); $balance += $brow['amount'] + $srow['amount'] - $drow['payments'] - $drow['adjustments']; } } return sprintf('%01.2f', $balance); } function get_patient_balance_excluding($pid, $excluded = -1) { // We join form_encounter here to make sure we only count amounts for // encounters that exist. We've had some trouble before with encounters // that were deleted but leaving line items in the database. $brow = sqlQuery( "SELECT SUM(b.fee) AS amount " . "FROM billing AS b, form_encounter AS fe WHERE " . "b.pid = ? AND b.encounter != 0 AND b.encounter != ? AND b.activity = 1 AND " . "fe.pid = b.pid AND fe.encounter = b.encounter", array($pid, $excluded) ); $srow = sqlQuery( "SELECT SUM(s.fee) AS amount " . "FROM drug_sales AS s, form_encounter AS fe WHERE " . "s.pid = ? AND s.encounter != 0 AND s.encounter != ? AND " . "fe.pid = s.pid AND fe.encounter = s.encounter", array($pid, $excluded) ); $drow = sqlQuery( "SELECT SUM(a.pay_amount) AS payments, " . "SUM(a.adj_amount) AS adjustments " . "FROM ar_activity AS a, form_encounter AS fe WHERE " . "a.deleted IS NULL AND a.pid = ? AND a.encounter != 0 AND a.encounter != ? AND " . "fe.pid = a.pid AND fe.encounter = a.encounter", array($pid, $excluded) ); return sprintf( '%01.2f', $brow['amount'] + $srow['amount'] - $drow['payments'] - $drow['adjustments'] ); } // Function to check if patient is deceased. // Param: // $pid - patient id // $date - date checking if deceased (will default to current date if blank) // Return: // If deceased, then will return the number of // days that patient has been deceased and the deceased date. // If not deceased, then will return false. function is_patient_deceased($pid, $date = '') { // Set date to current if not set $date = (!empty($date)) ? $date : date('Y-m-d H:i:s'); // Query for deceased status (if person is deceased gets days_deceased and date_deceased) $results = sqlQuery("SELECT DATEDIFF(?,`deceased_date`) AS `days_deceased`, `deceased_date` AS `date_deceased` " . "FROM `patient_data` " . "WHERE `pid` = ? AND " . dateEmptySql('deceased_date', true, true) . "AND `deceased_date` <= ?", array($date,$pid,$date)); if (empty($results)) { // Patient is alive, so return false return false; } else { // Patient is dead, so return the number of days patient has been deceased. // Don't let it be zero days or else will confuse calls to this function. if ($results['days_deceased'] === 0) { $results['days_deceased'] = 1; } return $results; } } // This computes, sets and returns the dup score for the given patient. // function updateDupScore($pid) { $row = sqlQuery( "SELECT MAX(" . getDupScoreSQL() . ") AS dupscore " . "FROM patient_data AS p1, patient_data AS p2 WHERE " . "p1.pid = ? AND p2.pid < p1.pid", array($pid) ); $dupscore = empty($row['dupscore']) ? 0 : $row['dupscore']; sqlStatement( "UPDATE patient_data SET dupscore = ? WHERE pid = ?", array($dupscore, $pid) ); return $dupscore; }