Finance-QIF-3.02/0000755000076400007640000000000011451006654013334 5ustar matthewmatthewFinance-QIF-3.02/examples/0000755000076400007640000000000011451006654015152 5ustar matthewmatthewFinance-QIF-3.02/examples/readwrite0000644000076400007640000000105410615042633017061 0ustar matthewmatthewuse Finance::QIF; if ( $#ARGV != 1 ) { print STDERR "USAGE: readwrite \n"; exit; } my $in = Finance::QIF->new( file => $ARGV[0], autodetect => 1 ); my $out = Finance::QIF->new( file => ">" . $ARGV[1], record_separator => $in->record_separator ); my $header = ""; while ( my $record = $in->next() ) { if ( $header ne $record->{header} ) { $out->header( $record->{header} ); $header = $record->{header}; } $out->write($record); } $in->close(); $out->close(); Finance-QIF-3.02/examples/qifdumper0000644000076400007640000000252210615042632017067 0ustar matthewmatthewuse Finance::QIF; if ( $#ARGV != 0 ) { print STDERR "USAGE: qifdumper \n"; exit; } my $qif = Finance::QIF->new( file => $ARGV[0], autodetect => 1 ); while ( my $record = $qif->next() ) { print "Header: " . $record->{header} . "\n"; foreach my $key ( keys %{$record} ) { next if ( $key eq "header" || $key eq "splits" || $key eq "budget" || $key eq "prices" ); print " " . $key . ": " . $record->{$key} . "\n"; } if ( exists( $record->{splits} ) ) { foreach my $split ( @{ $record->{splits} } ) { foreach my $key ( keys %{$split} ) { print " Split: " . $key . ": " . $split->{$key} . "\n"; } } } if ( exists( $record->{budget} ) ) { print " budget: "; foreach my $amount ( @{ $record->{budget} } ) { print " " . $amount; } print "\n"; } if ( exists( $record->{prices} ) ) { print " Date Close Max Min Volume\n"; $format = " %8s %7.2f %7.2f %7.2f %-8d\n"; foreach my $price ( @{ $record->{prices} } ) { printf( $format, $price->{"date"}, $price->{"close"}, $price->{"max"}, $price->{"min"}, $price->{"volume"} ); } } } Finance-QIF-3.02/META.yml0000664000076400007640000000077611451006654014621 0ustar matthewmatthew--- #YAML:1.0 name: Finance-QIF version: 3.02 abstract: Parse and create Quicken Interchange Format files license: perl author: - Matthew McGillis & Phil Lobbes generated_by: ExtUtils::MakeMaker version 6.42 distribution_type: module requires: IO::File: 1.11 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.3.html version: 1.3 Finance-QIF-3.02/Makefile.PL0000644000076400007640000000126611042154333015305 0ustar matthewmatthewuse ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( 'NAME' => 'Finance::QIF', 'VERSION_FROM' => 'lib/Finance/QIF.pm', 'PREREQ_PM' => { 'IO::File' => 1.11 }, # e.g., Module::Name => 1.1 ( $] >= 5.005 ? ## keywords supported since 5.005 ( LICENSE => 'perl', ABSTRACT_FROM => 'lib/Finance/QIF.pm', AUTHOR => 'Matthew McGillis & ' . 'Phil Lobbes ' ) : () ), ); Finance-QIF-3.02/lib/0000755000076400007640000000000011451006654014102 5ustar matthewmatthewFinance-QIF-3.02/lib/Finance/0000755000076400007640000000000011451006654015445 5ustar matthewmatthewFinance-QIF-3.02/lib/Finance/QIF.pm0000644000076400007640000010032611451005232016413 0ustar matthewmatthewpackage Finance::QIF; use 5.006; use strict; use warnings; use Carp; use IO::File; our $VERSION = '3.02'; $VERSION = eval $VERSION; my %noninvestment = ( "D" => "date", "T" => "transaction", "U" => "total", #Quicken 2005 added this which is usually the same #as T but can sometimes be higher. "C" => "status", "N" => "number", "P" => "payee", "M" => "memo", "A" => "address", "L" => "category", "S" => "splits" ); my %split = ( "S" => "category", "E" => "memo", '$' => "amount" ); my %investment = ( "D" => "date", "N" => "action", "Y" => "security", "I" => "price", "Q" => "quantity", "T" => "transaction", "U" => "total", #Quicken 2005 added this which is usually the same #as T but can sometimes be higher. "C" => "status", "P" => "text", "M" => "memo", "O" => "commission", "L" => "account", '$' => "amount" ); my %account = ( "N" => "name", "D" => "description", "L" => "limit", "X" => "tax", "A" => "note", "T" => "type", "B" => "balance" ); my %category = ( "N" => "name", "D" => "description", "B" => "budget", "E" => "expense", "I" => "income", "T" => "tax", "R" => "schedule" ); my %class = ( "N" => "name", "D" => "description" ); my %memorized = ( "K" => "type", "T" => "transaction", "U" => "total", #Quicken 2005 added this which is usually the same as #as T but can sometimes be higher. "C" => "status", "P" => "payee", "M" => "memo", "A" => "address", "L" => "category", "S" => "splits", "N" => "action", #Quicken 2006 added N, Y, I, Q, $ for investment "Y" => "security", "I" => "price", "Q" => "quantity", '$' => "amount", "1" => "first", "2" => "years", "3" => "made", "4" => "periods", "5" => "interest", "6" => "balance", "7" => "loan" ); my %security = ( "N" => "security", "S" => "symbol", "T" => "type", "G" => "goal", ); my %budget = ( "N" => "name", "D" => "description", "E" => "expense", "I" => "income", "T" => "tax", "R" => "schedule", "B" => "budget" ); my %payee = ( "P" => "name", "A" => "address", "C" => "city", "S" => "state", "Z" => "zip", "Y" => "country", "N" => "phone", "#" => "account" ); my %prices = ( "S" => "symbol", "P" => "price" ); my %price = ( "C" => "close", "D" => "date", "X" => "max", "I" => "min", "V" => "volume" ); my %nofields = (); my %header = ( "Type:Bank" => \%noninvestment, "Type:Cash" => \%noninvestment, "Type:CCard" => \%noninvestment, "Type:Invst" => \%investment, "Type:Oth A" => \%noninvestment, "Type:Oth L" => \%noninvestment, "Account" => \%account, "Type:Cat" => \%category, "Type:Class" => \%class, "Type:Memorized" => \%memorized, "Type:Security" => \%security, "Type:Budget" => \%budget, "Type:Payee" => \%payee, "Type:Prices" => \%prices, "Option:AutoSwitch" => \%nofields, "Option:AllXfr" => \%nofields, "Clear:AutoSwitch" => \%nofields ); sub new { my $class = shift; my %opt = @_; my $self = {}; $self->{debug} = $opt{debug} || 0; $self->{autodetect} = $opt{autodetect} || 0; $self->{trim_white_space} = $opt{trim_white_space} || 0; $self->{record_separator} = $opt{record_separator} || $/; bless( $self, $class ); if ( $opt{file} ) { $self->file( $opt{file} ); $self->open; } return $self; } sub file { my $self = shift; if (@_) { my @file = ( ref( $_[0] ) eq "ARRAY" ? @{ shift @_ } : (), @_ ); $self->{file} = [@file]; } if ( $self->{file} ) { return wantarray ? @{ $self->{file} } : $self->{file}->[0]; } else { return undef; } } sub record_separator { my $self = shift; return $self->{record_separator}; } sub _filehandle { my $self = shift; if (@_) { my @args = @_; $self->{_filehandle} = IO::File->new(@args) or croak("Failed to open file '$args[0]': $!"); binmode( $self->{_filehandle} ); $self->{_linecount} = 0; } if ( !$self->{_filehandle} ) { croak("No filehandle available"); } return $self->{_filehandle}; } sub open { my $self = shift; if (@_) { $self->file(@_); } if ( $self->file ) { $self->_filehandle( $self->file ); if ( $self->{autodetect} ) { if ( $self->_filehandle->seek( -2, 2 ) ) { my $buffer = ""; $self->_filehandle->read( $buffer, 2 ); if ( $buffer eq "\015\012" ) { $self->{record_separator} = "\015\012"; } elsif ( $buffer =~ /\012$/ ) { $self->{record_separator} = "\012"; } elsif ( $buffer =~ /\015$/ ) { $self->{record_separator} = "\015"; } } } $self->reset(); } else { croak("No file specified"); } } sub next { my $self = shift; my %object; my $continue = 1; my $csplit; # Need to keep track of current split for adding split values if ( $self->_filehandle->eof ) { return undef; } if ( exists( $self->{header} ) ) { $object{header} = $self->{header}; } while ( !$self->_filehandle->eof && $continue ) { my $line = $self->_getline; next if ( $line =~ /^\s*$/ ); my ( $field, $value ) = $self->_parseline($line); if ( $field eq '!' ) { $value =~ s/\s+$//; # Headers sometimes have trailing white space $self->{header} = $value; $object{header} = $value; if ( !exists( $header{$value} ) ) { $self->_warning("Unknown header format '$value'"); } } else { if ( $field eq '^' ) { $continue = 0; } else { if ( !exists( $header{ $object{header} } ) && !( exists( $header{"split"} ) && ( $object{header} eq "noninvestment" || $object{header} eq "memorized" ) ) ) { $self->_warning( "Unknown header '$object{header}' can't process line"); } elsif ( $object{header} eq "Type:Prices" ) { $object{"symbol"} = $field; push( @{ $object{"prices"} }, $value ); } elsif ($field eq 'A' && $header{ $object{header} }{$field} eq "address" ) { if ( $self->{header} eq "Type:Payee" ) { # The address fields are numbered for this record type if ( length($value) == 0 ) { $self->_warning( 'Improper address record for ' . 'this record type' ); } else { $value = substr( $value, 1 ); } } if ( exists( $object{ $header{ $object{header} }{$field} } ) && $object{ $header{ $object{header} }{$field} } ne "" ) { $object{ $header{ $object{header} }{$field} } .= "\n"; } $object{ $header{ $object{header} }{$field} } .= $value; } elsif ($field eq 'S' && $header{ $object{header} }{$field} eq "splits" ) { my %mysplit; # We assume "S" always appears first $mysplit{ $split{$field} } = $value; push( @{ $object{splits} }, \%mysplit ); $csplit = \%mysplit; } elsif ( ( $field eq 'E' || $field eq '$' ) && $csplit ) { # this currently assumes the "S" was found first $csplit->{ $split{$field} } = $value; } elsif ($field eq 'B' && $header{ $object{header} }{$field} eq "budget" ) { push( @{ $object{budget} }, $value ); } elsif ( exists( $header{ $object{header} }{$field} ) ) { $object{ $header{ $object{header} }{$field} } = $value; } else { $self->_warning("Unknown field '$field'"); } } } } # Must check that we have a valid record to return if ( scalar( keys %object ) > 1 ) { return \%object; } else { return undef; } } sub _parseline { my $self = shift; my $line = shift; my @result; if ( $line !~ /^!/ && exists( $self->{header} ) && $self->{header} eq "Type:Prices" ) { my %price; $line =~ s/\"//g; my @data = split( ",", $line ); $result[0] = $data[0]; $price{"close"} = $data[1]; $price{"date"} = $data[2]; if ( scalar(@data) > 3 ) { $price{"max"} = $data[3]; $price{"min"} = $data[4]; $price{"volume"} = $data[5]; } $result[1] = \%price; } else { $result[0] = substr( $line, 0, 1 ); $result[1] = substr( $line, 1 ); if ( $self->{trim_white_space} ) { $result[1] =~ s/^\s*(.*?)\s*$/$1/; } } return @result; } sub _getline { my $self = shift; local $/ = $self->record_separator; my $line = $self->_filehandle->getline; chomp($line); $self->{_linecount}++; return $line; } sub _warning { my $self = shift; my $message = shift; carp( $message . " in file '" . $self->file . "' line " . $self->{_linecount} ); } sub header { my $self = shift; my $header = shift; my $fh = $self->_filehandle; local $\ = $self->{record_separator}; print( $fh "!", $header ); # used during write to validate passed record is appropriate for # current header also generate reverse lookup for mapping record # values to file key identifier. $self->{currentheader} = $header; foreach my $key ( keys %{ $header{$header} } ) { $self->{reversemap}{ $header{$header}{$key} } = $key; } if ( exists( $header{$header}{S} ) && $header{$header}{S} eq "splits" ) { foreach my $key ( keys %split ) { $self->{reversesplitsmap}{ $split{$key} } = $key; } } $self->{_linecount}++; if ( !exists( $header{$header} ) ) { $self->_warning("Unsupported header '$header' written to file"); } } sub write { my $self = shift; my $record = shift; if ( $record->{header} eq $self->{currentheader} ) { if ( $record->{header} eq "Type:Prices" ) { if ( exists( $record->{symbol} ) && exists( $record->{prices} ) ) { foreach my $price ( @{ $record->{prices} } ) { if ( exists( $price->{close} ) && exists( $price->{date} ) && exists( $price->{max} ) && exists( $price->{min} ) && exists( $price->{volume} ) ) { $self->_writeline( join( ",", '"' . $record->{symbol} . '"', $price->{close}, '"' . $price->{date} . '"', $price->{max}, $price->{min}, $price->{volume} ) ); } elsif (exists( $price->{close} ) && exists( $price->{date} ) ) { $self->_writeline( join( ",", '"' . $record->{symbol} . '"', $price->{close}, '"' . $price->{date} . '"' ) ); } else { $self->_warning("Prices missing a required field"); } } $self->_writeline("^"); } else { $self->_warning("Record missing 'symbol' or 'prices'"); } } else { foreach my $value ( keys %{$record} ) { next if ( $value eq "header" || $value eq "splits" || ( $self->{currentheader} eq "Type:Memorized" && $value eq "transaction" ) ); if ( exists( $self->{reversemap}{$value} ) ) { if ( $value eq "address" ) { my @lines = split( "\n", $record->{$value} ); if ( $self->{currentheader} eq "Type:Payee" ) { # The address fields are numbered for this record type for ( my $count = 0 ; $count < 3 ; $count++ ) { if ( $count <= $#lines ) { $self->_writeline( "A", $count, $lines[$count] ); } else { $self->_writeline( "A", $count ); } } } else { for ( my $count = 0 ; $count < 6 ; $count++ ) { if ( $count <= $#lines ) { $self->_writeline( "A", $lines[$count] ); } else { $self->_writeline("A"); } } } } elsif ( $value eq "budget" ) { foreach my $amount ( @{ $record->{$value} } ) { $self->_writeline( $self->{reversemap}{$value}, $amount ); } } else { $self->_writeline( $self->{reversemap}{$value}, $record->{$value} ); } } else { $self->_warning( "Unsupported field '$value'" . " found in record ignored" ); } } if ( exists( $record->{splits} ) ) { foreach my $s ( @{ $record->{splits} } ) { foreach my $key ( 'category', 'memo', 'amount' ) { if ( exists( $s->{$key} ) ) { $self->_writeline( $self->{reversesplitsmap}{$key}, $s->{$key} ); } else { $self->_writeline( $self->{reversesplitsmap}{$key} ); } } } } if ( $self->{currentheader} eq "Type:Memorized" && exists( $record->{transaction} ) ) { $self->_writeline( $self->{reversemap}{"transaction"}, $record->{"transaction"} ); } $self->_writeline("^"); } } else { $self->_warning( "Record header type '" . $record->{header} . "' does not match current output header type " . $self->{currentheader} . "." ); } } sub _writeline { my $self = shift; my $fh = $self->_filehandle; local $\ = $self->{record_separator}; print( $fh @_ ); $self->{_linecount}++; } sub reset { my $self = shift; map( $self->{$_} = undef, # initialize internally used variables qw(_linecount header currentheader reversemap reversesplitsmap) ); $self->_filehandle->seek( 0, 0 ); } sub close { my $self = shift; $self->_filehandle->close; } 1; __END__ =head1 NAME Finance::QIF - Parse and create Quicken Interchange Format files =head1 SYNOPSIS use Finance::QIF; my $qif = Finance::QIF->new( file => "test.qif" ); while ( my $record = $qif->next ) { print( "Header: ", $record->{header}, "\n" ); foreach my $key ( keys %{$record} ) { next if ( $key eq "header" || $key eq "splits" || $key eq "budget" || $key eq "prices" ); print( " ", $key, ": ", $record->{$key}, "\n" ); } if ( exists( $record->{splits} ) ) { foreach my $split ( @{ $record->{splits} } ) { foreach my $key ( keys %{$split} ) { print( " Split: ", $key, ": ", $split->{$key}, "\n" ); } } } if ( exists( $record->{budget} ) ) { print(" Budget: "); foreach my $amount ( @{ $record->{budget} } ) { print( " ", $amount ); } print("\n"); } if ( exists( $record->{prices} ) ) { print(" Date Close Max Min Volume\n"); $format = " %8s %7.2f %7.2f %7.2f %-8d\n"; foreach my $price ( @{ $record->{prices} } ) { printf( $format, $price->{"date"}, $price->{"close"}, $price->{"max"}, $price->{"min"}, $price->{"volume"} ); } } } =head1 DESCRIPTION Finance::QIF is a module for working with QIF (Quicken Interchange Format) files in Perl. This module reads QIF data records from a file passing each successive record to the caller for processing. This module also has the capability of writing QIF records to a file. The QIF file format typically consists of a header containing a record or transaction type, followed by associated data records. Within a file there may be multiple headers. Headers are usually followed by data records, however data is not required to always follow a header. A hash reference is returned for each record read from a file. The hash will have a "header" value which contains the header type that was read along with all supported values found for that record. If a value is not specified in the data file, the value will not exist in this hash. No processing or validation is done on values found in files or data structures to try and convert them into appropriate types and formats. It is expected that users of this module or extensions to this module will do any additional processing or validation as required. =head2 RECORD TYPES & VALUES The following record types are currently supported by this module: =over =item Type:Bank, Type:Cash, Type:CCard, Type:Oth A, Type:Oth L These are non investment ledger transactions. All of these record types support the following values. =over =item date Date of transaction. =item transaction Dollar amount of transaction. =item total Dollar amount of transaction. This is generally the same as transaction but in some cases can be higher. (Introduced in Quicken 2005 for windows) =item status Reconciliation status of transaction. =item number Check number of transaction. =item payee Who the transaction was made to. =item memo Additional text describing the transaction. =item address Address of payee. =item category Category the transaction is assigned to. =item splits If the transaction contains splits this will be defined and consist of an array of hash references. With each split potentially having the following values. =over =item category Category the split is assigned to. =item memo Additional text describing the split. =item amount Dollar amount of split. =back =back =item Type:Invst This is for Investment ledger transactions. The following values are supported for this record type. =over =item date Date of transaction. =item action Type of transaction like buy, sell, ... =item security Security name of transaction. =item price Price of security at time of transaction. =item quantity Number of shares purchased. =item transaction Cost of shares in transaction. =item status Reconciliation status of transaction. =item text Text for non security specific transaction. =item memo Additional text describing transaction. =item commission Commission fees related to transaction. =item account Account related to security specific transaction. =item amount Dollar amount of transaction. =item total Dollar amount of transaction. This is generally the same as amount but in some cases can be higher. (Introduced in Quicken 2005 for windows) =back =item Account This is a list of accounts. In cases where it is used in a file by first providing one account record followed by a investment or non-investment record type and its transactions, it means that that set of transactions is related to the specified account. In other cases it can just be a sequence of Account records. Each account record supports the following values. =over =item name Account name. =item description Account description. =item limit Account limit usually for credit card accounts that have some upper limit over credit. =item tax Defined if the account is tax related. =item note Additional information about the account. =item type Type of account. =item balance Current balance of account. =back =item Type:Cat This is a list of categories. The following values are supported for category records. =over =item name Name of category. =item description Description of category. =item budget An array of 12 values Jan-Dec to represent the budget amount for each month. =item expense Usually exists if the category is an expense account however this is often a default assumed value and doesn't show up in files. =item income Exists if the category is an income account. =item tax Exists if this category is tax related. =item schedule If this category is tax related this specifies what tax schedule it is related if defined. =back =item Type:Class This is a list of classes. The following values are supported for class records. =over =item name Name of class. =item description Description of class. =back =item Type:Memorized This is a list of memorized transactions. The following values are supported for memorized transaction records. =over =item type Type of memorized transaction "C" for check, "D" for deposit, "P" for payment, "I" for investment, and "E" for electronic payee. =item transaction Dollar amount of transaction. =item total Dollar amount of transaction. This is generally the same as amount but in some cases can be higher. (Introduced in Quicken 2005 for windows) =item status Reconciliation status of transaction. =item payee Who the transaction was made to. =item memo Additional text describing the transaction. =item address Address of payee. =item category Category the transaction is assigned to. =item action Type of investment transaction like buty, sell, ... (Inroduced in Quicken 2006 for windows) =item security Security name of transaction. (Inroduced in Quicken 2006 for windows) =item price Price of security. (Inroduced in Quicken 2006 for windows) =item quantity Quantity of security. (Inroduced in Quicken 2006 for windows) =item amount Dollar amount of transaction. (Introduced in Quicken 2006 for windows) =item splits If the transaction contains splits this will be defined and consist of an array of hashes. With each split potentially having the following values. =over =item category Category the split is assigned to. =item memo Additional text describing the split. =item amount Dollar amount of split. =back =item first First payment date. =item years Total years for loan. =item made Number of payments already made. =item periods Number of periods per year. =item interest Interest rate of loan. =item balance Current loan balance. =item loan Original loan amount. =back =item Type:Security This is a list of securities. The following values are supported for security records. =over =item security Security name. =item symbol Security symbol. =item type Security type. =item goal Security goal. =back =item Type:Budget This is a list of budget values for categories. The following values are supported for budget records. =over =item name Category name of budgeted item. =item description Category Description of budgeted item. =item expense Usually exists if the category is an expense account however this is often a default assumed value and doesn't show up in files. =item income Exists if the category is an income account. =item tax Exists if this category is tax related. =item schedule If this category is tax related this specifies what tax schedule it is related if defined. =item budget An array of 12 values Jan-Dec to represent the budget amount for each month. =back =item Type:Payee This is a list online payee accounts. The following values are supported for online payee account records. =over =item name Name of payees. =item address Address of payee. =item city City of payee. =item state State of payee =item zip Zipcode of payee. =item country Country of payee. =item phone Phone number of payee. =item account Account number for payee transaction. =back =item Type:Prices This is a list of prices for a security. The following values are supported for security prices records. =over =item symbol Security Symbol. =item prices An array of hashes. With each hash having the following values. =over =item date Date of security values. =item close Close value of security for the date. =item max Max value of security for the date. =item min Min value of security for the date. =item volume Number of shares of security exchanged for the date. =back =back =item Option:AllXfr, Option:AutoSwitch, Clear:AutoSwitch These record types aren't related to transactions but instead provided ways to control how Quicken processes the QIF file. They have no impact on how this software operates and are ignored when found. =back Note: If this software finds unsupported record types or values in a data file a warning will be generated containing information on what unexpected value was found. =head1 METHODS =head2 new() Creates a new instance of Finance::QIF. Supports the following initializing values. my $qif = Finance::QIF->new( file => "myfile", debug => 1 ); If the file is specified it will be opened on new. =over =item file Specifies file to use for processing. See L for details. my $in = Finance::QIF->new( file => "myfile" ); OR my $in = Finance::QIF->new( file => [ "myfile", "<:crlf" ] ); For output files, be sure to open the file in write mode. For example: my $out = Finance::QIF->new( file => ">myfile" ); =item record_separator Can be used to redefine the QIF record separator. Default is $/. my $in = Finance::QIF->new( record_separator => "\012" ); Note: For MacOS X it will most likely be necessary to change this to "\015". Quicken on MacOS X generates files with "\015" as the separator which is typical of Mac however the native perl in MacOS X is unix based and uses the default unix separator which is "\012". See L for another option. =item autodetect Enable auto detection of the record separator based on the file contents. Default is "0". my $in = Finance::QIF->new( autodetect => 1 ); Perl uses $/ to define line separators for text files. Perl sets this value according to the OS perl is running on: Windows="\015\012" Mac="\015" Unix="\012" In many cases you may find yourself with text files that do not match the OS. In these cases Finance::QIF by default will not process that QIF file correctly. This feature is an attempt to help with the most common cases of having the wrong text file for the OS Finance::QIF is running on. This feature depends on being able to seek to the end of the file and reading the last 2 characters to determine the proper separator. If a seek can not be performed or the last 2 characters are not a proper separator the record_separator will default to $/ or the value passed in. If a valid record_separator is found then it will be set according to what was in the file. This code requires a file use a consistent line separator. If you find your self dealing with unusual files containing mixed separators you need to first Normalize the file to a consistent separator. Normalizing a text file to have a consistent line separator is done in modules like File::LocalizeNewlines or Template::Parser::LocalizeNewlines so if you are having issues with trying to process poorly formated text files look at preprocessing them with something like those before passing on to Finance::QIF. =item trim_white_space Can be used to remove leading and trailing white space from values returned. Default is "0". my $qif = Finance::QIF->new( trim_white_space => 1 ); =item debug Can be used to output debug information. Default is "0". my $qif = Finance::QIF->new( debug => 1 ); =back =head2 file() Specify file name and optionally additional parameters that will be used to obtain a filehandle. The argument can be a filename (SCALAR), an ARRAY reference, or an ARRAY whose values must be valid arguments for passing to IO::File->new. $qif->file( "myfile" ); OR $qif->file( [ "myfile", "<:crlf" ] ); OR $qif->file( "myfile", "<:crlf" ); For output files, be sure to open the file in write mode. =head2 record_separator() Returns the currently used record_separator. This is used primarly in situations where you open a QIF file with autodetect and then want to write out a QIF file in the same format. my $in = Finance::QIF->new( file => "input.qif", autodetect => 1 ); my $out = Finance::QIF->new( file => ">write.qif", record_separator => $in->record_separator ); =head2 open() Open already specified file. $qif->open(); Open also supports the same arguments as L. =head2 next() For input files return the next record in the QIF file. my $record = $in->next(); Returns undef if no more records are available. =head2 header() For output files use to output the passed header for records that will then be written with write. $out->header( "Type:Bank" ); See L for list of possible record types that can be passed. =head2 write() For output files use to output the passed record to the file. $out->write( $record ); =head2 reset() Resets the filehandle so the records can be read again from the beginning of the file. $qif->reset(); =head2 close() Closes the open file. $qif->close(); =head1 EXAMPLES Read an existing QIF file then write out to new QIF file. my $in = Finance::QIF->new( file => "input.qif" ); my $out = Finance::QIF->new( file => ">write.qif" ); my $header = ""; while ( my $record = $in->next() ) { if ( $header ne $record->{header} ) { $out->header( $record->{header} ); $header = $record->{header}; } $out->write($record); } $in->close(); $out->close(); =head1 SEE ALSO L, L Quicken Interchange Format (QIF) specification L =head1 ACKNOWLEDGEMENTS Simon Cozens C, Author of original Finance::QIF Nathan McFarland C, Maintainer of original Finance::QIF =head1 AUTHORS Matthew McGillis Ematthew@mcgillis.orgE L Phil Lobbes Ephil at perkpartners dot comE Project maintained at L =head1 COPYRIGHT Copyright (C) 2006-2008 by Matthew McGillis. All rights reserved. =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Finance-QIF-3.02/MANIFEST0000644000076400007640000000034011451006654014462 0ustar matthewmatthewChanges examples/qifdumper examples/readwrite lib/Finance/QIF.pm Makefile.PL MANIFEST README t/Finance-QIF.t t/pod-coverage.t t/pod.t t/test.qif META.yml Module meta-data (added by MakeMaker) Finance-QIF-3.02/t/0000755000076400007640000000000011451006654013577 5ustar matthewmatthewFinance-QIF-3.02/t/pod-coverage.t0000644000076400007640000000032110572443057016340 0ustar matthewmatthew#!/usr/bin/perl use strict; use warnings; use Test::More; eval "use Test::Pod::Coverage 1.00"; plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD coverage" if $@; all_pod_coverage_ok(); Finance-QIF-3.02/t/Finance-QIF.t0000644000076400007640000007530511171217402015750 0ustar matthewmatthew#!/usr/bin/perl # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl Finance-IIF.t' use strict; use warnings; use Test::More tests => 998; use File::Temp qw(tempfile); my $DOWARN; BEGIN { BEGIN { $SIG{"__WARN__"} = sub { warn $_[0] if $DOWARN } } $DOWARN = 1; use_ok('Finance::QIF') or exit; } my $package = "Finance::QIF"; my $testfile = "t/test.qif"; { # new can_ok( $package, qw(new) ); my $obj = $package->new; isa_ok( $obj, $package ); is( $obj->{debug}, 0, "default debug value" ); is( $obj->{autodetect}, 0, "default autodetect value" ); is( $obj->{trim_white_space}, 0, "default trim_white_space value" ); is( $obj->record_separator, $/, "default record separator" ); $obj = $package->new( debug => 1, record_separator => "X\rX\n", ); is( $obj->{debug}, 1, "custom debug value" ); is( $obj->record_separator, "X\rX\n", "custom record separator" ); } { # autodetect my ( $fh, $fn, $obj ); ( $fh, $fn ) = tempfile(); close($fh); $obj = $package->new( file => $fn, autodetect => 1 ); is( $obj->record_separator, $/, "autodetect default record separator" ); ( $fh, $fn ) = tempfile(); print( $fh "Testing Windows\015\012" ); close($fh); $obj = $package->new( file => $fn, autodetect => 1 ); is( $obj->record_separator, "\015\012", "autodetect windows record separator" ); ( $fh, $fn ) = tempfile(); print( $fh "Testing Mac\015" ); close($fh); $obj = $package->new( file => $fn, autodetect => 1 ); is( $obj->record_separator, "\015", "autodetect mac record separator" ); ( $fh, $fn ) = tempfile(); print( $fh "Testing Unix\012" ); close($fh); $obj = $package->new( file => $fn, autodetect => 1 ); is( $obj->record_separator, "\012", "autodetect unix record separator" ); } { # trim_white_space my ( $fh, $fn ) = tempfile(); print( $fh "!Type:Security\nNIntuit \nS INTU\nT Stock \nGHigh Risk\n^\n" ); close($fh); my $obj = $package->new( file => $fn, autodetect => 1 ); ok( $obj->{trim_white_space} == 0, "trim_white_space not set" ); my $record = $obj->next(); ok( $record->{security} eq "Intuit ", "trim_white_space trailing" ); ok( $record->{symbol} eq " INTU", "trim_white_space leading" ); ok( $record->{type} eq " Stock ", "trim_white_space both" ); $obj = $package->new( file => $fn, autodetect => 1, trim_white_space => 1 ); ok( $obj->{trim_white_space} == 1, "trim_white_space set" ); $record = $obj->next(); ok( $record->{security} eq "Intuit", "trim_white_space trailing" ); ok( $record->{symbol} eq "INTU", "trim_white_space leading" ); ok( $record->{type} eq "Stock", "trim_white_space both" ); } { # reset can_ok( $package, qw(reset) ); my $obj = $package->new; eval { $obj->reset }; like( $@, qr/^No filehandle available/, "reset without a filehandle croaks" ); my ( $fh, $fn ) = tempfile(); print( $fh "!Type:Security\nNIntuit\nSINTU\nTStock\nGHigh Risk\n^\n" ); close($fh); $obj = $package->new( file => $fn, autodetect => 1 ); my $record1 = $obj->next; $obj->reset; my $record2 = $obj->next; ok( $record1->{header} eq $record2->{header} && $record1->{security} eq $record2->{security} && $record1->{symbol} eq $record2->{symbol} && $record1->{type} eq $record2->{type} && $record1->{goal} eq $record2->{goal}, "reset reads same record" ); } { # file can_ok( $package, qw(file) ); my $obj = $package->new; is( $obj->file, undef, "file undef by default" ); is( $obj->file($testfile), $testfile, "file with one arg" ); is( $obj->file( $testfile, "<" ), $testfile, "file with two args" ); $obj = $package->new( file => $testfile ); is( $obj->file, $testfile, "new with scalar file argument" ); SKIP: { skip "Perl 5.008 not installed", 1 if $]<5.008; $obj = $package->new( file => [ $testfile, "<:crlf" ] ); is( $obj->file, $testfile, "new with arrayref file argument" ); } is_deeply( [ $obj->file( 1, 2 ) ], [ 1, 2 ], "file returns list" ); } { # croak checks for: _filehandle next _getline close my @methods = qw(_filehandle next _getline close); can_ok( $package, @methods ); foreach my $method (@methods) { my $obj = $package->new; eval { $obj->$method }; like( $@, qr/^No filehandle available/, "$method without a filehandle croaks" ); } } { # open can_ok( $package, qw(open) ); my $obj = $package->new; eval { $obj->open }; like( $@, qr/^No file specified/, "open without a file croaks" ); $obj = $package->new; eval { $obj->open($testfile) }; is( $@, "", "open with file does not die" ); } { # _parseline can_ok( $package, qw(_parseline) ); } { # _warning can_ok( $package, qw(_warning) ); } testfile( "Read ", $testfile ); my $in = $package->new( file => $testfile, autodetect => 1 ); my ( $fh, $fn ) = tempfile(); close($fh); my $tempfile = $fn; my $out = $package->new( file => ">" . $tempfile, record_separator => $in->record_separator ); # Trap warning so we can validate message returned. $DOWARN = 0; # need to create a test that intentionally causes a warning so we can validate # warnings are always working properly # turn warnings back on $DOWARN = 1; my $header = ""; while ( my $record = $in->next ) { if ( $header ne $record->{header} ) { $out->header( $record->{header} ); $header = $record->{header}; } $out->write($record); } $in->close; $out->close; testfile( "Write ", $tempfile ); #test default write/write works $out = $package->new( file => ">" . $tempfile, ); my $record = { header => "Type:Security", security => "Intuit", symbol => "INTU", type => "Stock", goal => "High Risk" }; $out->header( $record->{header} ); $out->write($record); $out->close; $in = $package->new( file => $tempfile, ); $record = $in->next; ok( $record->{header} eq "Type:Security", "default write/read" ); ok( $record->{security} eq "Intuit", "default write/read" ); ok( $record->{symbol} eq "INTU", "default write/read" ); ok( $record->{type} eq "Stock", "default write/read" ); ok( $record->{goal} eq "High Risk", "default write/read" ); # Need a test for confirming we don't interfere with other open files # reading input with different line separator. sub testfile { my $test = shift; my $file = shift; my $qif = $package->new( file => $file, record_separator => "\n" ); # account tests { my $record; $record = $qif->next; ok( $record->{header} eq "Account", $test . "Account" ); ok( $record->{name} eq "Asset", $test . "Account" ); ok( $record->{description} eq "Sample Asset", $test . "Account" ); ok( $record->{tax} eq "", $test . "Account" ); ok( $record->{note} eq "Note on Asset", $test . "Account" ); ok( $record->{type} eq "Oth A", $test . "Account" ); ok( $record->{balance} eq "25,000.00", $test . "Account" ); $record = $qif->next; ok( $record->{header} eq "Account", $test . "Account" ); ok( $record->{name} eq "Bank", $test . "Account" ); ok( $record->{description} eq "Sample Bank", $test . "Account" ); ok( $record->{tax} eq "", $test . "Account" ); ok( $record->{note} eq "Notes on Sample", $test . "Account" ); ok( $record->{type} eq "Bank", $test . "Account" ); ok( $record->{balance} eq "1,465.00", $test . "Account" ); $record = $qif->next; ok( $record->{header} eq "Account", $test . "Account" ); ok( $record->{name} eq "Cash", $test . "Account" ); ok( $record->{description} eq "Sample Cash", $test . "Account" ); ok( $record->{tax} eq "", $test . "Account" ); ok( $record->{note} eq "Note on Cash", $test . "Account" ); ok( $record->{type} eq "Cash", $test . "Account" ); ok( $record->{balance} eq "0.00", $test . "Account" ); $record = $qif->next; ok( $record->{header} eq "Account", $test . "Account" ); ok( $record->{name} eq "Credit Card", $test . "Account" ); ok( $record->{description} eq "Sample Card", $test . "Account" ); ok( $record->{limit} eq "15,000.00", $test . "Account" ); ok( $record->{tax} eq "", $test . "Account" ); ok( $record->{note} eq "Note on Card", $test . "Account" ); ok( $record->{type} eq "CCard", $test . "Account" ); ok( $record->{balance} eq "0.00", $test . "Account" ); $record = $qif->next; ok( $record->{header} eq "Account", $test . "Account" ); ok( $record->{name} eq "Liability", $test . "Account" ); ok( $record->{description} eq "Sample Liability", $test . "Account" ); ok( $record->{tax} eq "", $test . "Account" ); ok( $record->{note} eq "Note on Liability", $test . "Account" ); ok( $record->{type} eq "Oth L", $test . "Account" ); ok( $record->{balance} eq "50,000.00", $test . "Account" ); $record = $qif->next; ok( $record->{header} eq "Account", $test . "Account" ); ok( $record->{name} eq "Mutual Fund", $test . "Account" ); ok( $record->{description} eq "Sample Fund", $test . "Account" ); ok( $record->{tax} eq "", $test . "Account" ); ok( $record->{note} eq "Note on Fund", $test . "Account" ); ok( $record->{type} eq "Mutual", $test . "Account" ); ok( $record->{balance} eq "672.87", $test . "Account" ); $record = $qif->next; ok( $record->{header} eq "Account", $test . "Account" ); ok( $record->{name} eq "Portfolio", $test . "Account" ); ok( $record->{description} eq "Sample Portfolio", $test . "Account" ); ok( $record->{tax} eq "", $test . "Account" ); ok( $record->{note} eq "Note on portfolio", $test . "Account" ); ok( $record->{type} eq "Port", $test . "Account" ); ok( $record->{balance} eq "2,651.00", $test . "Account" ); } # Added a trailing space to the !Clear:AutoSwitch line in data file. # We should test to make sure it is processed as a accepted header and # that no error message was generated during processing. # security tests { my $record = $qif->next; ok( $record->{header} eq "Type:Security", $test . "Security" ); ok( $record->{security} eq "Intuit", $test . "Security" ); ok( $record->{symbol} eq "INTU", $test . "Security" ); ok( $record->{type} eq "Stock", $test . "Security" ); ok( $record->{goal} eq "High Risk", $test . "Security" ); $record = $qif->next; ok( $record->{header} eq "Type:Security", $test . "Security" ); } # payee tests { my $record = $qif->next; ok( $record->{header} eq "Type:Payee", $test . "Payee" ); ok( $record->{name} eq "Safeway", $test . "Payee" ); ok( $record->{address} eq "Safeway Address\n\n", $test . "Payee" ); ok( $record->{city} eq "City", $test . "Payee" ); ok( $record->{state} eq "SC", $test . "Payee" ); ok( $record->{zip} eq "99999 ", $test . "Payee" ); ok( $record->{country} eq "", $test . "Payee" ); ok( $record->{phone} eq "3333333333", $test . "Payee" ); ok( $record->{account} eq "123456789", $test . "Payee" ); } # category tests { my $record = $qif->next; ok( $record->{header} eq "Type:Cat", $test . "Category" ); ok( $record->{name} eq "Auto", $test . "Category" ); ok( $record->{description} eq "Automobile Expenses", $test . "Category" ); ok( $record->{expense} eq "", $test . "Category" ); for ( my $count = 0 ; $count < 21 ; $count++ ) { $record = $qif->next; ok( $record->{header} eq "Type:Cat", $test . "Category" ); } $record = $qif->next; ok( $record->{header} eq "Type:Cat", $test . "Category" ); ok( $record->{name} eq "Interest Inc", $test . "Category" ); ok( $record->{description} eq "Interest Income", $test . "Category" ); ok( $record->{income} eq "", $test . "Category" ); ok( $record->{tax} eq "", $test . "Category" ); ok( $record->{schedule} eq "4592", $test . "Category" ); ok( $record->{budget}[0] eq "1.00", $test . "Category" ); ok( $record->{budget}[1] eq "0.00", $test . "Category" ); ok( $record->{budget}[2] eq "0.00", $test . "Category" ); ok( $record->{budget}[3] eq "1.00", $test . "Category" ); ok( $record->{budget}[4] eq "0.00", $test . "Category" ); ok( $record->{budget}[5] eq "0.00", $test . "Category" ); ok( $record->{budget}[6] eq "1.00", $test . "Category" ); ok( $record->{budget}[7] eq "0.00", $test . "Category" ); ok( $record->{budget}[8] eq "0.00", $test . "Category" ); ok( $record->{budget}[9] eq "0.00", $test . "Category" ); ok( $record->{budget}[10] eq "0.00", $test . "Category" ); ok( $record->{budget}[11] eq "1.00", $test . "Category" ); for ( my $count = 0 ; $count < 73 ; $count++ ) { $record = $qif->next; ok( $record->{header} eq "Type:Cat", $test . "Category" ); } } # budget tests { for ( my $count = 0 ; $count < 17 ; $count++ ) { my $record = $qif->next; ok( $record->{header} eq "Type:Budget", $test . "Budget" ); } my $record = $qif->next; ok( $record->{header} eq "Type:Budget", $test . "Budget" ); ok( $record->{name} eq "Groceries", $test . "Budget" ); ok( $record->{description} eq "Groceries", $test . "Budget" ); ok( $record->{budget}[0] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[1] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[2] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[3] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[4] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[5] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[6] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[7] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[8] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[9] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[10] eq "-100.00", $test . "Budget" ); ok( $record->{budget}[11] eq "-100.00", $test . "Budget" ); for ( my $count = 0 ; $count < 78 ; $count++ ) { $record = $qif->next; ok( $record->{header} eq "Type:Budget", $test . "Budget" ); } } # Class tests { my $record = $qif->next; ok( $record->{header} eq "Type:Class", $test . "Class" ); ok( $record->{name} eq "Class", $test . "Class" ); ok( $record->{description} eq "Class Description", $test . "Class" ); $record = $qif->next; ok( $record->{header} eq "Type:Class", $test . "Class" ); } # Oth A test { my $record = $qif->next; ok( $record->{header} eq "Account", $test . "Oth A" ); ok( $record->{name} eq "Asset", $test . "Oth A" ); ok( $record->{type} eq "Oth A", $test . "Oth A" ); ok( $record->{balance} eq "25,000.00", $test . "Oth A" ); for ( my $count = 0 ; $count < 2 ; $count++ ) { $record = $qif->next; ok( $record->{header} eq "Type:Oth A", $test . "Oth A" ); } } # Bank test { my $record = $qif->next; ok( $record->{header} eq "Account", $test . "Bank" ); ok( $record->{name} eq "Bank", $test . "Bank" ); ok( $record->{type} eq "Bank", $test . "Bank" ); ok( $record->{balance} eq "1,465.00", $test . "Bank" ); $record = $qif->next; ok( $record->{header} eq "Type:Bank", $test . "Bank" ); ok( $record->{date} eq "1/10/06", $test . "Bank" ); ok( $record->{payee} eq "Opening Balance", $test . "Bank" ); ok( $record->{memo} eq "", $test . "Bank" ); ok( $record->{transaction} eq "0.00", $test . "Bank" ); ok( $record->{address} eq "", $test . "Bank" ); ok( $record->{status} eq "X", $test . "Bank" ); ok( $record->{category} eq "[Bank]", $test . "Bank" ); $record = $qif->next; ok( $record->{header} eq "Type:Bank", $test . "Bank" ); ok( $record->{date} eq "1/10/06", $test . "Bank" ); ok( $record->{payee} eq "Paycheck", $test . "Bank" ); ok( $record->{memo} eq "", $test . "Bank" ); ok( $record->{transaction} eq "1,690.00", $test . "Bank" ); ok( $record->{address} eq "", $test . "Bank" ); ok( $record->{category} eq "Salary", $test . "Bank" ); ok( $record->{splits}[0]{category} eq "Salary", $test . "Bank" ); ok( $record->{splits}[0]{memo} eq "", $test . "Bank" ); ok( $record->{splits}[0]{amount} eq "2,000.00", $test . "Bank" ); ok( $record->{splits}[1]{category} eq "Payroll Taxes, Self:Federal", $test . "Bank" ); ok( $record->{splits}[1]{memo} eq "", $test . "Bank" ); ok( $record->{splits}[1]{amount} eq "-250.00", $test . "Bank" ); ok( $record->{splits}[2]{category} eq "Payroll Taxes, Self:Soc Sec", $test . "Bank" ); ok( $record->{splits}[2]{amount} eq "-50.00", $test . "Bank" ); ok( $record->{splits}[3]{category} eq "Payroll Taxes, Self:Medicare", $test . "Bank" ); ok( $record->{splits}[3]{memo} eq "", $test . "Bank" ); ok( $record->{splits}[3]{amount} eq "-10.00", $test . "Bank" ); $record = $qif->next; ok( $record->{header} eq "Type:Bank", $test . "Bank" ); ok( $record->{date} eq "1/17/06", $test . "Bank" ); ok( $record->{payee} eq "Safeway", $test . "Bank" ); ok( $record->{memo} eq "", $test . "Bank" ); ok( $record->{transaction} eq "-100.00", $test . "Bank" ); ok( $record->{address} eq "", $test . "Bank" ); ok( $record->{category} eq "Groceries", $test . "Bank" ); $record = $qif->next; ok( $record->{header} eq "Type:Bank", $test . "Bank" ); ok( $record->{date} eq "2/17/06", $test . "Bank" ); ok( $record->{payee} eq "Safeway", $test . "Bank" ); ok( $record->{memo} eq "", $test . "Bank" ); ok( $record->{transaction} eq "-125.00", $test . "Bank" ); ok( $record->{address} eq "", $test . "Bank" ); ok( $record->{number} eq ">>>>>", $test . "Bank" ); ok( $record->{category} eq "Groceries", $test . "Bank" ); ok( $record->{splits}[0]{category} eq "Groceries", $test . "Bank" ); ok( $record->{splits}[0]{memo} eq "", $test . "Bank" ); ok( $record->{splits}[0]{amount} eq "-100.00", $test . "Bank" ); ok( $record->{splits}[1]{category} eq "Misc", $test . "Bank" ); ok( $record->{splits}[1]{memo} eq "", $test . "Bank" ); ok( $record->{splits}[1]{amount} eq "-25.00", $test . "Bank" ); $record = $qif->next; ok( $record->{header} eq "Type:Bank", $test . "Bank" ); ok( $record->{date} eq "3/17/06", $test . "Bank" ); ok( $record->{payee} eq "Safeway", $test . "Bank" ); ok( $record->{memo} eq "", $test . "Bank" ); ok( $record->{transaction} eq "-100.00", $test . "Bank" ); ok( $record->{total} eq "-100.00", $test . "Bank" ); ok( $record->{address} eq "", $test . "Bank" ); ok( $record->{category} eq "Groceries", $test . "Bank" ); $qif->{trim_white_space} = 1; $record = $qif->next; ok( $record->{header} eq "Type:Bank", $test . "Bank" ); ok( $record->{date} eq "3/17/06", $test . "Bank" ); ok( $record->{payee} eq "QFC", $test . "Bank" ); ok( $record->{memo} eq "", $test . "Bank" ); ok( $record->{transaction} eq "-100.00", $test . "Bank" ); ok( $record->{total} eq "-100.00", $test . "Bank" ); ok( $record->{address} eq "", $test . "Bank" ); ok( $record->{category} eq "Groceries", $test . "Bank" ); $qif->{trim_white_space} = 0; } # Cash test { my $record = $qif->next; ok( $record->{header} eq "Account", $test . "Cash" ); ok( $record->{name} eq "Cash", $test . "Cash" ); ok( $record->{type} eq "Cash", $test . "Cash" ); ok( $record->{balance} eq "0.00", $test . "Cash" ); $record = $qif->next; ok( $record->{header} eq "Type:Cash", $test . "Cash" ); ok( $record->{date} eq "1/10/06", $test . "Cash" ); ok( $record->{payee} eq "Opening Balance", $test . "Cash" ); ok( $record->{memo} eq "", $test . "Cash" ); ok( $record->{transaction} eq "0.00", $test . "Cash" ); ok( $record->{address} eq "", $test . "Cash" ); ok( $record->{status} eq "X", $test . "Cash" ); ok( $record->{category} eq "[Cash]", $test . "Cash" ); } # Credit Card test { my $record = $qif->next; ok( $record->{header} eq "Account", $test . "Credit Card" ); ok( $record->{name} eq "Credit Card", $test . "Credit Card" ); ok( $record->{limit} eq "15,000.00", $test . "Credit Card" ); ok( $record->{type} eq "CCard", $test . "Credit Card" ); ok( $record->{balance} eq "0.00", $test . "Credit Card" ); $record = $qif->next; ok( $record->{header} eq "Type:CCard", $test . "Credit Card" ); ok( $record->{date} eq "1/10/06", $test . "Credit Card" ); ok( $record->{payee} eq "Opening Balance", $test . "Credit Card" ); ok( $record->{memo} eq "", $test . "Credit Card" ); ok( $record->{transaction} eq "0.00", $test . "Credit Card" ); ok( $record->{address} eq "", $test . "Credit Card" ); ok( $record->{status} eq "X", $test . "Credit Card" ); ok( $record->{category} eq "[Credit Card]", $test . "Credit Card" ); } # Liability test { my $record = $qif->next; ok( $record->{header} eq "Account", $test . "Liability" ); ok( $record->{name} eq "Liability", $test . "Liability" ); ok( $record->{type} eq "Oth L", $test . "Liability" ); ok( $record->{balance} eq "50,000.00", $test . "Liability" ); $record = $qif->next; ok( $record->{header} eq "Type:Oth L", $test . "Liability" ); ok( $record->{date} eq "1/10/06", $test . "Liability" ); ok( $record->{payee} eq "Opening Balance", $test . "Liability" ); ok( $record->{memo} eq "", $test . "Liability" ); ok( $record->{transaction} eq "-50,000.00", $test . "Liability" ); ok( $record->{address} eq "", $test . "Liability" ); ok( $record->{status} eq "X", $test . "Liability" ); ok( $record->{category} eq "[Liability]", $test . "Liability" ); } # Mutual Fund test { my $record = $qif->next; ok( $record->{header} eq "Account", $test . "Mutual Fund" ); ok( $record->{name} eq "Mutual Fund", $test . "Mutual Fund" ); ok( $record->{type} eq "Mutual", $test . "Mutual Fund" ); ok( $record->{balance} eq "672.87", $test . "Mutual Fund" ); for ( my $count = 0 ; $count < 1 ; $count++ ) { $record = $qif->next; ok( $record->{header} eq "Type:Invst", $test . "Mutual Fund" ); } } # Portfolio test { my $record = $qif->next; ok( $record->{header} eq "Account", $test . "Portfolio" ); ok( $record->{name} eq "Portfolio", $test . "Portfolio" ); ok( $record->{type} eq "Port", $test . "Portfolio" ); ok( $record->{balance} eq "2,651.00", $test . "Portfolio" ); } # Invest test { my $record = $qif->next; ok( $record->{header} eq "Type:Invst", $test . "Invest" ); ok( $record->{date} eq "1/10/06", $test . "Invest" ); ok( $record->{action} eq "ShrsIn", $test . "Invest" ); ok( $record->{security} eq "Intuit", $test . "Invest" ); ok( $record->{quantity} eq "50", $test . "Invest" ); ok( $record->{memo} eq "Initial Move", $test . "Invest" ); } # Prices test { my $record = $qif->next; ok( $record->{header} eq "Type:Prices", $test . "Prices" ); ok( $record->{symbol} eq "INTU", $test . "Prices" ); ok( $record->{prices}[0]{close} eq "55.400", $test . "Prices" ); ok( $record->{prices}[0]{date} eq "12/12/05", $test . "Prices" ); ok( $record->{prices}[0]{max} eq "55.560", $test . "Prices" ); ok( $record->{prices}[0]{min} eq "53.660", $test . "Prices" ); ok( $record->{prices}[0]{volume} eq "3120461", $test . "Prices" ); ok( $record->{prices}[1]{close} eq "55.570", $test . "Prices" ); ok( $record->{prices}[1]{date} eq "12/13/05", $test . "Prices" ); ok( $record->{prices}[1]{max} eq "55.740", $test . "Prices" ); ok( $record->{prices}[1]{min} eq "54.600", $test . "Prices" ); ok( $record->{prices}[1]{volume} eq "2437647", $test . "Prices" ); $record = $qif->next; ok( $record->{header} eq "Type:Prices", $test . "Prices" ); $record = $qif->next; ok( $record->{header} eq "Type:Prices", $test . "Prices" ); ok( $record->{symbol} eq "WIN", $test . "Prices" ); ok( $record->{prices}[0]{close} eq "19.740", $test . "Prices" ); ok( $record->{prices}[0]{date} eq "12/12/05", $test . "Prices" ); ok( exists($record->{prices}[0]{max}) == 0 , $test . "Prices" ); ok( exists($record->{prices}[0]{min}) == 0, $test . "Prices" ); ok( exists($record->{prices}[0]{volume}) == 0, $test . "Prices" ); } # Memorized test { my $record = $qif->next; ok( $record->{header} eq "Type:Memorized", $test . "Memorized" ); ok( $record->{transaction} eq "-50.00", $test . "Memorized" ); ok( $record->{payee} eq "Safeway", $test . "Memorized" ); ok( $record->{memo} eq "", $test . "Memorized" ); ok( $record->{type} eq "C", $test . "Memorized" ); $record = $qif->next; ok( $record->{header} eq "Type:Memorized", $test . "Memorized" ); ok( $record->{transaction} eq "-1,140.17", $test . "Memorized" ); ok( $record->{payee} eq "Bank", $test . "Memorized" ); ok( $record->{memo} eq "", $test . "Memorized" ); ok( $record->{address} eq "", $test . "Memorized" ); ok( $record->{category} eq "[Liability]", $test . "Memorized" ); ok( $record->{splits}[0]{category} eq "[Liability]", $test . "Memorized" ); ok( $record->{splits}[0]{memo} eq "principal", $test . "Memorized" ); ok( $record->{splits}[0]{amount} eq "-952.67", $test . "Memorized" ); ok( $record->{splits}[1]{category} eq "Mortgage Int", $test . "Memorized" ); ok( $record->{splits}[1]{memo} eq "interest", $test . "Memorized" ); ok( $record->{splits}[1]{amount} eq "-187.50", $test . "Memorized" ); ok( $record->{first} eq "2/1/06", $test . "Memorized" ); ok( $record->{years} eq "4", $test . "Memorized" ); ok( $record->{made} eq "0", $test . "Memorized" ); ok( $record->{periods} eq "12", $test . "Memorized" ); ok( $record->{interest} eq "4.5", $test . "Memorized" ); ok( $record->{balance} eq "25,000.00", $test . "Memorized" ); ok( $record->{loan} eq "25,000.00", $test . "Memorized" ); ok( $record->{type} eq "P", $test . "Memorized" ); $record = $qif->next; ok( $record->{header} eq "Type:Memorized", $test . "Memorized" ); ok( $record->{action} eq "SellX", $test . "Memorized" ); ok( $record->{security} eq "IDS New D A", $test . "Memorized" ); ok( $record->{price} eq "22.096936", $test . "Memorized" ); ok( $record->{quantity} eq "158.393", $test . "Memorized" ); ok( $record->{total} eq "3,500.00", $test . "Memorized" ); ok( $record->{transaction} eq "3,500.00", $test . "Memorized" ); ok( $record->{memo} eq "Investment", $test . "Memorized" ); ok( $record->{category} eq "[Invest]", $test . "Memorized" ); ok( $record->{amount} eq "3,500.00", $test . "Memorized" ); ok( $record->{type} eq "I", $test . "Memorized" ); } } Finance-QIF-3.02/t/pod.t0000644000076400007640000000025510572442307014552 0ustar matthewmatthew#!/usr/bin/perl use strict; use warnings; use Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); Finance-QIF-3.02/t/test.qif0000644000076400007640000004614711042154333015265 0ustar matthewmatthew !Option:AutoSwitch !Account NAsset DSample Asset X ANote on Asset TOth A B25,000.00 ^ NBank DSample Bank X ANotes on Sample TBank B1,465.00 ^ NCash DSample Cash X ANote on Cash TCash B0.00 ^ NCredit Card DSample Card L15,000.00 X ANote on Card TCCard B0.00 ^ NLiability DSample Liability X ANote on Liability TOth L B50,000.00 ^ NMutual Fund DSample Fund X ANote on Fund TMutual B672.87 ^ NPortfolio DSample Portfolio X ANote on portfolio TPort B2,651.00 ^ !Clear:AutoSwitch !Type:Security NIntuit SINTU TStock GHigh Risk ^ NMutual Fund SPGRWX TMutual Fund GCollege Fund ^ !Type:Payee PSafeway A0Safeway Address A1 A2 CCity SSC Z99999 Y N3333333333 #123456789 ^ !Type:Cat NAuto DAutomobile Expenses E ^ NAuto:Fuel DAuto Fuel E ^ NAuto:Insurance DAuto Insurance E ^ NAuto:Registration DAuto Registration E T R8560 ^ NAuto:Service DAuto Service E ^ NBank Charge DBank Charge E ^ NBonus DBonus Income I T R7360 ^ NCash DMisc Cash E ^ NCharity DCharitable Donations E T R4480 ^ NChild Care DChild Care Expenses E T R6416 ^ NClothing DClothing E ^ NDining DDining Out E ^ NDiv Income DDividend Income I ^ NEducation DEducation E ^ NEntertainment DEntertainment E ^ NGift Received DGift Received I ^ NGifts Given DGift Expenses E ^ NGroceries DGroceries E ^ NHome Repair DHome Repair E ^ NHousehold DHousehold Misc. Expenses E ^ NInsurance DInsurance E ^ NInsurance:Home DHomeowner's Insurance E ^ NInterest Inc DInterest Income B1.00 B0.00 B0.00 B1.00 B0.00 B0.00 B1.00 B0.00 B0.00 B0.00 B0.00 B1.00 I T R4592 ^ NInvestment Exp D E T R4512 ^ NInvestment Inc DInvestment Income I T ^ NIRA Contrib DIRA Contribution E T R4192 ^ NIRA Contrib Spouse DIRA Contribution Spouse E T R7696 ^ NMedical D E T R4368 ^ NMedical:Doctor DDoctor & Dental Visits E T R7744 ^ NMedical:Health Insurance DHealth Insurance E T R7744 ^ NMedical:Hospital DHospital Expenses E T R7744 ^ NMedical:Medicine DMedicine & Drugs E T R4368 ^ NMisc DMiscellaneous E ^ NMortgage Int DMortgage Interest Paid E T R4528 ^ NOther Inc DOther Income I T ^ NPayroll Taxes, Self D E T ^ NPayroll Taxes, Self:Federal DFederal Withholding E T R7376 ^ NPayroll Taxes, Self:Medicare DMedicare Tax Withholding E T R7680 ^ NPayroll Taxes, Self:SDI DState Disability Insurance E T R7408 ^ NPayroll Taxes, Self:Soc Sec DSoc Sec Tax Withholding E T R7392 ^ NPayroll Taxes, Self:State DState Withholding E T R7424 ^ NPayroll Taxes, Spouse D E T ^ NPayroll Taxes, Spouse:Federal DFederal Withholding E T R8112 ^ NPayroll Taxes, Spouse:Medicare DMedicare Tax Withholding E T R8160 ^ NPayroll Taxes, Spouse:SDI DState Disability Insurance E T R8144 ^ NPayroll Taxes, Spouse:Soc Sec DSoc Sec Tax Withholding E T R8128 ^ NPayroll Taxes, Spouse:State DState Withholding E T R8176 ^ NPension D I T ^ NRecreation DRecreation Expense E ^ NRent DHousing Rent E ^ NSalary D I T R7360 ^ NSalary Spouse DSalary Income, Spouse I T R8096 ^ NSubscriptions DSubscriptions E ^ NTax Refund DState/Local Tax Refund I T R4160 ^ NTaxes DTaxes E T ^ NTaxes:Other DMisc. Taxes E T R4432 ^ NTaxes:Property DProperty Tax E T R4416 ^ NTravel DTransportation Exp E ^ NUnemployment Inc DUnemployment Compensation I T R7664 ^ NUtilities DWater, Gas, Electric E ^ NUtilities:Cable TV DCable TV E ^ NUtilities:Gas & Electric DGas and Electricity E ^ NUtilities:Telephone DTelephone Expense E ^ NUtilities:Water DWater E ^ NVacation DVacation Expenses E ^ NVacation:Lodging DMotel/Hotel Costs E ^ N Canada Expense D E ^ N Canada Expense:GST DGoods and Services Tax E T ^ N Canada Expense:Private Health DPrivate Health Care E T ^ N Canada Expense:Prov Health DProvincial Health Care E T ^ N Canada Expense:PST DProvincial Sales Tax E T ^ N Canada Expense:RPP Contrib DRegistered Pension Plan E T ^ N Canada Expense:RRSP DRegistered Ret. Savings Plan E T ^ N Canada Income D I ^ N Canada Income:CPP QPP DCanada/Quebec Pension Plan I T ^ N Canada Income:RRIF Income DRRIF Income I T ^ N Canada Income:RRSP Income DReg Retirement Plan Income I T ^ N Canada Taxes D E ^ N Canada Taxes:CPP QPP Premium DCPP QPP Contribution E T ^ N Canada Taxes:Income Tax DIncome Tax Deducted E T ^ N Canada Taxes:UIC DUnemployment Ins. Commission E T ^ N¥Accrued Int DAccrued Interest E T R4592 ^ N¥Accrued Int NT DNon-Tax Accrued Interest E ^ N¥Div Income DDividend I T R4576 ^ N¥Div Income NT DNon-Tax Dividend I ^ N¥Int Expense DInvestment Interest Exp E T ^ N¥Int Expense NT DNon-Tax Inv Interest Exp E ^ N¥Int Income DInvestment Interest Inc I T R4592 ^ N¥Int Income NT DNon-Tax Investment Interest Inc I ^ N¥Long CapGnDst DLong Term Cap Gain Dist I T R7808 ^ N¥Long CapGnDst NT DNon-Tax Long Term Cap Gain Dist I ^ N¥Realized Gain DRealized Gain/Loss I T ^ N¥Realized Gain NT DNon-Tax Realized Gain/Loss I ^ N¥Short CapGnDst DShort Term Cap Gain Dist I T R4576 ^ N¥Short CapGnDst NT DNon-Tax Short Cap Gain Dist I ^ N¥Unrealized Gn DUnrealized Gain/Loss I ^ !Type:Budget NAuto DAutomobile Expenses B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NAuto:Fuel DAuto Fuel B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NAuto:Insurance DAuto Insurance B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NAuto:Registration DAuto Registration T R8560 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NAuto:Service DAuto Service B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NBank Charge DBank Charge B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NBonus DBonus Income I T R7360 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NCash DMisc Cash B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NCharity DCharitable Donations T R4480 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NChild Care DChild Care Expenses T R6416 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NClothing DClothing B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NDining DDining Out B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NDiv Income DDividend Income I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NEducation DEducation B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NEntertainment DEntertainment B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NGift Received DGift Received I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NGifts Given DGift Expenses B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NGroceries DGroceries B-100.00 B-100.00 B-100.00 B-100.00 B-100.00 B-100.00 B-100.00 B-100.00 B-100.00 B-100.00 B-100.00 B-100.00 ^ NHome Repair DHome Repair B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NHousehold DHousehold Misc. Expenses B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NInsurance DInsurance B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NInsurance:Home DHomeowner's Insurance B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NInterest Inc DInterest Income I T R4592 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NInvestment Exp D T R4512 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NInvestment Inc DInvestment Income I T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NIRA Contrib DIRA Contribution T R4192 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NIRA Contrib Spouse DIRA Contribution Spouse T R7696 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NMedical D T R4368 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NMedical:Doctor DDoctor & Dental Visits T R7744 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NMedical:Health Insurance DHealth Insurance T R7744 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NMedical:Hospital DHospital Expenses T R7744 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NMedical:Medicine DMedicine & Drugs T R4368 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NMisc DMiscellaneous B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NMortgage Int DMortgage Interest Paid T R4528 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NOther Inc DOther Income I T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Self D T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Self:Federal DFederal Withholding T R7376 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Self:Medicare DMedicare Tax Withholding T R7680 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Self:SDI DState Disability Insurance T R7408 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Self:Soc Sec DSoc Sec Tax Withholding T R7392 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Self:State DState Withholding T R7424 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Spouse D T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Spouse:Federal DFederal Withholding T R8112 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Spouse:Medicare DMedicare Tax Withholding T R8160 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Spouse:SDI DState Disability Insurance T R8144 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Spouse:Soc Sec DSoc Sec Tax Withholding T R8128 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPayroll Taxes, Spouse:State DState Withholding T R8176 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NPension D I T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NRecreation DRecreation Expense B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NRent DHousing Rent B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NSalary D I T R7360 B1,700.00 B1,700.00 B1,700.00 B1,700.00 B1,700.00 B1,700.00 B1,700.00 B1,700.00 B1,700.00 B1,700.00 B1,700.00 B1,700.00 ^ NSalary Spouse DSalary Income, Spouse I T R8096 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NSubscriptions DSubscriptions B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NTax Refund DState/Local Tax Refund I T R4160 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NTaxes DTaxes T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NTaxes:Other DMisc. Taxes T R4432 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NTaxes:Property DProperty Tax T R4416 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NTravel DTransportation Exp B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NUnemployment Inc DUnemployment Compensation I T R7664 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NUtilities DWater, Gas, Electric B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NUtilities:Cable TV DCable TV B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NUtilities:Gas & Electric DGas and Electricity B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NUtilities:Telephone DTelephone Expense B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NUtilities:Water DWater B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NVacation DVacation Expenses B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ NVacation:Lodging DMotel/Hotel Costs B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Expense D B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Expense:GST DGoods and Services Tax T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Expense:Private Health DPrivate Health Care T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Expense:Prov Health DProvincial Health Care T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Expense:PST DProvincial Sales Tax T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Expense:RPP Contrib DRegistered Pension Plan T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Expense:RRSP DRegistered Ret. Savings Plan T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Income D I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Income:CPP QPP DCanada/Quebec Pension Plan I T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Income:RRIF Income DRRIF Income I T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Income:RRSP Income DReg Retirement Plan Income I T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Taxes D B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Taxes:CPP QPP Premium DCPP QPP Contribution T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Taxes:Income Tax DIncome Tax Deducted T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N Canada Taxes:UIC DUnemployment Ins. Commission T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Accrued Int DAccrued Interest T R4592 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Accrued Int NT DNon-Tax Accrued Interest B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Div Income DDividend I T R4576 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Div Income NT DNon-Tax Dividend I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Int Expense DInvestment Interest Exp T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Int Expense NT DNon-Tax Inv Interest Exp B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Int Income DInvestment Interest Inc I T R4592 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Int Income NT DNon-Tax Investment Interest Inc I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Long CapGnDst DLong Term Cap Gain Dist I T R7808 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Long CapGnDst NT DNon-Tax Long Term Cap Gain Dist I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Realized Gain DRealized Gain/Loss I T B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Realized Gain NT DNon-Tax Realized Gain/Loss I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Short CapGnDst DShort Term Cap Gain Dist I T R4576 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Short CapGnDst NT DNon-Tax Short Cap Gain Dist I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ N¥Unrealized Gn DUnrealized Gain/Loss I B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 B0.00 ^ !Type:Class NClass DClass Description ^ NClass:Subclass DDescription of subclass of class ^ !Account NAsset DSample Asset X ANote on Asset TOth A B25,000.00 ^ !Type:Oth A D1/10/06 POpening Balance M T0.00 A A A A A A CX L[Asset] ^ D1/10/06 PFord MCar T25,000.00 A A A A A A L[Asset] ^ !Account NBank DSample Bank X ANotes on Sample TBank B1,465.00 ^ !Type:Bank D1/10/06 POpening Balance M T0.00 A A A A A A CX L[Bank] ^ D1/10/06 PPaycheck M T1,690.00 A A A A A A LSalary SSalary E $2,000.00 SPayroll Taxes, Self:Federal E $-250.00 SPayroll Taxes, Self:Soc Sec $-50.00 SPayroll Taxes, Self:Medicare E $-10.00 ^ D1/17/06 PSafeway M T-100.00 A A A A A A LGroceries ^ D2/17/06 PSafeway M T-125.00 A A A A A A N>>>>> LGroceries SGroceries E $-100.00 SMisc E $-25.00 ^ D3/17/06 PSafeway M T-100.00 U-100.00 A A A A A A LGroceries ^ D3/17/06 P QFC M T-100.00 U-100.00 A A A A A A LGroceries ^ !Account NCash DSample Cash X ANote on Cash TCash B0.00 ^ !Type:Cash D1/10/06 POpening Balance M T0.00 A A A A A A CX L[Cash] ^ !Account NCredit Card DSample Card L15,000.00 X ANote on Card TCCard B0.00 ^ !Type:CCard D1/10/06 POpening Balance M T0.00 A A A A A A CX L[Credit Card] ^ !Account NLiability DSample Liability X ANote on Liability TOth L B50,000.00 ^ !Type:Oth L D1/10/06 POpening Balance M T-50,000.00 A A A A A A CX L[Liability] ^ !Account NMutual Fund DSample Fund X ANote on Fund TMutual B672.87 ^ !Type:Invst D1/10/06 NShrsIn YMutual Fund Q33 ^ !Account NPortfolio DSample Portfolio X ANote on portfolio TPort B2,651.00 ^ !Type:Invst D1/10/06 NShrsIn YIntuit Q50 MInitial Move ^ !Type:Prices "INTU",55.400,"12/12/05",55.560,53.660,3120461 "INTU",55.570,"12/13/05",55.740,54.600,2437647 "INTU",54.920,"12/14/05",55.940,54.210,1884946 "INTU",54.430,"12/15/05",55.240,53.530,1610655 "INTU",53.190,"12/16/05",54.290,53.100,4344145 "INTU",52.520,"12/19/05",53.500,52.430,1593831 "INTU",52.810,"12/20/05",53.090,52.380,1496008 "INTU",53.200,"12/21/05",53.370,52.750,1026326 "INTU",53.730,"12/22/05",53.940,53.110,1079007 "INTU",53.980,"12/23/05",54.300,53.690,774293 "INTU",53.550,"12/27/05",54.490,53.300,930070 "INTU",53.600,"12/28/05",53.800,53.080,1067931 "INTU",53.660,"12/29/05",53.970,53.600,847535 "INTU",53.300,"12/30/05",53.980,53.280,1000959 "INTU",54.080,"1/3/06",54.410,51.870,2053047 "INTU",54.160,"1/4/06",54.160,53.570,1626743 "INTU",53.360,"1/5/06",54.160,53.200,1603426 "INTU",53.950,"1/6/06",53.950,52.890,1914459 "INTU",53.140,"1/9/06",53.790,52.930,1486963 "INTU",53.020,"1/10/06",53.620,52.560,1450376 ^ "PGRWX",19.740,"12/12/05",19.740,19.740,0 "PGRWX",19.880,"12/13/05",19.880,19.880,0 "PGRWX",20.010,"12/14/05",20.010,20.010,0 "PGRWX",19.960,"12/15/05",19.960,19.960,0 "PGRWX",19.900,"12/16/05",19.900,19.900,0 "PGRWX",19.800,"12/19/05",19.800,19.800,0 "PGRWX",19.810,"12/20/05",19.810,19.810,0 "PGRWX",19.880,"12/21/05",19.880,19.880,0 "PGRWX",19.990,"12/22/05",19.990,19.990,0 "PGRWX",20,"12/23/05",20,20,0 "PGRWX",19.810,"12/27/05",19.810,19.810,0 "PGRWX",19.840,"12/28/05",19.840,19.840,0 "PGRWX",19.800,"12/29/05",19.800,19.800,0 "PGRWX",19.730,"12/30/05",19.730,19.730,0 "PGRWX",20.030,"1/3/06",20.030,20.030,0 "PGRWX",20.110,"1/4/06",20.110,20.110,0 "PGRWX",20.130,"1/5/06",20.130,20.130,0 "PGRWX",20.290,"1/6/06",20.290,20.290,0 "PGRWX",20.380,"1/9/06",20.380,20.380,0 "PGRWX",20.390,"1/10/06",,,0 ^ "WIN",19.740,"12/12/05" "WIN",19.880,"12/13/05" "WIN",20.010,"12/14/05" "WIN",19.960,"12/15/05" ^ !Type:Memorized T-50.00 PSafeway M KC ^ T-1,140.17 PBank M A L[Liability] S[Liability] Eprincipal $-952.67 SMortgage Int Einterest $-187.50 12/1/06 24 30 412 54.5 625,000.00 725,000.00 KP ^ NSellX YIDS New D A I22.096936 Q158.393 U3,500.00 T3,500.00 MInvestment L[Invest] $3,500.00 KI ^ Finance-QIF-3.02/Changes0000644000076400007640000000562011451006407014626 0ustar matthewmatthewRevision history for Perl extension Finance::QIF. 3.02 Wed Sep 29 21:04:00 PDT 2010 - Fixed bugs with write support for splits with partial data. 3.01 Thu Apr 08 12:30:00 EDT 2010 - Corrected documentation for Account and Type:Memorized - Updated code separators based on perlport "NEWLINES" recommendation - Use binmode in more compatible manner (older IO::File missing method) 3.00 Thu Jul 24 12:17:00 2008 - Added support for Quicken 2006 new Memorized Investment fields - Added support for Quicken 2006 Price format - The changes for the Memorized investment created a conflict with naming to get things consistent and to hopefully avoid future issues some value names have changed. This is the reason for a 3.00. Old code may require modification this map will help in porting Old Name New Name noninvestment:amount noninvestment:transaction memorized:transaction memorized:type memorized:amount memorized:transaction 2.07 Tue Mar 18 12:06:00 2008 - No changes to actual code other than version number and COPYRIGHT update. - Added Requirement for IO::File 1.13 - Fixed tests so new test using "<:crlf" format is only done for perl 5.008 - These changes should allow this module to play nicer in perl 5.006 configurations 2.06 Sat Apr 28 10:25:00 2007 - Fixed bug 1704960 win32 line end /record sep test failed - Added pod and pod-coverage tests - Adjustments to MANIFEST and Makefile.PL for better compatibility with current versions of ExtUtils::MakeMaker 2.05 Sun Feb 04 13:00:00 2007 - Better recognition of headers that have trailing white space - Added support for budget ("B") item in category type - Added support for total ("U") item in memorized type - Number of documentation changes/corrections 2.04 Wed Jul 19 9:00:00 2006 - Added autodetect option - Fixed bug that caused wrong separator to be used on output files - Added support for standard file open options - Changed separator support to move to single record_separator 2.03 Mon Apr 17 09:33:00 2006 - Added support for "U" in investment and non-investment accounts. - Added support for trim_white_space. 2.02 Mon Mar 19 13:06:00 2006 - Fixed [rt.cpan.org #18246] Splits don't always have memo fields. 2.01 Mon Mar 3 15:25:00 2006 - Fixed bug that returned empty record in some cases. - Fixed a few typos in documentation. 2.00 Mon Feb 22 15:45:00 2006 - Initial 2.0 release of Finance::QIF this version supports all know QIF record types and values. The API's have changed since 1.X. 2.00B Mon Jan 26 19:14:00 2006 - Beta version of Finance::QIF that should be able to read and write exports from Quicken. 0.01 Mon Jan 9 12:07:56 2006 - original version; created by h2xs 1.23 with options -AX -n Finance::QIF Finance-QIF-3.02/README0000644000076400007640000000265711451006035014217 0ustar matthewmatthewFinance-QIF version 3.02 ======================= Simple QIF file reader. QIF is a common financial software export file format. This module was developed to support Quicken QIF exports. This module reads QIF records from a data file passing each successive record to the caller for processing. INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: Carp, IO::File COPYRIGHT AND LICENSE Copyright (C) 2006 by Matthew McGillis This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. COMMENTS We have put together a new version of FInance::QIF. I was looking for something that could process a complete export from Quicken. I took a look at the original Finance::QIF currently available at CPAN and found it could not process much of a typical Quicken QIF file. This new version of Finance::QIF is fairly different from the existing Finance::QIF. We have contacted the previous owner and asked for permission to take ownership of the existing Finance::QIF. They agreed to let us pick up the code. This version does have different API's than the 1.X so anyone porting code to this new version will have to make modifications to get things working. The software is available at http://sourceforge.net/projects/finance-qif Regards, Matthew McGillis & Phil Lobbes