From c71e7e46f66be91b14347825e25f70490203b410 Mon Sep 17 00:00:00 2001 From: Don Armstrong Date: Mon, 18 Feb 2008 10:06:57 +0000 Subject: [PATCH] add make invoice command --- make_invoice | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100755 make_invoice diff --git a/make_invoice b/make_invoice new file mode 100755 index 0000000..bce9c8a --- /dev/null +++ b/make_invoice @@ -0,0 +1,282 @@ +#! /usr/bin/perl +# make_invoice makes latex invoices, and is released +# under the terms of the GPL version 2, or any later version, at your +# option. See the file README and COPYING for more information. +# Copyright 2008 by Don Armstrong . +# $Id: perl_script 495 2006-08-10 08:02:01Z don $ + + +use warnings; +use strict; + +use Getopt::Long; +use Pod::Usage; + +=head1 NAME + +make_invoice - makes invoices using latex + +=head1 SYNOPSIS + + make_invoice [options] + + Options: + --log, the log file to use to make the invoice + --template, the template to use to make the invoice + --svn, whether to use subversion or not + --debug, -d debugging level (Default 0) + --help, -h display this help + --man, -m display manual + +=head1 OPTIONS + +=over + +=item B<--log, -l> + +The log file to use to generate the invoice + +=item B<--template, -t> + +The tex template to use to generate the invoice + +=item B<--svn, -s> + +Whether to use subversion or not; defaults to yes if .svn exists in +the current directory. + +=item B<--invoice,-i> + +Invoice directory to place invoice in (automatically calculated if not +passed.) + +=item B<--min-time-interval, -m> + +Minimum time interval to bill, defaults to 0. + +=item B<--time-granularity, -g> + +Time granularity, defaults to 0. + +=item B<--hourly-fee,-f> + +Hourly fee, defaults to 50.00 + +=item B<--debug, -d> + +Debug verbosity. (Default 0) + +=item B<--help, -h> + +Display brief useage information. + +=item B<--man, -m> + +Display this manual. + +=back + +=head1 EXAMPLES + + +=cut + + +use vars qw($DEBUG); + +use Date::Manip; +use POSIX qw(ceil); +use Cwd qw(cwd); +use Text::Template; + +my %options = (log => undef, + template => undef, + svn => undef, + invoice => undef, + time_interval => 0, + time_granularity => 0, + hourly_fee => '50.00', + debug => 0, + help => 0, + man => 0, + ); + +GetOptions(\%options, + 'log|l=s','template|t=s','invoice|i=s','svn|s!', + 'time_granularity|time-granularity|g=s', + 'time_interval|min-time-interval|T=s', + 'hourly_fee|hourly-fee|f=s', + 'debug|d+','help|h|?','man|m'); + +pod2usage() if $options{help}; +pod2usage({verbose=>2}) if $options{man}; + +$DEBUG = $options{debug}; + +my @USAGE_ERRORS; +if (not defined $options{log}) { + push @USAGE_ERRORS,"You must pass a log file with --log"; +} +if (not defined $options{template}) { + push @USAGE_ERRORS,"You must pass a template file with --template"; +} + +pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS; + +if (not defined $options{svn}) { + $options{svn} = -e '.svn'; +} + + +my $log_fh = IO::File->new($options{log},'r') or + die "Unable to open $options{log} for reading: $!"; +my $template_fh = IO::File->new($options{template},'r') or + die "Unable to open $options{template} for reading: $!"; + + +my $calc_log = ''; +my $tex_log = ''; +my $totaldelta = undef; + +my $first_date = undef; +my $last_date = undef; +my $time = undef; +my $date = undef; +my $date2 = undef; +my @events; + +while (<$log_fh>) { + chomp; + if (/^\s*\* /) { + if (defined $time) { + $tex_log .= format_events($date,$date2,$time,@events); + @events = (); + $date = undef; + $time = undef; + } + my $string = $_; + my ($d1,$d2) = map {s/^\s*\*\s*//; + ParseDate($_) + } split /\s*-\s*/; + if (not defined $first_date) { + $first_date = $d1; + } + $last_date = $d2; + my $delta = DateCalc($d1,$d2); + my $hours = Delta_Format($delta,0,'%ht'); + if ($hours < $options{time_interval}) { + $hours = $options{time_interval} + } + if ($options{time_granularity}) { + $hours = ceil($hours / $options{time_granularity})*$options{time_granularity}; + } + $delta = ParseDateDelta($hours * 60 * 60 . ' sec'); + ($date,$date2) = ($d1,$d2); + $time = $hours; + $totaldelta = defined($totaldelta)?DateCalc($delta,$totaldelta):$delta; + $calc_log .= qq($string [).Delta_Format($delta,2,q(%ht)).qq(] [).Delta_Format($totaldelta,2,q(%ht)).qq(]\n); + } + elsif (/^\s+-\s*(.+)/) { + my $event = $1; + chomp $event; + push @events,$event; + $calc_log .= $_.qq(\n); + } + else { + $calc_log .= $_.qq(\n); + } +} +$calc_log .= "\nTotal: ".Delta_Format($totaldelta,2,q(%ht)).qq(\n); +if (defined $time) { + $tex_log .= format_events($date,$date2,$time,@events); + @events = (); + $date = undef; + $date2 = undef; + $time = undef; +} + + +my $template; +{ + local $/; + $template = <$template_fh>; +} + +my $invoice_start = UnixDate($first_date,'%B %e, %Y'); +my $invoice_stop = UnixDate($last_date,'%B %e, %Y'); + +my $tt = Text::Template->new(TYPE=>'string', + SOURCE => $template, + DELIMITERS => ['{--','--}'], + ); +my $tex_invoice = $tt->fill_in(HASH=>{start => $invoice_start, + stop => $invoice_stop, + log => $tex_log, + } + ); +if (not defined $tex_invoice) { + die $Text::Template::ERROR; +} + +my $invoice_date = UnixDate($last_date,'%m_%d_%Y'); +my $invoice_dir = "invoice_$invoice_date"; + +if (not -d $invoice_dir) { + if ($options{svn}) { + system('svn','mkdir',$invoice_dir) == 0 or + die "Unable to create invoice directory $invoice_dir"; + } + else { + system('mkdir','-p',$invoice_dir) == 0 or + die "Unable to create invoice directory $invoice_dir"; + } +} + +my $cwd = cwd; +if (-e 'common_makefile' and not -e '$invoice_dir/Makefile') { + chdir($invoice_dir); + system('ln','-sf','../common_makefile','Makefile') == 0 or + die "Unable to link common_makefile to Makefile"; + if ($options{svn}) { + system('svn','add','Makefile') == 0 or + die "Unable to add Makefile"; + } + chdir($cwd); +} + +# now we write stuff out +chdir($invoice_dir); +my $calc_log_fh = IO::File->new("log_${invoice_date}",'w') or + die "Unable to open log_${invoice_date} for writing: $!"; +print {$calc_log_fh} $calc_log; +close($calc_log_fh); + +my $tex_invoice_fh = IO::File->new("invoice_${invoice_date}.tex",'w') or + die "Unable to open log_${invoice_date} for writing: $!"; +print {$tex_invoice_fh} $tex_invoice; +close($tex_invoice_fh); + +if ($options{svn}) { + system('svn','add', + "log_${invoice_date}", + "invoice_${invoice_date}.tex", + ) == 0 or die "Unable to add log and invoice to svn"; + system('svn','propset','svn:ignore', + "*.aux\n*.log\n*.dvi\n*.ps\n*.pdf\n", + '.' + ) == 0 or die "Unable to set svn:ignore"; +} + + +sub format_events{ + my ($date,$date2,$time,@events) = @_; + my $output = ' \Fee{'.UnixDate($date,'%A, %B %e, %H:%M'). + ' to '.UnixDate($date2,'%H:%M %Z')."\n". + ' \begin{itemize*}'."\n"; + $output .= join('',map {" \\item $_\n"} @events); + $output .= ' \end{itemize*}}{50.00}{'.$time.'}'."\n"; + return $output; +} + + +__END__ -- 2.39.2