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