=================================================================== RCS file: /cvs/RT/Invoicing/rt_invoices.pl,v retrieving revision 1.46 retrieving revision 1.56 diff -u -r1.46 -r1.56 --- RT/Invoicing/rt_invoices.pl 2012/01/27 04:13:45 1.46 +++ RT/Invoicing/rt_invoices.pl 2020/08/02 18:57:28 1.56 @@ -1,5 +1,5 @@ #!/usr/bin/perl -# $AFresh1: rt_invoices.pl,v 1.45 2011/12/31 02:14:32 andrew Exp $ +# $AFresh1: rt_invoices.pl,v 1.55 2020/08/02 17:52:40 afresh1 Exp $ ######################################################################## # Copyright (c) 2011 Andrew Fresh # @@ -20,6 +20,9 @@ use 5.010; +# Because we don't have a real cert +$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; + use Template; use RT::Client::REST; use RT::Client::REST::Ticket; @@ -28,6 +31,8 @@ use File::Path; use DateTime; +use List::Util qw/ sum /; + use lib './lib'; # XXX This is fragile, there are better ways use RTI::Config; use RTI::State; @@ -135,7 +140,8 @@ if ( my $unpaid_invoices = $state->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 $unpaid = 0; @@ -158,9 +164,7 @@ my $content = sprintf( "Invoice %06d from %s", $id, $invdate->ymd ); - if ( $cust->{duedate} - && DateTime->compare( $invdate, $cust->{duedate} ) > 0 ) - { + if ( $cust->{duedate} && $invdate < $cust->{duedate}) { $content = "PAST DUE: $content"; $past_due += $unpaid_invoices->{$custid}->{$id}; } @@ -178,20 +182,48 @@ } if ($past_due) { - $cust->{invoice} ||= make_invoice(); + $cust->{invoice} ||= make_invoice($cust); $cust->{invoice}->{past_due} = $past_due; $cust->{invoice}->{unpaid} = $unpaid; - $cust->{invoice}->{total_due} - = $cust->{invoice}->{total} + $past_due + $unpaid; 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}; + next unless $credits->{$custid} < 0; + + $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} ) { - my $invoice = $cust->{invoice}; + my $invoice = $cust->{invoice} ||= make_invoice($cust); next unless $invoice && $invoice->{projects} && @{ $invoice->{projects} }; $invoice->{custid} = $cust->{id}; @@ -211,6 +243,8 @@ $subtotal += round( $expense->{amount} ); } $project->{total} = $subtotal; + + next if $project->{no_total}; $invoice->{total} += $subtotal; } @{ $invoice->{transactions} } = sort { $a <=> $b } keys %transactions; @@ -229,6 +263,11 @@ $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}; $invoice->{info} = $config->get('info'); @@ -269,17 +308,23 @@ next unless $cust->{match}; foreach my $m ( @{ $cust->{match} } ) { my $type = $m->{type}; - my $match - = exists $m->{$type} - ? lc( $m->{$type} ) - : qr/\Q$m->{regex}\E/; - my $thing = [ map {lc} $ticket->$type ]; - - if ( !$match ) { - warn "Invalid match!"; - next; + my @things = map {lc} $ticket->$type; + if ( exists $m->{$type} ) { + if ( !$m->{$type} ) { + warn "Invalid match!"; + next; + } + my $match = lc $m->{$type}; + for my $thing (@things) { + return $cust if $thing eq $match; + } } - return $cust if ( $match ~~ $thing ); + else { + my $match = qr/\Q$m->{regex}\E/; + for my $thing (@things) { + return $cust if $thing =~ $match; + } + } } } @@ -340,8 +385,10 @@ sub make_invoice { my ($cust) = @_; - my %invoice - = ( end => $cust->{billend}->clone->subtract( seconds => 1 ) ); + my %invoice = ( + end => $cust->{billend}->clone->subtract( seconds => 1 ), + total => 0, + ); $invoice{start} = $cust->{startinvoicedate}->clone if $cust->{startinvoicedate}; @@ -384,7 +431,7 @@ while ( $date < $billend ) { my $start = $date->clone; - $date->add($freq); + $date->add_duration($freq); my $end = $date > $billend ? $billend->clone : $date->clone; $end->subtract( seconds => 1 ); @@ -500,7 +547,7 @@ # XXX Only need $ticket for the alternate subject my $work_time = sprintf "%.03f", $txn->time_taken / 60; - my $work_type = $txn->cf('WorkType'); + my $work_type = $txn->cf('WorkType') || ''; if ( $work_type =~ s/\s*Onsite//i ) { @@ -568,14 +615,12 @@ my $per = $cust->{per} || 'week'; my $freq = $cust->{frequency} || 1; - my $day_method; - given ($per) { - when ('week') { $per = 'weeks'; $day_method = 'dow' } - when ('month') { $per = 'months'; $day_method = 'day' } - default { die "Unknown per [$per]\n" } - } + my $day_method + = $per eq 'week' ? 'dow' + : $per eq 'month' ? 'day' + : die "Unknown per [$per]\n"; - return DateTime::Duration->new( $per => $freq ), $day_method; + return DateTime::Duration->new( "${per}s" => $freq ), $day_method; } sub set_dates { @@ -599,7 +644,9 @@ } } - return $newest_invoice->clone->subtract($max_duration) + $newest_invoice ||= DateTime->now; + + return $newest_invoice->clone->subtract_duration($max_duration) ->subtract( days => 1 ); } @@ -612,7 +659,7 @@ my $end = DateTime->now( time_zone => 'local' ) ->set( hour => 0, minute => 0, second => 0 ); - my $start = $end->clone->subtract($freq); + my $start = $end->clone->subtract_duration($freq); # XXX This is helpful, but monthly and billday > 28 == !!! $end->subtract( days => 1 ) while $day && $end->$day_method != $day; @@ -628,7 +675,7 @@ ? 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_duration($freq) > $end; $cust->{billend} = $end; $cust->{billstart} = $start; }