Solved

The combo of an array and a construct. Is this right?

Posted on 2016-07-18
5
24 Views
Last Modified: 2016-07-20
I'm trying to debug a page and as part of that process, I'm encountering a brand of syntax I don't typically write and I'm needing to deconstruct things in order to better understand it.

There's a function that appears to be creating an array. That array is referenced in the construct at the top of the page, or at least that appears to be the case. Tell me if this is right:

At the top of the page, you have this:

class StatementImage {

    protected $statementid;
    protected $pipe_delim;
    protected $statement_data;
    protected static $debug = false;

    /**
     * Takes the pipe delimited file and creates a statementimage entity.
     */
    public function __construct($pipe_delim) {

        $this->pipe_delim = $pipe_delim;
        $this->statement_data = StatementImage::pipeToArray($pipe_delim);
        $this->statementid = str_replace('T', '', $this->statement_data['statement']['statement_pin']);

    }

Open in new window


Further down, you've got this:

  //processing
        $results = StatementImage::processStatementSql($sql);

        //Create Objects
        $return = array();
        foreach($results as $pipe_delim) {
            $return[] = new StatementImage($pipe_delim);
        }

Is this correct:

$results - the data that's coming back from the SELECT statement represented by StatementImage::processStatementSql($sql);
$return = array(); - setting up an array
 foreach($results as $pipe_delim) {
      $return[] = new StatementImage($pipe_delim);
   } - this is packing all of the data in $results into an array called $return...

This is where I start to get a little hesitant. I'm accustomed to something like this:

while($row=$query->fetch_array())
            {
                  $result_array[]=$row;
            }

What is $StatementImage($pipe_delim)?
0
Comment
Question by:brucegust
5 Comments
 
LVL 21

Accepted Solution

by:
Kim Walker earned 300 total points
ID: 41717622
What you have is the beginning a class object and there must be more to it because all you're showing is where the argument supplied to new StatementImage is stored in a local variable. There must be a function called processStatementSql() stored in that class somewhere. And more than likely that function is taking an SQL statement, querying a database, and processing that data into a pipe delimited string (or file according to the comment in the class).

But if my assumptions on the rest of the class definition are correct, then your conclusions are mostly correct. But the $results variable is not holding a database query object as you might expect. It is likely holding the results of a database query that has been processed into a different format.
0
 
LVL 42

Assisted Solution

by:zephyr_hex
zephyr_hex earned 100 total points
ID: 41717634
What is $StatementImage($pipe_delim)?

I'm assuming you mean:  What is new StatementImage($pipe_delim);

That is instantiating a new instance of the StatementImage class.  $pipe_delim is passed to the constructor:
public function __construct($pipe_delim) {

Open in new window

0
 

Author Comment

by:brucegust
ID: 41719270
Gentlemen!

I appreciate your time! I've got the entire StatementImage.php page below. I'm taking your comments and attempting to retrace the logic of "$pipe_delim" throughout. Let me know if this is right.

<?php
require_once __dir__."/../pconnectdb_helper.php";
require_once(__dir__."/StandardStatementPDFGenerator.php");
require_once(__dir__."/LetterStatementPDFGenerator.php");

/**
 * REQURES THESE CONSTANTS BE SET
 *     DEFAULT_DB
 *
 * DEPENDS ON THIS FUNCTION
 *     pconnectdb()
 */
class StatementImage {

    protected $statementid;
    protected $pipe_delim;
    protected $statement_data;
    protected static $debug = false;

    /**
     * Takes the pipe delimited file and creates a statementimage entity.
     */
    public function __construct($pipe_delim) {

        $this->pipe_delim = $pipe_delim;
        $this->statement_data = StatementImage::pipeToArray($pipe_delim);
        $this->statementid = str_replace('T', '', $this->statement_data['statement']['statement_pin']);

    }

    /**
     * Default String is the pipe delimited file
     * @return string
     */
    public function __toString() {
        return $this->pipe_delim;
    }

    /**
     * Errors out and stops the pdf render.
     * @param  [type] $e [description]
     * @return [type]    [description]
     */
    public function error($e) {
        $erray = ((array) $e);
        echo ("PDFlib exception occurred in statement generator:\n" .
        "[" . $e->get_errnum() . "] " . $e->get_apiname() . ": " .
        $e->get_errmsg() . ": File " . $erray["\0*\0file"] . ": Line " . $erray["\0*\0line"] . "\n");
        StatementImage::$debug = true;
    }

    /**
     * Return a dataset array from the database.
     * @param string $sql
     * @return array results
     */
    protected static function runQuery($sql) {
        return pconnectdb($sql, DEFAULT_DB, true);
    }

    /**
     * Process single statement pipe to the array dataset that can be sent to the imager.
     * @param  String $pipe_delim Pipedelim for single statement
     * @return Array              Imager-ready dataset
     */
    protected function pipeToArray($pipe_delim){

        $lines = explode("\n", str_replace("\r\n","\n",$pipe_delim));

        //Subsets of each statement
        $statement = array(
                'statement' => array(),
                'client' => array(),
                'guarantor' => array(),
                'patient' => array(),
                'unrolled_data' => array(),
                'rolled_data' => array(),
                'paymentplan_options' => array(),
                'paymentplan' =>array()
            );

        //Loop through the lines
        foreach ($lines as $raw_line) {

            //Split into cells
            $line = explode("|",$raw_line);

            switch ($line[0]) {
                case 'S2' :
                    //Statement Loop
                    $statement['statement']['templateid']        = $line[1];
                    $statement['statement']['total_charges']     = $line[2];
                    $statement['statement']['total_payments']    = $line[3];
                    $statement['statement']['total_adjustments'] = $line[4];
                    $statement['statement']['balance']           = $line[5];
                    $statement['statement']['amount_due']        = $line[6];
                    $statement['statement']['due_date']          = $line[7];
                    $statement['statement']['statement_date']    = $line[8];
                    $statement['statement']['statement_pin']     = $line[9];
                    $statement['statement']['accountid']         = $line[10];
                    $statement['statement']['accountnumber']     = $line[11];
                    $statement['statement']['url']               = $line[12];
                    $statement['statement']['message']           = $line[13];
                    $statement['statement']['last_claim_date']   = $line[14];
                    $statement['statement']['legal_copy_color']  = $line[15];
                    $statement['statement']['legal_copy']        = $line[16];
                    $statement['statement']['legal_copy_back']   = $line[17];
                    break;
                case 'C3' :
                    //Client Loop
                    $statement['client']['practice_name']           = $line[1];
                    $statement['client']['client_name']             = $line[2];
                    $statement['client']['logo_file']               = $line[3];
                    $statement['client']['patient_portal_website']  = $line[4];
                    $statement['client']['phone_number']            = $line[5];
                    $statement['client']['stub_address_1']          = $line[6];
                    $statement['client']['stub_address_2']          = $line[7];
                    $statement['client']['stub_city']               = $line[8];
                    $statement['client']['stub_state']              = $line[9];
                    $statement['client']['stub_zip']                = $line[10];
                    $statement['client']['return_address_1']        = $line[11];
                    $statement['client']['return_address_2']        = $line[12];
                    $statement['client']['return_city']             = $line[13];
                    $statement['client']['return_state']            = $line[14];
                    $statement['client']['return_zip']              = $line[15];
                    $statement['client']['fax_number']              = $line[16];
                    $statement['client']['support_email']           = $line[17];
                    $statement['client']['hours_of_operation']      = $line[18];
                    break;
                case 'G4' :
                    //Guarantor Loop
                    $statement['guarantor']['last_name']                = $line[1];
                    $statement['guarantor']['middle_initial']           = $line[2];
                    $statement['guarantor']['first_name']               = $line[3];
                    $statement['guarantor']['dob']                      = $line[4];
                    $statement['guarantor']['address_1']                = $line[5];
                    $statement['guarantor']['address_2']                = $line[6];
                    $statement['guarantor']['address_3']                = $line[7];
                    $statement['guarantor']['city']                     = $line[8];
                    $statement['guarantor']['state']                    = $line[9];
                    $statement['guarantor']['zip']                      = $line[10];
                    $statement['guarantor']['phonenumber']              = $line[11];
                    $statement['guarantor']['parents_or_guardian_of']   = $line[12];
                    $statement['guarantor']['ssn']                      = $line[13];
                    break;
                case 'P5' :
                    //Patient Loop
                    $statement['patient']['last_name']      = $line[1];
                    $statement['patient']['middle_initial'] = $line[2];
                    $statement['patient']['first_name']     = $line[3];
                    $statement['patient']['dob']            = $line[4];
                    break;
                case 'N6' :
                    //Unrolled Account Information
                    $index = count($statement['unrolled_data']);
                    foreach($statement['unrolled_data'] as $i => $enc){
                        if($enc['encounterid'] === $line[1]) {
                            $index = $i;
                            continue;
                        }
                    }

                    $statement['unrolled_data'][$index]['txns'][] = array(

                        //'encounterid'   => $line[1],
                        //'provider_name' => $line[2],
                        'date'          => $line[3],
                        'type'          => $line[4],
                        'description'   => $line[5],
                        'amount'        => $line[6]
                        //'left_to_pay'   => $line[7]

                    );

                    //Encounter-level
                    // Provider
                    if (trim($line[2]) != '' || !isset($statement['unrolled_data'][$index]['provider'])) {
                        $statement['unrolled_data'][$index]['provider'] = $line[2];
                    }
                    // Left to pay
                    if (trim($line[7]) != '' || !isset($statement['unrolled_data'][$index]['left_to_pay'])) {
                        $statement['unrolled_data'][$index]['left_to_pay'] = $line[7];
                    }
                    // Encounter ID
                    if (trim($line[1]) != '' || !isset( $statement['unrolled_data'][$index]['encounterid']) ) {
                        $statement['unrolled_data'][$index]['encounterid'] = $line[1];
                    }

                    break;
                case 'A6' :
                    //Rolled Account Information
                    $statement['rolled_data'][] = array(

                        'encounterid'   => $line[1],
                        'provider'      => $line[2],
                        'description'   => $line[3],
                        'date'          => $line[4],
                        'charged'       => $line[5],
                        'adjustments'   => $line[6],
                        'payments'      => $line[7],
                        'left_to_pay'   => $line[8]

                    );
                    break;
                case 'O7' :
                    //Payment Plan Options
                    $statement['paymentplan_options'] = array(

                        'option_1' => $line[1],
                        'option_2' => $line[2],
                        'option_3' => $line[3]

                    );
                    break;
                case 'P8' :
                    //Current Payment Plan
                    $statement['paymentplan'] = array(

                        'amount'             => $line[1],
                        'progress'           => $line[2],
                        'balance'            => $line[3],
                        'remaining_payments' => $line[4]

                    );
                    break;
                case 'F1' :
                case 'H1' :
                    //Header / Footer line, ignore this.
                    //Do Nothing.
                    break;
                default :
                    //Unhandled Line Type.
                    //throw new Exception("Can't parse line type : " . $line[0]);
                    break;
            }
        }
        return $statement;
    }

    /**
     * Return the pdf buffer string
     * @return String pdf buffer
     * @todo THESE ARE BEING HARDCODED TO TEMPLATEIDS... THIS SHOULD BE DONE ELSEWHERE, IN A BUILDER/FACTORY PERHAPS.
     */
    protected function toPDF(){
        if($this->statement_data['statement']['templateid'] == 4) {
            $image = new StandardStatementPDFGenerator($this);
        } else if ($this->statement_data['statement']['templateid'] == 9) {
            $image = new LetterStatementPDFGenerator($this);
        } else {
            //This is a "Fail Gracefully" measure. We haven't generated a statement
            //without a templateid since July 2013, so we shouldn't hit this moving
            //forward.
            $image = new StandardStatementPDFGenerator($this);
        }

        return $image->getStream();

    }

    public function getData(){
        return $this->statement_data;
    }

    /**
     * Write a Pipe Delim to the provided filepath
     * @param  String   $filepath        File to write
     * @param  Boolean  $force_overwrite If true, then it will automatically overwrite any files of the same name. if
     *                                   false, write will fail when there is a file that exists.
     * @return Boolean                   Success
     */
    public function savePipeDelim($filepath, $force_overwrite = false) {

        $file_contents = $this->pipe_delim;

        if($force_overwrite === true || file_exists($filepath) === false){
            $file = fopen($filepath,'w');
            if ($file) {
                $success = fwrite($file, $file_contents) ? true : false;
            } else {
                $success = false;
            }
        } else {
            $success = false;
        }

        return $success;

    }

    /**
     * Write a Pipe Delim to the provided filepath
     * @param  String   $filepath        File to write
     * @param  Boolean  $force_overwrite If true, then it will automatically overwrite any files of the same name. if
     *                                   false, write will fail when there is a file that exists.
     * @return Boolean                   Success
     */
    public function savePDF($filepath, $force_overwrite = false) {

        //Force a PDF extension
        if(substr($filepath, -4) != '.pdf') $filepath .= '.pdf';

        //Get file contents
        $file_contents = $this->toPDF();
        $success       = true;

        if($force_overwrite === true || file_exists($filepath) === false){
            $file = fopen($filepath,'w');
            if ($file) {
                $success = fwrite($file, $file_contents) ? true : false;
            } else {
                $success = false;
            }
        } else {
            $success = false;
        }

    }

    /**
     * Prints the pdf to the screen
     * @param  String $filename Filename to use when saving
     */
    public function viewPDF($filename = null) {
        $pdf = $this->toPDF();
        if(!$filename) $filename = $this->defaultFilename() . '.pdf';

        $len = strlen($pdf);
        if(StatementImage::$debug === false) header("Content-type: application/pdf; charset=utf-8");
        header("Content-Length: $len");
        header("Content-Disposition: inline; filename=$filename");
        echo $pdf;
    }

    /**
     * Prints the Pipe delim to the screen
     */
    public function viewPipe() {

        $filename = $this->defaultFilename() . '.txt';

        header("Content-type: text/plain");
        header("filename=$filename");

        echo $this->pipe_delim;
    }

    /**
     * Forces a download of the pipe delim
     * @param  String $filename The filename to use in the download.
     */
    public function downloadPipe($filename = null) {
        if(!$filename) $filename = $this->defaultFilename() . '.txt';

        $len = strlen($this->pipe_delim);
        header("Content-type: application/octet-stream");
        header("Content-Disposition: inline; filename=$filename");

        echo $this->pipe_delim;
    }

    /**
     * Forces a download of the PDF
     * @param  String $filename The filename to use in the download.
     */
    public function downloadPDF($filename = null) {
        $pdf = $this->toPDF();

        if(!$filename) $filename = $this->defaultFilename() . '.pdf';

        $len = strlen($pdf);
        header("Content-type: application/octet-stream");
        header("Content-Length: $len");
        header("Content-Disposition: inline; filename=$filename");
        echo $pdf;
    }

    /**
     * Generates a default filename based on the statement information
     * @return String
     */
    public function defaultFilename() {
        return $this->statement_data['statement']['accountid'] . '-' . str_replace('T','',$this->statement_data['statement']['statement_pin']);
    }

    /**
     * The main loader for StatementImage. Digests a statementid and turns it into a StatementImage
     * @param  Mixed  $id Either a single statementid or an array of statementids
     * @param  Bool   $preview     If true, will skip the statement table and watermark the statement
     * @param  Int    $templateid  The id of the template to use
     * @return Mixed               If input is an array, return an array of StatementImages, otherwise return a single
     *                             StatementImage.
     */
    public function load($id, $preview = false, $templateid = 4) {

        if($preview === false) {
            $where = StatementImage::getStatementWhere($id);
            //Get the full sql string
            $sql = StatementImage::getSQL($where);
        } else {
            $where = StatementImage::getAccountWhere($id);
            $sql = StatementImage::getSQL($where, false, $templateid);
        }

        //processing
        $results = StatementImage::processStatementSql($sql);

        //Create Objects
        $return = array();
        foreach($results as $pipe_delim) {
            $return[] = new StatementImage($pipe_delim);
        }

        //Return an array if given an array
        //Return a single entry if given a single entry
        if(is_array($id) && count($return) > 0) {
            return $return;
        } else if (count($return) > 0){
            return $return[0];
        } else {
            return false;
        }

    }

    protected function getStatementWhere($statementid) {
        //Create the where clause
        $where = ' where st.statementid ';
        if (is_array($statementid)) {
            $where .= 'in (';
            $first = true;
            foreach($statementid as $st) {
                if($first) $first = false;
                else $where .= ',';
                if(is_array($st)) {
                    if(isset($st['statementid'])) {
                        $where .= $st['statementid'];
                    }
                } else {
                    $where .= $st;
                }
            }
            $where .= ')';
        } else {
            $where .= '= ' . $statementid;
        }
        return $where;
    }

    protected function getAccountWhere($accountid) {
        //Create the where clause
        $where = ' where ac.accountid ';
        if (is_array($accountid)) {
            $where .= 'in (';
            $first = true;
            foreach($accountid as $ac) {
                if($first) $first = false;
                else $where .= ',';
                if(is_array($ac)) {
                    if(isset($ac['accountid'])) {
                        $where .= $ac['accountid'];
                    }
                } else {
                    $where .= $ac;
                }
            }
            $where .= ')';
        } else {
            $where .= '= ' . $accountid;
        }
        return $where;
    }

    /**
     * Process a single statement dataset to a pipedelim
     * @param  Array  $st Dataset for a single-statement
     * @return String     PipeDelim
     */
    protected function toPipeDelim($st){

        //Loop through and create the PipeDelim field
        $file = "";
        /**
            Add Statement Segment
        **/
        //krumo($st);
        // Set the Silly Variables cause some folks don't like reading the array object in the string generation
        $totalcharges = number_format($st['TotalCharges'], 2, '.', '');
        $totaladjustments = number_format($st['TotalAdjustments'], 2, '.', '');
        $totalpayments = number_format($st['TotalPayments'], 2, '.', '');
        $stbalance = number_format($totalcharges+$totalpayments+$totaladjustments, 2, '.', '');
        $amountdue = ($st['AmountDue'] == '0')?$stbalance:number_format($st['AmountDue'], 2, '.', '');
        if($st['duedate'] == '0'){
            $duedate = 'Due Upon Receipt';
        } else {
            $duedate = date('m/').$st['duedate'].date('/Y');
            if(strtotime($duedate) < strtotime('now')) {
                $duedate = Date('m/d/Y', strtotime("$duedate + 1 month"));
            }
            $duedate = 'Due on ' . $duedate;
        }
        $statementdate = date('m/d/Y');
        $planbalance = number_format($st['planbalance'], 2, '.', '');

        $message = $st['parameters']['message'];
        $mmessage = $st['parameters']['legal_front'];
        $lmessage = $st['parameters']['legal_back'];
        $mmessagecolor = $st['parameters']['legal_color'];

        $statementpin = $st['statementid'] != '' ? "T".$st['statementid'] : "PREVIEW";
        $templateid = $st['templateid'];
        $accountid = $st['AccountID'];
        $accountnumber = $st['PracticeAccountID']."-".$accountid;
        $statementurl = "https://".strtolower($st['ClientTetrisID']).".patientfocus.com/?=$statementpin";
        $lastclaimdate = $st['lastclaimdate'] ? date('m/d/Y',strtotime($st['lastclaimdate'])) : '';

        $file .= "S2|$templateid|$totalcharges|$totalpayments|$totaladjustments|$stbalance|$amountdue|$duedate|$statementdate|$statementpin|$accountid|$accountnumber|$statementurl|$message|$lastclaimdate|$mmessagecolor|$mmessage|$lmessage\r\n";
        /**
            Add Client Segment
        **/
           // Set the Silly Variables cause some folks don't like reading the array object in the string generation
        $practicename = $st['PracticeName'];
        $clientname = $st['ClientName'];
        $logofile = strtolower($st['ClientTetrisID']).".jpg";
        $phonenumber = $st['twiliophone'];
        /* Temporary option to disinguish if the account has an encounter in placementgroup 1353 for the URMC lockbox move */
        if(($st['1353'] == null && $st['ClientID'] == '21')){
          $lad1 = "P.O. Box 1079";
          $lad2 = "";
          $ladcity = "Manchester";
          $ladstate = "TN";
          $ladzip = "37349";
        }else{
          $lad1 = $st['lockboxAddress1'];
          $lad2 = $st['lockboxAddress2'];
          $ladcity = $st['lockboxCity'];
          $ladstate = $st['lockboxState'];
          $ladzip = $st['lockboxZip'];
        }
        $portalurl = "https://".strtolower($st['ClientTetrisID']).".patientfocus.com/";
        $rad1 = "PO BOX 291569";
        $rad2 = "";
        $radcity = "Nashville";
        $radstate = "TN";
        $radzip = "37214";
        $fax = '8885511910';
        $email = 'support@patientfocus.com';
        $hours = 'Open Monday - Friday 8am - 6pm Central';
        $file .= "C3|$practicename|$clientname|$logofile|$portalurl|$phonenumber|$lad1|$lad2|$ladcity|$ladstate|$ladzip|$rad1|$rad2|$radcity|$radstate|$radzip|$fax|$email|$hours\r\n";

        /**
            Set the Guarantor Information
        **/
        $gfname = $st['patientfname'];
        $glname = $st['patientlname'];
        $patssn = $st['patientssn'];
        $gmi = $st['patientinitial'];
        if($st['patientdob'] != null){
            $gdob = date('Ymd', strtotime($st['patientdob']));
        }else{
            $gdob = $st['patientdob'];
        }
        if($st['patientdod'] != null){
            $gdod = date('Ymd', strtotime($st['patientdod']));
        }else{
            $gdod = $st['patientdod'];
        }
        $gaddress1 = $st['maddress1'];
        $gaddress2 = $st['maddress2'];
        $gaddress3 = $st['maddress3'];
        $gcity = $st['mcity'];
        $gstate = $st['mstate'];
        $gzip = $st['mzip'];
        $gphonenumber = $st['phonenumber'];

        $file .="G4|$glname|$gmi|$gfname|$gdob|$gaddress1|$gaddress2|$gaddress3|$gcity|$gstate|$gzip|$gphonenumber|0|$patssn\r\n";


        /**
            Set the Patient Information
        **/
            $file .="P5|$glname|$gmi|$gfname|$gdob\r\n";

        /**
            New and old txn info
         */
        //NEW
        if(isset($st['newtxns'][0])){
            foreach($st['newtxns'] as $newtxn){
                if($newtxn['balance'] > 0){
                    $practclaimid = $newtxn['practclaimid'];
                    $type = $newtxn['txntype'];
                    $physicianid = $newtxn['physicianid'];
                    $description = $newtxn['description'];
                    $amount = number_format($newtxn['amount'], 2, '.','');
                    $lefttopay = number_format($newtxn['balance'], 2, '.','');
                    $date =date('Ymd',strtotime($newtxn['date']));

                    $file .= "N6|$practclaimid|$physicianid|$date|$type|$description|$amount|$lefttopay\r\n";
                }
            }
        }
        //OLD
        if(isset($st['oldtxns'][0])){
            foreach($st['oldtxns'] as $oldtxn){
                if($oldtxn['left_to_pay'] > 0){
                    $practclaimid = $oldtxn['encountercode'];
                    $physicianid = ($oldtxn['provider'])?$oldtxn['provider']:"";
                    $description = $oldtxn['description'];
                    $dos = date('Ymd', strtotime($oldtxn['dos']));
                    $charges = number_format($oldtxn['total_charges'],2,'.','');
                    $adjs = number_format($oldtxn['total_adjustments'],2,'.','');
                    $payments  = number_format($oldtxn['total_payments'],2,'.','');
                    $balance = number_format($oldtxn['left_to_pay'],2,'.','');

                    $file .= "A6|$practclaimid|$physicianid|$description|$dos|$charges|$adjs|$payments|$balance\r\n";
                }
            }
        }

        /**
          Are they Elegible for a Payment Plan?  If So, let's add that segment.
        **/
        //krumo($duedate);exit();
        if($stbalance >= '180' && $st['duedate'] == '0'){
            $opt1 = number_format(ceil($stbalance/3),2,'.','');
            $opt2 = number_format(ceil($stbalance/6),2,'.','');
            $opt3 = number_format(ceil($stbalance/9),2,'.','');
            $file .= "O7|$opt1|$opt2|$opt3\r\n";
        }

        /**
          Are they on a Payment PLan?  If So, Let's add that segment.
        **/
        if($st['duedate'] != '0' ){
            //krumo($st);exit();
            $progress = number_format(($planbalance-$stbalance)/$planbalance, 2);
            $remaining = ceil($stbalance/$amountdue);
            $file .= "P8|$amountdue|$progress|$stbalance|$remaining\r\n";
        }

        return $file;
    }

    /**
     * Tne main statement SQL
     * @param  String $where The where clause to use.
     * @return String        The full sql statement
	 * added CASE WHEN ISNULL(pp.pmtplanamount, 0) <...before AmountDue was whatever the patient typically paid. By adding the CASE, it now made sure the amount being asked for was consistent with what was actually owed
     */
    protected function getSQL($where, $statementData = true, $templateid = 4) {
        $sql = "
            OPEN SYMMETRIC KEY PCAR_Key
            DECRYPTION BY CERTIFICATE PCAR_Certificate
            SELECT
                ac.AccountID,
                clt.ClientName,
                clt.ClientTetrisID,
                clt.ClientID,
                cast(dbo.ufn_ClientPreference(clt.ClientID, 'StatementRollUp') as int) as StatementRollUp,
                prt.PracticeID,
                prt.PracticeName,
                prt.twiliophone,
                prt.showphysician,
                pat.patientfname,
                pat.patientlname,
                pat.patientinitial,
                pat.patientdob,
                pat.patientdod,
                CONVERT(varchar(100), DecryptByKey(pat.eSSN)) as patientssn,
                coalesce(ac.mobilephone, ac.homephone, ac.workphone, null) as phonenumber,
                ac.maddress1,
                ac.maddress2,
                ac.maddress3,
                ac.mcity,
                ac.mstate,
                ac.mzip,
                dbo.ufn_StatementWorkingCharges(prt.PracticeCode,ac.practiceaccountid) as TotalCharges,
                dbo.ufn_StatementWorkingAdjustments(prt.PracticeCode,ac.practiceaccountid) * -1 as TotalAdjustments,
                dbo.ufn_StatementWorkingPayments2(prt.PracticeCode,ac.practiceaccountid) * -1 as TotalPayments,
				CASE WHEN ISNULL(pp.pmtplanamount, 0) < 
				(dbo.ufn_StatementWorkingCharges(prt.PracticeCode,ac.practiceaccountid) + 
				(dbo.ufn_StatementWorkingAdjustments(prt.PracticeCode,ac.practiceaccountid) * -1) + 
				(dbo.ufn_StatementWorkingPayments2(prt.PracticeCode,ac.practiceaccountid) * -1)) 
				THEN 
				isnull(pp.pmtplanamount, 0) 
				ELSE dbo.ufn_StatementWorkingCharges(prt.PracticeCode,ac.practiceaccountid) + 
				(dbo.ufn_StatementWorkingAdjustments(prt.PracticeCode,ac.practiceaccountid) * -1) + 
				(dbo.ufn_StatementWorkingPayments2(prt.PracticeCode,ac.practiceaccountid) * -1)
				END AS AmountDue,                
				 isnull(pp.pmtplanday,0) as  duedate,
                isnull(pp.startingplanbalance, 0) as planbalance,
                ac.PracticeAccountID,
                clt.lockboxAddress1,
                clt.lockboxAddress2,
                clt.lockboxCity,
                clt.lockboxState,
                clt.lockboxZip,"
            .( $statementData ? "
                st.templateid,
                st.statementid,
                null as lastclaimdate,
                /* Temporary option to disinguish if the account has an encounter in placementgroup 1353 for the URMC lockbox move */
                (select top 1 id from chargegroup cg where cg.accountid = ac.accountid and placementgroupid = 1353) as [1353]

            from Statement st with (nolock)
                Join Account ac with (nolock) on st.Accountid = ac.AccountID
            " : "
                '$templateid' as templateid,
                '' as statementid,
                null as lastclaimdate,
                /* Temporary option to disinguish if the account has an encounter in placementgroup 1353 for the URMC lockbox move */
                (select top 1 id from chargegroup cg where cg.accountid = ac.accountid and placementgroupid = 1353) as [1353]

            from Account ac with (nolock)
            " )."
                join Patient pat with (nolock) on pat.AccountID = ac.AccountID
                join Practice prt with (nolock) on ac.PracticeID = prt.PracticeID
                join Client clt with (nolock) on prt.ClientID = clt.ClientID
                left join
                (select pmtplanamount, pmtplanday, primarypatientid, startingplanbalance from PaymentPlan
                  where planendeddate is null
                  and iscurrentplan = 1) pp
                  on pp.primarypatientid = pat.patientid
            $where
        ";
        return $sql;
    }

    /**
     * Get Parameters for a particular statement.
     *
     * @param array $statement A single statements data.
     * @return array
     */
    protected static function getParameters($statement) {
        $fromDb = StatementImage::runQuery(StatementImage::statementParamSQL($statement['ClientID'], $statement['templateid']));
        $output = array();
        foreach ($fromDb as $row) {
            $output[$row['parameter']] = StatementImage::applyDataToValueTemplate($row['value'], $statement);
        }
        return $output;
    }

    /**
     * Go through a template string and replace instances of the $data key with it's value.
     *
     * So "I am a {{gender}}" and ['gender' => 'boy'] will return "I am a boy"
     *
     * @param String $template The string that we'll be applying
     * @param Array $data The array that holds all of the replaceable
     * @return String
     */
    protected static function applyDataToValueTemplate($template, array $data = array()) {
        $open = "{{";
        $close = "}}";

        foreach ($data as $key => $value) {
            // Check that value can be used as a string.
            if(
                ( !is_array( $value ) ) &&
                ( ( !is_object( $value ) && settype( $value, 'string' ) !== false ) ||
                    ( is_object( $value ) && method_exists( $value, '__toString' ) ) )
            ) {
                $template = str_replace($open . $key . $close, $value, $template);
            }
        }
        return $template;
    }

    /**
     * For a particular client and template, pull back all of the statement parameters.
     *
     * @param $clientid
     * @param $templateid
     * @return string
     */
    protected function statementParamSQL ($clientid, $templateid) {
        $sql = "
        declare @clientid int = $clientid
        declare @templateid int = $templateid

        select
            parameter,
            (
                select top 1 value
                from StatementParameters val
                where (
                    clientid = @clientid or clientid is null
                ) and (
                    templateid = @templateid or templateid is null
                )
                and val.VoidedDate is null
                and val.parameter = sp.parameter
                order by val.clientid desc, val.templateid desc
            ) as value
        from StatementParameters sp
        group by parameter
        ";

        return $sql;
		//echo $sql;
    }

    /**
     * Process the sql, creates the pipe delim, and the
     * @param  String $sql SQL from the getSQL function
     * @return Array       Dataset from the database, including a pipe_delim and updated array piece.
     */
    protected function processStatementSql ($sql) {

        $statements = StatementImage::runQuery($sql);
        $ret = array();

        //Second and Third DB Calls
        //TXN TABLE STUFF
        // NOTE - I don't know there is a reason to process these as separate queries
        // and I think they can be reasonably combined with the query held in getSQL()
        // and processed through to get the same dataset more quickly.
        if ($statements) {
            foreach($statements as $st) {
                $accountid = $st['AccountID'];

                if (!$st['StatementRollUp'] || $st['templateid'] == '9') {
                    $st['newtxns'] = StatementImage::getNewTransactions($st, $st['showphysician'] == '1');
                } else {
                    $st['newtxns'] = array();
                }

                // Clean up the statement level lastclaimdate by reading the new txns.
                foreach($st['newtxns'] as $txn){
                    $st['lastclaimdate'] = $txn['firstdos'] < $st['lastclaimdate'] || $st['lastclaimdate'] === null ? $txn['firstdos'] : $st['lastclaimdate'];
                }

                if($st['templateid'] != '9'){
                    $number_of_days_to_keep_zero_balance_encounters_on_the_statement = 45;
                    $st['oldtxns'] = StatementImage::getOldTransactions(
                        $st,
                        $st['showphysician'] == '1',
                        $st['StatementRollUp'] == '0',
                        $number_of_days_to_keep_zero_balance_encounters_on_the_statement
                    );
                } else {
                    $st['oldtxns'] = array();
                }

                $st['parameters'] = StatementImage::getParameters($st);

                $ret[] = StatementImage::toPipeDelim($st);
            }
        }

        return $ret;
    }

    /**
     * Return new transactions
     *
     * @param Array $statement
     * @param bool $filterDate
     * @return Array
     */
    protected static function getNewTransactions($statement, $showPhysician = true, $filterDate = true)
    {
        /**
        Get the New Transactions
        Seriously, stop changing the [date] to be only posted.  This jacks up all the things from a client perception standpoint. PMT 2014-11-07
        Haha, nobody changed this. This was copied from Monkey 2 days before you made the change, and the change was only made in Monkey CPC 2014-12-10
         **/
        //rumo("New Transaction Query");
        //ZOMG BIG QUERY.  This needs to be touched to insure we don't lock things up.  It's a bit too slow at the moment.
        if($statement['templateid'] != '9'){
            $hdsql = "and (cg.status1 not in ('rt','hd','rp','fc') or cg.status1 is null)
                    and cg.id in (
                        select chargegroupid from txn where accountid = $statement[AccountID] group by chargegroupid " .
                ($filterDate ? "having max(posted) > dbo.ufn_lastpaperstatementbefore($statement[AccountID], cast('2050-01-01' as date))" : "") . "
                    )
                    ";
        } else {
            $hdsql = " and type = 'c' and cg.status1 = 'hd' and cg.placementgroupid != 1374 and (select sum(amount) from txn with(nolock) where txn.chargegroupid = t.chargegroupid group by chargegroupid) > 0";
        }
        $sql = "
                select
                    LTRIM(
                        case
                            when type = 'a' then 'Adjustment'
                            when type = 'p' then 'Payment'
                            when type = 'c' then 'Charge'
                        end
                    ) as txntype,
                    [date],
                    patientname,
                    description,
                    amount,
                    physicianid,
                    practclaimid,
                    priority,
                    cpt,
                    case when practclaimid = '' then 1 else 0 end as toppriority,
                    balance,
                    firstdos
                from
                (
                    select
                        paymentmethod,
                        payerdesc,
                        type,
                        [date] = case when type = 'c' then dos else posted end,
                        pfirst+' '+plast as patientname,
                        LTRIM(
                            case
                                when type = 'a' then payerdesc
                                when type = 'p' then payerdesc
                                when type = 'c' then ISNULL(cpt, '') +' - '+ ISNULL(left(cptdesc,100),'')
                            end
                        ) as description,
                        amount," . (
                            $showPhysician?
                            "
                            coalesce(physician, '')
                            "
                            :
                            ("'" .
                            str_replace("'", "''", $statement["PracticeName"])
                            . "'")
                        ) . "  as physicianid,
                        coalesce(cg.encountercode, '') as practclaimid,
                        case
                            when type = 'c' then 0
                            else 1
                        end as priority,
                        left(cptdesc,100) as cptdesc,
                        cpt as cpt,
                        (select sum(amount) from txn where t.chargegroupid = txn.chargegroupid and t.accountid = txn.accountid) as balance,
                        (select min(dos) from txn where t.chargegroupid = txn.chargegroupid and t.accountid = txn.accountid and type = 'c') as firstdos

                    from txn t
                    left join chargegroup cg on t.chargegroupid = cg.id
                    where t.accountid =  $statement[AccountID] $hdsql
                    and reversedref is null
               ) u

                order by firstdos, practclaimid, priority,[date]
                ";

        return StatementImage::runQuery($sql);
    }

    /**
     * Get old transactions
     *
     * @param array $statement The statement data.
     * @param bool $showPhysician Whether or not to display the available physician name.
     * @param bool $filterDate Whether or not to apply the "old" date filter.
     * @param int|bool $ageOutZeroAfter If false, will include ALL old transactions. If int, will exclude any encounter
     *                                  with zero balance that has not had activity in the last (int) days.
     * @return array
     */
    protected static function getOldTransactions(
        $statement,
        $showPhysician = true,
        $filterDate = true,
        $ageOutZeroAfter = false
    ){
        /**
        Get the Old Transactions
         **/
        //krumo('oldtxn');
        $sql = "
            select
                t1.accountid,
                t1.chargegroupid,
                t1.encountercode,
                /* Max automatically disregards nulls */" . (
                    $showPhysician ?
                    "
                    coalesce(max(physician), '')
                    "
                    :
                    ("'" .
                    str_replace("'", "''", $statement["PracticeName"])
                    . "'")
                ) . " as provider,
                sum(case when t1.type = 'c' then amount else 0 end) as total_charges,
                sum(case when t1.type = 'a' then amount else 0 end) as total_adjustments,
                sum(case when t1.type = 'p' then amount else 0 end) as total_payments,
                sum(amount) as left_to_pay,
                /* Charge description */
                /* blank for now */
                '' as description,
                 cast(min(case when t1.type = 'c' then dos else null end) as date) as dos

            from txn t1
                left join chargegroup cg on t1.chargegroupid = cg.id
                /* Charge description join */
                left join (
                    select
                        max(case when (txn.type = 'C') then amount else null end ) as max_charge_amount,
                        accountid,
                        chargegroupid
                    from txn
                    group by accountid, chargegroupid
                ) t2 on t2.accountid = t1.accountid and t2.chargegroupid = t1.chargegroupid
                where t1.accountid = $statement[AccountID]
                and reversedref is null
                and (cg.status1 not in('rt','hd','rp','fc') or cg.status1 is null)
                and cg.id in (select chargegroupid from txn where accountid = $statement[AccountID]) "
            . (
                $filterDate ?
                    "
                    and cg.id not in (
                        select chargegroupid from txn where accountid = $statement[AccountID] group by chargegroupid having max(posted) > dbo.ufn_lastpaperstatementbefore($statement[AccountID], cast('2050-01-01' as date))
                    )"
                    :
                    ""
            ) . "
            group by t1.accountid, t1.chargegroupid, t1.encountercode "
            . (
                is_int($ageOutZeroAfter)?
                    " having datediff(d, max(t1.created), getDate()) <= $ageOutZeroAfter or sum(amount) <> 0 "
                    :
                    ""
            ) . "

        order by dos";

        return StatementImage::runQuery($sql);
    }

}

Open in new window


I) We're kicking things off by establishing three properties: $statementid, $pipe_delim and $statement_data. I'm not ignoring $debug, I'm just not including it at this point because it's not really doing anything significant from a programmatic standpoint.

II) Construct, by definition, is assigning values to the properties that were just mentioned. What's significant is that all three values are based off of what's coming in via whatever is in the $pipe_delim value. (Right?)

III) The entire machinery of StatementImage.php is put into motion with another page that calls the "load" function that's located on line 394.  Within that function you're constructing a SELECT statement (statement.php - I don't have that included) using other function within the StatementImage class. You run that SELECT at line 406 and at line 411, you pack all of the results into the $pipe_delim property. This is actually kind of clever because now you can efficiently move all of the results of your SELECT throughout your class without having to run the same SELECT statement over and over again. (Right?)

IV) Finally, the $pipe_delim property, now stuffed with the results from the load query, is now being broken down into the pdf content thanks to the pipeToArray function referenced on line 26 and documented on lines 66-238. [b}(Right?)[/b]

Thanks!
0
 
LVL 108

Assisted Solution

by:Ray Paseur
Ray Paseur earned 100 total points
ID: 41721078
This code needs a complete redesign.  It's is untestable as written, and I wouldn't want to trust it without automated testing.  I don't really have time to try to dissect all of this code, but I can offer a few suggestions that might be helpful.

Break it up!  A class that is over a thousand lines is a gross anti-pattern.  Don't do that.

Instead of sending data around in non-standard structures like pipe-delimited strings, learn about how you can use JSON for data interchange.  This will free you from dealing with positional arguments.  It will get rid of the ambiguity of variables like $line[1], $line[2], $line[3] (etc, etc, etc) where the variables mean different things, even inside the same method.  That's a recipe for confusion and is sure to cause regressions if you ever need to change any of the elements in the $line array.  JSON lets you use PHP objects with named properties, a much more stable and extensible approach.  Not to mention an approach that can be tested!

Consider using type hints and named data objects.

Consider the Single Responsibility Principle as you undertake the redesign.  It will save you a world of butt-hurt if you use smaller classes and methods.  One of the best design tools is decidedly low-tech: Class:Responsibility:Collaboration cards.  These work well because if you can't write everything a class does by hand on a 3x5 card, the class is doing too much and should be refactored into multiple classes that collaborate in the data model.

I could go on, but hopefully these basic design principles will help you get started refactoring into a more usable design.  Best of luck with it! ~Ray
0
 

Author Comment

by:brucegust
ID: 41721109
Ray, I agree with you! But this isn't my app and I'm not in a place, nor would it be appropriate for me, to rewrite the code. I'm on contract to simply plug some holes until the client moves to an entirely different software. I'm just trying to understand what's there, however convoluted it may be, so I can make some repairs and then watch this whole application fade into the sunset when the client migrates over to a new application.

I'm re-reading my question and I've decided it's probably not fair in that I've got several questions packed into one. Furthermore, I think I've got what I need based on your input as well as zephyr and Kim. That being the fact that you can pack an array, even a multi-dimensional array, into a property. Hadn't seen that before and wanted to confirm it. I think that's been done here.

As always, thanks for your time, gentlemen!
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

Introduction HTML checkboxes provide the perfect way for a web developer to receive client input when the client's options might be none, one or many.  But the PHP code for processing the checkboxes can be confusing at first.  What if a checkbox is…
Developers of all skill levels should learn to use current best practices when developing websites. However many developers, new and old, fall into the trap of using deprecated features because this is what so many tutorials and books tell them to u…
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now