version 1.41, 2011/12/22 05:21:56 |
version 1.43, 2011/12/30 03:30:51 |
|
|
#!/usr/bin/perl |
#!/usr/bin/perl |
# $AFresh1: rt_invoices.pl,v 1.40 2011/12/22 04:52:28 andrew Exp $ |
# $AFresh1: rt_invoices.pl,v 1.42 2011/12/30 03:20:45 andrew Exp $ |
######################################################################## |
######################################################################## |
# Copyright (c) 2011 Andrew Fresh <andrew@afresh1.com> |
# Copyright (c) 2011 Andrew Fresh <andrew@afresh1.com> |
# |
# |
|
|
#use YAML; |
#use YAML; |
#print Dump $config, $state; exit; |
#print Dump $config, $state; exit; |
|
|
|
|
my $customers = $config->get('customers'); |
my $customers = $config->get('customers'); |
my $startdate = set_dates($customers); |
my $startdate = set_dates($customers); |
|
|
|
|
|
|
my $iterator = $results->get_iterator; |
my $iterator = $results->get_iterator; |
while ( my $ticket = &$iterator ) { |
while ( my $ticket = &$iterator ) { |
my $cust = find_customer_for_ticket($ticket, $customers); |
my $cust = find_customer_for_ticket( $ticket, $customers ); |
if ( !$cust ) { |
if ( !$cust ) { |
warn "No customer found for ticket " . $ticket->id; |
warn "No customer found for ticket " . $ticket->id; |
next; |
next; |
|
|
push @{ $cust->{invoice}->{projects} }, $project; |
push @{ $cust->{invoice}->{projects} }, $project; |
} |
} |
|
|
|
|
foreach my $cust ( @{$customers} ) { |
foreach my $cust ( @{$customers} ) { |
my $invoice = $cust->{invoice}; |
my $invoice = $cust->{invoice}; |
next unless $invoice && $invoice->{projects} && @{ $invoice->{projects} }; |
next unless $invoice && $invoice->{projects} && @{ $invoice->{projects} }; |
|
|
$invoice->{custid} = $cust->{id}; |
$invoice->{custid} = $cust->{id}; |
$invoice->{transactions} = []; |
$invoice->{transactions} = []; |
|
|
my %transactions; |
my %transactions; |
|
|
$invoice->{total} -= round( $invoice->{discount}{amount} ); |
$invoice->{total} -= round( $invoice->{discount}{amount} ); |
} |
} |
|
|
$invoice->{past_due} = 0; |
if ( my $unpaid_invoices = $state->unpaid_invoices( $cust->{id} ) ) { |
if (my $unpaid_invoices = $state->unpaid_invoices($cust->{id})) { |
|
my %project = ( title => 'Unpaid Invoices', fees => [], ); |
my %project = ( title => 'Unpaid Invoices', fees => [], ); |
|
my $past_due = 0; |
|
|
foreach my $id ( sort { $a <=> $b } keys %{$unpaid_invoices} ) { |
foreach my $id ( sort { $a <=> $b } keys %{$unpaid_invoices} ) { |
my $unpaid = $state->get_invoice($id); |
my $unpaid = $state->get_invoice($id); |
$invoice->{past_due} += $unpaid_invoices->{$id}; |
my $invdate = ymd_to_DateTime( $unpaid->{invdate} ); |
push @{ $project{fees} }, { |
|
id => $id, |
next |
contents => sprintf( |
if $cust->{duedate} |
"Invoice %06d from %s", |
&& DateTime->compare( $invdate, $cust->{duedate} ) > 0; |
$id, ymd_to_DateTime( $unpaid->{invdate} )->ymd |
|
), |
$past_due += $unpaid_invoices->{$id}; |
|
push @{ $project{fees} }, |
|
{ |
|
id => $id, |
|
contents => |
|
sprintf( "Invoice %06d from %s", $id, $invdate->ymd ), |
count => 1, |
count => 1, |
rate => $unpaid_invoices->{$id}, |
rate => $unpaid_invoices->{$id}, |
}; |
}; |
} |
} |
unshift @{ $invoice->{projects} }, \%project; |
|
} |
|
|
|
if ( $invoice->{past_due} ) { |
if ($past_due) { |
$invoice->{total_due} = $invoice->{total} + $invoice->{past_due}; |
$invoice->{past_due} = $past_due; |
|
$invoice->{total_due} = $invoice->{total} + $invoice->{past_due}; |
|
|
|
unshift @{ $invoice->{projects} }, \%project; |
|
} |
} |
} |
|
|
next unless $invoice->{total} > 0 || $invoice->{total_due}; |
next unless $invoice->{total} > 0 || $invoice->{total_due}; |
|
|
$invoice->{to} = make_address( $cust->{address} || $cust->{id} ); |
$invoice->{to} = make_address( $cust->{address} || $cust->{id} ); |
$invoice->{logo} = $config->get('logo'); |
$invoice->{logo} = $config->get('logo'); |
|
|
$state->add_invoice( $invoice ); |
$state->add_invoice($invoice); |
|
|
|
|
foreach my $key (qw/ start end /) { |
foreach my $key (qw/ start end /) { |
if ( exists $invoice->{$key} ) { |
if ( exists $invoice->{$key} ) { |
$invoice->{$key} = $invoice->{$key}->strftime('%B %d, %Y'); |
$invoice->{$key} = $invoice->{$key}->strftime('%B %d, %Y'); |
|
|
} |
} |
|
|
sub find_customer_for_ticket { |
sub find_customer_for_ticket { |
my ($ticket, $customers) = @_; |
my ( $ticket, $customers ) = @_; |
|
|
foreach my $cust ( @{$customers} ) { |
foreach my $cust ( @{$customers} ) { |
next unless $cust->{match}; |
next unless $cust->{match}; |
|
|
return if $cust->{no_invoice}; |
return if $cust->{no_invoice}; |
return unless $cust->{billstart}; |
return unless $cust->{billstart}; |
|
|
my %invoice = ( end => $cust->{billend}->clone->subtract( seconds => 1 ) ); |
my %invoice |
$invoice{start} = $cust->{startinvoicedate}->clone |
= ( end => $cust->{billend}->clone->subtract( seconds => 1 ) ); |
|
$invoice{start} = $cust->{startinvoicedate}->clone |
if $cust->{startinvoicedate}; |
if $cust->{startinvoicedate}; |
|
|
return if $invoice{start} && $invoice{start} > $invoice{end}; |
return if $invoice{start} && $invoice{start} > $invoice{end}; |
|
|
if ( $cust->{base_rate} ) { |
if ( $cust->{base_rate} ) { |
my ( $project, $hours ) = make_base_project( $cust ); |
my ( $project, $hours ) = make_base_project($cust); |
|
|
if ( @{ $project->{fees} } ) { |
if ( @{ $project->{fees} } ) { |
$invoice{end} = $project->{end}; |
$invoice{end} = $project->{end}; |
|
|
} |
} |
|
|
sub make_base_project { |
sub make_base_project { |
my ( $cust ) = @_; |
my ($cust) = @_; |
|
|
my $date = $cust->{billstart}->clone; |
my $date = $cust->{billstart}->clone; |
my $billend = $cust->{billend}->clone; |
my $billend = $cust->{billend}->clone; |
my ($freq) = get_billing_frequency($cust); |
my ($freq) = get_billing_frequency($cust); |
|
|
my $title |
my $title |
= $cust->{frequency} == 1 |
= $cust->{frequency} == 1 |
|
|
while ( $date < $billend ) { |
while ( $date < $billend ) { |
my $start = $date->clone; |
my $start = $date->clone; |
|
|
$date->add( $freq ); |
$date->add($freq); |
|
|
my $end = $date > $billend ? $billend->clone : $date->clone; |
my $end = $date > $billend ? $billend->clone : $date->clone; |
$end->subtract( seconds => 1 ); |
$end->subtract( seconds => 1 ); |
|
|
default { die "Unknown per [$per]\n" } |
default { die "Unknown per [$per]\n" } |
} |
} |
|
|
return DateTime::Duration->new($per => $freq), $day_method; |
return DateTime::Duration->new( $per => $freq ), $day_method; |
} |
} |
|
|
sub set_dates { |
sub set_dates { |
my ($customers) = @_; |
my ($customers) = @_; |
|
|
my $newest_invoice; |
my $newest_invoice; |
my $max_duration; |
my $max_duration; |
|
|
|
|
my $start = $end->clone->subtract($freq); |
my $start = $end->clone->subtract($freq); |
|
|
# XXX This is helpful, but monthly and billday > 28 == !!! |
# XXX This is helpful, but monthly and billday > 28 == !!! |
$end->subtract( days => 1 ) |
$end->subtract( days => 1 ) while $day && $end->$day_method != $day; |
while $day && $end->$day_method != $day; |
|
|
|
my $lastinvoice = $state->last_invoice( $cust->{id} ); |
my $lastinvoice = $state->last_invoice( $cust->{id} ); |
if ( $lastinvoice && $lastinvoice->{end} ) { |
if ( $lastinvoice && $lastinvoice->{end} ) { |
$start = ymd_to_DateTime( $lastinvoice->{end} )->add( days => 1 ); |
$start = ymd_to_DateTime( $lastinvoice->{end} )->add( days => 1 ); |
$cust->{startinvoicedate} = $start->clone; |
$cust->{startinvoicedate} = $start->clone; |
} |
} |
|
|
|
$cust->{duedate} |
|
= $cust->{net} |
|
? DateTime->now->subtract( days => $cust->{net} ) |
|
: 0; |
|
|
$cust->{no_invoice} = 1 if $start->clone->add($freq) > $end; |
$cust->{no_invoice} = 1 if $start->clone->add($freq) > $end; |
$cust->{billend} = $end; |
$cust->{billend} = $end; |