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
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)
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,
'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};
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;
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;
$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;
{
$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,
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) {
"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__