Converting Print Files to HTML Invoices

Sometimes general purpose programs don't quite do what you need them to do. Such was the case for a small sales organization who uses Great Plains accounting software. They needed more flexibility for invoicing than what the software gave them.

That's not at all unusual. Although modern software is very flexible and can often be greatly customized, sometimes the end user still needs more than what it provides.

What they specifically wanted was:

  • To be able to quickly view historical invoices
  • To allow customers to view their own historical invoices
  • To be able to attach invoices to e-mail
  • To be able to print in different formats

The solution we developed takes data exported from Great Plains as a CSV file and turns it into HTML invoices that are ready to print. Additionally, it creates plain text files at the same time, and in the future will also create an XML representation of the invoice.

Note that ANY application that can print can be controlled in this way. This happened to be Great Plains, and the CSV files happened to be a convenient output format, but this type of thing can be accomplished with virtually any software.

The basic transformation is done by a Perl script running on a Windows server. The user doesn't directly interact with the script; they simply choose a printer and doing that automatically creates the output files.

The ability to define a Windows or Unix printer that actually runs a program opens up many possibilities for capturing, diverting, and changing output before sending to a real printer. Although we didn't do it in this project, the completed output could also be printed, faxed, e-mailed to others, or even transferred to CD storage all automatically simply by "printing" a file from any application.

This transparency eliminates user errors and can guarantee consistency. As data can be transformed as necessary, it is easy to do such things as removing information that should not be available publicly or merging in data from other sources. For example, internally stored invoices might extract cost information from another database while the publicly stored versions would have that removed. Remember that this can all happen with ONE print operation by the user, on Windows or Unix platforms. The data itself can have originated anywhere - if it can be printed, it can be transformed, diverted, stored - whatever you need.

An HTML Invoice

You can view a sample of a created HTML page at http://aplawrence.com/CS/F-net_002559.html. Note that this is a two page invoice that is combined on one HTML page; if you print it with any current browser it should correctly print two separate pages. How that is done is explained in the script itself.

The data that this works with looks like this:

(This was exported from Great Plains)

"002559"
"10/25/02"
"1"
"Univ of Hard Knocks","Univ of Hard Knocks
"PO Box 4031","345 Elling Street"
"",""
"Farmville MA    02098-4031",""
""
"Farmville MA    02098"
"3P579820","BM0210008","TJK","FEDEX","Upon Receipt","10/25/02","2,881"
"15","0","BJ-20822H","Visible Light Cloaking Shield",".00","0.00"
"18","0","BK-2L402","2 Ply Anti Gravity Sheets",".00","0.00"
"1","0","","--------------------------------------------","",""
"1","0","","Note that Anti-Grav Sheets are not returnable","",""
"1","0","","--------------------------------------------","",""
"1","0","AQ-02","Light Sabre",".00",".00"
"2","0","AQ-03","Junior Light Sabre",".00",".00"
"1","0","","--------------------------------------------","",""
"1","0","","No Diluthium Crystals included for Sabres","",""
"1","0","","--------------------------------------------","",""
"1","0","AKQ-02","Sonic Cannon","0.00","0.00"
"1","0","","--------------------------------------------","",""
"1","0","","AKQ-02 cannot be exported!!","",""
"1","0","","--------------------------------------------","",""
"1","0","","","",""
"1","0","AB-897-A","Laser Cannon","0.00","0.00"
"1","0","AB-897-B","Laser Cannon","0.00","0.00"
"1","0","AB-897-C","Laser Cannon","0.00","0.00"
"1","0","AB-897-D","Laser Cannon","0.00","0.00"
"1","0","AB-897-E","Laser Cannon","0.00","0.00"
"1","0","AB-897-R","Laser Cannon","0.00","0.00"
",900.00"
""," .00"
"",".00"
"","0.00"
"",",800.00"
 

The careful observer might note that Great Plains originally considered this a one page invoice; that's the "1" in the third line of the data. We reformatted it to two pages in our script. We've also eliminated extraneous "0"'s in an unused column of the item data and done other cosmetic changes. Changes like this are easy to do.

The Script

The Perl script allows flexibility of font choices, number of items printed per page and other user customization. The ability of one script to produce multiple outputs means that the user only has to select one "printer". The special printer redirects the output to our Perl script. We used the Redmon printer redirection program available from http://www.cs.wisc.edu/~ghost/redmon/en/redmon.htm to provide this capability. This freely downloadable program allows you to specify a script that will receive the output that normally will be printed (no special program is necessary on Linux or Unix systems).

In order to print correctly, the generated HTML output includes a special CSS style tag that asks browsers to separate printed output wherever we use it. This makes neat, professional output (note that you may want to adjust the margins and header/footer text in the Page Setup if you actually print this sample).

The script could have been written in Vbscript, C or any other language supported on Windows or Unix. I simply chose Perl because that's the easiest for me, and it is completely unimportant and transparent to the user.

A simplified version of the Perl script itself:


#!/usr/bin/perl
use POSIX;
# 
# These are things you can modify
#
my $DEBUG=1;
#
# note that these TEXT variables have to be things CSS2 understands
# see a Cascading Style sheets reference
#
# OK to list multiple fonts; browser will use first it finds
#
my $TEXTFONT="arial, courier new,courier,century schoolbook";
my $TEXTSIZE="small";
my $DETAILTEXTFONT="arial,helvetica,sans-serif";
my $DETAILTEXTSIZE="xx-small";
my $MYDIR="/fnet/BN_Invoices";
my $MYIMAGE="/fnet/form_logo/logos/fnet logo.jpg";
my $ITEMSPERPAGE=15;
my $ADDRESS="233 Oak Avenue,Southville MA 01950 USA";
my $COMPANY="F-Net, Inc.";
my $PHONE="Tel (555) 555-2960, Fax (555) 555-0638 sales\@effnettinc.com";
my $LARGEMSG1="THANK YOU FOR YOUR ORDER!";
my $LARGEMSG2="";
my $LARGEMSG3="";
my $LARGEMSG4="PLEASE CALL CUSTOMER SERVICE AT (555) 555-2926";
my $LARGEMSG5="IF YOU HAVE ANY QUESTIONS ABOUT THIS SHIPMENT.";
my $SMALLMSG1="THESE PRODUCTS ARE NON-HAZARDOUS, FOR LABORATORY";
my $SMALLMSG2="RESEARCH USE ONLY, AND ARE NOT INTENDED FOR USE IN";
my $SMALLMSG3="PUBLIC PLACES.  TERMS: ON RECEIPT.";
#
# Note that $COMPANY isn't just for display; it also
# is used to find things in the "noisy" format; see below
# The "noisy" invoices WON'T WORK IF THIS DOESN'T MATCH
# what GP puts out!
#
#
# Note you need to \ the @ in the email above
#
# Leave the rest alone unless you know what you are doing.
#
#  Anything between "print FILE EOF;" and EOF is 
#  just html code that you certainly can modify
#
my $MYDIR="/tmp";
my $MYIMAGE="/image21.gif";
my %values,%invoices;
my @lines;
my $x;
my $type="domestic";
#
# Read the data from stdin
# and change it from quoted comma delimited to pipe (|) delimited
# Data is a Great Plains generated csv file
#
my @getall=<>;
decommify();
#
# Where are the individual invoices?
# This locates the data we want in the array
# Most of this is done by setting pointers to the 
# element of the array; see below
#
splitinv();
#
# Now run through the data and generate the html
# Each invoice will have an entry $invoices{"invoicenumber|Start"}
# set to the array element where it starts.  Examples
# invoices{"2077|Start"} might be 78, so $lines[78] is the
# start of that invoice.
#
foreach (sort keys %invoices) {
  next if not $_;
  ($invoice,$what) = split /\|/,$_;
  next if $what ne "Start";
  generatehtml($invoice);
  generatetext($invoice);
  }
exit 0;
#
#
# SUB DECOMMIFY()
# Changes a csv to pipe delimited
#
sub decommify {
   #
   # I don't like quoted comma separated, so I change it here
   #
   foreach (@getall) {
     chomp;
     # retain blank items
     s/""/"~"/g;
     # change to pipe
     s/","/\|/g;
     s/^"//;
     s/"$//;
     # remove space at beginning
     s/^  *//g;
     # long lines get split with a "="; we rejoin them
     $tt .= $_;
     if (/=$/) {
      $tt =~ s/=$//;
      next;
     } 
     # transfer to the @lines array.
     $lines[$x++]=$tt; 
     $tt="";
   } 
}
#
# SUB HTMLHEAD
#
# 
sub htmlhead {
my $invoice=shift;
my $date=shift;
my $pages=shift;
my $x, $stuff;
# 
# Start the page, defining the "breakhere" that lets
# us control page breaks when it is printed
#
# Note that IE sets a 3/4" margin by default, top, bottom, and 
# both sides.  Use Page Setup to set that to zero
#
# The Style Sheet would also be a great place to define 
# any other special effects you want, font sizes, styles etc.
#
print FILE <<EOF;
<html>
<head><title>$invoice $date (Pages: $pages)</title>
<style type="text/css">
<!--
SPAN { font-size: $TEXTSIZE; font-family: $TEXTFONT; }
SPAN.small { font-size: $DETAILTEXTSIZE;font-family: $DETAILTEXTFONT; }
P.breakhere { page-break-before:always }
-->
</style>
</head>
<body bgcolor="#ffffff">
<!--
EOF
#
# This next part just puts the raw variables into the html file
# as comments- it just makes debugging easier
#
foreach (sort keys %invoices) {
  ($key,$what)=split /\|/;
  next if $key ne $invoice;
  $x = $invoices{$_};
  $stuff="";
  $stuff=$lines[$x] if $x=~ /^[0-9][0-9]*$/;
  $stuff="" if $what eq "Pages";
  
  print FILE "$key $what $invoices{$_} $stuff\n"; 
  
}
print FILE "-->\n";
}
#
# SUB INVHEADER
#
sub invheader {
  #
  # This is the header for each page
  # We're outputting ONE html page per invoice that will PRINT as multiple 
  # pages
  #
  my $invoice=shift;
  my $date=shift;
  my $thispage=shift;
  my $pages=shift;
  # 
  # This first part is pretty simple because we have the data
  # we need already.
  #
  print FILE <<EOF;
  <span>
  <br>
  <table width=100%>
  <tr><td colspan=4 rowspan=8><img src="$MYIMAGE"></td></tr>
  <tr><td colspan=3 align=right><b>$COMPANY</b> $ADDRESS</td></tr>
  <tr><td colspan=3 align=right>$PHONE</td></tr>
  <tr><td></td></tr>
  <tr><td></td></tr>
  <tr><td></td></tr>
  <tr><td colspan=3 align=right rowspan=3><table border=1 cellpadding=5 rules=none>
  <tr><td><b>INVOICE # </b></td><td> $invoice</td></tr>
  <tr><td><b>DATE </b></td><td> $date</td></tr>
  <tr><td><b>PAGE </b></td><td> $thispage of $pages</td></tr>
  </table>
  </table>
  <br>
  <table width=100%>
  <tr><td align=center >BILL TO:</td><td align=center>SHIP TO:</td></tr>
  </table>
  <br>
  <table width=100% border=1 rules=cols cellpadding=3>
EOF
#
# Now we're ready to print address detail lines.
# Note that this routine prints whatever details it has;
# it's up to "getvalues" to find the addresses.
# 
  %values=();
  getvalues($invoice);
  print FILE "<tr><td><table width=50%>\n";
  foreach (sort keys %values) {
    next if not /^add:/;
    ($one,$two)=split /\|/, $values{$_};
    print FILE "<tr><td> $one</td></tr>" if $one !~ /~/; 
  }
  print FILE "</table></td><td><table width=50%>\n";
  foreach (sort keys %values) {
    next if not /^add:/;
    ($one,$two)=split /\|/, $values{$_};
    print FILE "<tr><td> $two</td></tr>" if $two !~ /~/;
    $one =~ s/~//;
    $two =~ s/~//;
  }
  foreach (sort keys %values) {
     $values{$_}=~ s/~/\  /g;
  }
 #
 # Now back to relative simplicity again
 #
  print FILE <<EOF;
  </table></td></tr>
  </table>
  <br>
  
  <table width=100% border=1>
  <tr><td>Purchase Order No.</td>
  <td>Customer ID</td>
  <td>Salesperson ID</td>
  <td>Ship Via</td>
  <td>Terms</td>
  <td>Requested Date</td>
  <td>Master No.</td></tr>
  <tr>
  <td>$values{'gen_ponum'}</td>
  <td>$values{'gen_custid'}</td>
  <td> $values{'gen_salesid'}</td>
  <td> $values{'gen_shipmethod'}</td>
  <td>$values{'gen_terms'}</td>
  <td>$values{'gen_reqship'}</td>
  <td>$values{'gen_masterno'}</td></tr>
  </table>
  <br>
EOF
}
#
# SUB INVDETAIL
#
sub invdetail {
#
# Now we're ready to print detail lines.
# Note that this routine prints whatever details it has;
# it's up to "getdetails" to find the lines, determine 
# lines per page and even to split the data.
# 
  my $invoice=shift;
  my $date=shift;
  my $thispage=shift;
  my $pages=shift;
  %values=();
  getdetails($invoice,$thispage-1);
  
  print FILE <<EOF;
  <span class="small">
  <table width=100% border=1 rules=none >
  <tr><th>Qty.</th>
  <th></th>
  <th>Item No.</th>
  <th>Description</th>
  <th>Unit Price</th>
  <th>Ext. Price</th></tr>
  <tr><td colspan=6><hr></td></tr>
EOF
# 
# The "printed" just keeps track of how many detail
# lines we did so we can fill out the page with blanks when
# it is less than $ITEMSPERPAGE
#
  $printed=0;
  foreach (sort keys %values) {
    next if not /^detail:/;
    $printed++;
    print FILE "<tr>";
    @stuff=split /\|/, $values{$_};
    $stuff[4]="~" if $stuff[4]=~ /^$0.00$/;
    $stuff[5]="~" if $stuff[5]=~ /^$0.00$/;
    $stuff[0]="" if ($stuff[4] eq "~" and $stuff[5] eq "~");
    foreach (@stuff) {
      s/^0$//;
      s/^~$//;
      s/^$0.00$//;
    }
$alignme="align=\"right\"";
if ($stuff[0]) {
      print FILE "<td $alignme>$stuff[0]</td> <td>  $stuff[1]</td> <td>$stuff[2]</td> <td>$stuff[3]</td> <td $alignme>$stuff[4]</td> <td $alignme>$stuff[5]</td>";
} else {
      print FILE "<td> </td><td>$stuff[1]</td><td colspan=2>$stuff[2] $stuff[3]</td> <td>$stuff[4]</td> <td>$stuff[5]</td>";

      print FILE "</tr>\n";
  }
}
  for ($x=$printed;$x<$ITEMSPERPAGE;$x++) {
    print FILE "<tr><td> </td></tr>";
  }
  print FILE <<EOF;
  </table>
  </span>
  <br>
  <table width=100% >
  <tr><td colspan=3><table >
  <tr><td>$LARGEMSG1</td></tr>
  <tr><td>$LARGEMSG2</td></tr>
  <tr><td>$LARGEMSG3</td></tr>
  <tr><td>$LARGEMSG4</td></tr>
  <tr><td>$LARGEMSG5</td></tr>
  <tr><td></td></tr>
  <tr><td><font size=-2>$SMALLMSG1</font></td></tr>
  <tr><td><font size=-2>$SMALLMSG2</font></td></tr>
  <tr><td><font size=-2>$SMALLMSG3</font></td></tr>
  </table>
  </td>
EOF
#
# We now need to go find the total lines
#
  gettotals($invoice);
# 
# If we're on the last page, print totals, otherwise not
#
  print FILE <<EOF if $thispage == $pages;
  <span>
<td><table width=100% border=1 rules=none cellpadding=4 >
  <tr><td>Subtotal</td><td></td><td align=right>$values{'t_subtotal'}</td></tr>
  <tr><td>Misc.</td><td></td><td align=right>$values{'t_misc'}</td></tr>
  <tr><td>Shipping</td><td></td><td align=right>$values{'t_shipping'}</td></tr>
  <tr><td>Discount</td><td align=right>$values{'t_discount'}</td></tr>
  <tr><td><b>Total</b></td><td></td><td align=right><b>$values{'t_total'}</b></td></tr>
  </table></td></tr>
  </table>
  </span>
EOF
  print FILE <<EOF if $thispage != $pages;
  <td><table width=100% border=1 rules=none cellpadding=4 >
  <tr><td> </td><td></td></tr>
  <tr><td> </td><td></td></tr>
  <tr><td></td><td></td><td><b>Continued on next page</b></td></tr>
  <tr><td> </td><td></td></tr>
  <tr><td> </td><td></td></tr>
  </table></td></tr>
  </table>
  </span>
EOF
  }
#
# SUB GENERATETEXT
#
sub generatetext {
  my $invoice=shift;
  my $filename="$MYDIR/F-net_$invoice.text";
  my $date=$lines[$invoices{"$invoice|Date"}];
  my $pages=$invoices{"$invoice|Pages"};
  my $x;
  # 
  # This is the only place we generate an error.
  # If we're screwed up in other ways, the text will
  # show it.
  #
  open (FILE,">$filename") or myerr("Can't open $filename $!");
  #
  # The stuff we get from GP might have originally
  # looked like this:
  #"Somewhere ","MA","09990","10/24/02"
  # or like this
  # "10/24/02"
  #
  $date =~ s/.*\|//;
  #
  # Now extract the detail lines and
  # figure out how many pages we need
  #
  $pages=prepdetails($invoice);
  #
  # Now ready to do the output
  #
  print FILE "$invoice $date (Pages: $pages)\n";
  # 
  # and then generate each page
  # We're outputting ONE page per invoice 
  #
  %values=();
  getvalues($invoice);
  print FILE <<EOF;
INVOICE #  $invoice
DATE $date
PAGES $pages

EOF
  foreach (sort keys %values) {
     $values{$_}=~ s/~/ /g;
  }
    print FILE "BILL TO\n";
  foreach (sort keys %values) {
    next if not /^add:/;
    ($one,$two)=split /\|/, $values{$_};
    print FILE "$one\n"
  }
    print FILE "\nSHIP TO\n";
  foreach (sort keys %values) {
    next if not /^add:/;
    ($one,$two)=split /\|/, $values{$_};
    print FILE "$two\n"
  }
  foreach (sort keys %values) {
     $values{$_}=~ s/~/\  /g;
  }
  print FILE "\nPurchase Order No.\tCustomer ID\tSalesperson ID\tShip Via\tTerms\tRequested Date\tMaster No.\n";
  print FILE "$values{'gen_ponum'}\t$values{'gen_custid'}\t$values{'gen_salesid'}\t$values{'gen_shipmethod'}\t$values{'gen_terms'}\t$values{'gen_reqship'}\t$values{'gen_masterno'}\n";
  %values=();
  getdetails($invoice,$thispage-1);
  print FILE "\nQty.\tCustoms\tItem No.\tDescription\tUnit Price\tExt. Price\n";
foreach (sort keys %tvalues) {
  @stuff=split /\|/, $tvalues{$_};
    $stuff[4]="~" if $stuff[4]=~ /^$0.00$/;
    $stuff[5]="~" if $stuff[5]=~ /^$0.00$/;
    $stuff[0]="" if ($stuff[4] eq "~" and $stuff[5] eq "~");
  foreach (@stuff) {
      s/^0$//;
      s/^~$/ /;
      s/^$0.00$//;
  print FILE "$_\t";
  }
  print FILE "\n";
}
  %values=();
  gettotals($invoice);
print FILE <<EOF;

  Subtotal\t$values{'t_subtotal'}
  Misc\t$values{'t_misc'}
  Shipping\t$values{'t_shipping'}
  Discount\t$values{'t_discount'}
  Total\t$values{'t_total'}
EOF
}
#
# SUB GENERATEHTML
#
sub generatehtml {
  my $invoice=shift;
  my $filename="$MYDIR/F-net_$invoice.html";
  my $date=$lines[$invoices{"$invoice|Date"}];
  my $pages=$invoices{"$invoice|Pages"};
  my $x;
  # 
  # This is the only place we generate an error.
  # If we're screwed up in other ways, the html will
  # show it.
  #
  open (FILE,">$filename") or myerr("Can't open $filename $!");
  #
  # The stuff we get from GP might have originally
  # looked like this:
  #"Somewhere ","MA","09990","10/24/02"
  # or like this
  # "10/24/02"
  #
  $date =~ s/.*\|//;
  #
  # Now extract the detail lines and
  # figure out how many pages we need
  #
  $pages=prepdetails($invoice);
  #
  # Now ready to do the output
  #
  # First the head, title and style sheet
  #
  htmlhead($invoice,$date, $pages);
  # 
  # and then generate each page
  # We're outputting ONE html page per invoice 
  # that will PRINT as multiple pages.
  #
  for ($x=1;$x <= $pages;$x++) {
    invheader($invoice,$date,$x,$pages);
    invdetail($invoice,$date,$x,$pages);
    # this lets us break physically printed pages (not screen)
    print FILE "<p class=\"breakhere\">\n" if $x < $pages;
    # 
    # Note that Explorer puts 3/4" margins all around what it prints
    # change that in Page Setup to 0 or these won't fit well
    #
  }
  print FILE <<EOF;
  </body>
  </html>
EOF
# 
# That's it- the rest is details :-)
#
}
# SUB SPLITINV()
# This boopy looks through the array of lines
# and figures out where important things are located
# 
# GP can put this out in various ways, and this routine 
# handles two very different formats.  For example, here's 
# the totals section from one of the formats:
#
#  "","Subtotal",",551.40"
#  "","Misc"," .00"
#  "","Shipping"," .00"
#  ""
#  "Total",",551.40"
#
# and here it is from the other
#
#  ",300.00"
#  ""," .00"
#  "",".00"
#  "","0.00"
#  "",",200.00"
#  
# If the format changes, this is the section to modify
#
sub splitinv {
  my $x=0;
  my $skipping=0;
  # how big is the total file?
  my $howmany=scalar(@lines);
  # this is true just because it always starts with the invoice
  my $invoice=$lines[0];
  # strip out the company if it is the noisy format
  $invoice =~ s/$COMPANY.Invoice\|//;
  # set our key
  $key="$invoice|Start";
  # and this is where it starts
  $invoices{$key}=0;
  # now go find the rest
  for ($x=0;$x < $howmany;$x++) {
    # we won't see blank lines execept POSSIBLY after an invoice
    $skipping++ if not $lines[$x];
    next if not $lines[$x];
    if ($skipping) {
     # we'll fall here after we've seen the END of an invoice
     $invoice=$lines[$x];
     $invoice =~ s/$COMPANY.Invoice\|//;
     $key="$invoice|Start";
     $invoices{$key}=$x;
     $skipping=0;
    }
    #
    # In order to figure out if we are at the end, we 
    # need to retain some state info.
    #
    $p5=$p4; $p4=$p3; $p3=$p2; $p2=$p1;
    $p1=$lines[$x];
    $c5=$c4; $c4=$c3; $c3=$c2; $c2=$c1;
    $c1=split /\|/,$lines[$x];
    if ( $p1 =~ /Total/ and $p3 =~ /Shipping/ and $p4 =~ /Misc./) {
       # this is the end of a 'noisy' invoice
       $key="$invoice|Total";
       $invoices{$key}=$x;
       $key="$invoice|Type";
       $invoices{$key}="noisy";
       $skipping=1;
    }
    if ( $c1 == 2 and $c2 ==2 and $c3 ==2 and $c4 ==2 and $c5 ==1  and $x > 7 )
    {
       # and this matches the end of the other format
       $key="$invoice|Total";
       $invoices{$key}=$x;
       $key="$invoice|Type";
       $invoices{$key}="quiet";
       $skipping=1;
    }
  }
  #
  # now we'll go looking for other stuff
  #
    foreach $key (sort keys %invoices) {
      ($invoice,$what) = split /\|/,$key;
      next if $what ne "Start";
      $start=$invoices{$key};
      $type=$invoices{"$invoice|Type"};
      $invoices{"$invoice|Date"}=$start + 3 if $type eq "noisy";
      $invoices{"$invoice|Date"}=$start + 1 if $type eq "quiet";
      $invoices{"$invoice|Add1"}=$start + 6 if $type eq "noisy";
      $invoices{"$invoice|Add1"}=$start + 3 if $type eq "quiet";
      $invoices{"$invoice|Terms"}=$start + 11 if $type eq "noisy";
      $invoices{"$invoice|Terms"}=$start + 9 if $type eq "quiet";
      $invoices{"$invoice|Detail0"}=$start + 13 if $type eq "noisy";
      $invoices{"$invoice|Detail0"}=$start + 10 if $type eq "quiet";
      #
      # Invoices can have multiple pages if "noisy"
      #
      if ($type eq "noisy") {
        $last=$invoices{"$invoice|Total"};
	$x=1;
	while ($start++ < $last ) {
	  if ($lines[$start] =~ /$COMPANY.Invoice\|/ ) {
             $invoices{"$invoice|Detail$x"}=$start + 13;
	     $x++;
	  }
	}
      }
    }
    if ($DEBUG) {
    foreach $key (sort keys %invoices) {
      print "$key $invoices{$key}\n";
      }
      }

}
#
# SUB GETVALUES()
#
sub getvalues {
#
# This picks out various things we need
#
  my $invoice=shift;
  my @stuff;
  my $x,$y;
  # 
  # We start with addresses.  These are a little tough because
  # the formats are very different.  Here's the "noisy" format:
  #
#  "Bill To:","Ship To:"
#  "Billto1","Shipto1"
#  "Billto2","Shipto2"
#  "Billto3","Shipto3"
#  "Billto4","Shipto4"
  # and here's the other format:
  #
#  "Billto1","Shipto1"
#  "Billto2","Shipto2"
#  "Billto3","Shipto3"
#  "Billto4","Shipto4"
#  "Billto5"
#  "Billto6"
#
# We handle the two formats with identical code except we change 
# how many lines we look at ($max)
#
# Remember that "splitinv()" has already told us where the address
# starts for each invoice
#
  my $add=$invoices{"$invoice|Add1"};
  $y=0;
  $max=$add +6;
  $max=$add+4 if $invoices{"$invoice|Type"} eq "noisy";
  for ($x=$add;$x < $max;$x++) {
    $count=split /\|/,$lines[$x];
    $key="add:$y";
    @stuff=split /\|/,$lines[$x];
    if ($count == 1) {
       $values{$key} .= "|$stuff[0] " if $values{$key};
       $values{$key} = "$stuff[0] " if not $values{$key};
    }
    if ($count >= 2) {
       if  ( not $values{$key}) {
         $values{$key} = "$stuff[0]|$stuff[1] $stuff[2]";
       } else {
         $values{$key} .= "|$stuff[1] $stuff[2] ";
	 $y++;
         $key="add:$y";
         $values{$key} = "$stuff[0] ";
       }
    }
    $y++ if $values{$key} =~ /\|/;
    $count=split /\|/,$lines[$x];
  }
  # 
  # next is the "po line"
  # Again, "splitinv()" has already told us where this line 
  # is for each invoice
  #
  @stuff=split /\|/,$lines[$invoices{"$invoice|Terms"}];
  $values{'gen_ponum'}=$stuff[0];$values{'gen_custid'}=$stuff[1];
  $values{'gen_salesid'}=$stuff[2];$values{'gen_shipmethod'}=$stuff[3];
  $values{'gen_terms'}=$stuff[4];$values{'gen_reqship'}=$stuff[5];
  $values{'gen_masterno'}=$stuff[6];
}
#
# SUB GETDETAILS()
#
sub getdetails {
#
# This moves detail lines from the temporary "%tvalues" to
# the "%values" hash.  This is necessary if we want to
# print less lines per page ($ITEMSPERPAGE) than GP does.
# In both formats, GP puts out not more than 33 detail
# lines.
#
  my $invoice=shift;
  my $k=shift;
  my $x;
  $k *=$ITEMSPERPAGE;
  for ($x=0; $x < $ITEMSPERPAGE; $x++) {
       $key=sprintf("fdetail:%.2d",$x+$k);
       next if not $tvalues{$key};
       $key2=sprintf("detail:%.2d",$x+$k);
       $values{$key2}=$tvalues{$key};
  }
  
}
#
# SUB PREPDETAILS()
# 
sub prepdetails {
#
# This is where the detail lines get extracted for each invoice
# In both formats, GP puts out not more than 33 detail
# lines.
#
  my $invoice=shift;
  my $max=0;
  my $x,$y=0;
  $k=0;
  %tvalues=();
  
  while ($invoices{"$invoice|Detail$k"}) {
     $x=$invoices{"$invoice|Detail$k"};
     $max=$x+33;
     #$max=$x+33 if $invoices{"$invoice|Detail$k"};
     #$max=$invoices{"$invoice|Total"} -4 if $max <= $x;
     $max2=$invoices{"$invoice|Total"} -4;
     $max=$max2 if ($max2 < $max or not $invoices{"$invoice|Detail$k"});
     while ($x < $max) {
       $key=sprintf("fdetail:%.2d",$y);
       $tvalues{$key}=$lines[$x];
       $x++;$y++;
     }
    $k++;
  }
  $pages = ceil($y /$ITEMSPERPAGE);
  return $pages;
}
#
# SUB GETTOTALS()
#
sub gettotals {
# 
#  Similar to "getvalues()" in concept
#
  my $invoice=shift;
  my $x=$invoices{"$invoice|Total"};
  $values{'t_subtotal'}=$lines[$x-4];
  ($a,$values{'t_misc'})=split /\|/, $lines[$x-3];
  ($a,$values{'t_shipping'})=split /\|/, $lines[$x -2];
  ($a,$discount)=split /\|/, $lines[$x -1];
  ($a,$values{'t_total'})=split /\|/, $lines[$x];
  if ($invoices{"$invoice|Type"} eq "noisy" ) {
    ($a,$b,$values{'t_subtotal'})=split /\|/, $lines[$x-4];
    ($a,$b,$values{'t_misc'})=split /\|/, $lines[$x-3];
    ($a,$b,$values{'t_shipping'})=split /\|/, $lines[$x -2];
    ($a,$b,$discount)=split /\|/, $lines[$x -1];
    ($a,$values{'t_total'})=split /\|/, $lines[$x];
  }
  $values{'t_discount'}= "-" . $discount if $discount;
  
}
 sub myerr {
 my $problem=shift;
 print "bailing..$problem\n";
 exit 1;
}



Got something to add? Send me email.





(OLDER) <- More Stuff -> (NEWER)    (NEWEST)   

Printer Friendly Version

-> -> Converting CSV Files to HTML Invoices




Increase ad revenue 50-250% with Ezoic


More Articles by

Find me on Google+

© Tony Lawrence



Kerio Samepage


Have you tried Searching this site?

Unix/Linux/Mac OS X support by phone, email or on-site: Support Rates

This is a Unix/Linux resource website. It contains technical articles about Unix, Linux and general computing related subjects, opinion, news, help files, how-to's, tutorials and more.

Contact us





I just invent, then wait until man comes around to needing what I've invented (R. Buckminster Fuller)

Better to fight for something than live for nothing. (George S. Patton)








This post tagged: