You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1593 lines
68 KiB
1593 lines
68 KiB
2 years ago
|
<?php
|
||
|
|
||
|
/**
|
||
|
* library/FeeSheet.class.php
|
||
|
*
|
||
|
* Base class for implementations of the Fee Sheet.
|
||
|
* This should not include UI but may be extended by a class that does.
|
||
|
*
|
||
|
* LICENSE: This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* as published by the Free Software Foundation; either version 3
|
||
|
* of the License, or (at your option) any later version.
|
||
|
* This program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see
|
||
|
* http://www.gnu.org/licenses/licenses.html#GPL .
|
||
|
*
|
||
|
* @package OpenEMR
|
||
|
* @license https://www.gnu.org/licenses/licenses.html#GPL GNU GPL V3+
|
||
|
* @author Rod Roark <rod@sunsetsystems.com>
|
||
|
* @link http://www.open-emr.org
|
||
|
*/
|
||
|
|
||
|
require_once(dirname(__FILE__) . "/../interface/globals.php");
|
||
|
require_once(dirname(__FILE__) . "/../custom/code_types.inc.php");
|
||
|
require_once(dirname(__FILE__) . "/../interface/drugs/drugs.inc.php");
|
||
|
require_once(dirname(__FILE__) . "/options.inc.php");
|
||
|
require_once(dirname(__FILE__) . "/appointment_status.inc.php");
|
||
|
require_once(dirname(__FILE__) . "/forms.inc");
|
||
|
|
||
|
use OpenEMR\Billing\BillingUtilities;
|
||
|
use OpenEMR\Common\Acl\AclMain;
|
||
|
use OpenEMR\Common\Logging\EventAuditLogger;
|
||
|
|
||
|
// For logging checksums set this to true.
|
||
|
define('CHECKSUM_LOGGING', true);
|
||
|
|
||
|
// require_once(dirname(__FILE__) . "/api.inc");
|
||
|
// require_once(dirname(__FILE__) . "/forms.inc");
|
||
|
|
||
|
class FeeSheet
|
||
|
{
|
||
|
public $pid; // patient id
|
||
|
public $encounter; // encounter id
|
||
|
public $got_warehouses = false; // if there is more than 1 warehouse
|
||
|
public $default_warehouse = ''; // logged-in user's default warehouse
|
||
|
public $visit_date = ''; // YYYY-MM-DD date of this visit
|
||
|
public $match_services_to_products = false; // For IPPF
|
||
|
public $patient_age = 0; // Age in years as of the visit date
|
||
|
public $patient_male = 0; // 1 if male
|
||
|
public $patient_pricelevel = ''; // From patient_data.pricelevel
|
||
|
public $provider_id = 0;
|
||
|
public $supervisor_id = 0;
|
||
|
public $code_is_in_fee_sheet = false; // Set by genCodeSelectorValue()
|
||
|
|
||
|
// Possible units of measure for NDC drug quantities.
|
||
|
public $ndc_uom_choices = array(
|
||
|
'ML' => 'ML',
|
||
|
'GR' => 'Grams',
|
||
|
'ME' => 'Milligrams',
|
||
|
'F2' => 'I.U.',
|
||
|
'UN' => 'Units'
|
||
|
);
|
||
|
|
||
|
// Set by checkRelatedForContraception():
|
||
|
public $line_contra_code = '';
|
||
|
public $line_contra_cyp = 0;
|
||
|
public $line_contra_methtype = 0; // 0 = None, 1 = Not initial, 2 = Initial consult
|
||
|
|
||
|
// Array of line items generated by addServiceLineItem().
|
||
|
// Each element is an array of line item attributes.
|
||
|
public $serviceitems = array();
|
||
|
|
||
|
// Array of line items generated by addProductLineItem().
|
||
|
// Each element is an array of line item attributes.
|
||
|
public $productitems = array();
|
||
|
|
||
|
// Indicates if any line item has a fee.
|
||
|
public $hasCharges = false;
|
||
|
|
||
|
// Indicates if any clinical services or products are in the fee sheet.
|
||
|
public $required_code_count = 0;
|
||
|
|
||
|
// These variables are used to compute the initial consult service with highest CYP (IPPF).
|
||
|
public $contraception_code = '';
|
||
|
public $contraception_cyp = 0;
|
||
|
|
||
|
public $ALLOW_COPAYS = false;
|
||
|
|
||
|
function __construct($pid = 0, $encounter = 0)
|
||
|
{
|
||
|
if (empty($pid)) {
|
||
|
$pid = $GLOBALS['pid'];
|
||
|
}
|
||
|
|
||
|
if (empty($encounter)) {
|
||
|
$encounter = $GLOBALS['encounter'];
|
||
|
}
|
||
|
|
||
|
$this->pid = $pid;
|
||
|
$this->encounter = $encounter;
|
||
|
// get provider field for pid's primary insurance from insurance_data to be added to billing row table as payer_id
|
||
|
$primary_insurance = getInsuranceData($this->pid);
|
||
|
$this->payer_id = $primary_insurance['provider'];
|
||
|
|
||
|
// IPPF doesn't want any payments to be made or displayed in the Fee Sheet.
|
||
|
$this->ALLOW_COPAYS = empty($GLOBALS['ippf_specific']);
|
||
|
|
||
|
// Get the user's default warehouse and an indicator if there's a choice of warehouses.
|
||
|
$wrow = sqlQuery("SELECT count(*) AS count FROM list_options WHERE list_id = 'warehouse' AND activity = 1");
|
||
|
$this->got_warehouses = $wrow['count'] > 1;
|
||
|
$wrow = sqlQuery(
|
||
|
"SELECT default_warehouse FROM users WHERE username = ?",
|
||
|
array($_SESSION['authUser'])
|
||
|
);
|
||
|
$this->default_warehouse = empty($wrow['default_warehouse']) ? '' : $wrow['default_warehouse'];
|
||
|
|
||
|
// Get some info about this visit.
|
||
|
$visit_row = sqlQuery("SELECT fe.date, fe.provider_id, fe.supervisor_id, " .
|
||
|
"opc.pc_catname, fac.extra_validation " .
|
||
|
"FROM form_encounter AS fe " .
|
||
|
"LEFT JOIN openemr_postcalendar_categories AS opc ON opc.pc_catid = fe.pc_catid " .
|
||
|
"LEFT JOIN facility AS fac ON fac.id = fe.facility_id " .
|
||
|
"WHERE fe.pid = ? AND fe.encounter = ? LIMIT 1", array($this->pid, $this->encounter));
|
||
|
$this->visit_date = substr($visit_row['date'], 0, 10);
|
||
|
$this->provider_id = $visit_row['provider_id'];
|
||
|
if (empty($this->provider_id)) {
|
||
|
$this->provider_id = $this->findProvider();
|
||
|
}
|
||
|
|
||
|
$this->supervisor_id = $visit_row['supervisor_id'];
|
||
|
// This flag is specific to IPPF validation at form submit time. It indicates
|
||
|
// that most contraceptive services and products should match up on the fee sheet.
|
||
|
$this->match_services_to_products = $GLOBALS['ippf_specific'] &&
|
||
|
!empty($visit_row['extra_validation']);
|
||
|
|
||
|
// Get some information about the patient.
|
||
|
$patientrow = getPatientData($this->pid, "DOB, sex, pricelevel");
|
||
|
$this->patient_age = $this->getAge($patientrow['DOB'], $this->visit_date);
|
||
|
$this->patient_male = strtoupper(substr($patientrow['sex'], 0, 1)) == 'M' ? 1 : 0;
|
||
|
$this->patient_pricelevel = $patientrow['pricelevel'];
|
||
|
}
|
||
|
|
||
|
// Convert numeric code type to the alpha version.
|
||
|
//
|
||
|
public static function alphaCodeType($id)
|
||
|
{
|
||
|
global $code_types;
|
||
|
foreach ($code_types as $key => $value) {
|
||
|
if ($value['id'] == $id) {
|
||
|
return $key;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
// Compute age in years given a DOB and "as of" date.
|
||
|
//
|
||
|
public static function getAge($dob, $asof = '')
|
||
|
{
|
||
|
if (empty($asof)) {
|
||
|
$asof = date('Y-m-d');
|
||
|
}
|
||
|
|
||
|
$a1 = explode('-', substr($dob, 0, 10));
|
||
|
$a2 = explode('-', substr($asof, 0, 10));
|
||
|
$age = $a2[0] - $a1[0];
|
||
|
if ($a2[1] < $a1[1] || ($a2[1] == $a1[1] && $a2[2] < $a1[2])) {
|
||
|
--$age;
|
||
|
}
|
||
|
|
||
|
return $age;
|
||
|
}
|
||
|
|
||
|
// Gets the provider from the encounter, logged-in user or patient demographics.
|
||
|
// Adapted from work by Terry Hill.
|
||
|
//
|
||
|
public function findProvider()
|
||
|
{
|
||
|
$find_provider = sqlQuery(
|
||
|
"SELECT provider_id FROM form_encounter " .
|
||
|
"WHERE pid = ? AND encounter = ? ORDER BY id DESC LIMIT 1",
|
||
|
array($this->pid, $this->encounter)
|
||
|
);
|
||
|
$providerid = $find_provider['provider_id'];
|
||
|
if (!$providerid) {
|
||
|
$get_authorized = $_SESSION['userauthorized'];
|
||
|
if ($get_authorized == 1) {
|
||
|
$providerid = $_SESSION['authUserID'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!$providerid) {
|
||
|
$find_provider = sqlQuery("SELECT providerID FROM patient_data " .
|
||
|
"WHERE pid = ?", array($this->pid));
|
||
|
$providerid = $find_provider['providerID'];
|
||
|
}
|
||
|
|
||
|
return intval($providerid);
|
||
|
}
|
||
|
|
||
|
// Close the designated visit, making sure it has no charges.
|
||
|
//
|
||
|
public static function closeVisit($pid, $encounter)
|
||
|
{
|
||
|
$tmp1 = sqlQuery(
|
||
|
"SELECT SUM(ABS(fee)) AS sum FROM drug_sales WHERE " .
|
||
|
"pid = ? AND encounter = ? AND billed = 0",
|
||
|
array($pid, $encounter)
|
||
|
);
|
||
|
$tmp2 = sqlQuery(
|
||
|
"SELECT SUM(ABS(fee)) AS sum FROM billing WHERE " .
|
||
|
"pid = ? AND encounter = ? AND billed = 0 AND activity = 1 AND code_type != 'TAX'",
|
||
|
array($pid, $encounter)
|
||
|
);
|
||
|
if ($tmp1['sum'] + $tmp2['sum'] == 0) {
|
||
|
$this_bill_date = date('Y-m-d H:i:s');
|
||
|
sqlStatement(
|
||
|
"update drug_sales SET billed = 1, bill_date = ? WHERE " .
|
||
|
"pid = ? AND encounter = ? AND billed = 0",
|
||
|
array($this_bill_date, $pid, $encounter)
|
||
|
);
|
||
|
// Unbilled tax would have been recomputed at checkout and is invalid here.
|
||
|
sqlStatement(
|
||
|
"UPDATE billing SET activity = 0 WHERE " .
|
||
|
"pid = ? AND encounter = ? AND code_type = 'TAX' AND activity = 1 AND billed = 0",
|
||
|
array($pid, $encounter)
|
||
|
);
|
||
|
sqlStatement(
|
||
|
"UPDATE billing SET billed = 1, bill_date = ? WHERE " .
|
||
|
"pid = ? AND encounter = ? AND billed = 0 AND activity = 1",
|
||
|
array($this_bill_date, $pid, $encounter)
|
||
|
);
|
||
|
// Generate and set invoice reference number if appropriate.
|
||
|
$tmprow = sqlQuery(
|
||
|
"SELECT invoice_refno FROM form_encounter WHERE pid = ? AND encounter = ?",
|
||
|
array($pid, $encounter)
|
||
|
);
|
||
|
if (empty($tmprow['invoice_refno'])) { // not empty means there was a previous checkout
|
||
|
$refno = updateInvoiceRefNumber();
|
||
|
if ($refno) { // means user has an invoice refno pool
|
||
|
sqlStatement(
|
||
|
"UPDATE form_encounter SET invoice_refno = ? WHERE pid = ? AND encounter = ?",
|
||
|
array($refno, $pid, $encounter)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
return xl('Cannot close because there are charges. Check out instead.');
|
||
|
}
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
public static function getBasicUnits($drug_id, $selector)
|
||
|
{
|
||
|
$basic_units = 1;
|
||
|
$tmp = sqlQuery(
|
||
|
"SELECT quantity FROM drug_templates WHERE drug_id = ? AND selector = ?",
|
||
|
array($drug_id, $selector)
|
||
|
);
|
||
|
if (!empty($tmp['quantity'])) {
|
||
|
$basic_units = 0 + $tmp['quantity'];
|
||
|
if (!$basic_units) {
|
||
|
$basic_units = 1;
|
||
|
}
|
||
|
}
|
||
|
return $basic_units;
|
||
|
}
|
||
|
|
||
|
// Log a message that is easy for the Re-Opened Visits Report to interpret.
|
||
|
// The optional $logarr contains these line item attributes:
|
||
|
// Code Type
|
||
|
// Code
|
||
|
// Selector (for code type "PROD")
|
||
|
// Price Level
|
||
|
// Fee
|
||
|
// Units
|
||
|
// Provider
|
||
|
// Warehouse
|
||
|
//
|
||
|
public function logFSMessage($action, $newvalue = '', $logarr = null)
|
||
|
{
|
||
|
$user_notes = $this->encounter;
|
||
|
if (is_array($logarr)) {
|
||
|
array_unshift($logarr, $newvalue);
|
||
|
foreach ($logarr as $tmp) {
|
||
|
$user_notes .= '|' . str_replace('|', '', $tmp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EventAuditLogger::instance()->newEvent(
|
||
|
'fee-sheet',
|
||
|
$_SESSION['authUser'],
|
||
|
$_SESSION['authProvider'],
|
||
|
1,
|
||
|
$action,
|
||
|
$this->pid,
|
||
|
$user_notes
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Compute a current checksum of this encounter's Fee Sheet data from the database.
|
||
|
//
|
||
|
public function visitChecksum($saved = false)
|
||
|
{
|
||
|
$rowb = sqlQuery(
|
||
|
"SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
|
||
|
"id, code, modifier, units, fee, authorized, provider_id, ndc_info, justify, billed" .
|
||
|
"))) AS checksum FROM billing WHERE " .
|
||
|
"pid = ? AND encounter = ? AND activity = 1",
|
||
|
array($this->pid, $this->encounter)
|
||
|
);
|
||
|
$rowp = sqlQuery(
|
||
|
"SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
|
||
|
"sale_id, inventory_id, prescription_id, quantity, fee, sale_date, billed" .
|
||
|
"))) AS checksum FROM drug_sales WHERE " .
|
||
|
"pid = ? AND encounter = ?",
|
||
|
array($this->pid, $this->encounter)
|
||
|
);
|
||
|
$rowv = sqlQuery(
|
||
|
"SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
|
||
|
"id, date, provider_id, supervisor_id, last_level_billed, last_level_closed" .
|
||
|
"))) AS checksum FROM form_encounter WHERE " .
|
||
|
"pid = ? AND encounter = ?",
|
||
|
array($this->pid, $this->encounter)
|
||
|
);
|
||
|
$ret = intval($rowb['checksum']) ^ intval($rowp['checksum']) ^ intval($rowv['checksum']);
|
||
|
if (CHECKSUM_LOGGING) {
|
||
|
$comment = "Checksum = '$ret'";
|
||
|
$comment .= ", Saved = " . ($saved ? "true" : "false");
|
||
|
EventAuditLogger::instance()->newEvent("checksum", $_SESSION['authUser'], $_SESSION['authProvider'], 1, $comment, $this->pid);
|
||
|
}
|
||
|
return $ret;
|
||
|
}
|
||
|
|
||
|
// IPPF-specific; get contraception attributes of the related codes.
|
||
|
//
|
||
|
public function checkRelatedForContraception($related_code, $is_initial_consult = false)
|
||
|
{
|
||
|
$this->line_contra_code = '';
|
||
|
$this->line_contra_cyp = 0;
|
||
|
$this->line_contra_methtype = 0; // 0 = None, 1 = Not initial, 2 = Initial consult
|
||
|
if (!empty($related_code)) {
|
||
|
$relcodes = explode(';', $related_code);
|
||
|
foreach ($relcodes as $relstring) {
|
||
|
if ($relstring === '') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
list($reltype, $relcode) = explode(':', $relstring);
|
||
|
if ($reltype !== 'IPPFCM') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$methtype = $is_initial_consult ? 2 : 1;
|
||
|
$tmprow = sqlQuery(
|
||
|
"SELECT cyp_factor FROM codes WHERE " .
|
||
|
"code_type = '32' AND code = ? ORDER BY active DESC, id LIMIT 1",
|
||
|
array($relcode)
|
||
|
);
|
||
|
$cyp = 0 + $tmprow['cyp_factor'];
|
||
|
if ($cyp > $this->line_contra_cyp) {
|
||
|
$this->line_contra_cyp = $cyp;
|
||
|
// Note this is an IPPFCM code, not an IPPF2 code.
|
||
|
$this->line_contra_code = $relcode;
|
||
|
$this->line_contra_methtype = $methtype;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************
|
||
|
// Insert a row into the lbf_data table. Returns a new form ID if that is not provided.
|
||
|
// This is only needed for auto-creating Contraception forms.
|
||
|
//
|
||
|
public function insert_lbf_item($form_id, $field_id, $field_value)
|
||
|
{
|
||
|
if ($form_id) {
|
||
|
sqlStatement("INSERT INTO lbf_data (form_id, field_id, field_value) " .
|
||
|
"VALUES (?, ?, ?)", array($form_id, $field_id, $field_value));
|
||
|
} else {
|
||
|
$form_id = sqlInsert("INSERT INTO lbf_data (field_id, field_value) " .
|
||
|
"VALUES (?, ?)", array($field_id, $field_value));
|
||
|
}
|
||
|
|
||
|
return $form_id;
|
||
|
}
|
||
|
******************************************************************/
|
||
|
|
||
|
// Insert a row into the shared_attributes table.
|
||
|
// This is only needed for auto-creating Contraception forms.
|
||
|
//
|
||
|
public function insert_lbf_item($field_id, $field_value)
|
||
|
{
|
||
|
sqlInsert(
|
||
|
"INSERT INTO shared_attributes (pid, encounter, last_update, user_id, field_id, field_value) " .
|
||
|
"VALUES (?, ?, 'NOW()', ?, ?, ?)",
|
||
|
array($this->pid, $this->encounter, $_SESSION['authId'], $field_id, $field_value)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Create an array of data for a particular billing table item that is useful
|
||
|
// for building a user interface form row. $args is an array containing:
|
||
|
// codetype
|
||
|
// code
|
||
|
// modifier
|
||
|
// ndc_info
|
||
|
// auth
|
||
|
// del
|
||
|
// units
|
||
|
// fee
|
||
|
// id
|
||
|
// billed
|
||
|
// code_text
|
||
|
// justify
|
||
|
// provider_id
|
||
|
// notecodes
|
||
|
// pricelevel
|
||
|
public function addServiceLineItem($args)
|
||
|
{
|
||
|
global $code_types;
|
||
|
|
||
|
// echo "<!-- \n"; // debugging
|
||
|
// print_r($args); // debugging
|
||
|
// echo "--> \n"; // debugging
|
||
|
|
||
|
$li = array();
|
||
|
$li['hidden'] = array();
|
||
|
|
||
|
$codetype = $args['codetype'];
|
||
|
$code = $args['code'];
|
||
|
$revenue_code = isset($args['revenue_code']) ? $args['revenue_code'] : '';
|
||
|
$modifier = isset($args['modifier']) ? $args['modifier'] : '';
|
||
|
$code_text = isset($args['code_text']) ? $args['code_text'] : '';
|
||
|
$units = intval(isset($args['units']) ? $args['units'] : 0);
|
||
|
$billed = !empty($args['billed']);
|
||
|
$auth = !empty($args['auth']);
|
||
|
$id = isset($args['id']) ? intval($args['id']) : 0;
|
||
|
$ndc_info = isset($args['ndc_info']) ? $args['ndc_info'] : '';
|
||
|
$provider_id = isset($args['provider_id']) ? intval($args['provider_id']) : 0;
|
||
|
$justify = isset($args['justify']) ? $args['justify'] : '';
|
||
|
$notecodes = isset($args['notecodes']) ? $args['notecodes'] : '';
|
||
|
$fee = isset($args['fee']) ? (0 + $args['fee']) : 0;
|
||
|
// Price level should be unset only if adding a new line item.
|
||
|
$pricelevel = isset($args['pricelevel']) ? $args['pricelevel'] : $this->patient_pricelevel;
|
||
|
$del = !empty($args['del']);
|
||
|
|
||
|
// If using line item billing and user wishes to default to a selected provider, then do so.
|
||
|
if (!empty($GLOBALS['default_fee_sheet_line_item_provider']) && !empty($GLOBALS['support_fee_sheet_line_item_provider'])) {
|
||
|
if ($provider_id == 0) {
|
||
|
$provider_id = (int) $this->findProvider();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($codetype == 'COPAY') {
|
||
|
if (!$code_text) {
|
||
|
$code_text = 'Cash';
|
||
|
}
|
||
|
|
||
|
if ($fee > 0) {
|
||
|
$fee = 0 - $fee;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get the matching entry from the codes table.
|
||
|
$sqlArray = array();
|
||
|
$query = "SELECT id, units, code_text, revenue_code FROM codes WHERE " .
|
||
|
"code_type = ? AND code = ?";
|
||
|
array_push($sqlArray, ($code_types[$codetype]['id'] ?? ''), $code);
|
||
|
if ($modifier) {
|
||
|
$query .= " AND modifier = ?";
|
||
|
array_push($sqlArray, $modifier);
|
||
|
} else {
|
||
|
$query .= " AND (modifier IS NULL OR modifier = '')";
|
||
|
}
|
||
|
$query .= " ORDER BY active DESC, id LIMIT 1";
|
||
|
$result = sqlQuery($query, $sqlArray);
|
||
|
$codes_id = $result['id'] ?? null;
|
||
|
$revenue_code = $revenue_code ? $revenue_code : ($result['revenue_code'] ?? null);
|
||
|
if (!$code_text) {
|
||
|
$code_text = $result['code_text'] ?? null;
|
||
|
if (empty($units) && !empty($result)) {
|
||
|
$units = intval($result['units']);
|
||
|
}
|
||
|
|
||
|
if (!isset($args['fee'])) {
|
||
|
// Fees come from the prices table now.
|
||
|
$query = "SELECT pr_price, lo.option_id AS pr_level, lo.notes FROM list_options lo " .
|
||
|
" LEFT OUTER JOIN prices p ON lo.option_id=p.pr_level AND pr_id = ? AND pr_selector = '' " .
|
||
|
" WHERE lo.list_id='pricelevel' " .
|
||
|
"ORDER BY seq";
|
||
|
// echo "\n<!-- $query -->\n"; // debugging
|
||
|
|
||
|
$prdefault = null;
|
||
|
$prrow = null;
|
||
|
$prrecordset = sqlStatement($query, array($codes_id));
|
||
|
while ($row = sqlFetchArray($prrecordset)) {
|
||
|
if (empty($prdefault)) {
|
||
|
$prdefault = $row;
|
||
|
}
|
||
|
|
||
|
if ($row['pr_level'] === $pricelevel) {
|
||
|
$prrow = $row;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$fee = 0;
|
||
|
if (!empty($prrow)) {
|
||
|
$fee = $prrow['pr_price'];
|
||
|
|
||
|
// if percent-based pricing is enabled...
|
||
|
if ($GLOBALS['enable_percent_pricing']) {
|
||
|
// if this price level is a percentage, calculate price from default price
|
||
|
if (!empty($prrow['notes']) && strpos($prrow['notes'], '%') > -1 && !empty($prdefault)) {
|
||
|
$percent = intval(str_replace('%', '', $prrow['notes']));
|
||
|
if ($percent > 0) {
|
||
|
$fee = $prdefault['pr_price'] * ((100 - $percent) / 100);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!$units) {
|
||
|
$units = 1;
|
||
|
}
|
||
|
$fee = sprintf('%01.2f', $fee);
|
||
|
|
||
|
$li['hidden']['code_type'] = $codetype;
|
||
|
$li['hidden']['code'] = $code;
|
||
|
$li['hidden']['revenue_code'] = $revenue_code;
|
||
|
$li['hidden']['mod'] = $modifier;
|
||
|
$li['hidden']['billed'] = $billed;
|
||
|
$li['hidden']['id'] = $id;
|
||
|
$li['hidden']['codes_id'] = $codes_id;
|
||
|
|
||
|
// This logic is only used for family planning clinics, and then only when
|
||
|
// the option is chosen to use or auto-generate Contraception forms.
|
||
|
// It adds contraceptive method and effectiveness to relevant lines.
|
||
|
if ($GLOBALS['ippf_specific'] && $GLOBALS['gbl_new_acceptor_policy'] && $codetype == 'MA') {
|
||
|
$codesrow = sqlQuery(
|
||
|
"SELECT related_code, cyp_factor FROM codes WHERE " .
|
||
|
"code_type = ? AND code = ? ORDER BY active DESC, id LIMIT 1",
|
||
|
array($code_types[$codetype]['id'], $code)
|
||
|
);
|
||
|
$this->checkRelatedForContraception($codesrow['related_code'], $codesrow['cyp_factor']);
|
||
|
if ($this->line_contra_code) {
|
||
|
$li['hidden']['method' ] = $this->line_contra_code;
|
||
|
$li['hidden']['cyp' ] = $this->line_contra_cyp;
|
||
|
$li['hidden']['methtype'] = $this->line_contra_methtype;
|
||
|
// contraception_code is only concerned with initial consults.
|
||
|
if ($this->line_contra_cyp > $this->contraception_cyp && $this->line_contra_methtype == 2) {
|
||
|
$this->contraception_cyp = $this->line_contra_cyp;
|
||
|
$this->contraception_code = $this->line_contra_code;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($codetype == 'COPAY') {
|
||
|
$li['codetype'] = xl($codetype);
|
||
|
if ($ndc_info) {
|
||
|
$li['codetype'] .= " ($ndc_info)";
|
||
|
}
|
||
|
$ndc_info = '';
|
||
|
} else {
|
||
|
$li['codetype'] = $codetype;
|
||
|
}
|
||
|
|
||
|
$li['code' ] = $codetype == 'COPAY' ? '' : $code;
|
||
|
$li['revenue_code' ] = $revenue_code;
|
||
|
$li['mod' ] = $modifier;
|
||
|
$li['fee' ] = $fee;
|
||
|
$li['price' ] = $fee / $units;
|
||
|
$li['pricelevel'] = $pricelevel;
|
||
|
$li['units' ] = $units;
|
||
|
$li['provid' ] = $provider_id;
|
||
|
$li['justify' ] = $justify;
|
||
|
$li['notecodes'] = $notecodes;
|
||
|
$li['del' ] = $id && $del;
|
||
|
$li['code_text'] = $code_text;
|
||
|
$li['auth' ] = $auth;
|
||
|
|
||
|
$li['hidden']['price'] = $li['price'];
|
||
|
|
||
|
// If NDC info exists or may be required, add stuff for it.
|
||
|
if ($codetype == 'HCPCS' && !$billed) {
|
||
|
if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndc_info, $tmp)) {
|
||
|
$ndcnum = $tmp[1];
|
||
|
$ndcuom = $tmp[2];
|
||
|
$ndcqty = $tmp[3];
|
||
|
} else {
|
||
|
$ndcnum = $ndc_info;
|
||
|
$ndcuom = "UN";
|
||
|
$ndcqty = "1";
|
||
|
}
|
||
|
|
||
|
$li['ndcnum' ] = $ndcnum;
|
||
|
$li['ndcuom' ] = $ndcuom;
|
||
|
$li['ndcqty' ] = $ndcqty;
|
||
|
} elseif ($ndc_info) {
|
||
|
$li['ndc_info' ] = $ndc_info;
|
||
|
}
|
||
|
|
||
|
// For Family Planning.
|
||
|
if ($codetype == 'MA') {
|
||
|
++$this->required_code_count;
|
||
|
}
|
||
|
|
||
|
if ($fee != 0) {
|
||
|
$this->hasCharges = true;
|
||
|
}
|
||
|
|
||
|
$this->serviceitems[] = $li;
|
||
|
}
|
||
|
|
||
|
// Create an array of data for a particular drug_sales table item that is useful
|
||
|
// for building a user interface form row. $args is an array containing:
|
||
|
// drug_id
|
||
|
// selector
|
||
|
// sale_id
|
||
|
// rx (boolean)
|
||
|
// del (boolean)
|
||
|
// units
|
||
|
// fee
|
||
|
// billed
|
||
|
// warehouse_id
|
||
|
// pricelevel
|
||
|
//
|
||
|
// Pass true for the 2nd argument if the data is coming from the database;
|
||
|
// if from user input, make it false.
|
||
|
//
|
||
|
public function addProductLineItem($args, $convert_units = true)
|
||
|
{
|
||
|
global $code_types;
|
||
|
|
||
|
$li = array();
|
||
|
$li['hidden'] = array();
|
||
|
|
||
|
$drug_id = $args['drug_id'];
|
||
|
$selector = isset($args['selector']) ? $args['selector'] : '';
|
||
|
$sale_id = isset($args['sale_id']) ? intval($args['sale_id']) : 0;
|
||
|
$units = intval(isset($args['units']) ? $args['units'] : 0);
|
||
|
if (!$units) {
|
||
|
$units = 1;
|
||
|
}
|
||
|
$billed = !empty($args['billed']);
|
||
|
$rx = !empty($args['rx']);
|
||
|
$del = !empty($args['del']);
|
||
|
$fee = isset($args['fee']) ? (0 + $args['fee']) : 0;
|
||
|
$pricelevel = isset($args['pricelevel']) ? $args['pricelevel'] : $this->patient_pricelevel;
|
||
|
$warehouse_id = isset($args['warehouse_id']) ? $args['warehouse_id'] : '';
|
||
|
|
||
|
if ($convert_units) {
|
||
|
// "Basic Units" is the quantity from the product template and is the number of
|
||
|
// inventory items in the package that the template represents.
|
||
|
// Units seen by the user should be inventory units divided by template quantity.
|
||
|
$units = $units / $this->getBasicUnits($drug_id, $selector);
|
||
|
}
|
||
|
|
||
|
$drow = sqlQuery("SELECT name, related_code FROM drugs WHERE drug_id = ?", array($drug_id));
|
||
|
$code_text = $drow['name'];
|
||
|
|
||
|
// If no warehouse ID passed, use the logged-in user's default.
|
||
|
if ($this->got_warehouses && $warehouse_id === '') {
|
||
|
$warehouse_id = $this->default_warehouse;
|
||
|
}
|
||
|
|
||
|
// If fee is not provided, get it from the prices table.
|
||
|
// It is assumed in this case that units will match what is in the product template.
|
||
|
if (!isset($args['fee'])) {
|
||
|
$query = "SELECT pr_price, lo.option_id AS pr_level, lo.notes FROM list_options lo " .
|
||
|
" LEFT OUTER JOIN prices p ON lo.option_id=p.pr_level AND pr_id = ? AND pr_selector = ? " .
|
||
|
" WHERE lo.list_id='pricelevel' " .
|
||
|
"ORDER BY seq";
|
||
|
|
||
|
$prdefault = null;
|
||
|
$prrow = null;
|
||
|
$prrecordset = sqlStatement($query, array($drug_id, $selector));
|
||
|
while ($row = sqlFetchArray($prrecordset)) {
|
||
|
if (empty($prdefault)) {
|
||
|
$prdefault = $row;
|
||
|
}
|
||
|
|
||
|
if ($row['pr_level'] === $pricelevel) {
|
||
|
$prrow = $row;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$fee = 0;
|
||
|
if (!empty($prrow)) {
|
||
|
$fee = $prrow['pr_price'];
|
||
|
|
||
|
// if percent-based pricing is enabled...
|
||
|
if ($GLOBALS['enable_percent_pricing']) {
|
||
|
// if this price level is a percentage, calculate price from default price
|
||
|
if (!empty($prrow['notes']) && strpos($prrow['notes'], '%') > -1 && !empty($prdefault)) {
|
||
|
$percent = intval(str_replace('%', '', $prrow['notes']));
|
||
|
if ($percent > 0) {
|
||
|
$fee = $prdefault['pr_price'] * ((100 - $percent) / 100);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$fee = sprintf('%01.2f', $fee);
|
||
|
|
||
|
$li['fee' ] = $fee;
|
||
|
$li['price' ] = $fee / $units;
|
||
|
$li['pricelevel'] = $pricelevel;
|
||
|
$li['units' ] = $units;
|
||
|
$li['del' ] = $sale_id && $del;
|
||
|
$li['code_text'] = $code_text;
|
||
|
$li['warehouse'] = $warehouse_id;
|
||
|
$li['rx' ] = $rx;
|
||
|
|
||
|
$li['hidden']['drug_id'] = $drug_id;
|
||
|
$li['hidden']['selector'] = $selector;
|
||
|
$li['hidden']['sale_id'] = $sale_id;
|
||
|
$li['hidden']['billed' ] = $billed;
|
||
|
$li['hidden']['price' ] = $li['price'];
|
||
|
|
||
|
// This logic is only used for family planning clinics, and then only when
|
||
|
// the option is chosen to use or auto-generate Contraception forms.
|
||
|
// It adds contraceptive method and effectiveness to relevant lines.
|
||
|
if ($GLOBALS['ippf_specific'] && $GLOBALS['gbl_new_acceptor_policy']) {
|
||
|
$this->checkRelatedForContraception($drow['related_code']);
|
||
|
if ($this->line_contra_code) {
|
||
|
$li['hidden']['method' ] = $this->line_contra_code;
|
||
|
$li['hidden']['methtype'] = $this->line_contra_methtype;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For Family Planning.
|
||
|
++$this->required_code_count;
|
||
|
if ($fee != 0) {
|
||
|
$this->hasCharges = true;
|
||
|
}
|
||
|
|
||
|
$this->productitems[] = $li;
|
||
|
}
|
||
|
|
||
|
// Generate rows for items already in the billing table for this encounter.
|
||
|
//
|
||
|
public function loadServiceItems()
|
||
|
{
|
||
|
$billresult = BillingUtilities::getBillingByEncounter($this->pid, $this->encounter, "*");
|
||
|
if ($billresult) {
|
||
|
foreach ($billresult as $iter) {
|
||
|
if (!$this->ALLOW_COPAYS && $iter["code_type"] == 'COPAY') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$justify = trim($iter['justify']);
|
||
|
if ($justify) {
|
||
|
$justify = substr(str_replace(':', ',', $justify), 0, strlen($justify) - 1);
|
||
|
}
|
||
|
|
||
|
$this->addServiceLineItem(array(
|
||
|
'id' => $iter['id'],
|
||
|
'codetype' => $iter['code_type'],
|
||
|
'code' => trim($iter['code']),
|
||
|
'revenue_code' => trim($iter["revenue_code"]),
|
||
|
'modifier' => trim($iter["modifier"]),
|
||
|
'code_text' => trim($iter['code_text']),
|
||
|
'units' => $iter['units'],
|
||
|
'fee' => $iter['fee'],
|
||
|
'pricelevel' => $iter['pricelevel'],
|
||
|
'billed' => $iter['billed'],
|
||
|
'ndc_info' => $iter['ndc_info'],
|
||
|
'provider_id' => $iter['provider_id'],
|
||
|
'justify' => $justify,
|
||
|
'notecodes' => trim($iter['notecodes']),
|
||
|
));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// echo "<!-- \n"; // debugging
|
||
|
// print_r($this->serviceitems); // debugging
|
||
|
// echo "--> \n"; // debugging
|
||
|
}
|
||
|
|
||
|
// Generate rows for items already in the drug_sales table for this encounter.
|
||
|
//
|
||
|
public function loadProductItems()
|
||
|
{
|
||
|
$query = "SELECT ds.*, di.warehouse_id FROM drug_sales AS ds, drug_inventory AS di WHERE " .
|
||
|
"ds.pid = ? AND ds.encounter = ? AND di.inventory_id = ds.inventory_id " .
|
||
|
"ORDER BY ds.sale_id";
|
||
|
$sres = sqlStatement($query, array($this->pid, $this->encounter));
|
||
|
while ($srow = sqlFetchArray($sres)) {
|
||
|
$this->addProductLineItem(
|
||
|
array(
|
||
|
'drug_id' => $srow['drug_id'],
|
||
|
'selector' => $srow['selector'],
|
||
|
'sale_id' => $srow['sale_id'],
|
||
|
'rx' => !empty($srow['prescription_id']),
|
||
|
'units' => $srow['quantity'],
|
||
|
'fee' => $srow['fee'],
|
||
|
'pricelevel' => $srow['pricelevel'],
|
||
|
'billed' => $srow['billed'],
|
||
|
'warehouse_id' => $srow['warehouse_id'],
|
||
|
),
|
||
|
true
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for insufficient product inventory levels.
|
||
|
// Returns an error message if any product items cannot be filled.
|
||
|
// You must call this before save().
|
||
|
//
|
||
|
public function checkInventory(&$prod)
|
||
|
{
|
||
|
$alertmsg = '';
|
||
|
$insufficient = 0;
|
||
|
$expiredlots = false;
|
||
|
if (is_array($prod)) {
|
||
|
foreach ($prod as $iter) {
|
||
|
if (!empty($iter['billed'])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$drug_id = $iter['drug_id'];
|
||
|
$sale_id = empty($iter['sale_id']) ? 0 : intval($iter['sale_id']); // present only if already saved
|
||
|
$units = empty($iter['units']) ? 1 : intval($iter['units']);
|
||
|
$selector = empty($iter['selector']) ? '' : $iter['selector'];
|
||
|
$inv_units = $units * $this->getBasicUnits($drug_id, $selector);
|
||
|
$warehouse_id = empty($iter['warehouse']) ? '' : $iter['warehouse'];
|
||
|
|
||
|
// Deleting always works.
|
||
|
if (!empty($iter['del'])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// If the item is already in the database...
|
||
|
if ($sale_id) {
|
||
|
$query = "SELECT ds.quantity, ds.inventory_id, di.on_hand, di.warehouse_id " .
|
||
|
"FROM drug_sales AS ds " .
|
||
|
"LEFT JOIN drug_inventory AS di ON di.inventory_id = ds.inventory_id " .
|
||
|
"WHERE ds.sale_id = ?";
|
||
|
$dirow = sqlQuery($query, array($sale_id));
|
||
|
// There's no inventory ID when this is a non-dispensible product (i.e. no inventory).
|
||
|
if (!empty($dirow['inventory_id'])) {
|
||
|
if ($warehouse_id && $warehouse_id != $dirow['warehouse_id']) {
|
||
|
// Changing warehouse so check inventory in the new warehouse.
|
||
|
// Nothing is updated by this call.
|
||
|
if (
|
||
|
!sellDrug(
|
||
|
$drug_id,
|
||
|
$inv_units,
|
||
|
0,
|
||
|
$this->pid,
|
||
|
$this->encounter,
|
||
|
0,
|
||
|
$this->visit_date,
|
||
|
'',
|
||
|
$warehouse_id,
|
||
|
true,
|
||
|
$expiredlots
|
||
|
)
|
||
|
) {
|
||
|
$insufficient = $drug_id;
|
||
|
}
|
||
|
} else {
|
||
|
if (($dirow['on_hand'] + $dirow['quantity'] - $inv_units) < 0) {
|
||
|
$insufficient = $drug_id;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else { // Otherwise it's a new item...
|
||
|
// This only checks for sufficient inventory, nothing is updated.
|
||
|
if (
|
||
|
!sellDrug(
|
||
|
$drug_id,
|
||
|
$inv_units,
|
||
|
0,
|
||
|
$this->pid,
|
||
|
$this->encounter,
|
||
|
0,
|
||
|
$this->visit_date,
|
||
|
'',
|
||
|
$warehouse_id,
|
||
|
true,
|
||
|
$expiredlots
|
||
|
)
|
||
|
) {
|
||
|
$insufficient = $drug_id;
|
||
|
}
|
||
|
}
|
||
|
} // end for
|
||
|
}
|
||
|
|
||
|
if ($insufficient) {
|
||
|
$drow = sqlQuery("SELECT name FROM drugs WHERE drug_id = ?", array($insufficient));
|
||
|
$alertmsg = xl('Insufficient inventory for product') . ' "' . $drow['name'] . '".';
|
||
|
if ($expiredlots) {
|
||
|
$alertmsg .= " " . xl('Check expiration dates.');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $alertmsg;
|
||
|
}
|
||
|
|
||
|
// Save posted data to the database. $bill and $prod are the incoming arrays of line items, with
|
||
|
// key names corresponding to those generated by addServiceLineItem() and addProductLineItem().
|
||
|
//
|
||
|
public function save(
|
||
|
&$bill,
|
||
|
&$prod,
|
||
|
$main_provid = null,
|
||
|
$main_supid = null,
|
||
|
$default_warehouse = null,
|
||
|
$mark_as_closed = false
|
||
|
) {
|
||
|
global $code_types;
|
||
|
|
||
|
if (isset($main_provid) && $main_supid == $main_provid) {
|
||
|
$main_supid = 0;
|
||
|
}
|
||
|
|
||
|
$copay_update = false;
|
||
|
$update_session_id = '';
|
||
|
$ct0 = ''; // takes the code type of the first fee type code type entry from the fee sheet, against which the copay is posted
|
||
|
$cod0 = ''; // takes the code of the first fee type code type entry from the fee sheet, against which the copay is posted
|
||
|
$mod0 = ''; // takes the modifier of the first fee type code type entry from the fee sheet, against which the copay is posted
|
||
|
|
||
|
if (is_array($bill)) {
|
||
|
foreach ($bill as $iter) {
|
||
|
// Skip disabled (billed) line items.
|
||
|
if (!empty($iter['billed'])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$id = $iter['id'] ?? null;
|
||
|
$code_type = $iter['code_type'];
|
||
|
$code = $iter['code'];
|
||
|
$del = !empty($iter['del']);
|
||
|
$units = empty($iter['units']) ? 1 : intval($iter['units']);
|
||
|
$pricelevel = empty($iter['pricelevel']) ? '' : $iter['pricelevel'];
|
||
|
$revenue_code = empty($iter['revenue_code']) ? '' : trim($iter['revenue_code']);
|
||
|
$modifier = empty($iter['mod']) ? '' : trim($iter['mod']);
|
||
|
$justify = empty($iter['justify' ]) ? '' : trim($iter['justify']);
|
||
|
$notecodes = empty($iter['notecodes']) ? '' : trim($iter['notecodes']);
|
||
|
$provid = empty($iter['provid' ]) ? 0 : intval($iter['provid']);
|
||
|
|
||
|
$price = 0;
|
||
|
if (!empty($iter['price'])) {
|
||
|
if ($code_type == 'COPAY' || $this->pricesAuthorized()) {
|
||
|
$price = 0 + trim($iter['price'] ?? 0);
|
||
|
} else {
|
||
|
$price = $this->getPrice($pricelevel, $code_type, $code);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$fee = sprintf('%01.2f', $price * $units);
|
||
|
|
||
|
if (!$cod0 && ($code_types[$code_type]['fee'] ?? null) == 1) {
|
||
|
$mod0 = $modifier;
|
||
|
$cod0 = $code;
|
||
|
$ct0 = $code_type;
|
||
|
}
|
||
|
|
||
|
if ($code_type == 'COPAY') {
|
||
|
if ($fee < 0) {
|
||
|
$fee = $fee * -1;
|
||
|
}
|
||
|
if ($id) {
|
||
|
// editing copay in ar_session
|
||
|
$session_id = $id;
|
||
|
$res_amount = sqlQuery(
|
||
|
"SELECT pay_amount FROM ar_activity WHERE pid = ? AND encounter = ? AND session_id = ?",
|
||
|
array($this->pid, $this->encounter, $session_id)
|
||
|
);
|
||
|
if ($fee != $res_amount['pay_amount']) {
|
||
|
sqlStatement(
|
||
|
"UPDATE ar_session SET user_id = ?, pay_total = ?, modified_time = now(), " .
|
||
|
"post_to_date = now() WHERE session_id = ?",
|
||
|
array($_SESSION['authUserID'], $fee, $session_id)
|
||
|
);
|
||
|
}
|
||
|
// deleting old copay
|
||
|
sqlStatement(
|
||
|
"UPDATE ar_activity SET deleted = NOW() WHERE " .
|
||
|
"deleted IS NULL AND pid = ? AND encounter = ? AND account_code = 'PCP' AND session_id = ?",
|
||
|
array($this->pid, $this->encounter, $id)
|
||
|
);
|
||
|
} else {
|
||
|
// adding new copay from fee sheet into ar_session
|
||
|
$session_id = sqlInsert(
|
||
|
"INSERT INTO ar_session " .
|
||
|
"(payer_id, user_id, pay_total, payment_type, description, patient_id, payment_method, " .
|
||
|
"adjustment_code, post_to_date) " .
|
||
|
"VALUES ('0',?,?,'patient','COPAY',?,'','patient_payment',now())",
|
||
|
array($_SESSION['authUserID'], $fee, $this->pid)
|
||
|
);
|
||
|
}
|
||
|
// adding new or changed copay from fee sheet into ar_activity
|
||
|
sqlBeginTrans();
|
||
|
$sequence_no = sqlQuery("SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE " .
|
||
|
"pid = ? AND encounter = ?", array($this->pid, $this->encounter));
|
||
|
SqlStatement(
|
||
|
"INSERT INTO ar_activity (pid, encounter, sequence_no, code_type, code, modifier, " .
|
||
|
"payer_type, post_time, post_user, session_id, pay_amount, account_code) " .
|
||
|
"VALUES (?,?,?,?,?,?,0,now(),?,?,?,'PCP')",
|
||
|
array($this->pid, $this->encounter, $sequence_no['increment'], $ct0, $cod0, $mod0,
|
||
|
$_SESSION['authUserID'], $session_id, $fee)
|
||
|
);
|
||
|
sqlCommitTrans();
|
||
|
|
||
|
if (!$cod0) {
|
||
|
$copay_update = true;
|
||
|
$update_session_id = $session_id;
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
# Code to create justification for all codes based on first justification
|
||
|
if ($GLOBALS['replicate_justification'] == '1') {
|
||
|
if ($justify != '') {
|
||
|
$autojustify = $justify;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (($GLOBALS['replicate_justification'] == '1') && ($justify == '') && check_is_code_type_justify($code_type)) {
|
||
|
$justify = $autojustify;
|
||
|
}
|
||
|
|
||
|
if ($justify) {
|
||
|
$justify = str_replace(',', ':', $justify) . ':';
|
||
|
}
|
||
|
|
||
|
$auth = "1";
|
||
|
|
||
|
$ndc_info = '';
|
||
|
if (!empty($iter['ndcnum'])) {
|
||
|
$ndc_info = 'N4' . trim($iter['ndcnum']) . ' ' . $iter['ndcuom'] .
|
||
|
trim($iter['ndcqty']);
|
||
|
}
|
||
|
|
||
|
// If the item is already in the database...
|
||
|
if ($id) {
|
||
|
// Get existing values from database.
|
||
|
$logarr = null;
|
||
|
$tmp = sqlQuery(
|
||
|
"SELECT * FROM billing WHERE id = ? AND billed = 0 AND activity = 1",
|
||
|
array($id)
|
||
|
);
|
||
|
if (!empty($tmp)) {
|
||
|
$logarr = array(
|
||
|
$tmp['code_type'],
|
||
|
$tmp['code'],
|
||
|
'',
|
||
|
$tmp['pricelevel'],
|
||
|
$tmp['fee'],
|
||
|
$tmp['units'],
|
||
|
$tmp['provider_id'],
|
||
|
''
|
||
|
);
|
||
|
}
|
||
|
//
|
||
|
if ($del) {
|
||
|
$this->logFSMessage(xl('Item deleted'), '', $logarr);
|
||
|
BillingUtilities::deleteBilling($id);
|
||
|
} else {
|
||
|
if (!empty($tmp)) {
|
||
|
$tmparr = array('code' => $code, 'authorized' => $auth);
|
||
|
if (isset($iter['units' ])) {
|
||
|
$tmparr['units' ] = $units;
|
||
|
}
|
||
|
|
||
|
if (isset($iter['price' ])) {
|
||
|
$tmparr['fee' ] = $fee;
|
||
|
}
|
||
|
|
||
|
if (isset($iter['pricelevel'])) {
|
||
|
$tmparr['pricelevel'] = $pricelevel;
|
||
|
}
|
||
|
|
||
|
if (isset($iter['mod' ])) {
|
||
|
$tmparr['modifier' ] = $modifier;
|
||
|
}
|
||
|
|
||
|
if (isset($iter['provid' ])) {
|
||
|
$tmparr['provider_id'] = $provid;
|
||
|
}
|
||
|
|
||
|
if (isset($iter['ndcnum' ])) {
|
||
|
$tmparr['ndc_info' ] = $ndc_info;
|
||
|
}
|
||
|
|
||
|
if (isset($iter['justify' ])) {
|
||
|
$tmparr['justify' ] = $justify;
|
||
|
}
|
||
|
|
||
|
if (isset($iter['notecodes'])) {
|
||
|
$tmparr['notecodes' ] = $notecodes;
|
||
|
}
|
||
|
|
||
|
if (isset($iter['revenue_code'])) {
|
||
|
$tmparr['revenue_code'] = $revenue_code;
|
||
|
}
|
||
|
|
||
|
foreach ($tmparr as $key => $value) {
|
||
|
if ($tmp[$key] != $value) {
|
||
|
if ('fee' == $key) {
|
||
|
$this->logFSMessage(xl('Price changed'), $value, $logarr);
|
||
|
}
|
||
|
|
||
|
if ('units' == $key) {
|
||
|
$this->logFSMessage(xl('Quantity changed'), $value, $logarr);
|
||
|
}
|
||
|
|
||
|
if ('provider_id' == $key) {
|
||
|
$this->logFSMessage(xl('Service provider changed'), $value, $logarr);
|
||
|
}
|
||
|
|
||
|
sqlStatement("UPDATE billing SET `$key` = ? WHERE id = ?", array($value, $id));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} elseif (!$del) { // Otherwise it's a new item...
|
||
|
$logarr = array($code_type, $code, '', $pricelevel, $fee, $units, $provid, '');
|
||
|
$this->logFSMessage(xl('Item added'), '', $logarr);
|
||
|
$code_text = lookup_code_descriptions($code_type . ":" . $code . ":" . $modifier);
|
||
|
BillingUtilities::addBilling(
|
||
|
$this->encounter,
|
||
|
$code_type,
|
||
|
$code,
|
||
|
$code_text,
|
||
|
$this->pid,
|
||
|
$auth,
|
||
|
$provid,
|
||
|
$modifier,
|
||
|
$units,
|
||
|
$fee,
|
||
|
$ndc_info,
|
||
|
$justify,
|
||
|
0,
|
||
|
$notecodes,
|
||
|
$pricelevel,
|
||
|
$revenue_code,
|
||
|
$this->payer_id
|
||
|
);
|
||
|
}
|
||
|
} // end for
|
||
|
}
|
||
|
|
||
|
// if modifier is not inserted during loop update the record using the first
|
||
|
// non-empty modifier and code
|
||
|
if ($copay_update == true && $update_session_id != '' && $mod0 != '') {
|
||
|
sqlStatement(
|
||
|
"UPDATE ar_activity SET code_type = ?, code = ?, modifier = ?" .
|
||
|
" WHERE deleted IS NULL AND pid = ? AND encounter = ? AND account_code = 'PCP' AND session_id = ?",
|
||
|
array($ct0, $cod0, $mod0, $this->pid, $this->encounter, $update_session_id)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Doing similarly to the above but for products.
|
||
|
if (is_array($prod)) {
|
||
|
foreach ($prod as $iter) {
|
||
|
// Skip disabled (billed) line items.
|
||
|
if (!empty($iter['billed'])) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$drug_id = $iter['drug_id'];
|
||
|
$selector = empty($iter['selector']) ? '' : $iter['selector'];
|
||
|
$sale_id = $iter['sale_id']; // present only if already saved
|
||
|
$units = intval(trim($iter['units']));
|
||
|
if (!$units) {
|
||
|
$units = 1;
|
||
|
}
|
||
|
$pricelevel = empty($iter['pricelevel']) ? '' : $iter['pricelevel'];
|
||
|
$del = !empty($iter['del']);
|
||
|
$rxid = 0;
|
||
|
$warehouse_id = empty($iter['warehouse']) ? '' : $iter['warehouse'];
|
||
|
$somechange = false;
|
||
|
|
||
|
$price = 0;
|
||
|
if (!empty($iter['price'])) {
|
||
|
if ($iter['price'] != 'X') {
|
||
|
// The price from the form is good.
|
||
|
$price = 0 + trim($iter['price']);
|
||
|
} else {
|
||
|
// Otherwise get its price for the given price level and product.
|
||
|
$price = $this->getPrice($pricelevel, 'PROD', $drug_id, $selector);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$fee = sprintf('%01.2f', $price * $units);
|
||
|
|
||
|
// $units is the user view, multipliers of Basic Units.
|
||
|
// Need to compute inventory units for the save logic below.
|
||
|
$inv_units = $units * $this->getBasicUnits($drug_id, $selector);
|
||
|
|
||
|
// If the item is already in the database...
|
||
|
if ($sale_id) {
|
||
|
// Get existing values from database.
|
||
|
$tmprow = sqlQuery(
|
||
|
"SELECT ds.prescription_id, ds.quantity, ds.inventory_id, ds.fee, " .
|
||
|
"ds.sale_date, ds.drug_id, ds.selector, ds.pricelevel, di.warehouse_id " .
|
||
|
"FROM drug_sales AS ds " .
|
||
|
"LEFT JOIN drug_inventory AS di ON di.inventory_id = ds.inventory_id " .
|
||
|
"WHERE ds.sale_id = ?",
|
||
|
array($sale_id)
|
||
|
);
|
||
|
$rxid = (int) $tmprow['prescription_id'];
|
||
|
$logarr = null;
|
||
|
if (!empty($tmprow)) {
|
||
|
$logarr = array(
|
||
|
'PROD',
|
||
|
$tmprow['drug_id'],
|
||
|
$tmprow['selector'],
|
||
|
$tmprow['pricelevel'],
|
||
|
$tmprow['fee'],
|
||
|
$tmprow['quantity'],
|
||
|
'',
|
||
|
$tmprow['warehouse_id']
|
||
|
);
|
||
|
}
|
||
|
//
|
||
|
if ($del) {
|
||
|
if (!empty($tmprow)) {
|
||
|
// Delete this sale and reverse its inventory update.
|
||
|
$this->logFSMessage(xl('Item deleted'), '', $logarr);
|
||
|
sqlStatement("DELETE FROM drug_sales WHERE sale_id = ?", array($sale_id));
|
||
|
if (!empty($tmprow['inventory_id'])) {
|
||
|
sqlStatement(
|
||
|
"UPDATE drug_inventory SET on_hand = on_hand + ? WHERE inventory_id = ?",
|
||
|
array($tmprow['quantity'], $tmprow['inventory_id'])
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($rxid) {
|
||
|
sqlStatement("DELETE FROM prescriptions WHERE id = ?", array($rxid));
|
||
|
}
|
||
|
} else {
|
||
|
// Modify the sale and adjust inventory accordingly.
|
||
|
if (!empty($tmprow)) {
|
||
|
foreach (
|
||
|
array(
|
||
|
'quantity' => $inv_units,
|
||
|
'fee' => $fee,
|
||
|
'pricelevel' => $pricelevel,
|
||
|
'selector' => $selector,
|
||
|
'sale_date' => $this->visit_date,
|
||
|
) as $key => $value
|
||
|
) {
|
||
|
if ($tmprow[$key] != $value) {
|
||
|
$somechange = true;
|
||
|
if ('fee' == $key) {
|
||
|
$this->logFSMessage(xl('Price changed'), $value, $logarr);
|
||
|
}
|
||
|
|
||
|
if ('pricelevel' == $key) {
|
||
|
$this->logFSMessage(xl('Price level changed'), $value, $logarr);
|
||
|
}
|
||
|
|
||
|
if ('selector' == $key) {
|
||
|
$this->logFSMessage(xl('Template selector changed'), $value, $logarr);
|
||
|
}
|
||
|
|
||
|
if ('quantity' == $key) {
|
||
|
$this->logFSMessage(xl('Quantity changed'), $value, $logarr);
|
||
|
}
|
||
|
|
||
|
sqlStatement(
|
||
|
"UPDATE drug_sales SET `$key` = ? WHERE sale_id = ?",
|
||
|
array($value, $sale_id)
|
||
|
);
|
||
|
if ($key == 'quantity' && $tmprow['inventory_id']) {
|
||
|
sqlStatement(
|
||
|
"UPDATE drug_inventory SET on_hand = on_hand - ? WHERE inventory_id = ?",
|
||
|
array($inv_units - $tmprow['quantity'], $tmprow['inventory_id'])
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($tmprow['inventory_id'] && $warehouse_id && $warehouse_id != $tmprow['warehouse_id']) {
|
||
|
// Changing warehouse. Requires deleting and re-adding the sale.
|
||
|
// Not setting $somechange because this alone does not affect a prescription.
|
||
|
$this->logFSMessage(xl('Warehouse changed'), $warehouse_id, $logarr);
|
||
|
sqlStatement("DELETE FROM drug_sales WHERE sale_id = ?", array($sale_id));
|
||
|
sqlStatement(
|
||
|
"UPDATE drug_inventory SET on_hand = on_hand + ? WHERE inventory_id = ?",
|
||
|
array($inv_units, $tmprow['inventory_id'])
|
||
|
);
|
||
|
$tmpnull = null;
|
||
|
$sale_id = sellDrug(
|
||
|
$drug_id,
|
||
|
$inv_units,
|
||
|
$fee,
|
||
|
$this->pid,
|
||
|
$this->encounter,
|
||
|
(empty($iter['rx']) ? 0 : $rxid),
|
||
|
$this->visit_date,
|
||
|
'',
|
||
|
$warehouse_id,
|
||
|
false,
|
||
|
$tmpnull,
|
||
|
$pricelevel,
|
||
|
$selector
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Delete Rx if $rxid and flag not set.
|
||
|
if ($GLOBALS['gbl_auto_create_rx'] && $rxid && empty($iter['rx'])) {
|
||
|
sqlStatement("UPDATE drug_sales SET prescription_id = 0 WHERE sale_id = ?", array($sale_id));
|
||
|
sqlStatement("DELETE FROM prescriptions WHERE id = ?", array($rxid));
|
||
|
}
|
||
|
}
|
||
|
} elseif (! $del) { // Otherwise it's a new item...
|
||
|
$somechange = true;
|
||
|
$logarr = array('PROD', $drug_id, $selector, $pricelevel, $fee, $units, '', $warehouse_id);
|
||
|
$this->logFSMessage(xl('Item added'), '', $logarr);
|
||
|
$tmpnull = null;
|
||
|
$sale_id = sellDrug(
|
||
|
$drug_id,
|
||
|
$inv_units,
|
||
|
$fee,
|
||
|
$this->pid,
|
||
|
$this->encounter,
|
||
|
0,
|
||
|
$this->visit_date,
|
||
|
'',
|
||
|
$warehouse_id,
|
||
|
false,
|
||
|
$tmpnull,
|
||
|
$pricelevel,
|
||
|
$selector
|
||
|
);
|
||
|
if (!$sale_id) {
|
||
|
die(xlt("Insufficient inventory for product ID") . " \"" . text($drug_id) . "\".");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If a prescription applies, create or update it.
|
||
|
if (!empty($iter['rx']) && !$del && ($somechange || empty($rxid))) {
|
||
|
// If an active rx already exists for this drug and date we will
|
||
|
// replace it, otherwise we'll make a new one.
|
||
|
if (empty($rxid)) {
|
||
|
$rxid = '';
|
||
|
}
|
||
|
|
||
|
// Get default drug attributes; prefer the template with the matching selector.
|
||
|
$drow = sqlQuery(
|
||
|
"SELECT dt.*, " .
|
||
|
"d.name, d.form, d.size, d.unit, d.route, d.substitute " .
|
||
|
"FROM drugs AS d, drug_templates AS dt WHERE " .
|
||
|
"d.drug_id = ? AND dt.drug_id = d.drug_id " .
|
||
|
"ORDER BY (dt.selector = ?) DESC, dt.quantity, dt.dosage, dt.selector LIMIT 1",
|
||
|
array($drug_id, $selector)
|
||
|
);
|
||
|
if (!empty($drow)) {
|
||
|
$rxobj = new Prescription($rxid);
|
||
|
$rxobj->set_patient_id($this->pid);
|
||
|
$rxobj->set_provider_id(isset($main_provid) ? $main_provid : $this->provider_id);
|
||
|
$rxobj->set_drug_id($drug_id);
|
||
|
$rxobj->set_quantity($inv_units);
|
||
|
$rxobj->set_per_refill($inv_units);
|
||
|
$rxobj->set_start_date_y(substr($this->visit_date, 0, 4));
|
||
|
$rxobj->set_start_date_m(substr($this->visit_date, 5, 2));
|
||
|
$rxobj->set_start_date_d(substr($this->visit_date, 8, 2));
|
||
|
$rxobj->set_date_added($this->visit_date);
|
||
|
// Remaining attributes are the drug and template defaults.
|
||
|
$rxobj->set_drug($drow['name']);
|
||
|
$rxobj->set_unit($drow['unit']);
|
||
|
$rxobj->set_dosage($drow['dosage']);
|
||
|
$rxobj->set_form($drow['form']);
|
||
|
$rxobj->set_refills($drow['refills']);
|
||
|
$rxobj->set_size($drow['size']);
|
||
|
$rxobj->set_route($drow['route']);
|
||
|
$rxobj->set_interval($drow['period']);
|
||
|
$rxobj->set_substitute($drow['substitute']);
|
||
|
//
|
||
|
$rxobj->persist();
|
||
|
// Set drug_sales.prescription_id to $rxobj->get_id().
|
||
|
$oldrxid = $rxid;
|
||
|
$rxid = (int) $rxobj->get_id();
|
||
|
if ($rxid != $oldrxid) {
|
||
|
sqlStatement(
|
||
|
"UPDATE drug_sales SET prescription_id = ? WHERE sale_id = ?",
|
||
|
array($rxid, $sale_id)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} // end for
|
||
|
}
|
||
|
|
||
|
// Set default and/or supervising provider for the encounter.
|
||
|
if (isset($main_provid) && $main_provid != $this->provider_id) {
|
||
|
$this->logFSMessage(xl('Default provider changed'));
|
||
|
sqlStatement(
|
||
|
"UPDATE form_encounter SET provider_id = ? WHERE pid = ? AND encounter = ?",
|
||
|
array($main_provid, $this->pid, $this->encounter)
|
||
|
);
|
||
|
$this->provider_id = $main_provid;
|
||
|
}
|
||
|
|
||
|
if (isset($main_supid) && $main_supid != $this->supervisor_id) {
|
||
|
sqlStatement(
|
||
|
"UPDATE form_encounter SET supervisor_id = ? WHERE pid = ? AND encounter = ?",
|
||
|
array($main_supid, $this->pid, $this->encounter)
|
||
|
);
|
||
|
$this->supervisor_id = $main_supid;
|
||
|
}
|
||
|
|
||
|
// Save-and-Close is currently specific to Family Planning but might be more
|
||
|
// generally useful. It provides the ability to mark an encounter as billed
|
||
|
// directly from the Fee Sheet, if there are no charges.
|
||
|
if ($mark_as_closed) {
|
||
|
$this->closeVisit($this->pid, $this->encounter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Call this after save() for Family Planning implementations.
|
||
|
// It checks the contraception data, or auto-creates it if $newmauser is set.
|
||
|
// Returns 0 unless user intervention is required to fix missing or incorrect data,
|
||
|
// and in that case the return value is a LBFcontra form_id or -1 if none.
|
||
|
// -1 could be returned if a non-LBFcontra form type recorded data that needs fixing.
|
||
|
//
|
||
|
public function doContraceptionForm($ippfconmeth = null, $newmauser = null, $main_provid = 0)
|
||
|
{
|
||
|
if (!empty($ippfconmeth)) {
|
||
|
/**********************************************************
|
||
|
$csrow = sqlQuery(
|
||
|
"SELECT f.form_id, ld.field_value FROM forms AS f " .
|
||
|
"LEFT JOIN lbf_data AS ld ON ld.form_id = f.form_id AND ld.field_id = 'newmethod' " .
|
||
|
"WHERE " .
|
||
|
"f.pid = ? AND f.encounter = ? AND " .
|
||
|
"f.formdir = 'LBFccicon' AND f.deleted = 0 " .
|
||
|
"ORDER BY f.form_id DESC LIMIT 1",
|
||
|
array($this->pid, $this->encounter)
|
||
|
);
|
||
|
**********************************************************/
|
||
|
$csrow = sqlQuery(
|
||
|
"SELECT f.form_id, d3.field_value " .
|
||
|
"FROM form_encounter AS fe " .
|
||
|
" JOIN shared_attributes AS d1 ON d1.pid = fe.pid AND d1.encounter = fe.encounter AND d1.field_id = 'cgen_newmauser' " .
|
||
|
"LEFT JOIN shared_attributes AS d3 ON d3.pid = fe.pid AND d3.encounter = fe.encounter AND d3.field_id = 'cgen_MethAdopt' " .
|
||
|
"LEFT JOIN forms AS f ON f.pid = fe.pid AND f.encounter = fe.encounter AND f.formdir = 'LBFcontra' AND f.deleted = 0 " .
|
||
|
"WHERE fe.pid = ? AND fe.encounter = ? " .
|
||
|
"ORDER BY fe.date DESC, fe.encounter DESC LIMIT 1",
|
||
|
array($this->pid, $this->encounter)
|
||
|
);
|
||
|
|
||
|
if (isset($newmauser)) {
|
||
|
// pastmodern is 0 iff new to modern contraception
|
||
|
$pastmodern = $newmauser == '2' ? 0 : 1;
|
||
|
if ($newmauser == '2') {
|
||
|
$newmauser = '1';
|
||
|
}
|
||
|
/******************************************************
|
||
|
// Add contraception form but only if it does not already exist
|
||
|
// (if it does, must be 2 users working on the visit concurrently).
|
||
|
if (empty($csrow)) {
|
||
|
$newid = $this->insert_lbf_item(0, 'newmauser', $newmauser);
|
||
|
$this->insert_lbf_item($newid, 'newmethod', "IPPFCM:$ippfconmeth");
|
||
|
$this->insert_lbf_item($newid, 'pastmodern', $pastmodern);
|
||
|
// Do we care about a service-specific provider here?
|
||
|
$this->insert_lbf_item($newid, 'provider', $main_provid);
|
||
|
addForm($this->encounter, 'Contraception', $newid, 'LBFccicon', $this->pid, $GLOBALS['userauthorized']);
|
||
|
}
|
||
|
******************************************************/
|
||
|
// Auto-add contraception visit data if it does not already exist.
|
||
|
if (empty($csrow)) {
|
||
|
// Creating a new form. Get the new form_id by inserting and deleting a dummy row.
|
||
|
// The form is required so there is a proper way to delete the data.
|
||
|
$newid = sqlInsert(
|
||
|
"INSERT INTO lbf_data " .
|
||
|
"( field_id, field_value ) VALUES ( '', '' )"
|
||
|
);
|
||
|
sqlStatement(
|
||
|
"DELETE FROM lbf_data WHERE form_id = ? AND field_id = ''",
|
||
|
array($newid)
|
||
|
);
|
||
|
addForm($this->encounter, 'Contraception Summary', $newid, 'LBFcontra', $this->pid, $GLOBALS['userauthorized']);
|
||
|
// Now add the needed visit fields.
|
||
|
$this->insert_lbf_item('cgen_newmauser', $newmauser);
|
||
|
$this->insert_lbf_item('cgen_MethAdopt', "IPPFCM:$ippfconmeth");
|
||
|
$this->insert_lbf_item('cgen_PastModern', $pastmodern);
|
||
|
$this->insert_lbf_item('cgen_provider', $main_provid);
|
||
|
}
|
||
|
} elseif (empty($csrow) || $csrow['field_value'] != "IPPFCM:$ippfconmeth") {
|
||
|
// Contraceptive method does not match what is in an existing Contraception
|
||
|
// form for this visit, or there is no such form. User intervention is needed.
|
||
|
return empty($csrow['form_id']) ? -1 : intval($csrow['form_id']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Get price level from patient demographics.
|
||
|
//
|
||
|
public function getPriceLevel()
|
||
|
{
|
||
|
return $this->patient_pricelevel;
|
||
|
}
|
||
|
|
||
|
// Update price level in patient demographics if it's changed.
|
||
|
//
|
||
|
public function updatePriceLevel($pricelevel)
|
||
|
{
|
||
|
if (!empty($pricelevel)) {
|
||
|
if ($this->patient_pricelevel != $pricelevel) {
|
||
|
$this->logFSMessage(xl('Price level changed'));
|
||
|
sqlStatement(
|
||
|
"UPDATE patient_data SET pricelevel = ? WHERE pid = ?",
|
||
|
array($pricelevel, $this->pid)
|
||
|
);
|
||
|
$this->patient_pricelevel = $pricelevel;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create JSON string representing code type, code and selector.
|
||
|
// This can be a checkbox value for parsing when the checkbox is clicked.
|
||
|
// As a side effect note if the code is already selected in the Fee Sheet.
|
||
|
//
|
||
|
public function genCodeSelectorValue($codes)
|
||
|
{
|
||
|
global $code_types;
|
||
|
list($codetype, $code, $selector) = explode(':', $codes);
|
||
|
if ($codetype == 'PROD') {
|
||
|
$crow = sqlQuery(
|
||
|
"SELECT sale_id " .
|
||
|
"FROM drug_sales WHERE pid = ? AND encounter = ? AND drug_id = ? " .
|
||
|
"LIMIT 1",
|
||
|
array($this->pid, $this->encounter, $code)
|
||
|
);
|
||
|
$this->code_is_in_fee_sheet = !empty($crow['sale_id']);
|
||
|
$cbarray = array($codetype, $code, $selector);
|
||
|
} else {
|
||
|
$crow = sqlQuery(
|
||
|
"SELECT c.id AS code_id, b.id " .
|
||
|
"FROM codes AS c " .
|
||
|
"LEFT JOIN billing AS b ON b.pid = ? AND b.encounter = ? AND b.code_type = ? AND b.code = c.code AND b.activity = 1 " .
|
||
|
"WHERE c.code_type = ? AND c.code = ? ORDER BY c.active DESC, c.id LIMIT 1",
|
||
|
array($this->pid, $this->encounter, $codetype, $code_types[$codetype]['id'], $code)
|
||
|
);
|
||
|
$this->code_is_in_fee_sheet = !empty($crow['id']);
|
||
|
$cbarray = array($codetype, $code);
|
||
|
}
|
||
|
|
||
|
$cbval = json_encode($cbarray);
|
||
|
return $cbval;
|
||
|
}
|
||
|
|
||
|
// Get price for a charge item and its price level.
|
||
|
//
|
||
|
public function getPrice($pr_level, $codetype, $code, $selector = '')
|
||
|
{
|
||
|
global $code_types;
|
||
|
if ($codetype == 'PROD') {
|
||
|
$pr_id = $code;
|
||
|
} else {
|
||
|
$crow = sqlQuery(
|
||
|
"SELECT id FROM codes WHERE code_type = ? AND code = ? " .
|
||
|
"ORDER BY active DESC, modifier, id LIMIT 1",
|
||
|
array($code_types[$codetype]['id'], $code)
|
||
|
);
|
||
|
$pr_id = $crow['id'];
|
||
|
$selector = '';
|
||
|
}
|
||
|
$prow = sqlQuery(
|
||
|
"SELECT pr_price FROM prices WHERE " .
|
||
|
"pr_id = ? AND pr_selector = ? AND pr_level = ?",
|
||
|
array($pr_id, $selector, $pr_level)
|
||
|
);
|
||
|
return empty($prow['pr_price']) ? 0 : $prow['pr_price'];
|
||
|
}
|
||
|
|
||
|
// Determine if the current user is allowed to see prices.
|
||
|
//
|
||
|
public function pricesAuthorized()
|
||
|
{
|
||
|
return AclMain::aclCheckCore('acct', 'disc') || acl_check('acct', 'bill');
|
||
|
}
|
||
|
}
|