#!/usr/local/bin/perl # # -------------------------------------------------------------------------------------- # This program reads an EOBS/T data file and transforms the data into multiple # output files ready for the DB2 LOAD utility. Each output file corresponds to # a DB2 table in the EOB database. # # Modification Log # # 02/03/2008 - Add a timestamp value to be used for a common creation # timestamp on all records # 03/03/2012 Create a Massachusetts specific copy to process thier # file because it has a slightly different format. # # --------------------------------------------------------------------------------------- use strict; sub trim($); sub trimToUndef($); sub toYMD($); sub launderMoney($); sub trimFinalSep($); sub pre_process_main($$); # ~ Variables ---------------------------------------------------- my $input_file_name = ""; my $record; my $input_record_count=0; my $eob_count=0; my $claim_count=0; my $message_count=0; my $provider_count=0; my $detail_count=0; my $MESSAGE_SEPARATOR = "!=!"; my $sep_length = length($MESSAGE_SEPARATOR); my $MESSAGE_SEPARATOR2 = "=&="; my $start_time; # Record layout for the EOB_MESSAGE table my $eob_message_template = "A3 A8 A9 A4 A1 A4 A1 A50 A3 A1 A14 A26 A1 A*"; # FINANCIAL_LEG # STATEMENT_DATE # FDSN # MESSAGE_GROUP # MESSAGE_TYPE # MESSAGE_NUMBER # PATIENT_NAME NULL INDICATOR # PATIENT_NAME # PATIENT_SEQUENCE # ICN NULL INDICTOR # ICN # MESSAGE NULL INDICATOR # MESSAGE # global holding variables for the EOB being processed $_=`date +%Y-%m-%d-%H.%M.%S`; #2008-02-03-13.17.04 chomp; my $db2_insert_timestamp="$_" . ".000000"; # DB2 format to be used for creation_timestamp #DRM my $db2_insert_timestamp="2008-06-04-01.01.01.000000"; # DB2 format to be used for creation_timestamp my $statement_date = ""; my $fdsn = ""; my $financial_leg = ""; # The financial leg this EOB is within #my %message_patient = (); # hash of the patient messages on an EOB #my %message_claim = (); # hash of the claim-level messages on an EOB my @messages = (); # ordered array of the claim and patient-level # message-lines on an EOB (ordered on message sequence) #my $message_document = ''; # concatenated document-level messages my %provider_hash; my @claim_recs; # --------------------------------------------------------------------- # ~ Main Procedure ---------------------------------------------------- # --------------------------------------------------------------------- # use as a library or directly from the command line if (@ARGV) { my ($input_file, $output_dir) = @ARGV; &pre_process_main($input_file, $output_dir); } 1; # ---------------------------------------------------------------------# # ~ Subroutines # # ---------------------------------------------------------------------# # ------------------------------------------- # Main - run the primary logic of this script # ------------------------------------------- sub pre_process_main($$) { # start the stopwatch $start_time = time; print "DB2 Insert Date db2_insert_timestamp=$db2_insert_timestamp \n\n"; # Initialize counters in case we are being called as a library $input_record_count=0; $eob_count=0; $claim_count=0; $message_count=0; $provider_count=0; $detail_count=0; # retrieve command line arguments my ($input_file, $output_dir) = @_; if (!$input_file || !$output_dir ) { print "Usage: \n\n"; exit(1); } print "Input Parms are <$input_file> <$output_dir>\n\n"; # extract the file name without the path if ($input_file=~m#([^/]+)$#) { $input_file_name =$1; } # Open Files open (INPUT_FILE, "<$input_file" ) || die "couldn't open input file ($input_file)"; open (EOB_FILE, ">${output_dir}/$input_file_name.eob.asc") || die "Couldn't open EOB_File"; open (EOB_MESSAGE_FILE, ">${output_dir}/$input_file_name.eob-message.asc") || die "Couldn't open EOB_Message_File"; open (EOB_CLAIM_FILE, ">${output_dir}/$input_file_name.eob-claim.asc") || die "Couldn't open EOB_Claim_File"; open (EOB_DETAIL_SERVICE_FILE, ">${output_dir}/$input_file_name.eob-detail-service.asc") || die "Couldn't open EOB_Detail_Service_File"; print "--------------------------\n"; print "Processing starting for input file ($input_file)\n"; # loop over all the records in the input file while ( $record = ) { chomp($record); $input_record_count++; for (substr($record, 0, 1) ) { if (/H/) { &H_header($record); } elsif (/C/) { &C_claim($record); } elsif (/P/) { &P_provider($record); } elsif (/D/) { &D_detail($record); } elsif (/M/) { &M_message($record); } } }# end-while # flush out the last records flush_eob(); # close all open files close(INPUT_FILE); close(EOB_FILE); close(EOB_MESSAGE_FILE); close(EOB_CLAIM_FILE); close(EOB_DETAIL_SERVICE_FILE); # print audit trail information print "Total input record count: $input_record_count\n"; print "Total EOB count: $eob_count\n"; print "Total claim count: $claim_count\n"; print "Total detail count: $detail_count\n"; print "Total message count: $message_count\n"; print "Total provider count: $provider_count\n"; print "Processing complete for input file ($input_file)\n"; print "Elapsed time " . (int(time - $start_time)) . " seconds\n"; print "--------------------------\n"; } # --------------------------- # Header record # --------------------------- sub H_header { $eob_count++; # if we aren't on the first record, write out the remaining stuff # for the last EOB we were on if ($input_record_count != 1) { flush_eob(); } # unpack the record into local fields my( $record_type, $fdsn_local, $statement_date_local, $contract_number, $group_number, $plan_inquiry_name, $inquiry_address_line_1, $inquiry_address_line_2, $inquiry_address_line_3, $inquiry_address_line_4, $antifraud_address_line_1, $antifraud_address_line_2, $antifraud_address_line_3, $antifraud_address_line_4, $antifraud_address_line_5, $subscriber_name, $subscriber_address_line_1, $subscriber_address_line_2, $subscriber_address_line_3, $plan_return_address, $plan_return_address_line_1, $plan_return_address_line_2, $plan_return_address_line_3, $plan_return_address_line_4, $your_responsibility, $tag_line_1_name, $tag_line_1_horizonal_coordinates, $tag_line_1_vertical_coordinates, $tag_line_2_name, $tag_line_2_horizonal_coordinates, $tag_line_2_vertical_coordinates, $tag_line_3_name, $tag_line_3_horizonal_coordinates, $tag_line_3_vertical_coordinates, $tag_line_4_name, $tag_line_4_horizonal_coordinates, $tag_line_4_vertical_coordinates, $tag_line_5_name, $tag_line_5_horizonal_coordinates, $tag_line_5_vertical_coordinates, $demand_feed_insert_1, $demand_feed_insert_2, $demand_feed_insert_3, $plan_code, $servicing_code, $total_amount_charged, $total_allowed_amount, $total_other_insurance, $total_deductible, $total_copay, $total_coinsurance, $total_other_amounts_not_covered, $total_amount_paid, $divert_code, $type_of_eob_record, $check_number, $zip_code, $postnet_code, $printable_subscriber_number, $nps_subscriber_number, $financial_leg_local, @rest) = unpack ("a1 a9 a8 a16 a14 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a40 a30 a8 a5 a5 a8 a5 a5 a8 a5 a5 a8 a5 a5 a8 a5 a5 a2 a2 a2 a3 a3 a12 a12 a12 a12 a12 a12 a12 a12 a1 a1 a9 a10 a14 a16 a13 a3 a*", "@_"); # debug stuff # print "record_type(" . $record_type . ")\n"; # print "fdsn_local(" . $fdsn_local . ")\n"; # print "statement_date_local(" . $statement_date_local . ")\n"; # print "contract_number(" . $contract_number . ")\n"; # print "group_number(" . $group_number . ")\n"; # print "plan_inquiry_name(" . $plan_inquiry_name . ")\n"; # print "inquiry_address_line_1(" . $inquiry_address_line_1 . ")\n"; # print "inquiry_address_line_2(" . $inquiry_address_line_2 . ")\n"; # print "inquiry_address_line_3(" . $inquiry_address_line_3 . ")\n"; # print "inquiry_address_line_4(" . $inquiry_address_line_4 . ")\n"; # print "antifraud_address_line_1(" . $antifraud_address_line_1 . ")\n"; # print "antifraud_address_line_2(" . $antifraud_address_line_2 . ")\n"; # print "antifraud_address_line_3(" . $antifraud_address_line_3 . ")\n"; # print "antifraud_address_line_4(" . $antifraud_address_line_4 . ")\n"; # print "antifraud_address_line_5(" . $antifraud_address_line_5 . ")\n"; # print "subscriber_name(" . $subscriber_name . ")\n"; # print "subscriber_address_line_1(" . $subscriber_address_line_1 . ")\n"; # print "subscriber_address_line_2(" . $subscriber_address_line_2 . ")\n"; # print "subscriber_address_line_3(" . $subscriber_address_line_3 . ")\n"; # print "plan_return_address(" . $plan_return_address . ")\n"; # print "plan_return_address_line_1(" . $plan_return_address_line_1 . ")\n"; # print "plan_return_address_line_2(" . $plan_return_address_line_2 . ")\n"; # print "plan_return_address_line_3(" . $plan_return_address_line_3 . ")\n"; # print "plan_return_address_line_4(" . $plan_return_address_line_4 . ")\n"; # print "your_responsibility(" . $your_responsibility . ")\n"; # print "tag_line_1_name(" . $tag_line_1_name . ")\n"; # print "tag_line_1_horizonal_coordinates(" . $tag_line_1_horizonal_coordinates . ")\n"; # print "tag_line_1_vertical_coordinates(" . $tag_line_1_vertical_coordinates . ")\n"; # print "tag_line_2_name(" . $tag_line_2_name . ")\n"; # print "tag_line_2_horizonal_coordinates(" . $tag_line_2_horizonal_coordinates . ")\n"; # print "tag_line_2_vertical_coordinates(" . $tag_line_2_vertical_coordinates . ")\n"; # print "tag_line_3_name(" . $tag_line_3_name . ")\n"; # print "tag_line_3_horizonal_coordinates(" . $tag_line_3_horizonal_coordinates . ")\n"; # print "tag_line_3_vertical_coordinates(" . $tag_line_3_vertical_coordinates . ")\n"; # print "tag_line_4_name(" . $tag_line_4_name . ")\n"; # print "tag_line_4_horizonal_coordinates(" . $tag_line_4_horizonal_coordinates . ")\n"; # print "tag_line_4_vertical_coordinates(" . $tag_line_4_vertical_coordinates . ")\n"; # print "tag_line_5_name(" . $tag_line_5_name . ")\n"; # print "tag_line_5_horizonal_coordinates(" . $tag_line_5_horizonal_coordinates . ")\n"; # print "tag_line_5_vertical_coordinates(" . $tag_line_5_vertical_coordinates . ")\n"; # print "demand_feed_insert_1(" . $demand_feed_insert_1 . ")\n"; # print "demand_feed_insert_2(" . $demand_feed_insert_2 . ")\n"; # print "demand_feed_insert_3(" . $demand_feed_insert_3 . ")\n"; # print "plan_code(" . $plan_code . ")\n"; # print "servicing_code(" . $servicing_code . ")\n"; # print "total_amount_charged(" . $total_amount_charged . ")\n"; # print "total_allowed_amount(" . $total_allowed_amount . ")\n"; # print "total_other_insurance(" . $total_other_insurance . ")\n"; # print "total_deductible(" . $total_deductible . ")\n"; # print "total_copay(" . $total_copay . ")\n"; # print "total_coinsurance(" . $total_coinsurance . ")\n"; # print "total_other_amounts_not_covered(" . $total_other_amounts_not_covered . ")\n"; # print "total_amount_paid(" . $total_amount_paid . ")\n"; # print "divert_code(" . $divert_code . ")\n"; # print "type_of_eob_record(" . $type_of_eob_record . ")\n"; # print "check_number(" . $check_number . ")\n"; # print "zip_code(" . $zip_code . ")\n"; # print "postnet_code(" . $postnet_code . ")\n"; # print "printable_subscriber_number(" . $printable_subscriber_number . ")\n"; # print "nps_subscriber_number(" . $nps_subscriber_number . ")\n"; # print "financial_leg_local(" . $financial_leg_local . ")\n"; # print "rest(" . @rest . ")\n"; # print "--------------------\n"; # Fix for pre financial_leg file versions, force to Central Financial (004) if ($financial_leg_local eq " ") { $financial_leg_local = "004"; } # load up the global variables for this EOB $financial_leg=$financial_leg_local; $fdsn=$fdsn_local; $statement_date=$statement_date_local; # Write the EOB record print EOB_FILE pack("A3 A8 A9 A16 A14 A1 A8 A5 A5 A1 A8 A5 A5 A1 A8 A5 A5 A1 A8 A5 A5 A1 A8 A5 A5 A2 A2 A2 A3 A3 A12 A12 A12 A12 A12 A12 A12 A12 A1 A1 A9 A1 A10 A1 A14 A16 A13 A26 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A40 A1 A30 A1 A1 A1", $financial_leg_local, toYMD($statement_date_local), $fdsn_local, $contract_number, $group_number, defined(trimToUndef($tag_line_1_name)) ? "N" : "Y", $tag_line_1_name, $tag_line_1_horizonal_coordinates, $tag_line_1_vertical_coordinates, defined(trimToUndef($tag_line_2_name)) ? "N" : "Y", $tag_line_2_name, $tag_line_2_horizonal_coordinates, $tag_line_2_vertical_coordinates, defined(trimToUndef($tag_line_3_name)) ? "N" : "Y", $tag_line_3_name, $tag_line_3_horizonal_coordinates, $tag_line_3_vertical_coordinates, defined(trimToUndef($tag_line_4_name)) ? "N" : "Y", $tag_line_4_name, $tag_line_4_horizonal_coordinates, $tag_line_4_vertical_coordinates, defined(trimToUndef($tag_line_5_name)) ? "N" : "Y", $tag_line_5_name, $tag_line_5_horizonal_coordinates, $tag_line_5_vertical_coordinates, $demand_feed_insert_1, $demand_feed_insert_2, $demand_feed_insert_3, $plan_code, $servicing_code, ## WTC - Mass file doesnt contain formatting, so removing the call to eat the dollar sign and comma. # launderMoney($total_amount_charged), # launderMoney($total_allowed_amount), # launderMoney($total_other_insurance), # launderMoney($total_deductible), # launderMoney($total_copay), # launderMoney($total_coinsurance), # launderMoney($total_other_amounts_not_covered), # launderMoney($total_amount_paid), $total_amount_charged, $total_allowed_amount, $total_other_insurance, $total_deductible, $total_copay, $total_coinsurance, $total_other_amounts_not_covered, $total_amount_paid, ## $divert_code, $type_of_eob_record, $check_number, defined(trimToUndef($zip_code)) ? "N" : "Y", $zip_code, defined(trimToUndef($postnet_code)) ? "N" : "Y", $postnet_code, $printable_subscriber_number, $nps_subscriber_number, $db2_insert_timestamp, defined(trimToUndef($plan_inquiry_name)) ? "N" : "Y", $plan_inquiry_name, defined(trimToUndef($inquiry_address_line_1)) ? "N" : "Y", $inquiry_address_line_1, defined(trimToUndef($inquiry_address_line_2)) ? "N" : "Y", $inquiry_address_line_2, defined(trimToUndef($inquiry_address_line_3)) ? "N" : "Y", $inquiry_address_line_3, defined(trimToUndef($inquiry_address_line_4)) ? "N" : "Y", $inquiry_address_line_4, defined(trimToUndef($antifraud_address_line_1)) ? "N" : "Y", $antifraud_address_line_1, defined(trimToUndef($antifraud_address_line_2)) ? "N" : "Y", $antifraud_address_line_2, defined(trimToUndef($antifraud_address_line_3)) ? "N" : "Y", $antifraud_address_line_3, defined(trimToUndef($antifraud_address_line_4)) ? "N" : "Y", $antifraud_address_line_4, defined(trimToUndef($antifraud_address_line_5)) ? "N" : "Y", $antifraud_address_line_5, defined(trimToUndef($subscriber_name)) ? "N" : "Y", $subscriber_name, defined(trimToUndef($subscriber_address_line_1)) ? "N" : "Y", $subscriber_address_line_1, defined(trimToUndef($subscriber_address_line_2)) ? "N" : "Y", $subscriber_address_line_2, defined(trimToUndef($subscriber_address_line_3)) ? "N" : "Y", $subscriber_address_line_3, defined(trimToUndef($plan_return_address)) ? "N" : "Y", $plan_return_address, defined(trimToUndef($plan_return_address_line_1)) ? "N" : "Y", $plan_return_address_line_1, defined(trimToUndef($plan_return_address_line_2)) ? "N" : "Y", $plan_return_address_line_2, defined(trimToUndef($plan_return_address_line_3)) ? "N" : "Y", $plan_return_address_line_3, defined(trimToUndef($plan_return_address_line_4)) ? "N" : "Y", $plan_return_address_line_4, defined(trimToUndef($your_responsibility)) ? "N" : "Y", $your_responsibility, "Y", "Y", "Y"); print EOB_FILE "\n"; } # -------------------------------------------- # Provider record # -------------------------------------------- sub P_provider { $provider_count++; # parse the input record into different fields my ( $record_type, $fdsn, $statement_date, $contract_number, $patient_name, $icn, $provider_number, $servicing_provider_number, $provider_name, $servicing_provider_name, $sequence_number, $activation_plan) = unpack("a1 a9 a8 a16 a50 a14 a14 a14 a50 a50 a3 a3", "@_"); # debug stuff #print "record_type(" . $record_type . ")\n"; #print "fdsn(" . $fdsn . ")\n"; #print "statement_date(" . $statement_date . ")\n"; #print "contract_number(" . $contract_number . ")\n"; #print "patient_name(" . $patient_name . ")\n"; #print "icn(" . $icn . ")\n"; #print "provider_number($provider_number)\n"; #print "servicing_provider_number($servicing_provider_number)\n"; #print "provider_name($provider_name)\n"; #print "servicing_provider_name($servicing_provider_name)\n"; #print "sequence_number(" . $sequence_number . ")\n"; #print "activation_plan(" . $activation_plan . ")\n"; #print "----------------------------\n"; # store the provider details in an array # (in a hash) for # later insertion into the claim record $provider_hash{"$icn"} = [ $provider_number, $servicing_provider_number, $provider_name, $servicing_provider_name ]; # alternate method of storing array reference in the hash # my(@provider_rec) = ( # $provider_number, # $servicing_provider_number, # $provider_name, # $servicing_provider_name ); # $provider_hash{"$icn"} = \@provider_rec; } # ----------------------------------------------- # Claim record # ----------------------------------------------- sub C_claim { $claim_count++; # store the claim record into an array for later use push(@claim_recs, "@_"); } # ----------------------------------------------- # Process Claim record # ----------------------------------------------- sub C_write_claims { # loop through all claim records in the array and write # each one to the output file foreach (@claim_recs) { # parse the input record into different fields my ( $record_type, $fdsn, $statement_date, $contract_number, $patient_name, $icn, $total_amount_charged, $total_allowed_amount, $total_other_insurance, $total_deductible, $total_copay, $total_coinsurance, $total_other_amounts_not_covered, $total_amount_paid, $notice_line, ## WTC - Relocated reason_code field to end of record, this displacement is now filler ## $reason_code, $filler, $sequence_number, $pat_sequence_number, $activation_plan, $total_discount, $total_your_responsibility, $reason_code) = unpack("a1 a9 a8 a16 a50 a14 a12 a12 a12 a12 a12 a12 a12 a12 a50 a4 a3 a3 a3 a12 a12 a12", "$_"); # = unpack("a1 a9 a8 a16 a50 a14 a12 a12 a12 a12 a12 a12 a12 a12 a50 a4 a3 a3 a3", "$_"); # debug stuff ## WTC - Added new fields to the debug section. #print "record_type(" . $record_type . ")\n"; #print "fdsn(" . $fdsn . ")\n"; #print "statement_date(" . $statement_date . ")\n"; #print "contract_number(" . $contract_number . ")\n"; #print "patient_name(" . $patient_name . ")\n"; #print "icn(" . $icn . ")\n"; #print "total_amount_charged(" . $total_amount_charged . ")\n"; #print "total_allowed_amount(" . $total_allowed_amount . ")\n"; #print "total_other_insurance(" . $total_other_insurance . ")\n"; #print "total_deductible(" . $total_deductible . ")\n"; #print "total_copay(" . $total_copay . ")\n"; #print "total_coinsurance(" . $total_coinsurance . ")\n"; #print "total_other_amounts_not_covered(" . $total_other_amounts_not_covered . ")\n"; #print "total_amount_paid(" . $total_amount_paid . ")\n"; #print "notice_line(" . $notice_line . ")\n"; #print "filler(" . $filler . ")\n"; #print "sequence_number(" . $sequence_number . ")\n"; #print "pat_sequence_number(" . $pat_sequence_number . ")\n"; #print "activation_plan(" . $activation_plan . ")\n"; #print "total_discount(" . $total_discount . ")\n"; #print "total_your_responsibility(" . $total_your_responsibility . ")\n"; #print "reason_code(" . $reason_code . ")\n"; #print "----------------------------\n"; # retrieve the provider values from the hash # hint. it's a reference to an array my($provider_rec) = $provider_hash{"$icn"}; # debug stuff # print "($provider_rec->[0])\n"; # print "($provider_rec->[1])\n"; # print "($provider_rec->[2])\n"; # print "($provider_rec->[3])\n"; # write the claim record print EOB_CLAIM_FILE pack("A3 A8 A9 A14 A3 A3 A12 A12 A12 A12 A12 A12 A12 A12 A4 A14 A1 A14 A1 A50 A1 A50 A1 A50 A3 A1 A50 A26 A16", $financial_leg, toYMD($statement_date), $fdsn, $icn, $sequence_number, $activation_plan, ## WTC - Mass file doesnt contain formatting, so removing the call to eat the dollar sign and comma. # launderMoney($total_amount_charged), # launderMoney($total_allowed_amount), # launderMoney($total_other_insurance), # launderMoney($total_deductible), # launderMoney($total_copay), # launderMoney($total_coinsurance), # launderMoney($total_other_amounts_not_covered), # launderMoney($total_amount_paid), $total_amount_charged, $total_allowed_amount, $total_other_insurance, $total_deductible, $total_copay, $total_coinsurance, $total_other_amounts_not_covered, $total_amount_paid, ## $reason_code, $provider_rec->[0], # provider number defined(trimToUndef($provider_rec->[1])) ? "N" : "Y", $provider_rec->[1], # servicing provider number defined(trimToUndef($provider_rec->[2])) ? "N" : "Y", $provider_rec->[2], # provider name defined(trimToUndef($provider_rec->[3])) ? "N" : "Y", $provider_rec->[3], # servicing provider name defined(trimToUndef($patient_name)) ? "N" : "Y", $patient_name, $pat_sequence_number, defined(trimToUndef($notice_line)) ? "N" : "Y", $notice_line, $db2_insert_timestamp, $contract_number); print EOB_CLAIM_FILE "\n"; }# end-foreach } # ------------------------------------------- # Detail Service record # ------------------------------------------- sub D_detail { $detail_count++; # parse the input record into different individual fields my ($record_type, $fdsn, $statement_date, $contract_number, $icn, $provider_number, $servicing_provider_number, $dates_of_service_from, $dates_of_service_to, ## WTC - Relocated description_of_services field to end of record, this displacement is now filler ## $description_of_services, $filler1, $amount_charged, $allowed_amount, $other_insurance, $deductible, $copay, $coinsurance, $other_amounts_not_covered, $amount_paid, ## WTC - Relocated rsn_code field to end of record, this displacement is now filler ## $rsn_code, $filler2, $exclude_from_totals, $provider_name, $sequence_number, $activation_plan, $discount, $your_responsibility, $description_of_services, $rsn_code) = unpack("a1 a9 a8 a16 a14 a14 a14 a8 a8 a21 a12 a12 a12 a12 a12 a12 a12 a12 a4 a1 a32 a4 a3 a12 a12 a60 a12", "@_"); # = unpack("a1 a9 a8 a16 a14 a14 a14 a8 a8 a21 a12 a12 a12 a12 a12 a12 a12 a12 a4 a1 a32 a4 a3", "@_"); # debug stuff ## WTC - Added new fields to the debug section. # print "record_type(" . $record_type . ")\n"; # print "fdsn(" . $fdsn . ")\n"; # print "statement_date(" . $statement_date . ")\n"; # print "contract_number(" . $contract_number . ")\n"; # print "icn(" . $icn . ")\n"; # print "provider_number(" . $provider_number . ")\n"; # print "servicing_provider_number(" . $servicing_provider_number . ")\n"; # print "dates_of_service_from(" . $dates_of_service_from . ")\n"; # print "dates_of_service_to(" . $dates_of_service_to . ")\n"; # print "filler1(" . $filler1 . ")\n"; # print "amount_charged(" . $amount_charged . ")\n"; # print "allowed_amount(" . $allowed_amount . ")\n"; # print "other_insurance(" . $other_insurance . ")\n"; # print "deductible(" . $deductible . ")\n"; # print "copay(" . $copay . ")\n"; # print "coinsurance(" . $coinsurance . ")\n"; # print "other_amounts_not_covered(" . $other_amounts_not_covered . ")\n"; # print "amount_paid(" . $amount_paid . ")\n"; # print "filler2(" . $filler2 . ")\n"; # print "exclude_from_totals(" . $exclude_from_totals . ")\n"; # print "provider_name(" . $provider_name . ")\n"; # print "sequence_number(" . $sequence_number . ")\n"; # print "activation_plan(" . $activation_plan . ")\n"; # print "discount(" . $discount . ")\n"; # print "your_responsibility(" . $your_responsibility . ")\n"; # print "description_of_services(" . $description_of_services . ")\n"; # print "rsn_code(" . $rsn_code . ")\n"; # print "--------------------------\n"; # write the detail service record print EOB_DETAIL_SERVICE_FILE pack("A3 A8 A9 A14 A4 A8 A8 A12 A12 A12 A12 A12 A12 A12 A12 A4 A1 A21 A26", $financial_leg, toYMD($statement_date), $fdsn, $icn, $sequence_number, toYMD($dates_of_service_from), toYMD($dates_of_service_to), ## WTC - Mass file doesnt contain formatting, so removing the call to eat the dollar sign and comma. # launderMoney($amount_charged), # launderMoney($allowed_amount), # launderMoney($other_insurance), # launderMoney($deductible), # launderMoney($copay), # launderMoney($coinsurance), # launderMoney($other_amounts_not_covered), # launderMoney($amount_paid), $amount_charged, $allowed_amount, $other_insurance, $deductible, $copay, $coinsurance, $other_amounts_not_covered, $amount_paid, ## $rsn_code, $exclude_from_totals, $description_of_services, $db2_insert_timestamp); print EOB_DETAIL_SERVICE_FILE "\n"; } # ----------------------------------------------- # Write the Message record(s) # One for each message_group # ----------------------------------------------- sub M_write_messages { if (@messages) { foreach my $message_rec (@messages) { print EOB_MESSAGE_FILE pack("$eob_message_template", $financial_leg, toYMD($statement_date), $fdsn, $message_rec->[0], # message_group $message_rec->[1], # message_type $message_rec->[2], # message_number defined(trimToUndef($message_rec->[3])) ? "N" : "Y", $message_rec->[3], # patient_name $message_rec->[4], # patient sequence number defined(trimToUndef($message_rec->[5])) ? "N" : "Y", $message_rec->[5], # icn $db2_insert_timestamp, defined(trimToUndef(trimFinalSep($message_rec->[6]))) ? "N" : "Y", trimFinalSep($message_rec->[6]) #message ); print EOB_MESSAGE_FILE "\n"; } # end-foreach } # end-if } # ----------------------------------------------- # Message record # Here we combine all individual message records for # the same message group. # ----------------------------------------------- sub M_message { $message_count++; # parse the input record into the individual message record fields my( $record_type, $fdsn, $statement_date, $contract_number, $patient_name, $icn, $message_sequence, $message, $patient_sequence_number, $activation_plan, $message_type, $message_number, $message_group) = unpack ("A1 A9 A8 A16 A50 A14 A4 A113 A3 A3 A1 A4 A4", "@_"); # ensure the message data doesn't contain the logical record separator if ($message =~s/$MESSAGE_SEPARATOR/$MESSAGE_SEPARATOR2/g){ print "Separator ($MESSAGE_SEPARATOR) found in message record (@_), replacing with ($MESSAGE_SEPARATOR2)\n"; } # concat the individual records together and save # for the final flush. if (!(@messages) || ($messages[-1]->[0] ne "$message_group")) { push(@messages,[$message_group, $message_type, $message_number, $patient_name, $patient_sequence_number, $icn, trim($message) . $MESSAGE_SEPARATOR]); } else { $messages[-1]->[6] .= (trim($message). $MESSAGE_SEPARATOR); } } # ------------------------------------------------ # Perform final processing for the logical EOB # ------------------------------------------------ sub flush_eob { # write the claim records C_write_claims(); # write the message records M_write_messages(); # Reinitialize the global variables @claim_recs = (); %provider_hash = (); @messages = (); } # -------------------------------------------------------- # Remove whitespace from the end of the string only # -------------------------------------------------------- sub trim($) { my $string = shift; ## $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } # -------------------------------------------------------- # Remove whitespace from the end of the string only # return undef if blank string # -------------------------------------------------------- sub trimToUndef($) { my $string = shift; $string =~ s/\s+$//; return ($string eq '') ? undef : $string; } # -------------------------------------------------------- # Transform a MMDDYYYY date string into a YYYYMMDD date string # -------------------------------------------------------- sub toYMD($) { my $mdy = shift; return substr($mdy, 4, 4) . substr($mdy, 0, 4); } # -------------------------------------------------------- # Remove unwanted characters from monetary amount fields # ensure length is always 12 # -------------------------------------------------------- sub launderMoney($) { my $amt = shift; $amt=~s/,|\$//g; return sprintf "%12s", $amt; } # -------------------------------------------------------- # Trim the final trailing separator from the message # -------------------------------------------------------- sub trimFinalSep($) { my $msg = shift; return substr($msg, 0, length($msg) - $sep_length); } #### #!/usr/local/bin/perl # # -------------------------------------------------------------------------------------- # Macro script to run the EOB daily scripts upon # notification that a new file(s) has arrived # # RESTART NOTE: If this script stops abnormally all committed records and *.asc files must # be deleted before the job is rerun. # # Modification Log # # 02/03/2008 - Add a timestamp value to be used for a common creation # timestamp on all records # 04/11/2008 - Update to check for an empty file # 07/09/2009 - remove set integrity # 03/03/2013 - Cloned to create Massachusetts specific version. # # --------------------------------------------------------------------------------------- use strict; use POSIX qw(strftime); # ~ Variables ---------------------------------------------------- my $base_dir = "/nmas_eob"; #my $data_dir = $base_dir."/data"; my $data_dir = $base_dir."/data_mass"; my $script_dir = $base_dir."/scripts"; my %properties = readproperties("$script_dir/nmase.properties"); my $db2_user = $properties{db2_user}; my $db2_password = $properties{db2_password}; my $db2_db = $properties{db2_db}; my $files_to_process = $properties{files_to_process}; my $files_processed = 0; #my $db2_load_mode = "REPLACE"; my $db2_load_mode = "INSERT"; my $rc = ""; my $running_marker_file = "$script_dir/daily_batch_running.txt"; my $load_eob_message = " load client FROM %s.eob-message.asc OF ASC modified BY striptblanks dateformat=\"YYYYMMDD\" timestampformat=\"YYYY-MM-DD-HH.MM.SS.UUUUUU\" method l (1 3,4 11,12 20,21 24,25 25,26 29,31 80,81 83,85 98,99 124,126 15000) NULL indicators (0,0,0,0,0,0,30,0,84,0,125) $db2_load_mode INTO NMASE.EOB_MESSAGE statistics no nonrecoverable without prompting; "; my $load_eob_claim = " load client FROM %s.eob-claim.asc OF ASC modified BY striptblanks dateformat=\"YYYYMMDD\" timestampformat=\"YYYY-MM-DD-HH.MM.SS.UUUUUU\" method l (1 3,4 11,12 20,21 34,35 37,38 40,41 52,53 64,65 76,77 88,89 100, 101 112,113 124,125 136,137 140,141 154,156 169,171 220,222 271, 273 322,323 325,327 376,377 402,403 418) NULL indicators (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,155,170,221,272,0,326,0,0) $db2_load_mode INTO NMASE.EOB_CLAIM statistics no nonrecoverable without prompting; "; my $load_eob_detail_service = " load client FROM %s.eob-detail-service.asc OF ASC modified BY striptblanks dateformat=\"YYYYMMDD\" timestampformat=\"YYYY-MM-DD-HH.MM.SS.UUUUUU\" method l (1 3,4 11,12 20,21 34,35 38,39 46,47 54,55 66,67 78,79 90, 91 102,103 114,115 126,127 138,139 150,151 154,155 155,156 176,177 202) $db2_load_mode INTO NMASE.EOB_DETAIL_SERVICE statistics no nonrecoverable without prompting; "; my $load_eob = " load client FROM %s.eob.asc OF ASC modified BY striptblanks dateformat=\"YYYYMMDD\" timestampformat=\"YYYY-MM-DD-HH.MM.SS.UUUUUU\" method l (1 3,4 11,12 20,21 36,37 50,52 59,60 64,65 69,71 78,79 83, 84 88,90 97,98 102,103 107,109 116,117 121,122 126,128 135, 136 140,141 145,146 147,148 149,150 151,152 154,155 157, 158 169,170 181,182 193,194 205,206 217,218 229,230 241, 242 253,254 254,255 255,256 264,266 275,277 290,291 306, 307 319,320 345,347 386,388 427,429 468,470 509,511 550, 552 591,593 632,634 673,675 714,716 755,757 796,798 837, 839 878,880 919,921 960,962 1001,1003 1042,1044 1083,1085 1124, 1126 1155,1156 1156,1157 1157,1158 1159) NULL indicators (0,0,0,0,0,51,0,0,70,0,0,89,0,0,108,0,0,127,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,265,276,0,0,0,346,387,428,469,510,551, 592,633,674,715,756,797,838,879,920,974,1002,1043,1084,1125,1156,1157,1158) $db2_load_mode INTO NMASE.EOB statistics no nonrecoverable without prompting; "; # -- CREATION_TIMESTAMP defaults to CURRENT_TIMESTAMP ## WTC - Change to call the Massachusetts version of this pre-processor ##require "$script_dir/pre_process_eob.pl"; require "$script_dir/pre_process_eob_mass.pl"; # --------------------------------------------------------------------- # ~ Main Procedure ---------------------------------------------------- # --------------------------------------------------------------------- # start the stopwatch and set variable for timestamp in file names my $start_time = time; # Build log file name ## WTC - changed to ass mass to the file name my $log = "$script_dir/daily_batch_run_mass.".&prettyTimestamp($start_time).".log"; # redirect STDOUT to the log file open STDOUT, ">$log" or die "open STDOUT, >$log ) failed with OS_ERROR = $!"; chmod 0777, $log; # Ensure the last run completed successfully if (-e $running_marker_file) { writeLog("This job didn't finish normally the last time it ran, Need to research previous"); writeLog("logs and see what happened. Will need to remove marker file <$running_marker_file>"); writeLog("after root cause of the problem has been resolved and then rerun the job"); exit 1; } open RUNNING_MARKER, ">$running_marker_file" or die "Couldn't open running marker file"; printf RUNNING_MARKER "$log\n"; close RUNNING_MARKER; writeLog("-------------------Start-------------------------"); writeLog("daily_batch_run.pl script starting"); # loop through and process each input file my @files = glob("$data_dir/$files_to_process"); if (scalar(@files) > 0) { writeLog(""); writeLog("RESTART NOTE: If this script stops abnormally after any of *.asc files"); writeLog("have been processed and rows have been inserted and commited into DB"); writeLog("Those rows that have been committed must be deleted before rerunning"); writeLog("this job or the committed records will error on the load insert of the rerun. "); writeLog("The /nmas_eob_/data/*.asc files will need to be deleted. The marker file"); writeLog("<$running_marker_file> will need to be deleted."); writeLog("The file <$_> is the file that is currently being processed."); writeLog(" "); writeLog("You will have to use a delete SQL statement to clean off all rows with the"); writeLog("timestamp of this run. The EOB EOB_CLAIM EOB_DETAIL_SERVICE EOB_MESSAGE could potentially "); writeLog("need to be cleaned up depending on where the error occurred."); writeLog(" "); writeLog(" "); writeLog(" "); # connect to the DB $rc = system "db2 -v CONNECT TO $db2_db USER $db2_user USING $db2_password "; die "Error signaled when attempting to connect to the database" if ($rc); foreach (@files) { # bypass the file if the execute bit isn't on # (The execute permission is used to tell us this file is ready to process) unless (-x $_) { writeLog("Bypassing $_ because the executible permission is not on"); next; } if ( -s $_ == 0 ) { writeLog("The Following is an EMPTY FILE: $_"); writeLog(" "); $rc=system("mv $_ $_.EMPTY_FILE"); writeLog("MV RC: $rc"); next; } writeLog(" "); writeLog("# of files processed so far is <$files_processed>"); writeLog("About to issue the following command to the pre_process to split file"); writeLog("which will call the pre_process_main function in the pre_process_eob.pl script"); writeLog("pre_process_main($_, $data_dir)"); writeLog(" "); # pre process the input file &pre_process_main($_, $data_dir); $files_processed++; # set the permissions so the db2 instance will permit load chmod 0777, glob("$data_dir/*.asc"); # load the data to the DB writeLog(" "); writeLog("About to run build db2 script to run the load command. for file <$_>"); writeLog(" "); writeLog("Here is the load command about to be run for EOB file <$load_eob>"); writeLog(" "); writeLog(" "); db2f("$load_eob", $_); writeLog("Here is the load command about to be run for EOB_CLAIM file <$load_eob_claim>"); writeLog(" "); writeLog(" "); db2f("$load_eob_claim", $_); writeLog("Here is the load command about to be run for EOB_DETAIL_SERVICE file <$load_eob_detail_service>"); writeLog(" "); writeLog(" "); db2f("$load_eob_detail_service", $_); writeLog("Here is the load command about to be run for EOB_MESSAGE file <$load_eob_message>"); writeLog(" "); writeLog(" "); db2f("$load_eob_message", $_); # zip up the input files writeLog("Cleaning up temp files with this command:"); $rc = system "rm $_*.asc"; die "Error ($rc) attempting to remove $_*.asc" if ($rc); writeLog("Compressing the file with the following command "); ###DRM $rc = system "gzip $_"; ###DRM die "Error ($rc) attempting to gzip $_" if ($rc); $rc = system "compress $_"; die "Error ($rc) attempting to compress $_" if ($rc); } # if ($files_processed) # { ###DRM # set the referential integrity back on ###DRM if ($db2_load_mode eq "REPLACE") { ###DRM db2("set integrity for nmase.eob immediate checked"); ###DRM } ###DRM writeLog(" "); ###DRM writeLog("run "); ###DRM db2("set integrity for nmase.eob_claim immediate checked"); ###DRM ###DRM writeLog(" "); ###DRM writeLog("run "); ###DRM db2("set integrity for nmase.eob_detail_service immediate checked"); ###DRM ###DRM writeLog(" "); ###DRM writeLog("run "); ###DRM db2("set integrity for nmase.eob_message immediate checked"); # # Do runstats # writeLog(" "); # writeLog("run "); # db2('runstats on table nmase.eob with distribution and sampled detailed indexes all tablesample bernoulli\(30\)'); # # writeLog(" "); # writeLog("run "); # db2('runstats on table nmase.eob_claim with distribution and sampled detailed indexes all tablesample bernoulli\(30\)'); # # writeLog(" "); # writeLog("run "); # db2('runstats on table nmase.eob_detail_service with distribution and sampled detailed indexes all tablesample bernoulli\(30\)'); # # writeLog(" "); # writeLog("run "); # db2('runstats on table nmase.eob_message with distribution and sampled detailed indexes all tablesample bernoulli\(30\)'); # } # disconnect db2("disconnect ALL"); } else { print "No files found to process in this run\n"; exit 1; } my $finish_time = time; writeLog(" "); writeLog(" "); print "Daily batch run ".&prettyTimestamp($start_time)." completed at ".&prettyTimestamp($finish_time)."\n"; print "Elapsed time for daily batch run: " . (int($finish_time - $start_time)) . " seconds\n"; writeLog(" "); writeLog("---------------End-------------------------"); # remove the running marker file unlink $running_marker_file or die "error deleteing $running_marker_file"; # -------------------------------------------------------- # Execute the parameter as a DB2 command # -------------------------------------------------------- sub db2($) { my $command = "db2 -vo @_"; my $rc = system $command; die "\nError attempting to execute [$command]\n" if ($rc); return 0; } # -------------------------------------------------------- # Execute the DB2 command(s) within a file # -------------------------------------------------------- sub db2f($$) { my ($command, $file_name) = @_; # create the external file open TEMP_FILE, ">$script_dir/temp.sql" or die "Couldn't open temporary file"; chmod 0777, "$script_dir/temp.sql"; printf TEMP_FILE $command, $file_name; close TEMP_FILE; # execute the DB2 commands my $command = "db2 -tsvof $script_dir/temp.sql"; my $rc = system $command; writeLog(" "); writeLog(" "); writeLog("Processing File $file_name - RC=<$rc>"); writeLog(" "); writeLog(" "); die "\nError attempting to execute [$command] RC=$rc\n" if ($rc); # clean up and delete the file unlink "$script_dir/temp.sql" or die "error deleteing $script_dir/temp.sql"; return 0; } # -------------------------------------------------------- # Format a time stamp # -------------------------------------------------------- sub prettyTimestamp($) { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(@_[0]); sprintf "%4d-%02d-%02d-%02d.%02d.%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec; } # -------------------------------------------------------- # Read a property file and create a hash of the values # -------------------------------------------------------- sub readproperties($) { my ($propfile) = @_; my (%props); # Return value: a hash of property values. my ($ln,$name,$value,@nv); open(PROPFILE,"$propfile") or die "Unable to open $propfile"; while ($ln = ) { chomp $ln; $ln =~ s/#(.*$)//; # Remove line comments in the properties file. # if ($1 ne "") { print "# $1\n"; } ### DEBUG ### @nv = split /\s*=/,$ln,2; $value = pop @nv; $value =~ s/^\s*=?\s*//; $value =~ s/\s*$//; $name = pop @nv; $name =~ s/^\s*//; $name =~ s/\s*$//; if ($name ne "") { $props{$name} = $value; # print "$name=$value\n"; ### DEBUG ### } } close(PROPFILE); return %props; } #---------------------------------------------------------------------------------- # writeLog Subroutine # # This subroutine writes messages to the log file # #---------------------------------------------------------------------------------- sub writeLog { my $line_head = strftime "%m/%d/%Y %H:%M:%S - ", localtime; my $print_line = shift; print STDOUT "$line_head$print_line\n" or die "print STDOUT failed with OS_ERROR = $!"; }