php fixes for #1486 #1471 and #1469
[openemr.git] / library / invoice_summary.inc.php
blobac03f22fe247cbdb33081972fa8611d4cc1e9187
1 <?php
2 // Copyright (C) 2005-2010 Rod Roark <rod@sunsetsystems.com>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 // This returns an associative array keyed on procedure code, representing
10 // all charge items for one invoice. This array's values are themselves
11 // associative arrays having the following keys:
13 // chg - the sum of line items, including adjustments, for the code
14 // bal - the unpaid balance
15 // adj - the (positive) sum of inverted adjustments
16 // ins - the id of the insurance company that was billed (obsolete)
17 // dtl - associative array of details, if requested
19 // Where details are requested, each dtl array is keyed on a string
20 // beginning with a date in yyyy-mm-dd format, or blanks in the case
21 // of the original charge items. The value array is:
23 // pmt - payment amount as a positive number, only for payments
24 // src - check number or other source, only for payments
25 // chg - invoice line item amount amount, only for charges or
26 // adjustments (adjustments may be zero)
27 // rsn - adjustment reason, only for adjustments
28 // plv - provided for "integrated A/R" only: 0=pt, 1=Ins1, etc.
29 // dsc - for tax charges, a description of the tax
30 // arseq - ar_activity.sequence_no when it applies.
32 require_once("sl_eob.inc.php");
33 require_once(dirname(__FILE__) . "/../custom/code_types.inc.php");
36 // for Integrated A/R.
38 function ar_get_invoice_summary($patient_id, $encounter_id, $with_detail = false)
40 $codes = array();
41 $keysuff1 = 1000;
42 $keysuff2 = 5000;
44 // Get charges from services.
45 $res = sqlStatement("SELECT " .
46 "date, code_type, code, modifier, code_text, fee " .
47 "FROM billing WHERE " .
48 "pid = ? AND encounter = ? AND " .
49 "activity = 1 AND fee != 0.00 ORDER BY id", array($patient_id,$encounter_id));
51 while ($row = sqlFetchArray($res)) {
52 $amount = sprintf('%01.2f', $row['fee']);
54 $code = $row['code'];
55 if (! $code) {
56 $code = "Unknown";
59 if ($row['modifier']) {
60 $code .= ':' . $row['modifier'];
63 $codes[$code]['chg'] += $amount;
64 $codes[$code]['bal'] += $amount;
66 // Pass the code type, code and code_text fields
67 // Although not all used yet, useful information
68 // to improve the statement reporting etc.
69 $codes[$code]['code_type'] = $row['code_type'];
70 $codes[$code]['code_value'] = $row['code'];
71 $codes[$code]['modifier'] = $row['modifier'];
72 $codes[$code]['code_text'] = $row['code_text'];
74 // Add the details if they want 'em.
75 if ($with_detail) {
76 if (! $codes[$code]['dtl']) {
77 $codes[$code]['dtl'] = array();
80 $tmp = array();
81 $tmp['chg'] = $amount;
82 $tmpkey = " " . $keysuff1++;
83 $codes[$code]['dtl'][$tmpkey] = $tmp;
87 // Get charges from product sales.
88 $query = "SELECT s.drug_id, s.sale_date, s.fee, s.quantity " .
89 "FROM drug_sales AS s " .
90 "WHERE " .
91 "s.pid = ? AND s.encounter = ? AND s.fee != 0 " .
92 "ORDER BY s.sale_id";
93 $res = sqlStatement($query, array($patient_id,$encounter_id));
94 while ($row = sqlFetchArray($res)) {
95 $amount = sprintf('%01.2f', $row['fee']);
96 $code = 'PROD:' . $row['drug_id'];
97 $codes[$code]['chg'] += $amount;
98 $codes[$code]['bal'] += $amount;
99 // Add the details if they want 'em.
100 if ($with_detail) {
101 if (! $codes[$code]['dtl']) {
102 $codes[$code]['dtl'] = array();
105 $tmp = array();
106 $tmp['chg'] = $amount;
107 $tmpkey = " " . $keysuff1++;
108 $codes[$code]['dtl'][$tmpkey] = $tmp;
112 // Get payments and adjustments. (includes copays)
113 $res = sqlStatement("SELECT " .
114 "a.code_type, a.code, a.modifier, a.memo, a.payer_type, a.adj_amount, a.pay_amount, a.reason_code, " .
115 "a.post_time, a.session_id, a.sequence_no, a.account_code, " .
116 "s.payer_id, s.reference, s.check_date, s.deposit_date " .
117 ",i.name " .
118 "FROM ar_activity AS a " .
119 "LEFT OUTER JOIN ar_session AS s ON s.session_id = a.session_id " .
120 "LEFT OUTER JOIN insurance_companies AS i ON i.id = s.payer_id " .
121 "WHERE a.pid = ? AND a.encounter = ? " .
122 "ORDER BY s.check_date, a.sequence_no", array($patient_id,$encounter_id));
123 while ($row = sqlFetchArray($res)) {
124 $code = $row['code'];
125 if (! $code) {
126 $code = "Unknown";
129 if ($row['modifier']) {
130 $code .= ':' . $row['modifier'];
133 $ins_id = 0 + $row['payer_id'];
134 $codes[$code]['bal'] -= $row['pay_amount'];
135 $codes[$code]['bal'] -= $row['adj_amount'];
136 $codes[$code]['chg'] -= $row['adj_amount'];
137 $codes[$code]['adj'] += $row['adj_amount'];
138 if ($ins_id) {
139 $codes[$code]['ins'] = $ins_id;
142 // Add the details if they want 'em.
143 if ($with_detail) {
144 if (! $codes[$code]['dtl']) {
145 $codes[$code]['dtl'] = array();
148 $tmp = array();
149 $paydate = empty($row['deposit_date']) ? substr($row['post_time'], 0, 10) : $row['deposit_date'];
150 if ($row['pay_amount'] != 0) {
151 $tmp['pmt'] = $row['pay_amount'];
154 if (isset($row['reason_code'])) {
155 $tmp['msp'] = $row['reason_code'];
158 if ($row['adj_amount'] != 0 || $row['pay_amount'] == 0) {
159 $tmp['chg'] = 0 - $row['adj_amount'];
160 // $tmp['rsn'] = (empty($row['memo']) || empty($row['session_id'])) ? 'Unknown adjustment' : $row['memo'];
161 $tmp['rsn'] = empty($row['memo']) ? 'Unknown adjustment' : $row['memo'];
162 $tmpkey = $paydate . $keysuff1++;
163 } else {
164 $tmpkey = $paydate . $keysuff2++;
167 if ($row['account_code'] == "PCP") {
168 //copay
169 $tmp['src'] = 'Pt Paid';
170 } else {
171 $tmp['src'] = empty($row['session_id']) ? $row['memo'] : $row['reference'];
174 $tmp['insurance_company'] = substr($row['name'], 0, 10);
175 if ($ins_id) {
176 $tmp['ins'] = $ins_id;
179 $tmp['plv'] = $row['payer_type'];
180 $tmp['arseq'] = $row['sequence_no'];
181 $codes[$code]['dtl'][$tmpkey] = $tmp;
185 return $codes;
188 // This determines the party from whom payment is currently expected.
189 // Returns: -1=Nobody, 0=Patient, 1=Ins1, 2=Ins2, 3=Ins3.
190 // for Integrated A/R.
192 function ar_responsible_party($patient_id, $encounter_id)
194 $row = sqlQuery("SELECT date, last_level_billed, last_level_closed " .
195 "FROM form_encounter WHERE " .
196 "pid = ? AND encounter = ? " .
197 "ORDER BY id DESC LIMIT 1", array($patient_id,$encounter_id));
198 if (empty($row)) {
199 return -1;
202 $next_level = $row['last_level_closed'] + 1;
203 if ($next_level <= $row['last_level_billed']) {
204 return $next_level;
207 if (arGetPayerID($patient_id, substr($row['date'], 0, 10), $next_level)) {
208 return $next_level;
211 // There is no unclosed insurance, so see if there is an unpaid balance.
212 // Currently hoping that form_encounter.balance_due can be discarded.
213 $balance = 0;
214 $codes = ar_get_invoice_summary($patient_id, $encounter_id);
215 foreach ($codes as $cdata) {
216 $balance += $cdata['bal'];
219 if ($balance > 0) {
220 return 0;
223 return -1;