Add translation
[openemr.git] / library / FeeSheetHtml.class.php
blob4f5a49fe7d7280899f0b35a2e51fa41a8e766ff7
1 <?php
2 /**
3 * library/FeeSheetHtml.class.php
4 *
5 * Class for HTML-specific implementations of the Fee Sheet.
7 * LICENSE: This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see
17 * http://www.gnu.org/licenses/licenses.html#GPL .
19 * @package OpenEMR
20 * @license http://www.gnu.org/licenses/licenses.html#GPL GNU GPL V3+
21 * @author Rod Roark <rod@sunsetsystems.com>
22 * @link http://www.open-emr.org
25 require_once(dirname(__FILE__) . "/FeeSheet.class.php");
26 require_once(dirname(__FILE__) . "/api.inc");
27 require_once(dirname(__FILE__) . "/formdata.inc.php");
29 class FeeSheetHtml extends FeeSheet {
31 // Dynamically generated JavaScript to maintain justification codes.
32 public $justinit = "var f = document.forms[0];\n";
34 function __construct($pid=0, $encounter=0) {
35 parent::__construct($pid, $encounter);
38 // Build a drop-down list of providers. This includes users who
39 // have the word "provider" anywhere in their "additional info"
40 // field, so that we can define providers (for billing purposes)
41 // who do not appear in the calendar.
43 public static function genProviderOptionList($toptext, $default=0) {
44 $s = '';
45 // Get user's default facility, or 0 if none.
46 $drow = sqlQuery("SELECT facility_id FROM users where username = '" . $_SESSION['authUser'] . "'");
47 $def_facility = 0 + $drow['facility_id'];
49 $sqlarr = array($def_facility);
50 $query = "SELECT id, lname, fname, facility_id FROM users WHERE " .
51 "( authorized = 1 OR info LIKE '%provider%' ) AND username != '' " .
52 "AND active = 1 AND ( info IS NULL OR info NOT LIKE '%Inactive%' )";
53 // If restricting to providers matching user facility...
54 if ($GLOBALS['gbl_restrict_provider_facility']) {
55 $query .= " AND ( facility_id = 0 OR facility_id = ? )";
56 $query .= " ORDER BY lname, fname";
58 // If not restricting then sort the matching providers first.
59 else {
60 $query .= " ORDER BY (facility_id = ?) DESC, lname, fname";
62 $res = sqlStatement($query, $sqlarr);
63 $s .= "<option value=''>" . text($toptext) . "</option>";
64 while ($row = sqlFetchArray($res)) {
65 $provid = $row['id'];
66 $s .= "<option value='" . attr($provid) . "'";
67 if ($provid == $default) $s .= " selected";
68 $s .= ">";
69 if (!$GLOBALS['gbl_restrict_provider_facility'] && $def_facility && $row['facility_id'] == $def_facility) {
70 // Mark providers in the matching facility with an asterisk.
71 $s .= "* ";
73 $s .= text($row['lname'] . ", " . $row['fname']) . "</option>";
75 return $s;
78 // Does the above but including <select> ... </select>.
80 public static function genProviderSelect($tagname, $toptext, $default=0, $disabled=false) {
81 $s = " <select name='" . attr($tagname) . "'";
82 if ($disabled) $s .= " disabled";
83 $s .= ">";
84 $s .= self::genProviderOptionList($toptext, $default);
85 $s .= "</select>\n";
86 return $s;
89 // Build a drop-down list of warehouses.
91 public function genWarehouseSelect($tagname, $toptext, $default='', $disabled=false, $drug_id=0, $is_sold=0) {
92 $s = '';
93 if ($this->got_warehouses) {
94 // Normally would use generate_select_list() but it's not flexible enough here.
95 $s .= "<select name='" . attr($tagname) . "'";
96 if (!$disabled) $s .= " onchange='warehouse_changed(this);'";
97 if ($disabled ) $s .= " disabled";
98 $s .= ">";
99 $s .= "<option value=''>" . text($toptext) . "</option>";
100 $lres = sqlStatement("SELECT * FROM list_options " .
101 "WHERE list_id = 'warehouse' AND activity = 1 ORDER BY seq, title");
102 while ($lrow = sqlFetchArray($lres)) {
103 $s .= "<option value='" . attr($lrow['option_id']) . "'";
104 if ($disabled) {
105 if ($lrow['option_id'] == $default) $s .= " selected";
107 else {
108 $has_inventory = sellDrug($drug_id, 1, 0, 0, 0, 0, '', '', $lrow['option_id'], true);
109 if (((strlen($default) == 0 && $lrow['is_default']) ||
110 (strlen($default) > 0 && $lrow['option_id'] == $default)) &&
111 ($is_sold || $has_inventory))
113 $s .= " selected";
115 else {
116 // Disable this warehouse option if not selected and has no inventory.
117 if (!$has_inventory) $s .= " disabled";
120 $s .= ">" . text(xl_list_label($lrow['title'])) . "</option>\n";
122 $s .= "</select>";
124 return $s;
127 // Build a drop-down list of price levels.
128 // Includes the specified item's price in the "id" of each option.
130 public function genPriceLevelSelect($tagname, $toptext, $pr_id, $pr_selector='', $default='', $disabled=false) {
131 // echo "<!-- pr_id = '$pr_id', pr_selector = '$pr_selector' -->\n"; // debugging
132 $s = "<select name='" . attr($tagname) . "'";
133 if (!$disabled) $s .= " onchange='pricelevel_changed(this);'";
134 if ($disabled ) $s .= " disabled";
135 $s .= ">";
136 $s .= "<option value=''>" . text($toptext) . "</option>";
137 $lres = sqlStatement("SELECT lo.*, p.pr_price " .
138 "FROM list_options AS lo " .
139 "LEFT JOIN prices AS p ON p.pr_id = ? AND p.pr_selector = ? AND p.pr_level = lo.option_id " .
140 "WHERE lo.list_id = 'pricelevel' AND lo.activity = 1 ORDER BY lo.seq, lo.title",
141 array($pr_id, $pr_selector));
142 while ($lrow = sqlFetchArray($lres)) {
143 $price = empty($lrow['pr_price']) ? 0 : $lrow['pr_price'];
144 $s .= "<option value='" . attr($lrow['option_id']) . "'";
145 $s .= " id='prc_$price'";
146 if ((strlen($default) == 0 && $lrow['is_default'] && !$disabled) ||
147 (strlen($default) > 0 && $lrow['option_id'] == $default)
149 $s .= " selected";
151 $s .= ">" . text(xl_list_label($lrow['title'])) . "</option>\n";
153 $s .= "</select>";
154 return $s;
157 // If Contraception forms can be auto-created by the Fee Sheet we might need
158 // to ask about the client's prior contraceptive use.
160 public function generateContraceptionSelector($tagname='newmauser') {
161 $s = '';
162 if ($GLOBALS['gbl_new_acceptor_policy'] == '1') {
163 $csrow = sqlQuery("SELECT COUNT(*) AS count FROM forms AS f WHERE " .
164 "f.pid = ? AND f.encounter = ? AND " .
165 "f.formdir = 'LBFccicon' AND f.deleted = 0",
166 array($this->pid, $this->encounter));
167 // Do it only if a contraception form does not already exist for this visit.
168 // Otherwise assume that whoever created it knows what they were doing.
169 if ($csrow['count'] == 0) {
170 // Determine if this client ever started contraception with the MA.
171 // Even if only a method change, we assume they have.
172 $query = "SELECT f.form_id FROM forms AS f " .
173 "JOIN form_encounter AS fe ON fe.pid = f.pid AND fe.encounter = f.encounter " .
174 "WHERE f.formdir = 'LBFccicon' AND f.deleted = 0 AND f.pid = ? " .
175 "ORDER BY fe.date DESC LIMIT 1";
176 $csrow = sqlQuery($query, array($this->pid));
177 if (empty($csrow)) {
178 $s .= "<select name='$tagname'>\n";
179 $s .= " <option value='2'>" . xlt('First Modern Contraceptive Use (Lifetime)') . "</option>\n";
180 $s .= " <option value='1'>" . xlt('First Modern Contraception at this Clinic (with Prior Contraceptive Use)') . "</option>\n";
181 $s .= " <option value='0'>" . xlt('Method Change at this Clinic') . "</option>\n";
182 $s .= "</select>\n";
186 return $s;
189 // Generate a price level drop-down defaulting to the patient's current price level.
191 public function generatePriceLevelSelector($tagname='pricelevel', $disabled=false) {
192 $s = "<select name='" . attr($tagname) . "'";
193 if ($disabled) $s .= " disabled";
194 $s .= ">";
195 $pricelevel = $this->getPriceLevel();
196 $plres = sqlStatement("SELECT option_id, title FROM list_options " .
197 "WHERE list_id = 'pricelevel' AND activity = 1 ORDER BY seq");
198 while ($plrow = sqlFetchArray($plres)) {
199 $key = $plrow['option_id'];
200 $val = $plrow['title'];
201 $s .= "<option value='" . attr($key) . "'";
202 if ($key == $pricelevel) $s .= ' selected';
203 $s .= ">" . text(xl_list_label($val)) . "</option>";
205 $s .= "</select>";
206 return $s;
209 // Return Javascript that defines a function to validate the line items.
210 // Most of this is currently IPPF-specific, but NDC codes are also validated.
211 // This also computes and sets the form's ippfconmeth value if appropriate.
212 // This does not validate form fields not related to or derived from line items.
213 // Do not call this javascript function if you are just refreshing the form.
214 // The arguments are the names of the form arrays for services and products.
216 public function jsLineItemValidation($bill='bill', $prod='prod') {
217 $s = "
218 function jsLineItemValidation(f) {
219 var max_contra_cyp = 0;
220 var max_contra_code = '';
221 var required_code_count = 0;
222 // Loop thru the services.
223 for (var lino = 0; f['{$bill}['+lino+'][code_type]']; ++lino) {
224 var pfx = '{$bill}[' + lino + ']';
225 if (f[pfx + '[del]'] && f[pfx + '[del]'].checked) continue;
226 if (f[pfx + '[ndcnum]'] && f[pfx + '[ndcnum]'].value) {
227 // Check NDC number format.
228 var ndcok = true;
229 var ndc = f[pfx + '[ndcnum]'].value;
230 var a = ndc.split('-');
231 if (a.length != 3) {
232 ndcok = false;
234 else if (a[0].length < 1 || a[1].length < 1 || a[2].length < 1 ||
235 a[0].length > 5 || a[1].length > 4 || a[2].length > 2) {
236 ndcok = false;
238 else {
239 for (var i = 0; i < 3; ++i) {
240 for (var j = 0; j < a[i].length; ++j) {
241 var c = a[i].charAt(j);
242 if (c < '0' || c > '9') ndcok = false;
246 if (!ndcok) {
247 alert('" . xls('Format incorrect for NDC') . "\"' + ndc +
248 '\", " . xls('should be like nnnnn-nnnn-nn') . "');
249 if (f[pfx+'[ndcnum]'].focus) f[pfx+'[ndcnum]'].focus();
250 return false;
252 // Check for valid quantity.
253 var qty = f[pfx+'[ndcqty]'].value - 0;
254 if (isNaN(qty) || qty <= 0) {
255 alert('" . xls('Quantity for NDC') . " \"' + ndc +
256 '\" " . xls('is not valid (decimal fractions are OK).') . "');
257 if (f[pfx+'[ndcqty]'].focus) f[pfx+'[ndcqty]'].focus();
258 return false;
261 if (f[pfx+'[method]'] && f[pfx+'[method]'].value) {
262 // The following applies to contraception for family planning clinics.
263 var tmp_cyp = parseFloat(f[pfx+'[cyp]'].value);
264 var tmp_meth = f[pfx+'[method]'].value;
265 var tmp_methtype = parseInt(f[pfx+'[methtype]'].value);
266 if (tmp_cyp > max_contra_cyp && tmp_methtype == 2) {
267 // max_contra_* tracks max cyp for initial consults only.
268 max_contra_cyp = tmp_cyp;
269 max_contra_code = tmp_meth;
272 if ($this->patient_male) {
273 $s .= "
274 var male_compatible_method = (
275 // TBD: Fix hard coded dependency on IPPFCM codes here.
276 tmp_meth == '4450' || // male condoms
277 tmp_meth == '4570'); // male vasectomy
278 if (!male_compatible_method) {
279 if (!confirm('" . xls('Warning: Contraceptive method is not compatible with a male patient.') . "'))
280 return false;
283 } // end if male patient
284 if ($this->patient_age < 10 || $this->patient_age > 50) {
285 $s .= "
286 if (!confirm('" . xls('Warning: Contraception for a patient under 10 or over 50.') . "'))
287 return false;
289 } // end if improper age
290 if ($this->match_services_to_products) {
291 $s .= "
292 // Nonsurgical methods should normally include a corresponding product.
293 // This takes advantage of the fact that only nonsurgical methods have CYP
294 // less than 10, in both the old and new frameworks.
295 if (tmp_cyp < 10.0) {
296 // Was: if (tmp_meth.substring(0, 2) != '12') {
297 var got_prod = false;
298 for (var plino = 0; f['{$prod}['+plino+'][drug_id]']; ++plino) {
299 var ppfx = '{$prod}[' + plino + ']';
300 if (f[ppfx+'[del]'] && f[ppfx+'[del]'].checked) continue;
301 if (f[ppfx+'[method]'] && f[ppfx+'[method]'].value) {
302 if (f[ppfx+'[method]'].value == tmp_meth) got_prod = true;
305 if (!got_prod) {
306 if (!confirm('" . xls('Warning: There is no product matching the contraceptive service.') . "'))
307 return false;
311 } // end match services to products
312 $s .= "
314 ++required_code_count;
317 if ($this->match_services_to_products) {
318 $s .= "
319 // The following applies to contraception for family planning clinics.
320 // Loop thru the products.
321 for (var lino = 0; f['{$prod}['+lino+'][drug_id]']; ++lino) {
322 var pfx = '{$prod}[' + lino + ']';
323 if (f[pfx + '[del]'] && f[pfx + '[del]'].checked) continue;
324 if (f[pfx + '[method]'] && f[pfx + '[method]'].value) {
325 var tmp_meth = f[pfx + '[method]'].value;
326 // Contraceptive products should normally include a corresponding method.
327 var got_svc = false;
328 for (var slino = 0; f['{$bill}[' + slino + '][code_type]']; ++slino) {
329 var spfx = '{$bill}[' + slino + ']';
330 if (f[spfx + '[del]'] && f[spfx + '[del]'].checked) continue;
331 if (f[spfx + '[method]'] && f[spfx + '[method]'].value) {
332 if (f[spfx + '[method]'].value == tmp_meth) got_svc = true;
335 if (!got_svc) {
336 if (!confirm('" . xls('Warning: There is no service matching the contraceptive product.') . "'))
337 return false;
340 ++required_code_count;
343 } // end match services to products
344 if (isset($GLOBALS['code_types']['MA'])) {
345 $s .= "
346 if (required_code_count == 0) {
347 if (!confirm('" . xls('You have not entered any clinical services or products. Click Cancel to add them. Or click OK if you want to save as-is.') . "')) {
348 return false;
353 $s .= "
354 // End contraception validation.
355 if (f.ippfconmeth) {
356 // Save the primary contraceptive method to its hidden form field.
357 f.ippfconmeth.value = max_contra_code;
359 return true;
362 return $s;