| 
									
										
										
										
											2011-12-02 07:21:57 -05:00
										 |  |  | #!/usr/bin/perl | 
					
						
							|  |  |  | # general-ledger-report.plx                                    -*- Perl -*- | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #    Script to generate a General Ledger report that accountants like | 
					
						
							|  |  |  | #    using Ledger. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (C) 2011, Bradley M. Kuhn | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program gives you software freedom; you can copy, modify, convey, | 
					
						
							|  |  |  | # and/or redistribute it under the terms of the GNU General Public License | 
					
						
							|  |  |  | # as published by the Free Software Foundation; either version 3 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 in a file called 'GPLv3'.  If not, write to the: | 
					
						
							|  |  |  | #    Free Software Foundation, Inc., 51 Franklin St, Fifth Floor | 
					
						
							|  |  |  | #                                    Boston, MA 02110-1301, USA. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use strict; | 
					
						
							|  |  |  | use warnings; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | use Math::BigFloat; | 
					
						
							| 
									
										
										
										
											2011-12-02 08:21:15 -05:00
										 |  |  | use Date::Manip; | 
					
						
							| 
									
										
										
										
											2011-12-02 07:21:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | my $LEDGER_CMD = "/usr/bin/ledger"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | my $ACCT_WIDTH = 75; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | sub ParseNumber($) { | 
					
						
							|  |  |  |   $_[0] =~ s/,//g; | 
					
						
							|  |  |  |   return Math::BigFloat->new($_[0]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Math::BigFloat->precision(-2); | 
					
						
							|  |  |  | my $ZERO =  Math::BigFloat->new("0.00"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (@ARGV < 2) { | 
					
						
							|  |  |  |   print STDERR "usage: $0 <BEGIN_DATE> <END_DATE> <OTHER_LEDGER_OPTS>\n"; | 
					
						
							|  |  |  |   exit 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | my($beginDate, $endDate, @otherLedgerOpts) = @ARGV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | my(@chartOfAccountsOpts) = ('--wide-register-format', "%150A\n",  '-w', '-s', | 
					
						
							| 
									
										
										
										
											2011-12-02 08:28:14 -05:00
										 |  |  |                             '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg'); | 
					
						
							| 
									
										
										
										
											2011-12-02 07:21:57 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | open(CHART_DATA, "-|", $LEDGER_CMD, @chartOfAccountsOpts) | 
					
						
							|  |  |  |   or die "Unable to run $LEDGER_CMD @chartOfAccountsOpts: $!"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | open(CHART_OUTPUT, ">", "chart-of-accounts.txt") or die "unable to write chart-of-accounts.txt: $!"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | my @accounts; | 
					
						
							|  |  |  | while (my $line = <CHART_DATA>) { | 
					
						
							|  |  |  |   chomp $line; | 
					
						
							|  |  |  |   $line =~ s/^\s*//;   $line =~ s/\s*$//; | 
					
						
							|  |  |  |   push(@accounts, $line); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | close(CHART_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | open(CHART_OUTPUT, ">", "chart-of-accounts.txt") or die "unable to write chart-of-accounts.txt: $!"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | my @sortedAccounts; | 
					
						
							|  |  |  | foreach my $acct ( | 
					
						
							|  |  |  |                   # Proper sorting for a chart of accounts | 
					
						
							|  |  |  |                   sort { | 
					
						
							|  |  |  |                     if ($a =~ /^Assets/ and $b !~ /^Assets/) { | 
					
						
							| 
									
										
										
										
											2011-12-02 07:23:35 -05:00
										 |  |  |                       return -1; | 
					
						
							| 
									
										
										
										
											2011-12-02 07:24:51 -05:00
										 |  |  |                     } elsif ($a =~ /^Liabilities/ and $b !~ /^Liabilitie/) { | 
					
						
							| 
									
										
										
										
											2011-12-02 07:23:35 -05:00
										 |  |  |                       return -1; | 
					
						
							| 
									
										
										
										
											2011-12-02 07:21:57 -05:00
										 |  |  |                     } else { | 
					
						
							|  |  |  |                       return $a cmp $b; | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2011-12-02 07:24:51 -05:00
										 |  |  |                     } @accounts) { | 
					
						
							| 
									
										
										
										
											2011-12-02 07:21:57 -05:00
										 |  |  |   print CHART_OUTPUT "$acct\n"; | 
					
						
							|  |  |  |   push(@sortedAccounts, $acct); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | close(CHART_OUTPUT); die "error writing to chart-of-accounts.txt: $!" unless $? == 0; | 
					
						
							| 
									
										
										
										
											2011-12-02 08:21:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | my $formattedEndDate = new Date::Manip::Date; | 
					
						
							|  |  |  | die "badly formatted end date, $endDate" if $formattedEndDate->parse($endDate); | 
					
						
							|  |  |  | my $oneDayLess = new Date::Manip::Delta; | 
					
						
							|  |  |  | die "bad one day less" if $oneDayLess->parse("- 1 day"); | 
					
						
							|  |  |  | $formattedEndDate = $formattedEndDate->calc($oneDayLess); | 
					
						
							|  |  |  | $formattedEndDate = $formattedEndDate->printf("%Y/%m/%d"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-02 08:27:48 -05:00
										 |  |  | open(GL_TEXT_OUT, ">", "general-ledger.txt") or die "unable to write general-ledger.txt: $!"; | 
					
						
							|  |  |  | open(GL_CSV_OUT, ">", "general-ledger.csv") or die "unable to write general-ledger.csv: $!"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-02 08:21:15 -05:00
										 |  |  | foreach my $acct (@sortedAccounts) { | 
					
						
							| 
									
										
										
										
											2011-12-02 08:27:48 -05:00
										 |  |  |   print GL_TEXT_OUT "\n\nACCOUNT: $acct\nFROM:    $beginDate TO $formattedEndDate\n\n"; | 
					
						
							| 
									
										
										
										
											2011-12-02 08:21:15 -05:00
										 |  |  |   my @acctLedgerOpts = ('--wide-register-format', | 
					
						
							| 
									
										
										
										
											2011-12-02 08:42:55 -05:00
										 |  |  |                         "%D  %-.10C   %-.80P  %-.80N  %18t  %18T\n", '-w', '--sort', 'd', | 
					
						
							| 
									
										
										
										
											2011-12-02 08:21:15 -05:00
										 |  |  |                         '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg', $acct); | 
					
						
							| 
									
										
										
										
											2011-12-02 08:27:48 -05:00
										 |  |  |   open(GL_TEXT_DATA, "-|", $LEDGER_CMD, @acctLedgerOpts) | 
					
						
							|  |  |  |     or die "Unable to run $LEDGER_CMD @acctLedgerOpts: $!"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   foreach my $line (<GL_TEXT_DATA>) { | 
					
						
							|  |  |  |     print GL_TEXT_OUT $line; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   close(GL_TEXT_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-02 08:42:55 -05:00
										 |  |  |   print GL_CSV_OUT "\n\"ACCOUNT:\",\"$acct\"\n\"PERIOD START:\",\"$beginDate\"\n\"PERIOD END:\",\"$formattedEndDate\"\n"; | 
					
						
							|  |  |  |   print GL_CSV_OUT '"DATE","CHECK NUM","NAME","MEMO","TRANSACTION AMT","RUNNING TOTAL"', "\n"; | 
					
						
							| 
									
										
										
										
											2011-12-02 08:27:48 -05:00
										 |  |  |   @acctLedgerOpts = ('--wide-register-format', | 
					
						
							| 
									
										
										
										
											2011-12-02 08:42:55 -05:00
										 |  |  |                      '"%D","%C","%P","%N","%t","%T"\n', '-w', '--sort', 'd', | 
					
						
							| 
									
										
										
										
											2011-12-02 08:27:48 -05:00
										 |  |  |                         '-b', $beginDate, '-e', $endDate, @otherLedgerOpts, 'reg', $acct); | 
					
						
							|  |  |  |   open(GL_CSV_DATA, "-|", $LEDGER_CMD, @acctLedgerOpts) | 
					
						
							| 
									
										
										
										
											2011-12-02 08:21:15 -05:00
										 |  |  |     or die "Unable to run $LEDGER_CMD @acctLedgerOpts: $!"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-02 08:27:48 -05:00
										 |  |  |   foreach my $line (<GL_CSV_DATA>) { | 
					
						
							|  |  |  |     print GL_CSV_OUT $line; | 
					
						
							| 
									
										
										
										
											2011-12-02 08:21:15 -05:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2011-12-02 08:27:48 -05:00
										 |  |  |   close(GL_CSV_DATA); die "error reading ledger output for chart of accounts: $!" unless $? == 0; | 
					
						
							| 
									
										
										
										
											2011-12-02 08:21:15 -05:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-12-02 08:27:48 -05:00
										 |  |  | close(GL_TEXT_OUT); die "error writing to general-ledger.txt: $!" unless $? == 0; | 
					
						
							|  |  |  | close(GL_CSV_OUT); die "error writing to general-ledger.csv: $!" unless $? == 0; | 
					
						
							| 
									
										
										
										
											2011-12-02 07:21:57 -05:00
										 |  |  | ############################################################################### | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Local variables: | 
					
						
							|  |  |  | # compile-command: "perl -c general-ledger-report.plx" | 
					
						
							|  |  |  | # End: | 
					
						
							|  |  |  | 
 |