Annotation of RT/Invoicing/lib/RTI/State.pm, Revision 1.3
1.1 andrew 1: package RTI::State;
2: use strict;
3: use warnings;
4:
1.3 ! andrew 5: use 5.010;
1.1 andrew 6:
7: use DateTime;
1.3 ! andrew 8: use Carp;
1.1 andrew 9:
10: use YAML::Any qw/ LoadFile DumpFile /;
1.3 ! andrew 11: use RTI::Util qw/ ymd_to_DateTime /;
1.1 andrew 12:
13: my $file = '';
14:
15: sub new {
16: my $class;
17: ( $class, $file ) = @_;
18:
19: my $self = { lastinvoice => 0, };
20: if ( -e $file ) {
21: $self = LoadFile($file) or die "Unable to load state: $!";
22:
23: $self->{lastinvoice} ||= 0;
24: while ( my ( $id, $invoice ) = each %{ $self->{invoice} } ) {
25: $self->{lastinvoice} = $id if $self->{lastinvoice} < $id;
1.3 ! andrew 26:
1.1 andrew 27: $invoice->{id} = $id;
1.3 ! andrew 28: $invoice->{$_} = ymd_to_DateTime( $invoice->{$_} )
! 29: for qw/ invdate start end /;
1.1 andrew 30: }
31: }
32:
33: bless $self, $class;
34:
35: die "Need to pass filename to new: $!" unless $file;
36:
37: return $self;
38: }
39:
40: sub next_invoice_id {
41: my ($self) = @_;
42: return $self->{lastinvoice} + 1;
43: }
44:
45: sub add_invoice {
46: my ( $self, $invoice ) = @_;
47:
48: my $id = $invoice->{id} || $self->next_invoice_id;
49:
50: croak "Can't add duplicate invoice $id\n"
51: if exists $self->{invoice}->{$id};
52:
53: $invoice->{id} ||= $id;
1.3 ! andrew 54: $invoice->{invdate} ||= DateTime->now( time_zone => 'local' ),
1.1 andrew 55:
56: $self->{lastinvoice} = $id if $self->{lastinvoice} < $id;
57:
1.3 ! andrew 58: $self->{invoice}->{$id} = $invoice;
1.1 andrew 59: delete $self->{_tables};
60:
61: return $self->{lastinvoice};
62: }
63:
1.3 ! andrew 64: sub get_invoice {
1.1 andrew 65: my ( $self, $id ) = @_;
66: return $self->{invoice}->{$id};
67: }
68:
69: sub last_invoice {
70: my ( $self, $custid ) = @_;
71:
72: if ( !$self->{_table}->{last_invoice} ) {
73: my $invoices = $self->{invoice};
74: foreach my $id ( sort { $a <=> $b } keys %{$invoices} ) {
75: my $inv = $invoices->{$id};
76: next unless $inv->{custid};
77: $self->{_table}->{last_invoice}->{ $inv->{custid} } = $inv;
78: }
79: }
80:
81: return $self->{_table}->{last_invoice}->{$custid};
82: }
83:
84: sub txn_is_invoiced {
85: my ( $self, $txn ) = @_;
1.3 ! andrew 86:
1.1 andrew 87: if ( !$self->{_table}->{txn} ) {
88: my $invoices = $self->{invoice};
89: foreach my $id ( sort { $a <=> $b } keys %{$invoices} ) {
90: my $inv = $invoices->{$id};
91: foreach my $t ( @{ $inv->{transactions} } ) {
92: $self->{_table}->{txn}->{$t} = 1;
93: }
94: }
95: }
96: return $self->{_table}->{txn}->{$txn};
97: }
98:
99: sub unpaid_invoices {
1.3 ! andrew 100: my ( $self, $custid ) = @_;
1.1 andrew 101:
102: $self->_match_payments;
1.2 andrew 103: return defined $custid
104: ? $self->{_table}->{unpaid}->{$custid}
105: : $self->{_table}->{unpaid};
1.1 andrew 106: }
107:
108: sub save {
109: my ($self) = @_;
110:
111: delete $self->{_table};
112: delete $self->{lastinvoice};
113: foreach my $invoice ( values %{ $self->{invoice} } ) {
1.3 ! andrew 114: delete $invoice->{$_} for qw/
! 115: id
! 116: from
! 117: to
! 118: info
! 119: logo
! 120: projects
! 121: expenses
! 122: discounts
! 123: /;
! 124:
! 125: foreach my $k ( keys %{$invoice} ) {
! 126: my $v = $invoice->{$k};
! 127:
! 128: if ( defined $v && length $v ) {
! 129: if ( ref $v eq 'DateTime' ) {
! 130: $invoice->{$k} = $v->ymd;
! 131: }
! 132: }
! 133: else {
! 134: delete $invoice->{$k};
! 135: }
! 136: }
1.1 andrew 137: }
138: DumpFile( $file, {%$self} ) or die "Unable to save state: $!";
139: }
140:
141: sub _match_payments {
142: my ($self) = @_;
143:
144: return if $self->{_table}{credit} && $self->{_table}{unpaid};
145:
146: my $invoices = $self->{invoice};
1.3 ! andrew 147: my %owes = map { $_ => $invoices->{$_}->{total} } keys %{$invoices};
1.1 andrew 148:
149: my %credit;
150:
151: foreach my $custid ( keys %{ $self->{payment} } ) {
152: $credit{$custid} = 0;
153:
154: my $payments = $self->{payment}->{$custid};
155: foreach my $p ( @{$payments} ) {
156: my $paid = $p->{paid};
157: $p->{invoices} ||= [];
158:
159: foreach my $id ( @{ $p->{invoices} } ) {
160: $owes{$id} ||= 0;
161:
162: if ( $owes{$id} == $paid ) {
163: $owes{$id} = 0;
164: $paid = 0;
165: }
166: elsif ( $owes{$id} > $paid ) {
167: $owes{$id} -= $paid;
168: $paid = 0;
169: }
170: elsif ( $owes{$id} < $paid ) {
171: $paid -= $owes{$id};
172: $owes{$id} = 0;
173: }
174: }
175:
176: $credit{$custid} += $paid;
177: }
178:
179: delete $credit{$custid} unless $credit{$custid};
180: }
181:
182: foreach my $id ( sort { $b <=> $a } keys %owes ) {
1.3 ! andrew 183: my $i = $invoices->{$id};
1.1 andrew 184: my $custid = $i->{custid} or next;
185:
186: my $owes = sprintf "%0.2f", $owes{$id} || 0;
187: my $paid = sprintf "%0.2f", $credit{$custid} || 0;
188:
189: if ( $owes == $paid ) {
190: $owes = 0;
191: $paid = 0;
192: }
193: elsif ( $owes > $paid ) {
194: $owes -= $paid;
195: $paid = 0;
196: }
197: elsif ( $owes < $paid ) {
198: $paid -= $owes;
199: $owes = 0;
200: }
201:
202: $self->{_table}{unpaid}{$custid}{$id} = $owes if $owes;
203:
204: if ($paid) {
205: $credit{$custid} = $paid;
206: }
1.3 ! andrew 207: elsif ( exists $credit{$custid} ) {
1.1 andrew 208: delete $credit{$custid};
209: }
210: }
211:
212: $self->{_table}{credit} = \%credit;
213: }
214:
215: 1;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>