File: [local] / RT / Invoicing / lib / RTI / State.pm (download)
Revision 1.6, Sat Aug 18 20:21:54 2012 UTC (12 years, 1 month ago) by andrew
Branch: MAIN
Changes since 1.5: +12 -0 lines
Inflate and deflate payment dates
I needed them for a reporting script.
|
package RTI::State;
use strict;
use warnings;
use 5.010;
use DateTime;
use Carp;
use YAML::XS qw/ LoadFile DumpFile /;
$YAML::XS::QuoteNumericStrings = 0;
use RTI::Util qw/ ymd_to_DateTime /;
my $file = '';
sub new {
my $class;
( $class, $file ) = @_;
my $self = { lastinvoice => 0, };
if ( -e $file ) {
$self = LoadFile($file) or die "Unable to load state: $!";
$self->{lastinvoice} ||= 0;
while ( my ( $id, $invoice ) = each %{ $self->{invoice} } ) {
$self->{lastinvoice} = $id if $self->{lastinvoice} < $id;
$invoice->{id} = $id;
$invoice->{$_} = ymd_to_DateTime( $invoice->{$_} )
for qw/ invdate start end /;
}
foreach my $custid (keys %{ $self->{payment} || {} }) {
foreach my $payment (@{ $self->{payment}->{$custid} || [] }) {
$payment->{date} = ymd_to_DateTime( $payment->{date} )
if $payment->{date};
}
}
}
bless $self, $class;
die "Need to pass filename to new: $!" unless $file;
return $self;
}
sub next_invoice_id {
my ($self) = @_;
return $self->{lastinvoice} + 1;
}
sub add_invoice {
my ( $self, $invoice ) = @_;
my $id = $invoice->{id} || $self->next_invoice_id;
croak "Can't add duplicate invoice $id\n"
if exists $self->{invoice}->{$id};
$invoice->{id} ||= $id;
$invoice->{invdate} ||= DateTime->now( time_zone => 'local' ),
$self->{lastinvoice} = $id if $self->{lastinvoice} < $id;
$self->{invoice}->{$id} = $invoice;
delete $self->{_tables};
return $self->{lastinvoice};
}
sub get_invoice {
my ( $self, $id ) = @_;
return $self->{invoice}->{$id};
}
sub last_invoice {
my ( $self, $custid ) = @_;
if ( !$self->{_table}->{last_invoice} ) {
my $invoices = $self->{invoice};
foreach my $id ( sort { $a <=> $b } keys %{$invoices} ) {
my $inv = $invoices->{$id};
next unless $inv->{custid};
$self->{_table}->{last_invoice}->{ $inv->{custid} } = $inv;
}
}
return $self->{_table}->{last_invoice}->{$custid};
}
sub txn_is_invoiced {
my ( $self, $txn ) = @_;
if ( !$self->{_table}->{txn} ) {
my $invoices = $self->{invoice};
foreach my $id ( sort { $a <=> $b } keys %{$invoices} ) {
my $inv = $invoices->{$id};
foreach my $t ( @{ $inv->{transactions} } ) {
$self->{_table}->{txn}->{$t} = 1;
}
}
}
return $self->{_table}->{txn}->{$txn};
}
sub unpaid_invoices {
my ( $self, $custid ) = @_;
$self->_match_payments;
return defined $custid
? $self->{_table}->{unpaid}->{$custid}
: $self->{_table}->{unpaid};
}
sub save {
my ($self) = @_;
delete $self->{_table};
delete $self->{lastinvoice};
foreach my $invoice ( values %{ $self->{invoice} } ) {
delete $invoice->{$_} for qw/
id
from
to
info
logo
projects
expenses
discount
hours
organization
/;
foreach my $k ( keys %{$invoice} ) {
my $v = $invoice->{$k};
if ( defined $v && length $v ) {
if ( ref $v eq 'DateTime' ) {
$invoice->{$k} = $v->ymd;
}
}
else {
delete $invoice->{$k};
}
}
}
foreach my $custid (keys %{ $self->{payment} || {} }) {
foreach my $payment (@{ $self->{payment}->{$custid} || [] }) {
$payment->{date} = $payment->{date}->ymd
if ref $payment->{date} eq 'DateTime';
}
}
DumpFile( $file, {%$self} ) or die "Unable to save state: $!";
}
sub _match_payments {
my ($self) = @_;
return if $self->{_table}{credit} && $self->{_table}{unpaid};
my $invoices = $self->{invoice};
my %owes = map { $_ => $invoices->{$_}->{total} } keys %{$invoices};
my %credit;
foreach my $custid ( keys %{ $self->{payment} } ) {
$credit{$custid} = 0;
my $payments = $self->{payment}->{$custid};
foreach my $p ( @{$payments} ) {
my $paid = $p->{paid};
$p->{invoices} ||= [];
foreach my $id ( @{ $p->{invoices} } ) {
$owes{$id} ||= 0;
if ( $owes{$id} == $paid ) {
$owes{$id} = 0;
$paid = 0;
}
elsif ( $owes{$id} > $paid ) {
$owes{$id} -= $paid;
$paid = 0;
}
elsif ( $owes{$id} < $paid ) {
$paid -= $owes{$id};
$owes{$id} = 0;
}
}
$credit{$custid} += $paid;
}
delete $credit{$custid} unless $credit{$custid};
}
foreach my $id ( sort { $b <=> $a } keys %owes ) {
my $i = $invoices->{$id};
my $custid = $i->{custid} or next;
my $owes = sprintf "%0.2f", $owes{$id} || 0;
my $paid = sprintf "%0.2f", $credit{$custid} || 0;
if ( $owes == $paid ) {
$owes = 0;
$paid = 0;
}
elsif ( $owes > $paid ) {
$owes -= $paid;
$paid = 0;
}
elsif ( $owes < $paid ) {
$paid -= $owes;
$owes = 0;
}
$self->{_table}{unpaid}{$custid}{$id} = $owes if $owes;
if ($paid) {
$credit{$custid} = $paid;
}
elsif ( exists $credit{$custid} ) {
delete $credit{$custid};
}
}
$self->{_table}{credit} = \%credit;
}
1;