Go Premium for a chance to win a PS4. Enter to Win

x
?
Solved

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

Posted on 2016-07-18
5
Medium Priority
?
53 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 22

Accepted Solution

by:
Kim Walker earned 1200 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 44

Assisted Solution

by:zephyr_hex (Megan)
zephyr_hex (Megan) earned 400 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 111

Assisted Solution

by:Ray Paseur
Ray Paseur earned 400 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

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Author Note: Since this E-E article was originally written, years ago, formal testing has come into common use in the world of PHP.  PHPUnit (http://en.wikipedia.org/wiki/PHPUnit) and similar technologies have enjoyed wide adoption, making it possib…
These days socially coordinated efforts have turned into a critical requirement for enterprises.
Learn how to match and substitute tagged data using PHP regular expressions. Demonstrated on Windows 7, but also applies to other operating systems. Demonstrated technique applies to PHP (all versions) and Firefox, but very similar techniques will w…
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
Suggested Courses

877 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