version 1.44, 2011/12/30 05:01:41 |
version 1.48, 2012/11/28 02:23:34 |
|
|
#!/usr/bin/perl |
#!/usr/bin/perl |
# $AFresh1: rt_invoices.pl,v 1.43 2011/12/30 03:30:51 andrew Exp $ |
# $AFresh1: rt_invoices.pl,v 1.47 2012/01/31 04:36:28 andrew Exp $ |
######################################################################## |
######################################################################## |
# Copyright (c) 2011 Andrew Fresh <andrew@afresh1.com> |
# Copyright (c) 2011 Andrew Fresh <andrew@afresh1.com> |
# |
# |
|
|
use File::Path; |
use File::Path; |
use DateTime; |
use DateTime; |
|
|
|
use List::Util qw/ sum /; |
|
|
use lib './lib'; # XXX This is fragile, there are better ways |
use lib './lib'; # XXX This is fragile, there are better ways |
use RTI::Config; |
use RTI::Config; |
use RTI::State; |
use RTI::State; |
|
use RTI::Util qw/ round ymd_to_DateTime /; |
|
|
my $config = RTI::Config->new(); |
my $config = RTI::Config->new(); |
my $state = RTI::State->new( $config->get('state') ); |
my $state = RTI::State->new( $config->get('state') ); |
|
|
|
|
if ( my $unpaid_invoices = $state->unpaid_invoices() ) { |
if ( my $unpaid_invoices = $state->unpaid_invoices() ) { |
foreach my $custid ( keys %{$unpaid_invoices} ) { |
foreach my $custid ( keys %{$unpaid_invoices} ) { |
my %project = ( title => 'Unpaid Invoices', fees => [], ); |
my %project |
|
= ( title => 'Unpaid Invoices', fees => [], no_total => 1 ); |
my $past_due = 0; |
my $past_due = 0; |
|
my $unpaid = 0; |
|
|
my $cust; |
my $cust; |
foreach ( @{$customers} ) { |
foreach ( @{$customers} ) { |
|
|
} |
} |
$cust ||= fake_customer($custid); |
$cust ||= fake_customer($custid); |
|
|
foreach my $id ( sort { $a <=> $b } |
foreach my $id ( |
keys %{ $unpaid_invoices->{$custid} } ) |
sort { $a <=> $b } |
|
keys %{ $unpaid_invoices->{$custid} } |
|
) |
{ |
{ |
my $unpaid = $state->get_invoice($id); |
my $unpaid = $state->get_invoice($id); |
my $invdate = ymd_to_DateTime( $unpaid->{invdate} ); |
my $invdate = ymd_to_DateTime( $unpaid->{invdate} ); |
|
|
next |
my $content |
if $cust->{duedate} |
= sprintf( "Invoice %06d from %s", $id, $invdate->ymd ); |
&& DateTime->compare( $invdate, $cust->{duedate} ) > 0; |
if ( $cust->{duedate} && $invdate < $cust->{duedate}) { |
|
$content = "PAST DUE: $content"; |
|
$past_due += $unpaid_invoices->{$custid}->{$id}; |
|
} |
|
else { |
|
$unpaid += $unpaid_invoices->{$custid}->{$id}; |
|
} |
|
|
$past_due += $unpaid_invoices->{$custid}->{$id}; |
|
push @{ $project{fees} }, |
push @{ $project{fees} }, |
{ |
{ |
id => $id, |
id => $id, |
contents => |
contents => $content, |
sprintf( "Invoice %06d from %s", $id, $invdate->ymd ), |
count => 1, |
count => 1, |
rate => $unpaid_invoices->{$custid}->{$id}, |
rate => $unpaid_invoices->{$custid}->{$id}, |
|
}; |
}; |
} |
} |
|
|
if ($past_due) { |
if ($past_due) { |
$cust->{invoice} ||= make_invoice(); |
$cust->{invoice} ||= make_invoice($cust); |
|
|
$cust->{invoice}->{past_due} = $past_due; |
$cust->{invoice}->{past_due} = $past_due; |
$cust->{invoice}->{total_due} |
$cust->{invoice}->{unpaid} = $unpaid; |
= $cust->{invoice}->{total} + $past_due; |
|
|
|
unshift @{ $cust->{invoice}->{projects} }, \%project; |
unshift @{ $cust->{invoice}->{projects} }, \%project; |
} |
} |
} |
} |
} |
} |
|
|
|
if ( my $credits = $state->credits ) { |
|
foreach my $custid ( keys %{$credits} ) { |
|
|
|
my $cust; |
|
foreach ( @{$customers} ) { |
|
if ( $_->{id} eq $custid ) { |
|
$cust = $_; |
|
last; |
|
} |
|
} |
|
|
|
next unless $cust; |
|
next unless $cust->{invoice}; |
|
|
|
$cust->{invoice}->{credit} = $credits->{$custid}; |
|
|
|
unshift @{ $cust->{invoice}->{projects} }, { |
|
title => 'Credits', |
|
no_total => 1, |
|
fees => [ |
|
{ contents => 'Available Credit', |
|
count => 1, |
|
rate => -$credits->{$custid}, |
|
} |
|
], |
|
}; |
|
} |
|
} |
|
|
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} }; |
|
|
$subtotal += round( $expense->{amount} ); |
$subtotal += round( $expense->{amount} ); |
} |
} |
$project->{total} = $subtotal; |
$project->{total} = $subtotal; |
|
|
|
next if $project->{no_total}; |
$invoice->{total} += $subtotal; |
$invoice->{total} += $subtotal; |
} |
} |
@{ $invoice->{transactions} } = sort { $a <=> $b } keys %transactions; |
@{ $invoice->{transactions} } = sort { $a <=> $b } keys %transactions; |
|
|
$invoice->{total} -= round( $invoice->{discount}{amount} ); |
$invoice->{total} -= round( $invoice->{discount}{amount} ); |
} |
} |
|
|
|
if ($invoice->{past_due}) { |
|
$invoice->{total_due} |
|
= sum( @{ $invoice }{ qw/ total past_due unpaid / } ); |
|
} |
|
|
next unless $invoice->{total} > 0 || $invoice->{total_due}; |
next unless $invoice->{total} > 0 || $invoice->{total_due}; |
|
|
$invoice->{info} = $config->get('info'); |
$invoice->{info} = $config->get('info'); |
|
|
|
|
$state->add_invoice($invoice); |
$state->add_invoice($invoice); |
|
|
foreach my $key (qw/ start end /) { |
|
if ( exists $invoice->{$key} ) { |
|
$invoice->{$key} = $invoice->{$key}->strftime('%B %d, %Y'); |
|
} |
|
} |
|
|
|
my $invoice_dir = $config->get('invoice_dir'); |
my $invoice_dir = $config->get('invoice_dir'); |
File::Path::make_path($invoice_dir); |
File::Path::make_path($invoice_dir); |
my $file = join '/', $invoice_dir, $invoice->{file}; |
my $file = join '/', $invoice_dir, $invoice->{file}; |
|
|
|
|
$state->save; |
$state->save; |
|
|
sub round { |
|
my ($amount) = @_; |
|
|
|
#$amount =~ s/\.\d\d\K.*$//; |
|
#return $amount; |
|
return sprintf "%.02f", $amount; |
|
} |
|
|
|
sub find_customer_for_ticket { |
sub find_customer_for_ticket { |
my ( $ticket, $customers ) = @_; |
my ( $ticket, $customers ) = @_; |
|
|
|
|
sub make_invoice { |
sub make_invoice { |
my ($cust) = @_; |
my ($cust) = @_; |
|
|
my %invoice |
my %invoice = ( |
= ( end => $cust->{billend}->clone->subtract( seconds => 1 ) ); |
end => $cust->{billend}->clone->subtract( seconds => 1 ), |
|
total => 0, |
|
); |
$invoice{start} = $cust->{startinvoicedate}->clone |
$invoice{start} = $cust->{startinvoicedate}->clone |
if $cust->{startinvoicedate}; |
if $cust->{startinvoicedate}; |
|
|
|
|
} |
} |
} |
} |
return $hours; |
return $hours; |
} |
|
|
|
sub ymd_to_DateTime { |
|
my ($ymd) = @_; |
|
my ( $date, $time ) = split /[\sT]/, $ymd; |
|
my ( $year, $month, $day ) = split '-', $date; |
|
my ( $hour, $minute, $second ) = split ':', $time if $time; |
|
|
|
return DateTime->new( |
|
year => $year, |
|
month => $month, |
|
day => $day, |
|
hour => $hour || 0, |
|
minute => $minute || 0, |
|
second => $second || 0, |
|
time_zone => 'local', |
|
); |
|
} |
} |
|
|
sub get_billing_frequency { |
sub get_billing_frequency { |