#!/usr/local/bin/perl
#  engine.pl - the cbb 'engine'.
#              This script implements a transaction abstract data type
#              It encapsulates a list a transactions and the functions
#              required to manipulate the transactions.
#
#  Written by Curtis Olson.  Started August 22, 1994.
#
#  Copyright (C) 1994  Curtis L. Olson  - curt@sledge.mn.org
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# $Id: engine.pl,v 1.6 1994/10/17 13:26:21 curt Exp $
# (Log is kept at end of this file)


$| = 1;				# flush buffers after every write
$debug = 0;			# 0 = off,  1 = on

# Global variables

# %TRANS - an associative array of transactions and transaction keys
# @KEYS - a sorted list of transaction keys (for traversing the trans list)
# $sorted_keys - specifies whether the list in @KEYS is valid
# $calced - specified whether the transactions have been properly calculated
# $current - specifies the "current" position in the @KEYS array

# %MEMS - an associative array of transactions and description keys
# @MEMKEYS - a sorted list of description keys (for memorized transactions)

# %CATS - an associative array of categories
# @CATKEYS - a sorted list of category keys (for traversing the cat list)
# $sorted_catkeys - specifies whether the list in @CATKEYS is valid

open(DEBUG, ">debug") if $debug;

&init_trans();			# initialize %TRANS, @KEYS, and $sorted_keys


# Sure there might be a better way to do this, but I think this
# way is sexy ... DT
%N=(0,'00',1,'01',2,'02',3,'03',4,'04',5,'05',6,'06',7,'07',8,'08',9,'09');
sub pad {
    local($in) = @_;

    if ($in <= 9) {
        $out = $N{$in};
    }
    else {
        $in;
    }
}


# get next available key for a specified date
sub get_next_key {
    # in: date
    # out: key

    local($date) = @_;
    $count = 0;

    while ( $TRANS{"$date-".&pad($count)} ) {
	$count++;
    }

    return "$date-".&pad($count);
}


# set @KEYS = sorted list of transaction keys
sub sort_keys {
    $sorted_keys = 1;
    $current = 0;

    print DEBUG "sort_keys()\n" if $debug;
    @KEYS = sort(keys %TRANS);
}


# recalculate the transactions
sub calc_trans {
    $calced = 1;
    local($total) = 0.00;

    print DEBUG "calc_trans()\n" if $debug;

    if ($sorted_keys == 0) {
	&sort_keys();
    }

    foreach $key (@KEYS) {
        ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, 
        	$junk) = split(/:/, $TRANS{$key});

	$total = $total + $credit - $debit;

	$TRANS{$key} = 
	  "$date:$check:$desc:$debit:$credit:$cat:$com:$cleared:".
	  	sprintf("%.2f", $total);
    }
}


# create a transaction (and add to the transaction list)
sub create_trans {
    # in: transaction
    # out: keyed_transaction

    local($trans) = @_;
    $sorted_keys = 0;
    $calced = 0;

    ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
	split(/:/, $trans);

    $key = &get_next_key($date);

    $TRANS{$key} = "$trans";

    print DEBUG "created:  $key:$trans\n" if $debug;

    return "$key:$trans";
}


# update a transaction (replace in the transaction list)
sub update_trans {
    # in: keyed_transaction
    # out: keyed_transaction

    local($keyed_trans) = @_;
    $sorted_keys = 0;
    $calced = 0;

    ($key, $trans) = split(/:/, $keyed_trans, 2);

    &delete_trans($key);
    $result = &create_trans($trans);

    print DEBUG "updated:  $key\n" if $debug;
    print DEBUG "     to:  $result\n" if $debug;

    return "$result";
}


# delete a transaction given the key
sub delete_trans {
    # in: key

    local($key) = @_;
    $sorted_keys = 0;
    $calced = 0;

    delete $TRANS{$key};

    print DEBUG "deleted:  $key\n" if $debug;

    return "ok";
}


# return the next transaction
sub next_trans {
    if ($sorted_keys == 0) {
	&sort_keys();
    }

    if ($calced == 0) {
	&calc_trans();
    }

    ++$current;
    $trans = $TRANS{$KEYS[$current]};
    if ( $trans ) {
        return "$KEYS[$current]:$trans";
    } else {
        return "none";
    }
}


# return the transaction specified by a key
sub find_trans {
    # uses a binary search so that we can keep $current current.   
    # Yeeeks! I have to think for a change.
    # Hmmm, maybe I should rethink my data structures ... nah. :)

    local($key) = @_;
    local($left, $middle, $right) = (0, 0, $#KEYS);

    if ($calced == 0) {
	&calc_trans();
    }

    $trans = "";

    while ( $left <= $right ) {
	$middle = int( ($left + $right) / 2 );
        print DEBUG "$left < $middle < $right\n" if $debug;
	if ( $KEYS[$middle] lt $key ) {
	    $left = $middle + 1;
	    print DEBUG "  left = middle + 1\n" if $debug;
        } elsif ( $KEYS[$middle] gt $key ) {
	    $right = $middle - 1;
	    print DEBUG "  right = middle - 1\n" if $debug;
        } else {
	    # we found it, set $trans to what we want and force an exit of
	    # the while loop
	    $trans = $TRANS{$KEYS[$middle]};
	    print DEBUG "  found it: $trans\n" if $debug;
	    $current = $middle;
	    $left = $right + 1;
        }
    }

    print DEBUG "found:  $key:$trans\n" if $debug;

    if ( $trans ) {
        return "$key:$trans";
    } else {
        return "none";
    }
}


# returns the current index
sub get_current_index {
    return $current;
}


# return the first transaction
sub first_trans {
    if ($sorted_keys == 0) {
	&sort_keys();
    }

    if ($calced == 0) {
	&calc_trans();
    }

    $current = 0;
    $trans = $TRANS{$KEYS[$current]};
    if ( $trans ) {
        return "$KEYS[$current]:$trans";
    } else {
        return "none";
    }
}


# returns the entire transaction list in one big chunk.
sub all_trans {
    # in: file base name
    # out: result

    $| = 0;				# turn off buffer flushing

    local($file) = @_;

    if ($calced == 0) {
	&calc_trans();
    }

    if ($sorted_keys == 0) {
	&sort_keys();
    }

    foreach $key (@KEYS) {
	print ("$key:$TRANS{$key}\n");
    }

    $| = 1;				# turn buffer flushing back on

    return "none";
}

# return the first uncleared transaction
sub first_uncleared_trans {
    if ($sorted_keys == 0) {
	&sort_keys();
    }

    if ($calced == 0) {
	&calc_trans();
    }

    $current = 0;
    $trans = $TRANS{$KEYS[$current]};
    ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk) = 
    	    split(/:/, $trans);
    while ( $cleared eq "x" ) {
        ++$current;
        $trans = $TRANS{$KEYS[$current]};
        ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk) = 
    	        split(/:/, $trans);
    }

    if ( $trans ) {
        return "$KEYS[$current]:$trans";
    } else {
        return "none";
    }
}


# return the next uncleared transaction
sub next_uncleared_trans {
    if ($sorted_keys == 0) {
	&sort_keys();
    }

    if ($calced == 0) {
	&calc_trans();
    }

    ++$current;
    $trans = $TRANS{$KEYS[$current]};
    ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk) = 
    	    split(/:/, $trans);
    while ( $cleared eq "x" ) {
        ++$current;
        $trans = $TRANS{$KEYS[$current]};
        ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk) = 
    	        split(/:/, $trans);
    }

    if ( $trans ) {
        return "$KEYS[$current]:$trans";
    } else {
        return "none";
    }
}


# select transaction -- primes a transaction for future clearing
sub select_trans {
    # in: key
    # out: keyed_transaction

    local($key) = @_;
    $sorted_keys = 0;
    $calced = 0;

    $trans = $TRANS{$key};
    ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) = 
    	    split(/:/, $trans);

    $cleared = "*";

    $TRANS{$key} = 
	  "$date:$check:$desc:$debit:$credit:$cat:$com:$cleared:$total";

    print DEBUG "selected:  $key to be cleared\n" if $debug;

    return "$key:$TRANS{$key}";
}


# select transaction -- primes a transaction for future clearing
sub unselect_trans {
    # in: key
    # out: keyed_transaction

    local($key) = @_;
    $sorted_keys = 0;
    $calced = 0;

    $trans = $TRANS{$key};
    ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) = 
    	    split(/:/, $trans);

    $cleared = "";

    $TRANS{$key} = 
	  "$date:$check:$desc:$debit:$credit:$cat:$com:$cleared:$total";

    print DEBUG "unselected:  $key will not be cleared\n" if $debug;

    return "$key:$TRANS{$key}";
}


# clear all selected transactions
sub clear_trans {
    if ($calced == 0) {
	&calc_trans();
    }

    if ($sorted_keys == 0) {
	&sort_keys();
    }

    foreach $key (@KEYS) {
        $trans = $TRANS{$key};
        ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) = 
    	        split(/:/, $trans);

	if ( $cleared eq "*" ) {
            $cleared = "x";

            $TRANS{$key} = 
	          "$date:$check:$desc:$debit:$credit:$cat:$com:$cleared:$total";
        }
    }
}


# initialize the transactions data structure
sub init_trans {
    # out: result

    $sorted_keys = 1;
    $calced = 1;
    %TRANS = ();
    @KEYS = ();

    return "ok";
}


# load the data from a file
sub load_trans {
    # in: file base name
    # out: result

    local($file) = @_;
    $sorted_keys = 0;
    $calced = 0;

    open(LOAD, "<$file.cbb") || return "error";

    while ( <LOAD> ) {
	chop;
	&create_trans($_);
    }

    close(LOAD);

    return "ok";
}


# import a quicken export file (.qif)
sub import_qif {
    # in: file
    # out: result

    local($file) = @_;
    $sorted_keys = 0;
    $calced = 0;

    open(QIF, "<$file");

    ($date, $check, $desc, $debit, $credit, $cat, $split, $com, 
    	$cleared) = ("", "", "", "", "", "", "", "", "");

    while ( <QIF> ) {
	chop;			# get rid of that pesky newline.
	s/:/-/g;		# eliminate our delimiter characters from
	s/\|/-/g;		# the import file.
	s/\r//g;		# strip the dos ^M if needed
	if ( m/^\!/ ) {
	    # Type
	    # print "$_\n";
	} elsif ( m/^D/ ) {
	    # Date
	    ($month, $day, $year) = split(/\/ */, substr($_,1));
	    $month = &pad($month);
	    $day = &pad($day);
	    $date = "$year$month$day";
	    # print "$date\n";
	} elsif ( m/^T/ ) {
	    # Transaction Amount
	    s/,//g;		# remove , from numbers (i.e. thousands)
	    $amt = substr($_,1);
	    if ($amt >= 0) {
		$credit = $amt;
		$debit = 0;
	    } else {
		$debit = substr($amt,1); # remove the '-' to make amt >= 0
		$credit = 0;
	    }
	    # print "credit = $credit  debit = $debit\n";
	} elsif ( m/^C/ ) {
	    # Cleared
	    $cleared = substr($_,1);
	    # print "Cleared = $cleared\n";
	} elsif ( m/^N/ ) {
	    # check Number
	    $check = substr($_,1);
	    # print "Check # = $check\n";
	} elsif ( m/^P/ ) {
	    # descriPtion
	    $desc = substr($_,1);
	    # print "$desc\n";
	} elsif ( m/^L/ ) {
	    # category
	    $cat = substr($_,1);
	    #print "$cat\n";
	} elsif ( m/^S/ ) {
	    # split category
	    if ($split eq "") {
		# first split
		$split = "|".substr($_,1)."|";
	    } else {
		# not first split :)
		$split = $split.substr($_,1)."|";
	    }
	} elsif ( m/^E/ ) {
	    # split comment
	    $split = $split.substr($_,1)."|"
	} elsif ( m/^\$/ ) {
	    # split amount
	    $split = $split.substr($_,1)."|";
	} elsif ( m/^M/ ) {
	    # coMment
	    $com = substr($_,1);
	    #print "Comment = $com\n";
	} elsif ( m/^\^/ ) {
	    #print "End of record\n";
	    if ($split ne "") {
		$cat = $split;
	    }
	    &create_trans(
	    	"$date:$check:$desc:$debit:$credit:$cat:$com:$cleared:0.00" );
	    ($date, $check, $desc, $debit, $credit, $cat, $split, $com, 
	    	$cleared) = ("", "", "", "", "", "", "", "", "");
	} elsif ( $_ eq "" ) {
	    # toss empty lines ...
	} else {
	    print "unknown data: $_\n";
	}
    }

    close(QIF);

    return "ok";
}


# save all the precious data to a file
sub save_trans {
    # in: file base name
    # out: result

    local($file) = @_;

    if ($calced == 0) {
	&calc_trans();
    }

    open(SAVE, ">$file.new");

    if ($sorted_keys == 0) {
	&sort_keys();
    }

    foreach $key (@KEYS) {
	print (SAVE "$TRANS{$key}\n");
    }

    close(SAVE);

    unlink("$file.bak");
    rename("$file.cbb", "$file.bak");
    rename("$file.new", "$file.cbb");

    return "ok";
}


# initialize the memorized transaction list
sub init_mems {
    # out: result

    %MEMS = ();
    $sorted_memkeys = 1;

    return "ok";
}


# insert a memorized transaction into the MEMS list
sub insert_mem {
    # in: trans
    # out: trans

    local($trans) = @_;

    ($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
	split(/:/, $trans);

    $MEMS{$desc} = $trans;

    print DEBUG "insert_mem:  $desc -> $trans\n" if $debug;

    return "$trans";
}


# build the MEMS list
sub rehash_mems {
    # out: result

    &init_mems();

    if ($calced == 0) {
	&calc_trans();
    }

    if ($sorted_keys == 0) {
	&sort_keys();
    }

    foreach $key (@KEYS) {
	&insert_mem($TRANS{$key});
    }

    @MEMKEYS = sort(keys %MEMS);

    return "ok";
}


# attempt to find a transaction matching the description
# incomplete descriptions are allowed
sub find_mem {
    # in: desc
    # out: trans

    local($desc) = @_;

    if ( $desc ne "" ) {
        foreach $memkey (@MEMKEYS) {
	    if ( $memkey =~ m/^$desc/i ) {
	        return "$desc:$MEMS{$memkey}";
	    }
        }
    }

    return "none";
}


# initialize the categories list
sub init_cats {
    # out: result

    %CATS = ();
    $sorted_catkeys = 1;

    return "ok";
}


# set @CATKEYS = sorted list of transaction keys
sub sort_catkeys {
    $sorted_catkeys = 1;

    print DEBUG "sort_catkeys()\n" if $debug;
    @CATKEYS = sort(keys %CATS);
}


# edit a category in the category list
sub edit_cat {
    # in: category
    # out: category

    local($cat) = @_;

    $sorted_catkeys = 0;
    ($key, $desc, $tax) = split(/:/, $cat);

    $CATS{$key} = "$desc:$tax";

    print DEBUG "cat-edit:  $cat\n" if $debug;

    return "$cat";
}


# insert a category into the category list
sub insert_cat {
    # in: category
    # out: category

    local($cat) = @_;

    $sorted_catkeys = 0;
    ($key, $desc, $tax) = split(/:/, $cat);

    $CATS{$key} = "$desc:$tax";

    print DEBUG "cat-insert:  $cat\n" if $debug;

    return "$cat";
}


# delete a category from the category list
sub delete_cat {
    # in: category

    local($cat) = @_;

    $sorted_catkeys = 0;
    ($key, $desc, $tax) = split(/:/, $cat);

    delete $CATS{$key};

    print DEBUG "cat-deleted:  $cat\n" if $debug;

    return "$cat";
}


# attempt to find a category matching the key
# incomplete keys are allowed
sub find_cat {
    # in: key
    # out: category

    local($key) = @_;

    if ($sorted_catkeys == 0) {
	&sort_catkeys();
    }

    if ( $key ne "" ) {
        foreach $catkey (@CATKEYS) {
	    if ( $catkey =~ m/^$key/i ) {
	        return $catkey;
	    }
        }
    }

    return "none";
}


# load a categories list
sub load_cats {
    # in: file base name
    # out: result

    local($file) = @_;

    $sorted_catkeys = 0;

    open(LOAD, "<$file.cat") || return "error";

    while ( <LOAD> ) {
	chop;
	&insert_cat($_);
    }

    close(LOAD);

    return "ok";
}


# save the category list
sub save_cats {
    # in: file base name
    # out: result

    local($file) = @_;

    if ($sorted_catkeys == 0) {
	&sort_catkeys();
    }

    open(SAVE, ">$file.cat") || return "error";

    foreach $key (@CATKEYS) {
        print( SAVE "$key:$CATS{$key}\n" );
    }

    close(SAVE);

    return "ok";
}


# main command parsing loop
while (<>) {
    chop;

    print DEBUG "Command string:  '$_'\n" if $debug;

    if ( m/ / ) {
	($command, $arg) = split(/ /, $_, 2);
    } else {
	($command, $arg) = ($_, "");
    }

    if ($command eq "create_trans") {
	print DEBUG "calling create_trans($arg)\n" if $debug;
	print &create_trans($arg)."\n";
    } elsif ($command eq "update_trans") {
	print DEBUG "calling update_trans($arg)\n" if $debug;
	print &update_trans($arg)."\n";
    } elsif ($command eq "delete_trans") {
	print DEBUG "calling delete_trans($arg)\n" if $debug;
	print &delete_trans($arg)."\n";
    } elsif ($command eq "next_trans") {
	print DEBUG "next_trans()\n" if $debug;
	print &next_trans()."\n";
    } elsif ($command eq "prev_trans") {
	print "prev_trans()\n";
    } elsif ($command eq "find_trans") {
	print DEBUG "find_trans($arg)\n" if $debug;
	print &find_trans($arg)."\n";
    } elsif ($command eq "get_current_index") {
	print DEBUG "get_current_index()\n" if $debug;
	print &get_current_index()."\n";
    } elsif ($command eq "first_trans") {
	print DEBUG "first_trans()\n" if $debug;
	print &first_trans()."\n";
    } elsif ($command eq "all_trans") {
	print DEBUG "all_trans()\n" if $debug;
	print &all_trans()."\n";
    } elsif ($command eq "last_trans") {
	print "last_trans()\n";
    } elsif ($command eq "first_uncleared_trans") {
	print DEBUG "first_uncleared_trans()\n" if $debug;
	print &first_uncleared_trans()."\n";
    } elsif ($command eq "last_uncleared_trans") {
	print "last_uncleared_trans()\n";
    } elsif ($command eq "next_uncleared_trans") {
	print DEBUG "next_uncleared_trans()\n" if $debug;
	print &next_uncleared_trans()."\n";
    } elsif ($command eq "prev_uncleared_trans") {
	print "prev_uncleared_trans()\n";
    } elsif ($command eq "select_trans") {
	print DEBUG "select_trans()\n" if $debug;
	print &select_trans($arg)."\n";
    } elsif ($command eq "unselect_trans") {
	print DEBUG "unselect_trans()\n" if $debug;
	print &unselect_trans($arg)."\n";
    } elsif ($command eq "clear_trans") {
	print DEBUG "clear_trans()\n" if $debug;
	print &clear_trans()."\n";
    } elsif ($command eq "sort_trans") {
	print "sort_trans()\n";
    } elsif ($command eq "init_trans") {
	print DEBUG "calling init_trans()\n" if $debug;
	print &init_trans()."\n";
    } elsif ($command eq "load_trans") {
	print DEBUG "calling load_trans($arg)\n" if $debug;
	print &load_trans($arg)."\n";
    } elsif ($command eq "import_qif") {
	print DEBUG "calling import_qif($arg)\n" if $debug;
	print &import_qif($arg)."\n";
    } elsif ($command eq "save_trans") {
	print DEBUG "calling save_trans($arg)\n" if $debug;
	print &save_trans($arg)."\n";
    } elsif ($command eq "rehash_mems") {
	print DEBUG "calling rehash_mems()\n" if $debug;
	print &rehash_mems()."\n";
    } elsif ($command eq "find_mem") {
	print DEBUG "calling find_mem($arg)\n" if $debug;
	print &find_mem($arg)."\n";
    } elsif ($command eq "init_cats") {
	print DEBUG "calling init_cats()\n" if $debug;
	print &init_cats()."\n";
    } elsif ($command eq "edit_cat") {
	print DEBUG "calling edit_cat($arg)\n" if $debug;
	print &edit_cat($arg)."\n";
    } elsif ($command eq "insert_cat") {
	print DEBUG "calling insert_cat($arg)\n" if $debug;
	print &insert_cat($arg)."\n";
    } elsif ($command eq "delete_cat") {
	print DEBUG "calling delete_cat($arg)\n" if $debug;
	print &delete_cat($arg)."\n";
    } elsif ($command eq "first_cat") {
	print "calling first_cat($arg)\n";
    } elsif ($command eq "next_cat") {
	print "calling next_cat($arg)\n";
    } elsif ($command eq "find_cat") {
	print DEBUG "calling find_cat($arg)\n";
        print &find_cat($arg)."\n";
    } elsif ($command eq "load_cats") {
	print DEBUG "calling load_cats($arg)\n" if $debug;
	print &load_cats($arg)."\n";
    } elsif ($command eq "save_cats") {
	print DEBUG "calling save_cats($arg)\n";
	print &save_cats($arg)."\n";
    } elsif ($command eq "quit") {
	exit(0);
    } else {
	# unknown command ... return error
	print "error\n";
    }
}

# ----------------------------------------------------------------------------
# $Log: engine.pl,v $
# Revision 1.6  1994/10/17  13:26:21  curt
# Added all-trans function.
#
# Revision 1.5  1994/10/14  17:05:18  clolson
# Miscellaneous cleanups in preparation for releasing verion 0.40a
#
# Revision 1.4  1994/10/13  23:57:14  curt
# Finished memorized transactions.
#
# Revision 1.3  1994/10/13  22:08:56  clolson
# Worked on adding memorized transactions.
#
# Revision 1.2  1994/10/13  15:54:46  curt
# Turned debugging off by default.
#
# Revision 1.1  1994/10/11  15:05:01  curt
# Official name is now cbb (for now)
#
# Revision 1.22  1994/10/04  15:40:33  curt
# Categories saved when data is saved.
#
# Revision 1.21  1994/10/03  02:52:07  curt
# *.dat --> *.cbb
#
# Revision 1.20  1994/09/30  19:49:34  clolson
# Working on split category completion.
#
# Revision 1.19  1994/09/29  23:16:02  clolson
# Started work on categories
#
# Revision 1.18  1994/09/28  13:44:26  curt
# Restructured things so that data files look like file.dat, file.bat, file.cat
# Loading and saving are done by basename, i.e. file, data files from previous
# versions will have to be renamed to something.dat
#
# Also fixed a small bug where default tab bindings weren't being set at
# startup.
#
# Revision 1.17  1994/09/20  14:12:45  clolson
# Subjected code to gnu public license :)
#
# Revision 1.16  1994/09/14  03:49:25  clolson
# Worked on selecting and clearing transactions
#
# Revision 1.15  1994/09/12  15:19:45  curt
# Fixed first_uncleared_trans() and next_uncleared_trans() to really work
# right.
#
# Revision 1.14  1994/09/09  20:25:32  clolson
# Started work on first_uncleared_trans() and next_uncleared_trans()
#
# Revision 1.13  1994/09/07  23:07:48  clolson
# Minor modification to import_qif ... to toss blank lines
#
# Revision 1.12  1994/09/06  03:02:15  curt
# Removed data file '*.data' dependency
#
# Revision 1.11  1994/09/03  02:47:05  clolson
# Working on updating entries
#
# Revision 1.10  1994/09/02  03:40:28  curt
# Updated debugging messages.
# Removed print statement.
#
# Revision 1.9  1994/08/29  03:53:37  curt
# Added header to listbox (needs tweaking)
# Category now says -Splits- if there are splits.
#
# Revision 1.8  1994/08/29  00:16:23  curt
# Added calc_trans()
#
# Revision 1.7  1994/08/26  20:33:46  clolson
# Added @KEYS, $sorted_keys, and $current (documented in code)
# Added first_trans() and next_trans()
#
# Revision 1.6  1994/08/25  18:47:34  clolson
# Added a init_trans command.
#
# Revision 1.5  1994/08/25  04:17:14  curt
# Finished first stab at import_qif().
# Also finished load_trans() and save_trans().
#
# Revision 1.4  1994/08/25  02:08:58  clolson
# More work on .qif imports
#
# Revision 1.3  1994/08/24  04:09:11  curt
# Started working on routine to import qif (Quicken export) files.  But it is
# getting late so I must go to bed.
#
# Revision 1.2  1994/08/24  02:38:07  curt
# Added create, update, delete, and print transaction functions.
#
# Revision 1.1  1994/08/23  03:59:47  curt
# Initial stub of a transaction handling perl script.
#
