X-Git-Url: https://git.donarmstrong.com/?p=bin.git;a=blobdiff_plain;f=make_invoice;h=0047d45079b0544e92a4c1121489629ad1c55a02;hp=bce9c8aa4a9d083fc805be2a4fd7f92b6e67ac02;hb=HEAD;hpb=c71e7e46f66be91b14347825e25f70490203b410 diff --git a/make_invoice b/make_invoice index bce9c8a..0047d45 100755 --- a/make_invoice +++ b/make_invoice @@ -21,9 +21,12 @@ make_invoice - makes invoices using latex 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 + --log,-l the log file to use to make the invoice + --template,-t the template to use to make the invoice + --min-time-interval, -m minimum time to bill, default 0 + --time-granularity, -g time granularity, default 0 + --hourly-fee, -f hourly fee, default 50.00 + --svn,-s whether to use subversion or not --debug, -d debugging level (Default 0) --help, -h display this help --man, -m display manual @@ -62,6 +65,14 @@ Time granularity, defaults to 0. Hourly fee, defaults to 50.00 +=item B<--tex-only> + +Only output the LaTeX file + +=item B<--log-only> + +Only output the log file + =item B<--debug, -d> Debug verbosity. (Default 0) @@ -85,20 +96,24 @@ Display this manual. use vars qw($DEBUG); use Date::Manip; -use POSIX qw(ceil); +use POSIX qw(ceil strftime); use Cwd qw(cwd); use Text::Template; +use Params::Validate qw(validate_with :types); + my %options = (log => undef, template => undef, svn => undef, invoice => undef, - time_interval => 0, - time_granularity => 0, + time_interval => 0.00, + time_granularity => 0.00, hourly_fee => '50.00', debug => 0, help => 0, man => 0, + log_only => 0, + tex_only => 0, ); GetOptions(\%options, @@ -106,6 +121,8 @@ GetOptions(\%options, 'time_granularity|time-granularity|g=s', 'time_interval|min-time-interval|T=s', 'hourly_fee|hourly-fee|f=s', + 'log_only|log-only', + 'tex_only|tex-only', 'debug|d+','help|h|?','man|m'); pod2usage() if $options{help}; @@ -135,9 +152,19 @@ my $template_fh = IO::File->new($options{template},'r') or my $calc_log = ''; -my $tex_log = ''; +my $tex_log = <<'EOF'; +\setlength\LTleft{0pt plus 1fill minus 1fill}% +\let\LTright\LTleft +\begin{longtable}{|p{9cm}|r|r|r|r|}% +% \caption*{} +\hline + \mbox{Description} & Item Cost & Quantity & Cost & Total \\ +\endhead +EOF my $totaldelta = undef; +my $total = 0; + my $first_date = undef; my $last_date = undef; my $time = undef; @@ -147,34 +174,54 @@ my @events; while (<$log_fh>) { chomp; + next if /^Total: \d+\.\d{2}$/; if (/^\s*\* /) { + print STDERR $_."\n"; if (defined $time) { - $tex_log .= format_events($date,$date2,$time,@events); + $tex_log .= format_events(date => $date, + date2 => $date2, + time => $time, + total => \$total, + events => \@events); @events = (); $date = undef; $time = undef; } - my $string = $_; - my ($d1,$d2) = map {s/^\s*\*\s*//; - ParseDate($_) - } split /\s*-\s*/; + s/\s*\[[\.\d]+\]\s*\[[\.\d]+\]\s*$//; + my ($d1,$d2); + if (/\s*\*\s*CLOCK:\s+\[([^\]]+)\]--\[([^\]]+)\]/ or + /^\s*\*\s*(.+)?\s* - \s*(.+)?\s*$/ + ) { + $d1 = UnixDate(ParseDate($1),'%s'); + $d2 = UnixDate(ParseDate($2),'%s'); + if (not defined $d1) { + die "Invalid date: $1"; + } + if (not defined $d2) { + die "Invalid date: $2"; + } + } else { + die "malformed line $_"; + } + my $string = '* '.strftime('%A, %B %e, %H:%M:%S',localtime($d1)).' - '. + strftime('%A, %B %e, %H:%M:%S',localtime($d2)); if (not defined $first_date) { $first_date = $d1; } $last_date = $d2; - my $delta = DateCalc($d1,$d2); - my $hours = Delta_Format($delta,0,'%ht'); + my $delta = $d2-$d1; + $date = $d1; + $date2 = $d2; + my $hours = $delta / (60*60); if ($hours < $options{time_interval}) { $hours = $options{time_interval} } - if ($options{time_granularity}) { + if ($options{time_granularity} > 0) { $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); + $totaldelta += $delta; + $calc_log .= $string.q( [).sprintf('%.2f',$hours).qq(] [).sprintf('%.2f',$totaldelta/(60*60)).qq(]\n); } elsif (/^\s+-\s*(.+)/) { my $event = $1; @@ -186,15 +233,31 @@ while (<$log_fh>) { $calc_log .= $_.qq(\n); } } -$calc_log .= "\nTotal: ".Delta_Format($totaldelta,2,q(%ht)).qq(\n); +$calc_log .= "\nTotal: ".sprintf('%.2f',$totaldelta/(60*60)).qq(\n); if (defined $time) { - $tex_log .= format_events($date,$date2,$time,@events); + $tex_log .= format_events(date => $date, + date2 => $date2, + time => $time, + total => \$total, + events => \@events); @events = (); $date = undef; $date2 = undef; $time = undef; } +$tex_log .= <<'EOF'; +\hline\hline +\multicolumn{4}{|r|}{\textbf{Total}} & \$% +EOF + +$tex_log .= sprintf('%.2f',$total)."%\n"; + +$tex_log .= <<'EOF'; +\\ +\hline +\end{longtable} +EOF my $template; { @@ -202,8 +265,8 @@ my $template; $template = <$template_fh>; } -my $invoice_start = UnixDate($first_date,'%B %e, %Y'); -my $invoice_stop = UnixDate($last_date,'%B %e, %Y'); +my $invoice_start = strftime('%c',localtime($first_date)); +my $invoice_stop = strftime('%c',localtime($last_date)); my $tt = Text::Template->new(TYPE=>'string', SOURCE => $template, @@ -212,13 +275,25 @@ my $tt = Text::Template->new(TYPE=>'string', my $tex_invoice = $tt->fill_in(HASH=>{start => $invoice_start, stop => $invoice_stop, log => $tex_log, + total => sprintf('%0.2f',$total), } ); if (not defined $tex_invoice) { die $Text::Template::ERROR; } -my $invoice_date = UnixDate($last_date,'%m_%d_%Y'); +if ($options{log_only}) { + print $calc_log; + exit 0; +} + +if ($options{tex_only}) { + print $tex_invoice; + exit 0; +} + + +my $invoice_date = strftime('%Y_%m_%d',localtime($last_date)); my $invoice_dir = "invoice_$invoice_date"; if (not -d $invoice_dir) { @@ -262,21 +337,49 @@ if ($options{svn}) { "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", + "*.aux\n*.log\n*.dvi\n*.ps\n*.pdf\nauto\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"; + my %param = validate_with(params => \@_, + spec => {time => {type => SCALAR, + }, + date => {type => SCALAR, + }, + date2 => {type => SCALAR, + }, + total => {type => SCALARREF, + }, + events => {type => ARRAYREF, + }, + }, + ); + ${$param{total}} += $param{time} * $options{hourly_fee}; + +# $param{date} =~ s/\s+\d+\:\d+\:\d+\s+[A-Z]{0,3}\s*//; + my $output = '\hline'."\n".' \mbox{'.strftime('%A, %B %e, %H:%M',localtime($param{date})). + ' to '.strftime('%H:%M %Z',localtime($param{date2}))."}\n\n". + ' \begin{itemize*}'."\n"; + $output .= join('',map { s/_/\\_/g; " \\item $_\n";} @{$param{events}}); + $output .= ' \end{itemize*} & \$'.sprintf('%.2f',$options{hourly_fee}).' & '.sprintf('%.2f',$param{time}). + ' & \$'.sprintf('%.2f',$param{time}*$options{hourly_fee}).' & \$'. + sprintf('%.2f',${$param{total}}) . + " \\\\\n"; return $output; } +## sub format_events{ +## my ($date,$date2,$time,@events) = @_; +## my $output = ' \Fee{'.strftime('%A, %B %e, %H:%M',localtime(UnixDate($date,'%s'))). +## ' to '.strftime('%H:%M %Z',localtime(UnixDate($date2,'%s')))."\n". +## ' \begin{itemize*}'."\n"; +## $output .= join('',map {" \\item $_\n"} @events); +## $output .= ' \end{itemize*}}{50.00}{'.$time.'}'."\n"; +## return $output; +## } + __END__