some portal work
[openemr.git] / library / FeeSheet.class.php
blob8b48b4de6f2d75769a0b79c255ad6be36561d60e
1 <?php
2 /**
3 * library/FeeSheet.class.php
5 * Base class for implementations of the Fee Sheet.
6 * This should not include UI but may be extended by a class that does.
8 * LICENSE: This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see
18 * http://www.gnu.org/licenses/licenses.html#GPL .
20 * @package OpenEMR
21 * @license https://www.gnu.org/licenses/licenses.html#GPL GNU GPL V3+
22 * @author Rod Roark <rod@sunsetsystems.com>
23 * @link http://www.open-emr.org
26 require_once(dirname(__FILE__) . "/../interface/globals.php");
27 require_once(dirname(__FILE__) . "/acl.inc");
28 require_once(dirname(__FILE__) . "/../custom/code_types.inc.php");
29 require_once(dirname(__FILE__) . "/../interface/drugs/drugs.inc.php");
30 require_once(dirname(__FILE__) . "/options.inc.php");
31 require_once(dirname(__FILE__) . "/appointment_status.inc.php");
32 require_once(dirname(__FILE__) . "/forms.inc");
34 use OpenEMR\Billing\BillingUtilities;
35 use OpenEMR\Common\Logging\EventAuditLogger;
37 // For logging checksums set this to true.
38 define('CHECKSUM_LOGGING', true);
40 // require_once(dirname(__FILE__) . "/api.inc");
41 // require_once(dirname(__FILE__) . "/forms.inc");
43 class FeeSheet
46 public $pid; // patient id
47 public $encounter; // encounter id
48 public $got_warehouses = false; // if there is more than 1 warehouse
49 public $default_warehouse = ''; // logged-in user's default warehouse
50 public $visit_date = ''; // YYYY-MM-DD date of this visit
51 public $match_services_to_products = false; // For IPPF
52 public $patient_age = 0; // Age in years as of the visit date
53 public $patient_male = 0; // 1 if male
54 public $patient_pricelevel = ''; // From patient_data.pricelevel
55 public $provider_id = 0;
56 public $supervisor_id = 0;
57 public $code_is_in_fee_sheet = false; // Set by genCodeSelectorValue()
59 // Possible units of measure for NDC drug quantities.
60 public $ndc_uom_choices = array(
61 'ML' => 'ML',
62 'GR' => 'Grams',
63 'ME' => 'Milligrams',
64 'F2' => 'I.U.',
65 'UN' => 'Units'
68 // Set by checkRelatedForContraception():
69 public $line_contra_code = '';
70 public $line_contra_cyp = 0;
71 public $line_contra_methtype = 0; // 0 = None, 1 = Not initial, 2 = Initial consult
73 // Array of line items generated by addServiceLineItem().
74 // Each element is an array of line item attributes.
75 public $serviceitems = array();
77 // Array of line items generated by addProductLineItem().
78 // Each element is an array of line item attributes.
79 public $productitems = array();
81 // Indicates if any line item has a fee.
82 public $hasCharges = false;
84 // Indicates if any clinical services or products are in the fee sheet.
85 public $required_code_count = 0;
87 // These variables are used to compute the initial consult service with highest CYP (IPPF).
88 public $contraception_code = '';
89 public $contraception_cyp = 0;
91 public $ALLOW_COPAYS = false;
93 function __construct($pid = 0, $encounter = 0)
95 if (empty($pid)) {
96 $pid = $GLOBALS['pid'];
99 if (empty($encounter)) {
100 $encounter = $GLOBALS['encounter'];
103 $this->pid = $pid;
104 $this->encounter = $encounter;
106 // IPPF doesn't want any payments to be made or displayed in the Fee Sheet.
107 $this->ALLOW_COPAYS = !$GLOBALS['ippf_specific'];
109 // Get the user's default warehouse and an indicator if there's a choice of warehouses.
110 $wrow = sqlQuery("SELECT count(*) AS count FROM list_options WHERE list_id = 'warehouse' AND activity = 1");
111 $this->got_warehouses = $wrow['count'] > 1;
112 $wrow = sqlQuery(
113 "SELECT default_warehouse FROM users WHERE username = ?",
114 array($_SESSION['authUser'])
116 $this->default_warehouse = empty($wrow['default_warehouse']) ? '' : $wrow['default_warehouse'];
118 // Get some info about this visit.
119 $visit_row = sqlQuery("SELECT fe.date, fe.provider_id, fe.supervisor_id, " .
120 "opc.pc_catname, fac.extra_validation " .
121 "FROM form_encounter AS fe " .
122 "LEFT JOIN openemr_postcalendar_categories AS opc ON opc.pc_catid = fe.pc_catid " .
123 "LEFT JOIN facility AS fac ON fac.id = fe.facility_id " .
124 "WHERE fe.pid = ? AND fe.encounter = ? LIMIT 1", array($this->pid, $this->encounter));
125 $this->visit_date = substr($visit_row['date'], 0, 10);
126 $this->provider_id = $visit_row['provider_id'];
127 if (empty($this->provider_id)) {
128 $this->provider_id = $this->findProvider();
131 $this->supervisor_id = $visit_row['supervisor_id'];
132 // This flag is specific to IPPF validation at form submit time. It indicates
133 // that most contraceptive services and products should match up on the fee sheet.
134 $this->match_services_to_products = $GLOBALS['ippf_specific'] &&
135 !empty($visit_row['extra_validation']);
137 // Get some information about the patient.
138 $patientrow = getPatientData($this->pid, "DOB, sex, pricelevel");
139 $this->patient_age = $this->getAge($patientrow['DOB'], $this->visit_date);
140 $this->patient_male = strtoupper(substr($patientrow['sex'], 0, 1)) == 'M' ? 1 : 0;
141 $this->patient_pricelevel = $patientrow['pricelevel'];
144 // Convert numeric code type to the alpha version.
146 public static function alphaCodeType($id)
148 global $code_types;
149 foreach ($code_types as $key => $value) {
150 if ($value['id'] == $id) {
151 return $key;
155 return '';
158 // Compute age in years given a DOB and "as of" date.
160 public static function getAge($dob, $asof = '')
162 if (empty($asof)) {
163 $asof = date('Y-m-d');
166 $a1 = explode('-', substr($dob, 0, 10));
167 $a2 = explode('-', substr($asof, 0, 10));
168 $age = $a2[0] - $a1[0];
169 if ($a2[1] < $a1[1] || ($a2[1] == $a1[1] && $a2[2] < $a1[2])) {
170 --$age;
173 return $age;
176 // Gets the provider from the encounter, logged-in user or patient demographics.
177 // Adapted from work by Terry Hill.
179 public function findProvider()
181 $find_provider = sqlQuery(
182 "SELECT provider_id FROM form_encounter " .
183 "WHERE pid = ? AND encounter = ? ORDER BY id DESC LIMIT 1",
184 array($this->pid, $this->encounter)
186 $providerid = $find_provider['provider_id'];
187 if (!$providerid) {
188 $get_authorized = $_SESSION['userauthorized'];
189 if ($get_authorized == 1) {
190 $providerid = $_SESSION['authUserID'];
194 if (!$providerid) {
195 $find_provider = sqlQuery("SELECT providerID FROM patient_data " .
196 "WHERE pid = ?", array($this->pid));
197 $providerid = $find_provider['providerID'];
200 return intval($providerid);
203 // Log a message that is easy for the Re-Opened Visits Report to interpret.
205 public function logFSMessage($action)
207 EventAuditLogger::instance()->newEvent(
208 'fee-sheet',
209 $_SESSION['authUser'],
210 $_SESSION['authProvider'],
212 $action,
213 $this->pid,
214 $this->encounter
218 // Compute a current checksum of this encounter's Fee Sheet data from the database.
220 public function visitChecksum($saved = false)
222 $rowb = sqlQuery(
223 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
224 "id, code, modifier, units, fee, authorized, provider_id, ndc_info, justify, billed" .
225 "))) AS checksum FROM billing WHERE " .
226 "pid = ? AND encounter = ? AND activity = 1",
227 array($this->pid, $this->encounter)
229 $rowp = sqlQuery(
230 "SELECT BIT_XOR(CRC32(CONCAT_WS(',', " .
231 "sale_id, inventory_id, prescription_id, quantity, fee, sale_date, billed" .
232 "))) AS checksum FROM drug_sales WHERE " .
233 "pid = ? AND encounter = ?",
234 array($this->pid, $this->encounter)
236 $ret = intval($rowb['checksum']) ^ intval($rowp['checksum']);
237 if (CHECKSUM_LOGGING) {
238 $comment = "Checksum = '$ret'";
239 $comment .= ", Saved = " . ($saved ? "true" : "false");
240 EventAuditLogger::instance()->newEvent("checksum", $_SESSION['authUser'], $_SESSION['authProvider'], 1, $comment, $this->pid);
243 return $ret;
246 // IPPF-specific; get contraception attributes of the related codes.
248 public function checkRelatedForContraception($related_code, $is_initial_consult = false)
250 $this->line_contra_code = '';
251 $this->line_contra_cyp = 0;
252 $this->line_contra_methtype = 0; // 0 = None, 1 = Not initial, 2 = Initial consult
253 if (!empty($related_code)) {
254 $relcodes = explode(';', $related_code);
255 foreach ($relcodes as $relstring) {
256 if ($relstring === '') {
257 continue;
260 list($reltype, $relcode) = explode(':', $relstring);
261 if ($reltype !== 'IPPFCM') {
262 continue;
265 $methtype = $is_initial_consult ? 2 : 1;
266 $tmprow = sqlQuery("SELECT cyp_factor FROM codes WHERE " .
267 "code_type = '32' AND code = ? LIMIT 1", array($relcode));
268 $cyp = 0 + $tmprow['cyp_factor'];
269 if ($cyp > $this->line_contra_cyp) {
270 $this->line_contra_cyp = $cyp;
271 // Note this is an IPPFCM code, not an IPPF2 code.
272 $this->line_contra_code = $relcode;
273 $this->line_contra_methtype = $methtype;
279 // Insert a row into the lbf_data table. Returns a new form ID if that is not provided.
280 // This is only needed for auto-creating Contraception forms.
282 public function insert_lbf_item($form_id, $field_id, $field_value)
284 if ($form_id) {
285 sqlStatement("INSERT INTO lbf_data (form_id, field_id, field_value) " .
286 "VALUES (?, ?, ?)", array($form_id, $field_id, $field_value));
287 } else {
288 $form_id = sqlInsert("INSERT INTO lbf_data (field_id, field_value) " .
289 "VALUES (?, ?)", array($field_id, $field_value));
292 return $form_id;
295 // Create an array of data for a particular billing table item that is useful
296 // for building a user interface form row. $args is an array containing:
297 // codetype
298 // code
299 // modifier
300 // ndc_info
301 // auth
302 // del
303 // units
304 // fee
305 // id
306 // billed
307 // code_text
308 // justify
309 // provider_id
310 // notecodes
311 // pricelevel
312 public function addServiceLineItem($args)
314 global $code_types;
316 // echo "<!-- \n"; // debugging
317 // print_r($args); // debugging
318 // echo "--> \n"; // debugging
320 $li = array();
321 $li['hidden'] = array();
323 $codetype = $args['codetype'];
324 $code = $args['code'];
325 $revenue_code = isset($args['revenue_code']) ? $args['revenue_code'] : '';
326 $modifier = isset($args['modifier']) ? $args['modifier'] : '';
327 $code_text = isset($args['code_text']) ? $args['code_text'] : '';
328 $units = isset($args['units']) ? $args['units'] : 0;
329 $units = max(1, intval($units));
330 $billed = !empty($args['billed']);
331 $auth = !empty($args['auth']);
332 $id = isset($args['id']) ? intval($args['id']) : 0;
333 $ndc_info = isset($args['ndc_info']) ? $args['ndc_info'] : '';
334 $provider_id = isset($args['provider_id']) ? intval($args['provider_id']) : 0;
335 $justify = isset($args['justify']) ? $args['justify'] : '';
336 $notecodes = isset($args['notecodes']) ? $args['notecodes'] : '';
337 $fee = isset($args['fee']) ? (0 + $args['fee']) : 0;
338 // Price level should be unset only if adding a new line item.
339 $pricelevel = isset($args['pricelevel']) ? $args['pricelevel'] : $this->patient_pricelevel;
340 $del = !empty($args['del']);
342 // If using line item billing and user wishes to default to a selected provider, then do so.
343 if (!empty($GLOBALS['default_fee_sheet_line_item_provider']) && !empty($GLOBALS['support_fee_sheet_line_item_provider'])) {
344 if ($provider_id == 0) {
345 $provider_id = 0 + $this->findProvider();
349 if ($codetype == 'COPAY') {
350 if (!$code_text) {
351 $code_text = 'Cash';
354 if ($fee > 0) {
355 $fee = 0 - $fee;
359 // Get the matching entry from the codes table.
360 $sqlArray = array();
361 $query = "SELECT id, units, code_text, revenue_code FROM codes WHERE " .
362 "code_type = ? AND code = ?";
363 array_push($sqlArray, $code_types[$codetype]['id'], $code);
364 if ($modifier) {
365 $query .= " AND modifier = ?";
366 array_push($sqlArray, $modifier);
367 } else {
368 $query .= " AND (modifier IS NULL OR modifier = '')";
371 $result = sqlQuery($query, $sqlArray);
372 $codes_id = $result['id'];
373 $revenue_code = $revenue_code ? $revenue_code : $result['revenue_code'];
374 if (!$code_text) {
375 $code_text = $result['code_text'];
376 if (empty($units)) {
377 $units = max(1, intval($result['units']));
380 if (!isset($args['fee'])) {
381 // Fees come from the prices table now.
382 $query = "SELECT pr_price, lo.option_id AS pr_level, lo.notes FROM list_options lo " .
383 " LEFT OUTER JOIN prices p ON lo.option_id=p.pr_level AND pr_id = ? AND pr_selector = '' " .
384 " WHERE lo.list_id='pricelevel' " .
385 "ORDER BY seq";
386 // echo "\n<!-- $query -->\n"; // debugging
388 $prdefault = null;
389 $prrow = null;
390 $prrecordset = sqlStatement($query, array($codes_id));
391 while ($row = sqlFetchArray($prrecordset)) {
392 if (empty($prdefault)) {
393 $prdefault = $row;
396 if ($row['pr_level'] === $pricelevel) {
397 $prrow = $row;
401 $fee = 0;
402 if (!empty($prrow)) {
403 $fee = $prrow['pr_price'];
405 // if percent-based pricing is enabled...
406 if ($GLOBALS['enable_percent_pricing']) {
407 // if this price level is a percentage, calculate price from default price
408 if (!empty($prrow['notes']) && strpos($prrow['notes'], '%') > -1 && !empty($prdefault)) {
409 $percent = intval(str_replace('%', '', $prrow['notes']));
410 if ($percent > 0) {
411 $fee = $prdefault['pr_price'] * ((100 - $percent) / 100);
419 $fee = sprintf('%01.2f', $fee);
421 $li['hidden']['code_type'] = $codetype;
422 $li['hidden']['code'] = $code;
423 $li['hidden']['revenue_code'] = $revenue_code;
424 $li['hidden']['mod'] = $modifier;
425 $li['hidden']['billed'] = $billed;
426 $li['hidden']['id'] = $id;
427 $li['hidden']['codes_id'] = $codes_id;
429 // This logic is only used for family planning clinics, and then only when
430 // the option is chosen to use or auto-generate Contraception forms.
431 // It adds contraceptive method and effectiveness to relevant lines.
432 if ($GLOBALS['ippf_specific'] && $GLOBALS['gbl_new_acceptor_policy'] && $codetype == 'MA') {
433 $codesrow = sqlQuery(
434 "SELECT related_code, cyp_factor FROM codes WHERE " .
435 "code_type = ? AND code = ? LIMIT 1",
436 array($code_types[$codetype]['id'], $code)
438 $this->checkRelatedForContraception($codesrow['related_code'], $codesrow['cyp_factor']);
439 if ($this->line_contra_code) {
440 $li['hidden']['method' ] = $this->line_contra_code;
441 $li['hidden']['cyp' ] = $this->line_contra_cyp;
442 $li['hidden']['methtype'] = $this->line_contra_methtype;
443 // contraception_code is only concerned with initial consults.
444 if ($this->line_contra_cyp > $this->contraception_cyp && $this->line_contra_methtype == 2) {
445 $this->contraception_cyp = $this->line_contra_cyp;
446 $this->contraception_code = $this->line_contra_code;
451 if ($codetype == 'COPAY') {
452 $li['codetype'] = xl($codetype);
453 if ($ndc_info) {
454 $li['codetype'] .= " ($ndc_info)";
457 $ndc_info = '';
458 } else {
459 $li['codetype'] = $codetype;
462 $li['code' ] = $codetype == 'COPAY' ? '' : $code;
463 $li['revenue_code' ] = $revenue_code;
464 $li['mod' ] = $modifier;
465 $li['fee' ] = $fee;
466 $li['price' ] = $fee / $units;
467 $li['pricelevel'] = $pricelevel;
468 $li['units' ] = $units;
469 $li['provid' ] = $provider_id;
470 $li['justify' ] = $justify;
471 $li['notecodes'] = $notecodes;
472 $li['del' ] = $id && $del;
473 $li['code_text'] = $code_text;
474 $li['auth' ] = $auth;
476 $li['hidden']['price'] = $li['price'];
478 // If NDC info exists or may be required, add stuff for it.
479 if ($codetype == 'HCPCS' && !$billed) {
480 $ndcnum = '';
481 $ndcuom = '';
482 $ndcqty = '';
483 if (preg_match('/^N4(\S+)\s+(\S\S)(.*)/', $ndc_info, $tmp)) {
484 $ndcnum = $tmp[1];
485 $ndcuom = $tmp[2];
486 $ndcqty = $tmp[3];
489 $li['ndcnum' ] = $ndcnum;
490 $li['ndcuom' ] = $ndcuom;
491 $li['ndcqty' ] = $ndcqty;
492 } else if ($ndc_info) {
493 $li['ndc_info' ] = $ndc_info;
496 // For Family Planning.
497 if ($codetype == 'MA') {
498 ++$this->required_code_count;
501 if ($fee != 0) {
502 $this->hasCharges = true;
505 $this->serviceitems[] = $li;
508 // Create an array of data for a particular drug_sales table item that is useful
509 // for building a user interface form row. $args is an array containing:
510 // drug_id
511 // selector
512 // sale_id
513 // rx (boolean)
514 // del (boolean)
515 // units
516 // fee
517 // billed
518 // warehouse_id
519 // pricelevel
521 public function addProductLineItem($args)
523 global $code_types;
525 $li = array();
526 $li['hidden'] = array();
528 $drug_id = $args['drug_id'];
529 $selector = isset($args['selector']) ? $args['selector'] : '';
530 $sale_id = isset($args['sale_id']) ? intval($args['sale_id']) : 0;
531 $units = isset($args['units']) ? $args['units'] : 0;
532 $units = max(1, intval($units));
533 $billed = !empty($args['billed']);
534 $rx = !empty($args['rx']);
535 $del = !empty($args['del']);
536 $fee = isset($args['fee']) ? (0 + $args['fee']) : 0;
537 $pricelevel = isset($args['pricelevel']) ? $args['pricelevel'] : $this->patient_pricelevel;
538 $warehouse_id = isset($args['warehouse_id']) ? $args['warehouse_id'] : '';
540 $drow = sqlQuery("SELECT name, related_code FROM drugs WHERE drug_id = ?", array($drug_id));
541 $code_text = $drow['name'];
543 // If no warehouse ID passed, use the logged-in user's default.
544 if ($this->got_warehouses && $warehouse_id === '') {
545 $warehouse_id = $this->default_warehouse;
548 // If fee is not provided, get it from the prices table.
549 // It is assumed in this case that units will match what is in the product template.
550 if (!isset($args['fee'])) {
551 $query = "SELECT pr_price, lo.option_id AS pr_level, lo.notes FROM list_options lo " .
552 " LEFT OUTER JOIN prices p ON lo.option_id=p.pr_level AND pr_id = ? AND pr_selector = ? " .
553 " WHERE lo.list_id='pricelevel' " .
554 "ORDER BY seq";
556 $prdefault = null;
557 $prrow = null;
558 $prrecordset = sqlStatement($query, array($drug_id, $selector));
559 while ($row = sqlFetchArray($prrecordset)) {
560 if (empty($prdefault)) {
561 $prdefault = $row;
564 if ($row['pr_level'] === $pricelevel) {
565 $prrow = $row;
569 $fee = 0;
570 if (!empty($prrow)) {
571 $fee = $prrow['pr_price'];
573 // if percent-based pricing is enabled...
574 if ($GLOBALS['enable_percent_pricing']) {
575 // if this price level is a percentage, calculate price from default price
576 if (!empty($prrow['notes']) && strpos($prrow['notes'], '%') > -1 && !empty($prdefault)) {
577 $percent = intval(str_replace('%', '', $prrow['notes']));
578 if ($percent > 0) {
579 $fee = $prdefault['pr_price'] * ((100 - $percent) / 100);
586 $fee = sprintf('%01.2f', $fee);
588 $li['fee' ] = $fee;
589 $li['price' ] = $fee / $units;
590 $li['pricelevel'] = $pricelevel;
591 $li['units' ] = $units;
592 $li['del' ] = $sale_id && $del;
593 $li['code_text'] = $code_text;
594 $li['warehouse'] = $warehouse_id;
595 $li['rx' ] = $rx;
597 $li['hidden']['drug_id'] = $drug_id;
598 $li['hidden']['selector'] = $selector;
599 $li['hidden']['sale_id'] = $sale_id;
600 $li['hidden']['billed' ] = $billed;
601 $li['hidden']['price' ] = $li['price'];
603 // This logic is only used for family planning clinics, and then only when
604 // the option is chosen to use or auto-generate Contraception forms.
605 // It adds contraceptive method and effectiveness to relevant lines.
606 if ($GLOBALS['ippf_specific'] && $GLOBALS['gbl_new_acceptor_policy']) {
607 $this->checkRelatedForContraception($drow['related_code']);
608 if ($this->line_contra_code) {
609 $li['hidden']['method' ] = $this->line_contra_code;
610 $li['hidden']['methtype'] = $this->line_contra_methtype;
614 // For Family Planning.
615 ++$this->required_code_count;
616 if ($fee != 0) {
617 $this->hasCharges = true;
620 $this->productitems[] = $li;
623 // Generate rows for items already in the billing table for this encounter.
625 public function loadServiceItems()
627 $billresult = BillingUtilities::getBillingByEncounter($this->pid, $this->encounter, "*");
628 if ($billresult) {
629 foreach ($billresult as $iter) {
630 if (!$this->ALLOW_COPAYS && $iter["code_type"] == 'COPAY') {
631 continue;
634 $justify = trim($iter['justify']);
635 if ($justify) {
636 $justify = substr(str_replace(':', ',', $justify), 0, strlen($justify) - 1);
639 $this->addServiceLineItem(array(
640 'id' => $iter['id'],
641 'codetype' => $iter['code_type'],
642 'code' => trim($iter['code']),
643 'revenue_code' => trim($iter["revenue_code"]),
644 'modifier' => trim($iter["modifier"]),
645 'code_text' => trim($iter['code_text']),
646 'units' => $iter['units'],
647 'fee' => $iter['fee'],
648 'pricelevel' => $iter['pricelevel'],
649 'billed' => $iter['billed'],
650 'ndc_info' => $iter['ndc_info'],
651 'provider_id' => $iter['provider_id'],
652 'justify' => $justify,
653 'notecodes' => trim($iter['notecodes']),
658 // echo "<!-- \n"; // debugging
659 // print_r($this->serviceitems); // debugging
660 // echo "--> \n"; // debugging
663 // Generate rows for items already in the drug_sales table for this encounter.
665 public function loadProductItems()
667 $query = "SELECT ds.*, di.warehouse_id FROM drug_sales AS ds, drug_inventory AS di WHERE " .
668 "ds.pid = ? AND ds.encounter = ? AND di.inventory_id = ds.inventory_id " .
669 "ORDER BY ds.sale_id";
670 $sres = sqlStatement($query, array($this->pid, $this->encounter));
671 while ($srow = sqlFetchArray($sres)) {
672 $this->addProductLineItem(array(
673 'drug_id' => $srow['drug_id'],
674 'selector' => $srow['selector'],
675 'sale_id' => $srow['sale_id'],
676 'rx' => !empty($srow['prescription_id']),
677 'units' => $srow['quantity'],
678 'fee' => $srow['fee'],
679 'pricelevel' => $srow['pricelevel'],
680 'billed' => $srow['billed'],
681 'warehouse_id' => $srow['warehouse_id'],
686 // Check for insufficient product inventory levels.
687 // Returns an error message if any product items cannot be filled.
688 // You must call this before save().
690 public function checkInventory(&$prod)
692 $alertmsg = '';
693 $insufficient = 0;
694 $expiredlots = false;
695 if (is_array($prod)) {
696 foreach ($prod as $iter) {
697 if (!empty($iter['billed'])) {
698 continue;
701 $drug_id = $iter['drug_id'];
702 $sale_id = empty($iter['sale_id']) ? 0 : intval($iter['sale_id']); // present only if already saved
703 $units = empty($iter['units']) ? 1 : intval($iter['units']);
704 $warehouse_id = empty($iter['warehouse']) ? '' : $iter['warehouse'];
706 // Deleting always works.
707 if (!empty($iter['del'])) {
708 continue;
711 // If the item is already in the database...
712 if ($sale_id) {
713 $query = "SELECT ds.quantity, ds.inventory_id, di.on_hand, di.warehouse_id " .
714 "FROM drug_sales AS ds " .
715 "LEFT JOIN drug_inventory AS di ON di.inventory_id = ds.inventory_id " .
716 "WHERE ds.sale_id = ?";
717 $dirow = sqlQuery($query, array($sale_id));
718 // There's no inventory ID when this is a non-dispensible product (i.e. no inventory).
719 if (!empty($dirow['inventory_id'])) {
720 if ($warehouse_id && $warehouse_id != $dirow['warehouse_id']) {
721 // Changing warehouse so check inventory in the new warehouse.
722 // Nothing is updated by this call.
723 if (!sellDrug(
724 $drug_id,
725 $units,
727 $this->pid,
728 $this->encounter,
730 $this->visit_date,
732 $warehouse_id,
733 true,
734 $expiredlots
735 )) {
736 $insufficient = $drug_id;
738 } else {
739 if (($dirow['on_hand'] + $dirow['quantity'] - $units) < 0) {
740 $insufficient = $drug_id;
744 } else { // Otherwise it's a new item...
745 // This only checks for sufficient inventory, nothing is updated.
746 if (!sellDrug(
747 $drug_id,
748 $units,
750 $this->pid,
751 $this->encounter,
753 $this->visit_date,
755 $warehouse_id,
756 true,
757 $expiredlots
758 )) {
759 $insufficient = $drug_id;
762 } // end for
765 if ($insufficient) {
766 $drow = sqlQuery("SELECT name FROM drugs WHERE drug_id = ?", array($insufficient));
767 $alertmsg = xl('Insufficient inventory for product') . ' "' . $drow['name'] . '".';
768 if ($expiredlots) {
769 $alertmsg .= " " . xl('Check expiration dates.');
773 return $alertmsg;
776 // Save posted data to the database. $bill and $prod are the incoming arrays of line items, with
777 // key names corresponding to those generated by addServiceLineItem() and addProductLineItem().
779 public function save(
780 &$bill,
781 &$prod,
782 $main_provid = null,
783 $main_supid = null,
784 $default_warehouse = null,
785 $mark_as_closed = false
787 global $code_types;
789 if (isset($main_provid) && $main_supid == $main_provid) {
790 $main_supid = 0;
793 $copay_update = false;
794 $update_session_id = '';
795 $ct0 = ''; // takes the code type of the first fee type code type entry from the fee sheet, against which the copay is posted
796 $cod0 = ''; // takes the code of the first fee type code type entry from the fee sheet, against which the copay is posted
797 $mod0 = ''; // takes the modifier of the first fee type code type entry from the fee sheet, against which the copay is posted
799 if (is_array($bill)) {
800 foreach ($bill as $iter) {
801 // Skip disabled (billed) line items.
802 if (!empty($iter['billed'])) {
803 continue;
806 $id = $iter['id'];
807 $code_type = $iter['code_type'];
808 $code = $iter['code'];
809 $del = !empty($iter['del']);
810 $units = empty($iter['units']) ? 1 : intval($iter['units']);
811 $price = empty($iter['price']) ? 0 : (0 + trim($iter['price']));
812 $pricelevel = empty($iter['pricelevel']) ? '' : $iter['pricelevel'];
813 $revenue_code = empty($iter['revenue_code']) ? '' : trim($iter['revenue_code']);
814 $modifier = empty($iter['mod']) ? '' : trim($iter['mod']);
815 $justify = empty($iter['justify' ]) ? '' : trim($iter['justify']);
816 $notecodes = empty($iter['notecodes']) ? '' : trim($iter['notecodes']);
817 $provid = empty($iter['provid' ]) ? 0 : intval($iter['provid']);
819 $fee = sprintf('%01.2f', $price * $units);
821 if (!$cod0 && $code_types[$code_type]['fee'] == 1) {
822 $mod0 = $modifier;
823 $cod0 = $code;
824 $ct0 = $code_type;
827 if ($code_type == 'COPAY') {
828 if ($fee < 0) {
829 $fee = $fee * -1;
832 if (!$id) {
833 // adding new copay from fee sheet into ar_session and ar_activity tables
834 $session_id = sqlInsert(
835 "INSERT INTO ar_session " .
836 "(payer_id, user_id, pay_total, payment_type, description, patient_id, payment_method, " .
837 "adjustment_code, post_to_date) " .
838 "VALUES ('0',?,?,'patient','COPAY',?,'','patient_payment',now())",
839 array($_SESSION['authUserID'], $fee, $this->pid)
841 sqlBeginTrans();
842 $sequence_no = sqlQuery("SELECT IFNULL(MAX(sequence_no),0) + 1 AS increment FROM ar_activity WHERE " .
843 "pid = ? AND encounter = ?", array($this->pid, $this->encounter));
844 SqlStatement(
845 "INSERT INTO ar_activity (pid, encounter, sequence_no, code_type, code, modifier, " .
846 "payer_type, post_time, post_user, session_id, " .
847 "pay_amount, account_code) VALUES (?,?,?,?,?,?,0,now(),?,?,?,'PCP')",
848 array($this->pid, $this->encounter, $sequence_no['increment'], $ct0, $cod0, $mod0,
849 $_SESSION['authUserID'],
850 $session_id,
851 $fee)
853 sqlCommitTrans();
854 } else {
855 // editing copay saved to ar_session and ar_activity
856 $session_id = $id;
857 $res_amount = sqlQuery(
858 "SELECT pay_amount FROM ar_activity WHERE pid=? AND encounter=? AND session_id=?",
859 array($this->pid, $this->encounter, $session_id)
861 if ($fee != $res_amount['pay_amount']) {
862 sqlStatement(
863 "UPDATE ar_session SET user_id=?,pay_total=?,modified_time=now(),post_to_date=now() WHERE session_id=?",
864 array($_SESSION['authUserID'], $fee, $session_id)
866 sqlStatement(
867 "UPDATE ar_activity SET code_type=?, code=?, modifier=?, post_user=?, post_time=now(),".
868 "pay_amount=?, modified_time=now() WHERE pid=? AND encounter=? AND account_code='PCP' AND session_id=?",
869 array($ct0, $cod0, $mod0, $_SESSION['authUserID'], $fee, $this->pid, $this->encounter, $session_id)
874 if (!$cod0) {
875 $copay_update = true;
876 $update_session_id = $session_id;
879 continue;
882 # Code to create justification for all codes based on first justification
883 if ($GLOBALS['replicate_justification'] == '1') {
884 if ($justify != '') {
885 $autojustify = $justify;
889 if (($GLOBALS['replicate_justification'] == '1') && ($justify == '') && check_is_code_type_justify($code_type)) {
890 $justify = $autojustify;
893 if ($justify) {
894 $justify = str_replace(',', ':', $justify) . ':';
897 $auth = "1";
899 $ndc_info = '';
900 if (!empty($iter['ndcnum'])) {
901 $ndc_info = 'N4' . trim($iter['ndcnum']) . ' ' . $iter['ndcuom'] .
902 trim($iter['ndcqty']);
905 // If the item is already in the database...
906 if ($id) {
907 if ($del) {
908 $this->logFSMessage(xl('Service deleted'));
909 BillingUtilities::deleteBilling($id);
910 } else {
911 $tmp = sqlQuery(
912 "SELECT * FROM billing WHERE id = ? AND (billed = 0 or billed is NULL) AND activity = 1",
913 array($id)
915 if (!empty($tmp)) {
916 $tmparr = array('code' => $code, 'authorized' => $auth);
917 if (isset($iter['units' ])) {
918 $tmparr['units' ] = $units;
921 if (isset($iter['price' ])) {
922 $tmparr['fee' ] = $fee;
925 if (isset($iter['pricelevel'])) {
926 $tmparr['pricelevel'] = $pricelevel;
929 if (isset($iter['mod' ])) {
930 $tmparr['modifier' ] = $modifier;
933 if (isset($iter['provid' ])) {
934 $tmparr['provider_id'] = $provid;
937 if (isset($iter['ndcnum' ])) {
938 $tmparr['ndc_info' ] = $ndc_info;
941 if (isset($iter['justify' ])) {
942 $tmparr['justify' ] = $justify;
945 if (isset($iter['notecodes'])) {
946 $tmparr['notecodes' ] = $notecodes;
949 if (isset($iter['revenue_code'])) {
950 $tmparr['revenue_code'] = $revenue_code;
953 foreach ($tmparr as $key => $value) {
954 if ($tmp[$key] != $value) {
955 if ('fee' == $key) {
956 $this->logFSMessage(xl('Price changed'));
959 if ('units' == $key) {
960 $this->logFSMessage(xl('Quantity changed'));
963 if ('provider_id' == $key) {
964 $this->logFSMessage(xl('Service provider changed'));
967 sqlStatement("UPDATE billing SET `$key` = ? WHERE id = ?", array($value, $id));
972 } else if (!$del) { // Otherwise it's a new item...
973 $this->logFSMessage(xl('Service added'));
974 $code_text = lookup_code_descriptions($code_type.":".$code);
975 BillingUtilities::addBilling(
976 $this->encounter,
977 $code_type,
978 $code,
979 $code_text,
980 $this->pid,
981 $auth,
982 $provid,
983 $modifier,
984 $units,
985 $fee,
986 $ndc_info,
987 $justify,
989 $notecodes,
990 $pricelevel,
991 $revenue_code
994 } // end for
997 // if modifier is not inserted during loop update the record using the first
998 // non-empty modifier and code
999 if ($copay_update == true && $update_session_id != '' && $mod0 != '') {
1000 sqlStatement(
1001 "UPDATE ar_activity SET code_type = ?, code = ?, modifier = ?".
1002 " WHERE pid = ? AND encounter = ? AND account_code = 'PCP' AND session_id = ?",
1003 array($ct0, $cod0, $mod0, $this->pid, $this->encounter, $update_session_id)
1007 // Doing similarly to the above but for products.
1008 if (is_array($prod)) {
1009 foreach ($prod as $iter) {
1010 // Skip disabled (billed) line items.
1011 if (!empty($iter['billed'])) {
1012 continue;
1015 $drug_id = $iter['drug_id'];
1016 $selector = empty($iter['selector']) ? '' : $iter['selector'];
1017 $sale_id = $iter['sale_id']; // present only if already saved
1018 $units = max(1, intval(trim($iter['units'])));
1019 $price = empty($iter['price']) ? 0 : (0 + trim($iter['price']));
1020 $pricelevel = empty($iter['pricelevel']) ? '' : $iter['pricelevel'];
1021 $fee = sprintf('%01.2f', $price * $units);
1022 $del = !empty($iter['del']);
1023 $rxid = 0;
1024 $warehouse_id = empty($iter['warehouse']) ? '' : $iter['warehouse'];
1025 $somechange = false;
1027 // If the item is already in the database...
1028 if ($sale_id) {
1029 $tmprow = sqlQuery("SELECT ds.prescription_id, ds.quantity, ds.inventory_id, ds.fee, " .
1030 "ds.sale_date, di.warehouse_id " .
1031 "FROM drug_sales AS ds " .
1032 "LEFT JOIN drug_inventory AS di ON di.inventory_id = ds.inventory_id " .
1033 "WHERE ds.sale_id = ?", array($sale_id));
1034 $rxid = 0 + $tmprow['prescription_id'];
1035 if ($del) {
1036 if (!empty($tmprow)) {
1037 // Delete this sale and reverse its inventory update.
1038 $this->logFSMessage(xl('Product deleted'));
1039 sqlStatement("DELETE FROM drug_sales WHERE sale_id = ?", array($sale_id));
1040 if (!empty($tmprow['inventory_id'])) {
1041 sqlStatement(
1042 "UPDATE drug_inventory SET on_hand = on_hand + ? WHERE inventory_id = ?",
1043 array($tmprow['quantity'], $tmprow['inventory_id'])
1048 if ($rxid) {
1049 sqlStatement("DELETE FROM prescriptions WHERE id = ?", array($rxid));
1051 } else {
1052 // Modify the sale and adjust inventory accordingly.
1053 if (!empty($tmprow)) {
1054 foreach (array(
1055 'quantity' => $units,
1056 'fee' => $fee,
1057 'pricelevel' => $pricelevel,
1058 'selector' => $selector,
1059 'sale_date' => $this->visit_date,
1060 ) as $key => $value) {
1061 if ($tmprow[$key] != $value) {
1062 $somechange = true;
1063 if ('fee' == $key) {
1064 $this->logFSMessage(xl('Price changed'));
1067 if ('pricelevel' == $key) {
1068 $this->logFSMessage(xl('Price level changed'));
1071 if ('selector' == $key) {
1072 $this->logFSMessage(xl('Template selector changed'));
1075 if ('quantity' == $key) {
1076 $this->logFSMessage(xl('Quantity changed'));
1079 sqlStatement(
1080 "UPDATE drug_sales SET `$key` = ? WHERE sale_id = ?",
1081 array($value, $sale_id)
1083 if ($key == 'quantity' && $tmprow['inventory_id']) {
1084 sqlStatement(
1085 "UPDATE drug_inventory SET on_hand = on_hand - ? WHERE inventory_id = ?",
1086 array($units - $tmprow['quantity'], $tmprow['inventory_id'])
1092 if ($tmprow['inventory_id'] && $warehouse_id && $warehouse_id != $tmprow['warehouse_id']) {
1093 // Changing warehouse. Requires deleting and re-adding the sale.
1094 // Not setting $somechange because this alone does not affect a prescription.
1095 $this->logFSMessage(xl('Warehouse changed'));
1096 sqlStatement("DELETE FROM drug_sales WHERE sale_id = ?", array($sale_id));
1097 sqlStatement(
1098 "UPDATE drug_inventory SET on_hand = on_hand + ? WHERE inventory_id = ?",
1099 array($units, $tmprow['inventory_id'])
1101 $tmpnull = null;
1102 $sale_id = sellDrug(
1103 $drug_id,
1104 $units,
1105 $fee,
1106 $this->pid,
1107 $this->encounter,
1108 (empty($iter['rx']) ? 0 : $rxid),
1109 $this->visit_date,
1111 $warehouse_id,
1112 false,
1113 $tmpnull,
1114 $pricelevel,
1115 $selector
1120 // Delete Rx if $rxid and flag not set.
1121 if ($GLOBALS['gbl_auto_create_rx'] && $rxid && empty($iter['rx'])) {
1122 sqlStatement("UPDATE drug_sales SET prescription_id = 0 WHERE sale_id = ?", array($sale_id));
1123 sqlStatement("DELETE FROM prescriptions WHERE id = ?", array($rxid));
1126 } else if (! $del) { // Otherwise it's a new item...
1127 $somechange = true;
1128 $this->logFSMessage(xl('Product added'));
1129 $tmpnull = null;
1130 $sale_id = sellDrug(
1131 $drug_id,
1132 $units,
1133 $fee,
1134 $this->pid,
1135 $this->encounter,
1137 $this->visit_date,
1139 $warehouse_id,
1140 false,
1141 $tmpnull,
1142 $pricelevel,
1143 $selector
1145 if (!$sale_id) {
1146 die(xlt("Insufficient inventory for product ID") . " \"" . text($drug_id) . "\".");
1150 // If a prescription applies, create or update it.
1151 if (!empty($iter['rx']) && !$del && ($somechange || empty($rxid))) {
1152 // If an active rx already exists for this drug and date we will
1153 // replace it, otherwise we'll make a new one.
1154 if (empty($rxid)) {
1155 $rxid = '';
1158 // Get default drug attributes; prefer the template with the matching selector.
1159 $drow = sqlQuery(
1160 "SELECT dt.*, " .
1161 "d.name, d.form, d.size, d.unit, d.route, d.substitute " .
1162 "FROM drugs AS d, drug_templates AS dt WHERE " .
1163 "d.drug_id = ? AND dt.drug_id = d.drug_id " .
1164 "ORDER BY (dt.selector = ?) DESC, dt.quantity, dt.dosage, dt.selector LIMIT 1",
1165 array($drug_id, $selector)
1167 if (!empty($drow)) {
1168 $rxobj = new Prescription($rxid);
1169 $rxobj->set_patient_id($this->pid);
1170 $rxobj->set_provider_id(isset($main_provid) ? $main_provid : $this->provider_id);
1171 $rxobj->set_drug_id($drug_id);
1172 $rxobj->set_quantity($units);
1173 $rxobj->set_per_refill($units);
1174 $rxobj->set_start_date_y(substr($this->visit_date, 0, 4));
1175 $rxobj->set_start_date_m(substr($this->visit_date, 5, 2));
1176 $rxobj->set_start_date_d(substr($this->visit_date, 8, 2));
1177 $rxobj->set_date_added($this->visit_date);
1178 // Remaining attributes are the drug and template defaults.
1179 $rxobj->set_drug($drow['name']);
1180 $rxobj->set_unit($drow['unit']);
1181 $rxobj->set_dosage($drow['dosage']);
1182 $rxobj->set_form($drow['form']);
1183 $rxobj->set_refills($drow['refills']);
1184 $rxobj->set_size($drow['size']);
1185 $rxobj->set_route($drow['route']);
1186 $rxobj->set_interval($drow['period']);
1187 $rxobj->set_substitute($drow['substitute']);
1189 $rxobj->persist();
1190 // Set drug_sales.prescription_id to $rxobj->get_id().
1191 $oldrxid = $rxid;
1192 $rxid = 0 + $rxobj->get_id();
1193 if ($rxid != $oldrxid) {
1194 sqlStatement(
1195 "UPDATE drug_sales SET prescription_id = ? WHERE sale_id = ?",
1196 array($rxid, $sale_id)
1201 } // end for
1204 // Set default and/or supervising provider for the encounter.
1205 if (isset($main_provid) && $main_provid != $this->provider_id) {
1206 $this->logFSMessage(xl('Default provider changed'));
1207 sqlStatement(
1208 "UPDATE form_encounter SET provider_id = ? WHERE pid = ? AND encounter = ?",
1209 array($main_provid, $this->pid, $this->encounter)
1211 $this->provider_id = $main_provid;
1214 if (isset($main_supid) && $main_supid != $this->supervisor_id) {
1215 sqlStatement(
1216 "UPDATE form_encounter SET supervisor_id = ? WHERE pid = ? AND encounter = ?",
1217 array($main_supid, $this->pid, $this->encounter)
1219 $this->supervisor_id = $main_supid;
1222 // Save-and-Close is currently specific to Family Planning but might be more
1223 // generally useful. It provides the ability to mark an encounter as billed
1224 // directly from the Fee Sheet, if there are no charges.
1225 if ($mark_as_closed) {
1226 $tmp1 = sqlQuery(
1227 "SELECT SUM(ABS(fee)) AS sum FROM drug_sales WHERE " .
1228 "pid = ? AND encounter = ? AND billed = 0",
1229 array($this->pid, $this->encounter)
1231 $tmp2 = sqlQuery(
1232 "SELECT SUM(ABS(fee)) AS sum FROM billing WHERE " .
1233 "pid = ? AND encounter = ? AND billed = 0 AND activity = 1",
1234 array($this->pid, $this->encounter)
1236 if ($tmp1['sum'] + $tmp2['sum'] == 0) {
1237 sqlStatement(
1238 "update drug_sales SET billed = 1 WHERE " .
1239 "pid = ? AND encounter = ? AND billed = 0",
1240 array($this->pid, $this->encounter)
1242 sqlStatement(
1243 "UPDATE billing SET billed = 1, bill_date = NOW() WHERE " .
1244 "pid = ? AND encounter = ? AND billed = 0 AND activity = 1",
1245 array($this->pid, $this->encounter)
1247 } else {
1248 // Would be good to display an error message here... they clicked
1249 // Save and Close but the close could not be done. However the
1250 // framework does not provide an easy way to do that.
1255 // Call this after save() for Family Planning implementations.
1256 // It checks the contraception form, or makes a new one if $newmauser is set.
1257 // Returns 0 unless user intervention is required to fix a missing or incorrect form,
1258 // and in that case the return value is an existing form ID, or -1 if none.
1260 // Returns FALSE if user intervention is required to fix a missing or incorrect form.
1262 public function doContraceptionForm($ippfconmeth = null, $newmauser = null, $main_provid = 0)
1264 if (!empty($ippfconmeth)) {
1265 $csrow = sqlQuery(
1266 "SELECT f.form_id, ld.field_value FROM forms AS f " .
1267 "LEFT JOIN lbf_data AS ld ON ld.form_id = f.form_id AND ld.field_id = 'newmethod' " .
1268 "WHERE " .
1269 "f.pid = ? AND f.encounter = ? AND " .
1270 "f.formdir = 'LBFccicon' AND f.deleted = 0 " .
1271 "ORDER BY f.form_id DESC LIMIT 1",
1272 array($this->pid, $this->encounter)
1274 if (isset($newmauser)) {
1275 // pastmodern is 0 iff new to modern contraception
1276 $pastmodern = $newmauser == '2' ? 0 : 1;
1277 if ($newmauser == '2') {
1278 $newmauser = '1';
1281 // Add contraception form but only if it does not already exist
1282 // (if it does, must be 2 users working on the visit concurrently).
1283 if (empty($csrow)) {
1284 $newid = $this->insert_lbf_item(0, 'newmauser', $newmauser);
1285 $this->insert_lbf_item($newid, 'newmethod', "IPPFCM:$ippfconmeth");
1286 $this->insert_lbf_item($newid, 'pastmodern', $pastmodern);
1287 // Do we care about a service-specific provider here?
1288 $this->insert_lbf_item($newid, 'provider', $main_provid);
1289 addForm($this->encounter, 'Contraception', $newid, 'LBFccicon', $this->pid, $GLOBALS['userauthorized']);
1291 } else if (empty($csrow) || $csrow['field_value'] != "IPPFCM:$ippfconmeth") {
1292 // Contraceptive method does not match what is in an existing Contraception
1293 // form for this visit, or there is no such form. User intervention is needed.
1294 return empty($csrow) ? -1 : intval($csrow['form_id']);
1298 return 0;
1301 // Get price level from patient demographics.
1303 public function getPriceLevel()
1305 return $this->patient_pricelevel;
1308 // Update price level in patient demographics if it's changed.
1310 public function updatePriceLevel($pricelevel)
1312 if (!empty($pricelevel)) {
1313 if ($this->patient_pricelevel != $pricelevel) {
1314 $this->logFSMessage(xl('Price level changed'));
1315 sqlStatement(
1316 "UPDATE patient_data SET pricelevel = ? WHERE pid = ?",
1317 array($pricelevel, $this->pid)
1319 $this->patient_pricelevel = $pricelevel;
1324 // Create JSON string representing code type, code and selector.
1325 // This can be a checkbox value for parsing when the checkbox is clicked.
1326 // As a side effect note if the code is already selected in the Fee Sheet.
1328 public function genCodeSelectorValue($codes)
1330 global $code_types;
1331 list($codetype, $code, $selector) = explode(':', $codes);
1332 if ($codetype == 'PROD') {
1333 $crow = sqlQuery(
1334 "SELECT sale_id " .
1335 "FROM drug_sales WHERE pid = ? AND encounter = ? AND drug_id = ? " .
1336 "LIMIT 1",
1337 array($this->pid, $this->encounter, $code)
1339 $this->code_is_in_fee_sheet = !empty($crow['sale_id']);
1340 $cbarray = array($codetype, $code, $selector);
1341 } else {
1342 $crow = sqlQuery(
1343 "SELECT c.id AS code_id, b.id " .
1344 "FROM codes AS c " .
1345 "LEFT JOIN billing AS b ON b.pid = ? AND b.encounter = ? AND b.code_type = ? AND b.code = c.code AND b.activity = 1 " .
1346 "WHERE c.code_type = ? AND c.code = ? LIMIT 1",
1347 array($this->pid, $this->encounter, $codetype, $code_types[$codetype]['id'], $code)
1349 $this->code_is_in_fee_sheet = !empty($crow['id']);
1350 $cbarray = array($codetype, $code);
1353 $cbval = json_encode($cbarray);
1354 return $cbval;