2 # make_invoice makes latex invoices, and is released
3 # under the terms of the GPL version 2, or any later version, at your
4 # option. See the file README and COPYING for more information.
5 # Copyright 2008 by Don Armstrong <don@donarmstrong.com>.
6 # $Id: perl_script 495 2006-08-10 08:02:01Z don $
17 make_invoice - makes invoices using latex
21 make_invoice [options]
24 --log, the log file to use to make the invoice
25 --template, the template to use to make the invoice
26 --svn, whether to use subversion or not
27 --debug, -d debugging level (Default 0)
28 --help, -h display this help
29 --man, -m display manual
37 The log file to use to generate the invoice
39 =item B<--template, -t>
41 The tex template to use to generate the invoice
45 Whether to use subversion or not; defaults to yes if .svn exists in
46 the current directory.
50 Invoice directory to place invoice in (automatically calculated if not
53 =item B<--min-time-interval, -m>
55 Minimum time interval to bill, defaults to 0.
57 =item B<--time-granularity, -g>
59 Time granularity, defaults to 0.
61 =item B<--hourly-fee,-f>
63 Hourly fee, defaults to 50.00
67 Debug verbosity. (Default 0)
71 Display brief useage information.
92 my %options = (log => undef,
97 time_granularity => 0,
98 hourly_fee => '50.00',
104 GetOptions(\%options,
105 'log|l=s','template|t=s','invoice|i=s','svn|s!',
106 'time_granularity|time-granularity|g=s',
107 'time_interval|min-time-interval|T=s',
108 'hourly_fee|hourly-fee|f=s',
109 'debug|d+','help|h|?','man|m');
111 pod2usage() if $options{help};
112 pod2usage({verbose=>2}) if $options{man};
114 $DEBUG = $options{debug};
117 if (not defined $options{log}) {
118 push @USAGE_ERRORS,"You must pass a log file with --log";
120 if (not defined $options{template}) {
121 push @USAGE_ERRORS,"You must pass a template file with --template";
124 pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
126 if (not defined $options{svn}) {
127 $options{svn} = -e '.svn';
131 my $log_fh = IO::File->new($options{log},'r') or
132 die "Unable to open $options{log} for reading: $!";
133 my $template_fh = IO::File->new($options{template},'r') or
134 die "Unable to open $options{template} for reading: $!";
139 my $totaldelta = undef;
141 my $first_date = undef;
142 my $last_date = undef;
152 $tex_log .= format_events($date,$date2,$time,@events);
158 my ($d1,$d2) = map {s/^\s*\*\s*//;
161 if (not defined $first_date) {
165 my $delta = DateCalc($d1,$d2);
166 my $hours = Delta_Format($delta,0,'%ht');
167 if ($hours < $options{time_interval}) {
168 $hours = $options{time_interval}
170 if ($options{time_granularity}) {
171 $hours = ceil($hours / $options{time_granularity})*$options{time_granularity};
173 $delta = ParseDateDelta($hours * 60 * 60 . ' sec');
174 ($date,$date2) = ($d1,$d2);
176 $totaldelta = defined($totaldelta)?DateCalc($delta,$totaldelta):$delta;
177 $calc_log .= qq($string [).Delta_Format($delta,2,q(%ht)).qq(] [).Delta_Format($totaldelta,2,q(%ht)).qq(]\n);
179 elsif (/^\s+-\s*(.+)/) {
183 $calc_log .= $_.qq(\n);
186 $calc_log .= $_.qq(\n);
189 $calc_log .= "\nTotal: ".Delta_Format($totaldelta,2,q(%ht)).qq(\n);
191 $tex_log .= format_events($date,$date2,$time,@events);
202 $template = <$template_fh>;
205 my $invoice_start = UnixDate($first_date,'%B %e, %Y');
206 my $invoice_stop = UnixDate($last_date,'%B %e, %Y');
208 my $tt = Text::Template->new(TYPE=>'string',
210 DELIMITERS => ['{--','--}'],
212 my $tex_invoice = $tt->fill_in(HASH=>{start => $invoice_start,
213 stop => $invoice_stop,
217 if (not defined $tex_invoice) {
218 die $Text::Template::ERROR;
221 my $invoice_date = UnixDate($last_date,'%m_%d_%Y');
222 my $invoice_dir = "invoice_$invoice_date";
224 if (not -d $invoice_dir) {
226 system('svn','mkdir',$invoice_dir) == 0 or
227 die "Unable to create invoice directory $invoice_dir";
230 system('mkdir','-p',$invoice_dir) == 0 or
231 die "Unable to create invoice directory $invoice_dir";
236 if (-e 'common_makefile' and not -e '$invoice_dir/Makefile') {
238 system('ln','-sf','../common_makefile','Makefile') == 0 or
239 die "Unable to link common_makefile to Makefile";
241 system('svn','add','Makefile') == 0 or
242 die "Unable to add Makefile";
247 # now we write stuff out
249 my $calc_log_fh = IO::File->new("log_${invoice_date}",'w') or
250 die "Unable to open log_${invoice_date} for writing: $!";
251 print {$calc_log_fh} $calc_log;
254 my $tex_invoice_fh = IO::File->new("invoice_${invoice_date}.tex",'w') or
255 die "Unable to open log_${invoice_date} for writing: $!";
256 print {$tex_invoice_fh} $tex_invoice;
257 close($tex_invoice_fh);
261 "log_${invoice_date}",
262 "invoice_${invoice_date}.tex",
263 ) == 0 or die "Unable to add log and invoice to svn";
264 system('svn','propset','svn:ignore',
265 "*.aux\n*.log\n*.dvi\n*.ps\n*.pdf\n",
267 ) == 0 or die "Unable to set svn:ignore";
272 my ($date,$date2,$time,@events) = @_;
273 my $output = ' \Fee{'.UnixDate($date,'%A, %B %e, %H:%M').
274 ' to '.UnixDate($date2,'%H:%M %Z')."\n".
275 ' \begin{itemize*}'."\n";
276 $output .= join('',map {" \\item $_\n"} @events);
277 $output .= ' \end{itemize*}}{50.00}{'.$time.'}'."\n";