function process_state( $ps_state ) //////////////////////////////////////////// // // A basic state machine. // // Given the $ps_state value passed, perform // the action dictated by that state. // //////////////////////////////////////////// { GLOBAL $argv; GLOBAL $msg_log; GLOBAL $db_host, $db_user, $db_pwd, $db_name; GLOBAL $DEBUG; $done = FALSE; // $cont_key is an integer 1 .. N of the contract number // we'll display. It indexes the arrays $headers and $details $cont_key = isset( $_SESSION[ 'cont_key' ] ) ? $_SESSION[ 'cont_key' ] : NULL; $campaigns = isset( $_SESSION[ 'campaigns' ] ) ? $_SESSION[ 'campaigns' ] : NULL; $headers = isset( $_SESSION[ 'headers' ] ) ? $_SESSION[ 'headers' ] : NULL; $details = isset( $_SESSION[ 'details' ] ) ? $_SESSION[ 'details' ] : NULL; // loop until we reach a state where the user (web client) // must perform an action to determine what the next state // will be. output_style_header(); do { message_log_reset( $msg_log ); if ($DEBUG) { message_log_append( $msg_log, "database name is $db_name\n" ); } if ($DEBUG) { message_log_append( $msg_log, "state is $ps_state\n" ); } switch (TRUE) { ///////////////////////////////// // // Initial state is BEGIN // ///////////////////////////////// case ($ps_state == 'BEGIN'): // Clear all keys in array $_SESSION foreach (array_keys( $_SESSION ) as $sess_key) { unset( $_SESSION[ $sess_key ] ); } // foreach // Proceed to a prompting state next_state( 'PROMPT_FOR_XML_INPUT' ); break; ///////////////////////////////// // // Prompt the user to provide an // XML file. // ///////////////////////////////// case ($ps_state == 'PROMPT_FOR_XML_INPUT'): // We are ready to prompt for an input file if (CLI) { ; // In CLI mode, the input file name will be on the command line. } else { prompt_for_xml_input(); } // The next step is to check the XML file for basic sanity checks next_state( 'CHECK_XML_FILENAME' ); // But first we have to wait for the web client to upload the file $done = TRUE; if (CLI) { $done = FALSE; } break; ///////////////////////////////// // // Do some basic checking on the // XML file the user provided. // ///////////////////////////////// case ($ps_state == 'CHECK_XML_FILENAME'): $input_nam = 'xmlfile'; // per the POST form $filnam = isset( $_FILES[ $input_nam ] ) ? $_FILES[ $input_nam ][ 'name' ] : NULL; // The full path to where the file was uploaded on the server $filtmp = isset( $_FILES[ $input_nam ] ) ? $_FILES[ $input_nam ][ 'tmp_name' ] : NULL; if (is_null( $filnam ) || is_null( $filtmp ) || (strlen( $filnam ) == 0) || (strlen( $filtmp ) == 0)) { next_state( 'BEGIN' ); } else { next_state( 'CHECK_XML_UPLOAD' ); } break; case ($ps_state == 'CHECK_XML_UPLOAD'): if (CLI) { $filtmp = $argv[1]; echo "input file is $filtmp\n"; } // The size of the file in bytes $filsiz = $_FILES[ $input_nam ][ 'size' ]; // The error code of the upload process $filerr = $_FILES[ $input_nam ][ 'error' ]; if (CLI || $filerr == UPLOAD_ERR_OK) { next_state( 'PARSE_XML_UPLOAD' ); } else { message_log_append( $msg_log, "Upload error $filerr", MSG_LOG_ERROR ); $done = TRUE; next_state( 'BEGIN' ); // start over } break; ///////////////////////////////// // // Do a full parsing of the // XML file the user provided. // ///////////////////////////////// case ($ps_state == 'PARSE_XML_UPLOAD'): open_mysql(); // What is the path portion of the $filtmp filename? $_SESSION[ 'xmlfile' ] = $filtmp; if ($DEBUG) { echo "calling parse_xml<br>\n"; echo "parsing file: $filtmp<br>\n"; } // Set state to start over, in case we fail before parse_xml // returns. next_state( 'BEGIN' ); // parse the XML file, returning an array of contract headers, // and an array of arrays of detail lines. First-order indices of // headers and details arrays will be integers starting from 1. if (parse_xml( $filtmp, $campaigns, $headers, $details )) { next_state( 'VERIFY_CAMPAIGN' ); } else { message_log_append( $msg_log, "XML order parsing failed", MSG_LOG_ERROR ); unset( $_SESSION[ 'campaigns' ] ); unset( $_SESSION[ 'headers' ] ); unset( $_SESSION[ 'details' ] ); next_state( 'TRY_AGAIN' ); } break; ///////////////////////////////// // // Verify some basic integrity // checks on the data, and create // human-readable log messages for // problems that are identified. // ///////////////////////////////// case ($ps_state == 'RE-VERIFY_CAMPAIGN'): // unset all header row MSG_LOG values foreach (array_keys( $headers ) as $key) { unset( $headers[ $key ][ 'MSG_LOG' ] ); } // unset all detail line MSG_LOG values foreach (array_keys( $details ) as $dkey) { foreach (array_keys( $details[ $dkey ] ) as $ln) { unset( $details[ $dkey ][ $ln ][ 'MSG_LOG' ] ); } } // and fall through to VERIFY_CAMPAIGN: case ($ps_state == 'VERIFY_CAMPAIGN'): verify_campaign( $campaigns, $headers, $details ); next_state( 'BEGIN' ); // as precaution //echo "calling verify_all_contracts<br>"; if (verify_all_contracts( $campaigns, $headers, $details )) { //echo "vac succeeded<br>"; } else { //echo "vac failed<br>"; } next_state( ($ps_state == 'VERIFY_CAMPAIGN') ? 'DISPLAY_CAMPAIGN' : 'DISPLAY_CONTRACT' ); break; case ($ps_state == 'DISPLAY_CAMPAIGN'): unset( $cont_key ); // contract array key next_state( 'DISPLAY_CONTRACT' ); break; case ($ps_state == 'DISPLAY_CONTRACT'): //echo "$ps_state<br>"; if (is_null( $campaigns ) || is_null( $headers ) || is_null( $details )) { next_state( 'BEGIN' ); } else { if (!isset( $cont_key ) || is_null( $cont_key )) { $keys = array_keys( $headers ); $cont_key = $keys[0]; } // if if (is_null( $cont_key )) { next_state( 'BEGIN' ); } else { echo html_campaign( $campaigns ); echo nav_buttons( array_keys( $headers ), TRUE, $headers, $cont_key ); echo html_order( $headers[$cont_key], $cont_key ); echo html_detail( $details[$cont_key], $cont_key ); // set a safe next state. form_handler ought to override, unless error. next_state( 'BEGIN' ); $done = TRUE; } //if } // if break; case ($ps_state == 'DISPLAY_NEXT'): $keys = array_keys( $headers ); $cont_key = next_cont_key( $cont_key, $keys ); // we might someday use a better state than BEGIN here. A NULL // return from next_cont_key would mean that no contracts remain. next_state( is_null( $cont_key ) ? 'BEGIN' : 'DISPLAY_CONTRACT' ); break; case ($ps_state == 'DISPLAY_PREV'): $keys = array_keys( $headers ); $cont_key = prev_cont_key( $cont_key, $keys ); next_state( is_null( $cont_key ) ? 'BEGIN' : 'DISPLAY_CONTRACT' ); break; ///////////////////////////////// // // Push SQL file out to // the web client for download. // ///////////////////////////////// case ($ps_state == 'PUSH_SQL_FILE'): next_state( 'BEGIN' ); if (CLI) { echo "SQL output is in file $sql_file\n"; } else { header( 'Content-type: application/force-download' ); header( 'Content-Transfer-Encoding: Binary' ); header( 'Content-length: ' . filesize( $sql_file ) ); header( 'Content-disposition: attachment; filename="' . basename( $sql_file ) . '"' ); readfile( $sql_file ); unlink( $sql_file ); // delete } $done = TRUE; break; ///////////////////////////////// // // Connect to the SQL server and // move the data into the database. // ///////////////////////////////// case ($ps_state == 'IMPORT'): next_state( 'BEGIN' ); open_mysql(); insert_sql( $campaigns, $headers, $details ); echo message_log_format( $msg_log ); message_log_reset( $msg_log ); break; ///////////////////////////////// // // Delete a LineID from a contract // ///////////////////////////////// case ($ps_state == 'DELETE_LINE'): $lineID = $_SESSION[ 'LineID' ]; // XML LineID number //message_log_append( $msg_log, 'You requested to delete LineID ' . $lineID . ' from contract key ' . $cont_key ); // Deleting detail lines will change the header spot and value totals. // delete_detail_lineid is responsible for telling us how many spots // were deleted, and what their total value was. $spots = 0; $value = 0; $details[ $cont_key ] = delete_detail_lineid( $details[ $cont_key ], $lineID, $spots, $value ); // subtract deleted spots and value from header 'detail_*' totals $headers[ $cont_key ][ 'detail_spots' ] -= $spots; $v = $headers[ $cont_key ][ 'detail_cost' ]; $v = bcsub( $v, $value, 2 ); $headers[ $cont_key ][ 'detail_cost' ] = $v; // subtract deleted spots and value from header 'total_*' totals $headers[ $cont_key ][ 'total_spots' ] -= $spots; $v = $headers[ $cont_key ][ 'total_cost' ]; $v = bcsub( $v, $value, 2 ); $headers[ $cont_key ][ 'total_cost' ] = $v; // subtract deleted spots and value from campaign 'detail_*' totals $campaigns[0][ 'detail_spots' ] -= $spots; $v = $campaigns[0][ 'detail_cost' ]; $v = bcsub( $v, $value, 2 ); $campaigns[0][ 'detail_cost' ] = $v; // subtract deleted spots and value from campaign 'total_*' totals $campaigns[0][ 'total_spots' ] -= $spots; $v = $campaigns[0][ 'total_cost' ]; $v = bcsub( $v, $value, 2 ); $campaigns[0][ 'total_cost' ] = $v; // Now re-validate everything, and re-display this // specific contract. next_state( 'RE-VERIFY_CAMPAIGN' ); break; ///////////////////////////////// // // Delete a Network from a contract // ///////////////////////////////// case ($ps_state == 'DELETE_NETWORK'): next_state( 'BEGIN' ); $lineID = $_SESSION[ 'LineID' ]; // XML LineID number $network = NULL; foreach ($details[ $cont_key ] as $det) { if ($det[ 'LineID' ] === $lineID) { $network = $det[ 'Network' ]; //echo "<pre>"; var_dump( $det ); echo "</pre>"; break; } } next_state( 'RE-VERIFY_CAMPAIGN' ); if (is_null( $network)) { message_log_append( $msg_log, 'Invalid LineID: ' . $lineID . ' not found in contract key ' . $cont_key ); break; } // Deleting detail lines will change the header spot and value totals. // delete_detail_network is responsible for telling us how many spots // were deleted, and what their total value was. $spots = 0; $value = 0; $details[ $cont_key ] = delete_detail_network( $details[ $cont_key ], $network, $spots, $value ); // subtract deleted spots and value from header 'detail_*' totals $headers[ $cont_key ][ 'detail_spots' ] -= $spots; $v = $headers[ $cont_key ][ 'detail_cost' ]; $v = bcsub( $v, $value, 2 ); $headers[ $cont_key ][ 'detail_cost' ] = $v; // subtract deleted spots and value from header 'total_*' totals $headers[ $cont_key ][ 'total_spots' ] -= $spots; $v = $headers[ $cont_key ][ 'total_cost' ]; $v = bcsub( $v, $value, 2 ); $headers[ $cont_key ][ 'total_cost' ] = $v; // subtract deleted spots and value from campaign 'detail_*' totals $campaigns[0][ 'detail_spots' ] -= $spots; $v = $campaigns[0][ 'detail_cost' ]; $v = bcsub( $v, $value, 2 ); $campaigns[0][ 'detail_cost' ] = $v; // subtract deleted spots and value from campaign 'total_*' totals $campaigns[0][ 'total_spots' ] -= $spots; $v = $campaigns[0][ 'total_cost' ]; $v = bcsub( $v, $value, 2 ); $campaigns[0][ 'total_cost' ] = $v; message_log_append( $msg_log, 'Network ' . $network . ' deleted from contract ' . $cont_key ); // Now re-validate everything, and re-display this // specific contract. next_state( 'RE-VERIFY_CAMPAIGN' ); break; ///////////////////////////////// // // Delete an entire contract // ///////////////////////////////// case ($ps_state == 'DELETE_CONTRACT'): //message_log_append( $msg_log, 'You requested to delete contract ' . $cont_key ); // Unset the individual detail lines for this contract. $dkeys = array_keys( $details[ $cont_key ] ); foreach ($dkeys as $key) { unset( $details[ $cont_key ][ $key ] ); } // subtract contract spots and value from campaign 'detail_*' totals $spots = $headers[ $cont_key ][ 'detail_spots' ]; $value = $headers[ $cont_key ][ 'detail_cost' ]; $campaigns[0][ 'detail_spots' ] -= $spots; $v = $campaigns[0][ 'detail_cost' ]; $v = bcsub( $v, $value, 2 ); $campaigns[0][ 'detail_cost' ] = $v; // subtract contract spots and value from campaign 'total_*' totals $spots = $headers[ $cont_key ][ 'total_spots' ]; $value = $headers[ $cont_key ][ 'total_cost' ]; $campaigns[0][ 'total_spots' ] -= $spots; $v = $campaigns[0][ 'total_cost' ]; $v = bcsub( $v, $value, 2 ); $campaigns[0][ 'total_cost' ] = $v; // Set the contract header to show 0 spots, 0 value. $spots = 0; $value = 0; $headers[ $cont_key ][ 'detail_spots' ] = $spots; $headers[ $cont_key ][ 'detail_cost' ] = $value; $headers[ $cont_key ][ 'total_spots' ] = $spots; $headers[ $cont_key ][ 'total_cost' ] = $value; // Unset the 'versionN' fields of deleted contracts so that version // problems don't prevent the remaining contracts from being imported. $j = 0; $done = FALSE; while (!$done) { $fld = 'version' . (++$j); $done = !isset( $headers[ $cont_key ][ $fld ] ); unset( $headers[ $cont_key ][ $fld ] ); } // while // auto-advance to next contract $done = FALSE; $keys = array_keys( $headers ); $cont_key = next_cont_key( $cont_key, $keys ); // we might someday use a better state than BEGIN here. A NULL // return from next_cont_key would mean that no contracts remain. next_state( is_null( $cont_key ) ? 'BEGIN' : 'RE-VERIFY_CAMPAIGN' ); break; ///////////////////////////////// // // Dummy stub. // ///////////////////////////////// case ($ps_state == 'DEAD_END'): case ($ps_state == 'STOP'): echo $ps_state; next_state( 'BEGIN' ); $done = TRUE; break; ///////////////////////////////// // // Screen has error messages. // Force user to go back and retry. // ///////////////////////////////// case ($ps_state == 'TRY_AGAIN'): next_state( 'BEGIN' ); echo nav_buttons( NULL, FALSE ); // Cancel only $done = TRUE; break; ///////////////////////////////// // // State is unknown or invalid. // Go back to BEGIN state. // ///////////////////////////////// default: message_log_append( $msg_log, 'Invalid state label: ' . $ps_state, MSG_LOG_ERROR ); next_state( 'BEGIN' ); $done = TRUE; } // switch $ps_state = current_state(); // echo message_log_format( $msg_log ); echo message_log_table( $msg_log ); } while (!$done); $_SESSION[ 'campaigns' ] = $campaigns; $_SESSION[ 'headers' ] = $headers; $_SESSION[ 'details' ] = $details; $_SESSION[ 'cont_key' ] = $cont_key; } // process_state
function verify_one_contract( $campaign, &$header, &$detail ) // check one contract's $campaign, $header and $detail arrays // the header array is passed by reference because // we will add detail_spots and detail_cost fields to the // header. // the detail array is passed by reference because // detail_time_validation will add the Priority element to // each row, and MSG_LOG entries could be added. // return TRUE if no errors, else FALSE { //GLOBAL $DEBUG; $success = TRUE; $done = FALSE; $test_number = 0; // total_spots is the XML reference field in the contract header: $total_spots = $header['total_spots']; // total_cost is the XML reference field in the contract header: $total_cost = $header['total_cost']; message_log_reset( $h_msg_log ); // local $h_msg_log specific to this header // sum spots and cost across all the detail lines passed to us $det_spots = 0; $det_cost = 0; foreach ($detail as $det) { $det_spots += $det[ 'nOrdered' ]; // calculate values with binary precision arithmetic routines $v = bcmul( bcmul( $det[ 'nOrdered' ], $det[ 'UnitPrice' ], 2 ), '0.01', 2 ); $det_cost = bcadd( $det_cost, $v, 2 ); } // foreach // Add 'detail_spots' and 'detail_cost' fields to header array. // These fields will hold the total spots and costs as summed from // individual detail lines. These should agree with 'total_spots' // and 'total_cost' (which are parsed directly from the XML) but we // will check that (elsewhere). $header[ 'detail_spots' ] = $det_spots; $header[ 'detail_cost' ] = $det_cost; //echo "test number " . ($test_number + 1) . "\n"; while (!$done) { switch (++$test_number) { case 1: break; // per 11-06-2012 email from Carolyn Boyer to Dustin Carlson, // we no longer care about header version numbers. case 2: break; // all contract headers must be version 1 //if ($DEBUG) //echo "<pre>VOC: calling header_version_check\n</pre>\n"; if (header_version_check( $header, $h_msg_log )) ; else { $success = FALSE; } // $done = (!$success); // don't continue if this fails break; case 3: // $header totals must match sum of detail if ($header[ 'detail_spots' ] != $header[ 'total_spots' ]) { $msg = "Total number of spots doesn't match"; message_log_append( $h_msg_log, $msg, MSG_LOG_ERROR ); //if ($DEBUG) //echo "<pre>$msg\n</pre>\n"; } if ($header[ 'detail_cost' ] != $header[ 'total_cost' ]) { $msg = "Total value of spots doesn't match"; message_log_append( $h_msg_log, $msg, MSG_LOG_ERROR ); //if ($DEBUG) //echo "<pre>$msg\n</pre>\n"; } break; case 4: break; case 5: // validate all detail lines' StartDate/TimeOn against EndDate/TimeOff // To figure the priority, we must know the customer name // for this contract. The customer record is stored in // the header array. Pass the Name field as a parameter. $cust_name = $campaign[ 'Customer Name' ]; if (!detail_time_validation( $detail, $cust_name )) { //echo "Received FALSE from DTV<br>"; $success = FALSE; // make the contract flag as red message_log_append( $h_msg_log, '', MSG_LOG_ERROR ); } // if break; // Let's look at the networks on these orders. // All networks on the order have to map to // networks available at the sitename/syscode. case 6: if (!detail_network_validation( $h_msg_log, $detail, $header[ 'SiteName' ], $campaign[ 'TELAMERICA' ] )) { $success = FALSE; } // if break; // total number of spots and weeks on each order must be in range case 7: $weeks = $header[ 'week_count' ]; $spots = $header[ 'total_spots' ]; //if ($DEBUG) //echo "week_count check, header ($weeks)\n"; if ($weeks > 0 && $weeks <= MAX_WEEKS) ; else { $success = FALSE; $msg = "Number of weeks ($weeks) is out of range"; message_log_append( $h_msg_log, $msg, MSG_LOG_ERROR ); } // if //if ($DEBUG) //echo "spot count check, header ($spots)\n"; if ($spots > 0 && $spots <= MAX_SPOTS) ; else if ($spots == 0) { $msg = "ZERO spots on this contract"; message_log_append( $h_msg_log, $msg, MSG_LOG_WARNING ); } else { $success = FALSE; $msg = "Number of spots ($spots) is out of range"; message_log_append( $h_msg_log, $msg, MSG_LOG_ERROR ); } // if break; case 8: break; case 9: break; case 10: break; default: $done = TRUE; } // switch } // while if (isset( $header[ 'MSG_LOG' ] )) { die( 'verify_one_contract: header already has a message log' ); } else { //if ($DEBUG) //echo "<pre>Setting header[MSG_LOG]\n</pre>\n"; $header[ 'MSG_LOG' ] = $h_msg_log; } //if ($DEBUG) { //echo "Returning " . ( $success ? 'TRUE' : 'FALSE' ) . " from VOC<br>"; //echo message_log_format( $header[ 'MSG_LOG' ] ); } return( $success ); } // verify_one_contract