summaryrefslogtreecommitdiff
path: root/contrib/raw/GenerateLatexExpeneseReport.pl
diff options
context:
space:
mode:
authoradamsrl <rladams@adamsinfoserv.com>2012-08-22 18:27:23 -0500
committeradamsrl <rladams@adamsinfoserv.com>2012-08-22 18:27:23 -0500
commit6de14e08677f6cb0c10c9d34c3024918bf979d68 (patch)
treecbc9b87ce831ac9282149724494c52e11c8c0e10 /contrib/raw/GenerateLatexExpeneseReport.pl
parentf01e09e5196127fa12e7291f3fc029ffa99eaef8 (diff)
downloadfork-ledger-6de14e08677f6cb0c10c9d34c3024918bf979d68.tar.gz
fork-ledger-6de14e08677f6cb0c10c9d34c3024918bf979d68.tar.bz2
fork-ledger-6de14e08677f6cb0c10c9d34c3024918bf979d68.zip
Added raw copies of my custom ledger scripts and elisp additions. Next need to isolate features and make them more generic for reuse by other Ledger users.
Diffstat (limited to 'contrib/raw/GenerateLatexExpeneseReport.pl')
-rwxr-xr-xcontrib/raw/GenerateLatexExpeneseReport.pl429
1 files changed, 429 insertions, 0 deletions
diff --git a/contrib/raw/GenerateLatexExpeneseReport.pl b/contrib/raw/GenerateLatexExpeneseReport.pl
new file mode 100755
index 00000000..670d9f21
--- /dev/null
+++ b/contrib/raw/GenerateLatexExpeneseReport.pl
@@ -0,0 +1,429 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+use Getopt::Long; # Options processing
+use Smart::Comments -ENV, "###"; # Ignore my dividers, and use
+ # Smart_Comments=1 to activate
+use Cwd;
+use File::Basename;
+use 5.10.0;
+use POSIX qw(strftime);
+use Date::Calc qw(Add_Delta_Days);
+
+use Template;
+my $TT = Template->new( { POST_CHOMP => 1 } );
+
+######################################################################
+# TODO
+#
+# DONE Meal summaries are broken for multi-week reports
+
+######################################################################
+# Options
+
+# Is this an internal report?
+my $ExpenseReportCode = undef;
+my $Internal = undef;
+my $SuppressMeals = 0;
+my $ViewAfter = 0;
+my $ImageDir = ".";
+my $Anonymize = 0;
+my $Help = undef;
+
+GetOptions( 'c' => \$Internal,
+ 'm' => \$SuppressMeals,
+ 'v' => \$ViewAfter,
+ 'a' => \$Anonymize,
+ 'I' => \$ImageDir,
+ 'e:s' => \$ExpenseReportCode,
+ 'h|help' => \$Help
+ );
+
+# Help
+
+defined $Help && do {
+ print <<EOF;
+Usage: GenerateLatexExpenseReport.pl [OPTION] -e ERCode
+
+Options:
+ -c Internal report
+ -m Suppress meals
+ -v View PDF on completion
+ -a Anonymous, omit header/footer
+ -I Image directory
+ -e ER Code (AISER0001)
+
+EOF
+ exit -1;
+};
+
+die "Pass -e <ExpenseReportCode>" unless defined $ExpenseReportCode;
+
+######################################################################
+# Report items
+
+my @ItemizedExpenses;
+my $ItemizedTotal = 0.00;
+
+my @ItemizedReceipts;
+
+my @MealsReport;
+
+######################################################################
+# Gather required data about this expense report from the directory name
+# ie: ./AISER0015 20090419-20090425 AGIL2078 Shands HACMP/
+#
+# ExpenseReportCode = AISER0015
+# DateRange = 20090419-20090425
+# ProjectCode = AGIL2078
+# Description = Shands HACMP
+
+
+######################################################################
+# Remaining options
+
+# Where is the ledger binary?
+my $LedgerBin = "./ledger -f ./.ledger -V";
+
+# -E Show empty accounts
+# -S d Sort by date
+# -V Convert mileage to $
+my $LedgerOpts = "--no-color -S d";
+
+my $LedgerAcct = "^Dest:Projects";
+
+my $LedgerCriteria = "%" . "ER=$ExpenseReportCode";
+
+# Internal report?
+
+if ( $Internal ) {
+
+ # No mileage on an internal report
+ # $LedgerCriteria .= "&!/Mileage/"; # This shouldn't matter, just don't put metadata for ER on mileage
+ $LedgerAcct = "^Dest:Internal";
+
+}
+
+my $CmdLine = "$LedgerBin reg $LedgerOpts -E \"$LedgerCriteria\" and ^Stub "
+ . "--format \"%(tag('ER'))~%(tag('PROJECT'))~%(tag('NOTE'))\n\"";
+### $CmdLine
+
+my @TempLine = `$CmdLine`;
+
+# Match all remaining items
+$TempLine[0] =~ m,^(?<Er>.*?)~
+ (?<Project>.*?)~
+ (?<Note>.*?)\s*$,x;
+
+my $ProjectCode= $+{'Project'};
+my $Description= $+{'Note'};
+
+### $ExpenseReportCode
+### $ProjectCode
+### $Description
+### $LedgerAcct
+### $Internal
+### $Anonymize
+### $LedgerAcct
+### $LedgerOpts
+### $LedgerCriteria
+
+
+######################################################################
+# Pull main ledger report of line items for the expense report
+# Using ~ as a delimiter
+#
+# Example:
+# '2009/04/25~AR:Projects:AGIL2078:PersMealsLunch~:AISER0015: PILOT 00004259 MIDWAY, FL~ 8.68~Receipts/AGIL2078/20090425_Pilot_8_68_21204.jpg\n'
+#
+#./ledger --no-color reg %ER=AISER0040 and ^Projects -y "%Y/%m/%d" -V --format "%(date)~%(account)~%(payee)~%(amount)~%(tag('NOTE'))\n"
+
+$CmdLine = "$LedgerBin reg $LedgerOpts \"$LedgerCriteria\" and \"$LedgerAcct\" "
+ . "-y \"%Y/%m/%d\" "
+ . "--format \"%(date)~%(tag('CATEGORY'))~%(payee)~%(display_amount)~%(tag('NOTE'))~%(tag('RECEIPT'))\\n\"";
+### $CmdLine
+my @MainReport = `$CmdLine`;
+
+
+### MainReport: @MainReport
+
+# Remove any project codes and linefeeds
+#map { chomp(); s/(:AISER[0-9][0-9][0-9][0-9])+://g; } @MainReport; # No need, thats now metadata
+
+foreach my $line (@MainReport) { ### Processing Main Report... done
+
+ # Remove bad chars (#&)
+ $line =~ tr/#&//d;
+
+ # Match all remaining items
+ $line =~ m,^(?<Date>[0-9]{4}/[0-9]{2}/[0-9]{2})~
+ (?<Category>.*?)~
+ (?<Vendor>.*?)~
+ (?<Amount>.*?)~
+ (?<Note>.*?)~
+ (?<Receipts>.*?)\s*$,x;
+ my %Record = %+;
+
+ $Record{'Amount'}=~tr/$,//d;
+
+ foreach (keys %Record) {
+ $Record{$_} =~ s/^\s+//g;
+ }
+
+
+ # Grab images from <<file:///dir/filename.jpg>>
+ my $ImageList = $Record{'Receipts'};
+ $ImageList //= '';
+ my @Images = split( /,/, $ImageList );
+
+ # Cleanup
+ # Take last word of account name as category
+ $Record{'Category'} = ( split( /:/, $Record{'Category'} ) )[-1];
+
+ # If no images, italicise the line item.
+ $Record{'Italics'} = 1;
+
+ # Test images
+ foreach my $Image (@Images) {
+
+ # Turn off italics because there is an image
+ $Record{'Italics'} = 0;
+
+ if (! -r $ImageDir . "/" . $Image) {
+ print STDERR "Missing $ImageDir/$Image\n";
+ }
+ }
+
+ # Add to itemized expenses to be printed
+ push( @ItemizedExpenses, \%Record );
+ $ItemizedTotal += $Record{'Amount'};
+
+ # Add to itemized reciepts for printing
+ push( @ItemizedReceipts, { 'Vendor' => $Record{'Vendor'},
+ 'Amount' => $Record{'Amount'},
+ 'Date' => $Record{'Date'},
+ 'Images' => \@Images } )
+ if $ImageList;
+
+}
+
+### @ItemizedExpenses
+
+######################################################################
+# Meals report
+
+# Summarize total spent on meals by day
+$CmdLine = "$LedgerBin reg $LedgerOpts "
+ . "\"$LedgerCriteria\" and \"$LedgerAcct\" and \%CATEGORY=Meals "
+ . "-D -n "
+ . "--format \"%(account)~%(payee)~%(display_amount)~%(total)\n\" "
+ . "| grep -v '<None>'";
+
+### $CmdLine
+my @MealsOutput = `$CmdLine`;
+
+### @MealsOutput
+
+foreach my $line (@MealsOutput) {
+
+ # Match all remaining items
+ $line =~ m,^(?<Account>.*?)~
+ (?<DOW>.*?)~\$
+ (?<Amount>\s*[0-9]+\.[0-9]+)~\$
+ (?<RunningTotal>.*?)\s*$,x;
+ my %TRecord=%+;
+ $TRecord{'Account'}=~s/^Projects://g;
+ $TRecord{'DOW'}=~s/^- //g;
+
+ # Add to itemized expenses to be printed
+ push( @MealsReport, \%TRecord );
+
+}
+
+######################################################################
+# Total by category
+
+$CmdLine = "$LedgerBin bal $LedgerOpts "
+ . "\"$LedgerCriteria\" and \"$LedgerAcct\" '--account=tag(\"CATEGORY\")' "
+ . "--format \"%(account)~%(display_total)\\n\"";
+
+### $CmdLine
+my @CategoryOutput = `$CmdLine`;
+
+### @CategoryOutput
+
+my @CategoryReport;
+
+foreach my $line (@CategoryOutput) {
+
+ chomp $line;
+ $line =~ tr/\$,//d;
+
+ # Match all remaining items
+ my @Temp = split(/~/,$line);
+
+ my %TRecord= ( 'Category' => $Temp[0],
+ 'Amount' => $Temp[1]);
+
+ if ($TRecord{'Category'} eq '') {
+ $TRecord{'Category'} = '\\hline \\bf Total';
+ }
+
+ # Cleanup
+ # Take last word of account name as category
+ $TRecord{'Category'} = ( split( /:/, $TRecord{'Category'} ) )[0];
+
+ # Add to itemized expenses to be printed
+ push( @CategoryReport, \%TRecord );
+
+}
+
+### @CategoryReport
+
+
+######################################################################
+# Output
+######################################################################
+
+my $TTVars = {
+ 'Internal' => $Internal,
+ 'SuppressMeals' => $SuppressMeals,
+ 'ExpenseReportCode' => $ExpenseReportCode,
+ 'ProjectCode' => $ProjectCode,
+ 'Description' => $Description,
+ 'ItemizedExpenses' => \@ItemizedExpenses,
+ 'ItemizedTotal' => $ItemizedTotal,
+ 'MealsReport' => \@MealsReport,
+ 'CategoryReport' => \@CategoryReport,
+ 'ItemizedReceipts' => \@ItemizedReceipts,
+ 'ImageDir' => $ImageDir,
+ 'Anonymize' => $Anonymize
+};
+
+#### $TTVars
+
+my $LatexTemplate = <<EOF;
+[% USE format %][% ToDollars = format('\\\$%.2f') %]
+%%%%%%%%%%% Header
+
+\\documentclass[10pt,letterpaper]{article}
+\\usepackage[letterpaper,includeheadfoot,top=0.5in,bottom=0.5in,left=0.75in,right=0.75in]{geometry}
+\\usepackage[utf8]{inputenc}
+\\usepackage[T1]{fontenc}
+\\usepackage[scaled]{helvet}
+\\renewcommand*\\familydefault{\\sfdefault}
+\\usepackage{lastpage}
+\\usepackage{fancyhdr}
+\\usepackage{graphicx}
+\\usepackage{multicol}
+\\usepackage[colorlinks,linkcolor=blue]{hyperref}
+\\pagestyle{fancy}
+\\renewcommand{\\headrulewidth}{1pt}
+\\renewcommand{\\footrulewidth}{0.5pt}
+\\geometry{headheight=48pt}
+
+
+\\begin{document}
+
+%%%%%%%%%% Itemized table
+
+\\section{Itemized Expenses}
+[% FOREACH Expense IN ItemizedExpenses %]
+[% IF loop.first() or ( ( loop.count mod 20 ) == 0 ) %]
+\\begin{tabular}{|l|l|p{2in}|r|p{2in}|}
+\\hline
+\\hline
+\\bf Date & \\bf Category & \\bf Expense & \\bf Amount & \\bf Notes \\\\
+\\hline \\hline
+[% END %]
+[% IF Expense.Italics %]\\it[% END %] [% Expense.Date %] & [% IF Expense.Italics %]\\it[% END %] [% Expense.Category %] & [% IF Expense.Italics %]\\it[% END %] [% Expense.Vendor %] & [% IF Expense.Italics %]\\it[% END %] [% ToDollars(Expense.Amount) %] & [% IF Expense.Italics %]\\it[% END %] [% Expense.Note %] \\\\ \\hline
+[% IF loop.last() %]
+\\hline
+ & & \\bf Total & \\bf [% ToDollars(ItemizedTotal) %] & \\\\
+[% END %]
+[% IF ( ( (loop.count + 1) mod 20 ) == 0 ) or loop.last() %]
+\\hline
+\\hline
+\\end{tabular}
+[% IF ( ( (loop.count + 1) mod 20 ) == 0 ) %]\\newline {\\it Continued on next page...}[% END %]
+\\newpage
+[% END %]
+[% END %]
+
+[% IF ! Internal %][% IF ! SuppressMeals %]
+%%%%%%%%%% Meals summary table
+
+\\section{Meals Summary By Day}
+
+\\begin{tabular}{|l|l|p{2in}|p{2in}|}
+\\hline
+\\hline
+\\bf DOW & \\bf Daily Total & \\bf Running Total \\\\
+\\hline \\hline
+[% FOREACH Meal IN MealsReport %]
+[% Meal.DOW %] & [% ToDollars(Meal.Amount) %] & [% ToDollars(Meal.RunningTotal) %] \\\\ \\hline
+[% END %]
+\\hline
+\\hline
+\\end{tabular}
+[% END %][% END %]
+
+%%%%%%%%%% Category summary
+
+\\section{Expenses Summary}
+
+\\begin{tabular}{|l|l|}
+\\hline
+\\hline
+\\bf Category & \\bf Total \\\\
+\\hline \\hline
+[% FOREACH Category IN CategoryReport %]
+[% Category.Category %] & [% ToDollars(Category.Amount) %] \\\\ \\hline
+[% END %]
+\\hline
+\\hline
+\\end{tabular}
+
+
+%%%%%%%%%% Begin receipts
+
+\\section{Scanned Receipts}
+
+[% FOREACH Receipt IN ItemizedReceipts %]
+\\subsection{[% Receipt.Date %] [% Receipt.Vendor %]: [% ToDollars(Receipt.Amount) %]}
+[% FOREACH Image IN Receipt.Images %]
+\\includegraphics[angle=90,width=\\textwidth,keepaspectratio]{[% ImageDir %]/[% Image %]} \\\\
+[% END %]
+
+[% END %]
+
+
+%%%%%%%%%% Footer
+
+\\end{document}
+EOF
+
+my $LatexFN = $ExpenseReportCode . "-" . $ProjectCode . ".tex";
+### $LatexFN
+
+$TT->process( \$LatexTemplate, $TTVars, "./tmp/" . $LatexFN ) || do {
+ my $error = $TT->error();
+ print "error type: ", $error->type(), "\n";
+ print "error info: ", $error->info(), "\n";
+ die $error;
+};
+
+
+my $LatexOutput = `pdflatex -interaction batchmode -output-directory ./tmp "$LatexFN"`;
+### $LatexOutput
+
+ $LatexOutput = `pdflatex -interaction batchmode -output-directory ./tmp "$LatexFN"`;
+### $LatexOutput
+
+if ($ViewAfter) {
+ my $ViewFN = $LatexFN;
+ $ViewFN =~ s/\.tex$/.pdf/;
+ `acroread "./tmp/$ViewFN"`;
+}
+