Annotation of RT/Invoicing/lib/RTI/State.pm, Revision 1.4
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
1.4 ! andrew 122: discount
! 123: hours
! 124: organization
1.3 andrew 125: /;
126:
127: foreach my $k ( keys %{$invoice} ) {
128: my $v = $invoice->{$k};
129:
130: if ( defined $v && length $v ) {
131: if ( ref $v eq 'DateTime' ) {
132: $invoice->{$k} = $v->ymd;
133: }
134: }
135: else {
136: delete $invoice->{$k};
137: }
138: }
1.1 andrew 139: }
140: DumpFile( $file, {%$self} ) or die "Unable to save state: $!";
141: }
142:
143: sub _match_payments {
144: my ($self) = @_;
145:
146: return if $self->{_table}{credit} && $self->{_table}{unpaid};
147:
148: my $invoices = $self->{invoice};
1.3 andrew 149: my %owes = map { $_ => $invoices->{$_}->{total} } keys %{$invoices};
1.1 andrew 150:
151: my %credit;
152:
153: foreach my $custid ( keys %{ $self->{payment} } ) {
154: $credit{$custid} = 0;
155:
156: my $payments = $self->{payment}->{$custid};
157: foreach my $p ( @{$payments} ) {
158: my $paid = $p->{paid};
159: $p->{invoices} ||= [];
160:
161: foreach my $id ( @{ $p->{invoices} } ) {
162: $owes{$id} ||= 0;
163:
164: if ( $owes{$id} == $paid ) {
165: $owes{$id} = 0;
166: $paid = 0;
167: }
168: elsif ( $owes{$id} > $paid ) {
169: $owes{$id} -= $paid;
170: $paid = 0;
171: }
172: elsif ( $owes{$id} < $paid ) {
173: $paid -= $owes{$id};
174: $owes{$id} = 0;
175: }
176: }
177:
178: $credit{$custid} += $paid;
179: }
180:
181: delete $credit{$custid} unless $credit{$custid};
182: }
183:
184: foreach my $id ( sort { $b <=> $a } keys %owes ) {
1.3 andrew 185: my $i = $invoices->{$id};
1.1 andrew 186: my $custid = $i->{custid} or next;
187:
188: my $owes = sprintf "%0.2f", $owes{$id} || 0;
189: my $paid = sprintf "%0.2f", $credit{$custid} || 0;
190:
191: if ( $owes == $paid ) {
192: $owes = 0;
193: $paid = 0;
194: }
195: elsif ( $owes > $paid ) {
196: $owes -= $paid;
197: $paid = 0;
198: }
199: elsif ( $owes < $paid ) {
200: $paid -= $owes;
201: $owes = 0;
202: }
203:
204: $self->{_table}{unpaid}{$custid}{$id} = $owes if $owes;
205:
206: if ($paid) {
207: $credit{$custid} = $paid;
208: }
1.3 andrew 209: elsif ( exists $credit{$custid} ) {
1.1 andrew 210: delete $credit{$custid};
211: }
212: }
213:
214: $self->{_table}{credit} = \%credit;
215: }
216:
217: 1;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>