[BACK]Return to State.pm CVS log [TXT][DIR] Up to [local] / RT / Invoicing / lib / RTI

File: [local] / RT / Invoicing / lib / RTI / State.pm (download)

Revision 1.4, Tue Jan 3 06:23:18 2012 UTC (12 years, 5 months ago) by andrew
Branch: MAIN
Changes since 1.3: +3 -1 lines

more things I don't want to keep state on

package RTI::State;
use strict;
use warnings;

use 5.010;

use DateTime;
use Carp;

use YAML::Any qw/ LoadFile DumpFile /;
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 /;
        }
    }

    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};
            }
        }
    }
    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;