Bug 19420: Improve display of errors from failure of uploading file during stage...
[koha.git] / Koha / Account.pm
1 package Koha::Account;
2
3 # Copyright 2016 ByWater Solutions
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use Carp;
23 use Data::Dumper;
24
25 use C4::Log qw( logaction );
26 use C4::Stats qw( UpdateStats );
27
28 use Koha::Account::Lines;
29 use Koha::Account::Offsets;
30 use Koha::DateUtils qw( dt_from_string );
31
32 =head1 NAME
33
34 Koha::Accounts - Module for managing payments and fees for patrons
35
36 =cut
37
38 sub new {
39     my ( $class, $params ) = @_;
40
41     Carp::croak("No patron id passed in!") unless $params->{patron_id};
42
43     return bless( $params, $class );
44 }
45
46 =head2 pay
47
48 This method allows payments to be made against fees/fines
49
50 Koha::Account->new( { patron_id => $borrowernumber } )->pay(
51     {
52         amount      => $amount,
53         sip         => $sipmode,
54         note        => $note,
55         description => $description,
56         library_id  => $branchcode,
57         lines        => $lines, # Arrayref of Koha::Account::Line objects to pay
58         account_type => $type,  # accounttype code
59         offset_type => $offset_type,    # offset type code
60     }
61 );
62
63 =cut
64
65 sub pay {
66     my ( $self, $params ) = @_;
67
68     my $amount       = $params->{amount};
69     my $sip          = $params->{sip};
70     my $description  = $params->{description};
71     my $note         = $params->{note} || q{};
72     my $library_id   = $params->{library_id};
73     my $lines        = $params->{lines};
74     my $type         = $params->{type} || 'payment';
75     my $account_type = $params->{account_type};
76     my $offset_type  = $params->{offset_type} || $type eq 'writeoff' ? 'Writeoff' : 'Payment';
77
78     my $userenv = C4::Context->userenv;
79
80     # We should remove accountno, it is no longer needed
81     my $last = Koha::Account::Lines->search(
82         {
83             borrowernumber => $self->{patron_id}
84         },
85         {
86             order_by => 'accountno'
87         }
88     )->next();
89     my $accountno = $last ? $last->accountno + 1 : 1;
90
91     my $manager_id = $userenv ? $userenv->{number} : 0;
92
93     my @fines_paid; # List of account lines paid on with this payment
94
95     my $balance_remaining = $amount; # Set it now so we can adjust the amount if necessary
96     $balance_remaining ||= 0;
97
98     my @account_offsets;
99
100     # We were passed a specific line to pay
101     foreach my $fine ( @$lines ) {
102         my $amount_to_pay =
103             $fine->amountoutstanding > $balance_remaining
104           ? $balance_remaining
105           : $fine->amountoutstanding;
106
107         my $old_amountoutstanding = $fine->amountoutstanding;
108         my $new_amountoutstanding = $old_amountoutstanding - $amount_to_pay;
109         $fine->amountoutstanding($new_amountoutstanding)->store();
110         $balance_remaining = $balance_remaining - $amount_to_pay;
111
112         if ( $fine->itemnumber && $fine->accounttype && ( $fine->accounttype eq 'Rep' || $fine->accounttype eq 'L' ) )
113         {
114             C4::Circulation::ReturnLostItem( $self->{patron_id}, $fine->itemnumber );
115         }
116
117         my $account_offset = Koha::Account::Offset->new(
118             {
119                 debit_id => $fine->id,
120                 type     => $offset_type,
121                 amount   => $amount_to_pay * -1,
122             }
123         );
124         push( @account_offsets, $account_offset );
125
126         if ( C4::Context->preference("FinesLog") ) {
127             logaction(
128                 "FINES", 'MODIFY',
129                 $self->{patron_id},
130                 Dumper(
131                     {
132                         action                => 'fee_payment',
133                         borrowernumber        => $fine->borrowernumber,
134                         old_amountoutstanding => $old_amountoutstanding,
135                         new_amountoutstanding => 0,
136                         amount_paid           => $old_amountoutstanding,
137                         accountlines_id       => $fine->id,
138                         accountno             => $fine->accountno,
139                         manager_id            => $manager_id,
140                         note                  => $note,
141                     }
142                 )
143             );
144             push( @fines_paid, $fine->id );
145         }
146     }
147
148     # Were not passed a specific line to pay, or the payment was for more
149     # than the what was owed on the given line. In that case pay down other
150     # lines with remaining balance.
151     my @outstanding_fines;
152     @outstanding_fines = Koha::Account::Lines->search(
153         {
154             borrowernumber    => $self->{patron_id},
155             amountoutstanding => { '>' => 0 },
156         }
157     ) if $balance_remaining > 0;
158
159     foreach my $fine (@outstanding_fines) {
160         my $amount_to_pay =
161             $fine->amountoutstanding > $balance_remaining
162           ? $balance_remaining
163           : $fine->amountoutstanding;
164
165         my $old_amountoutstanding = $fine->amountoutstanding;
166         $fine->amountoutstanding( $old_amountoutstanding - $amount_to_pay );
167         $fine->store();
168
169         my $account_offset = Koha::Account::Offset->new(
170             {
171                 debit_id => $fine->id,
172                 type     => $offset_type,
173                 amount   => $amount_to_pay * -1,
174             }
175         );
176         push( @account_offsets, $account_offset );
177
178         if ( C4::Context->preference("FinesLog") ) {
179             logaction(
180                 "FINES", 'MODIFY',
181                 $self->{patron_id},
182                 Dumper(
183                     {
184                         action                => "fee_$type",
185                         borrowernumber        => $fine->borrowernumber,
186                         old_amountoutstanding => $old_amountoutstanding,
187                         new_amountoutstanding => $fine->amountoutstanding,
188                         amount_paid           => $amount_to_pay,
189                         accountlines_id       => $fine->id,
190                         accountno             => $fine->accountno,
191                         manager_id            => $manager_id,
192                         note                  => $note,
193                     }
194                 )
195             );
196             push( @fines_paid, $fine->id );
197         }
198
199         $balance_remaining = $balance_remaining - $amount_to_pay;
200         last unless $balance_remaining > 0;
201     }
202
203     $account_type ||=
204         $type eq 'writeoff' ? 'W'
205       : defined($sip)       ? "Pay$sip"
206       :                       'Pay';
207
208     $description ||= $type eq 'writeoff' ? 'Writeoff' : q{};
209
210     my $payment = Koha::Account::Line->new(
211         {
212             borrowernumber    => $self->{patron_id},
213             accountno         => $accountno,
214             date              => dt_from_string(),
215             amount            => 0 - $amount,
216             description       => $description,
217             accounttype       => $account_type,
218             amountoutstanding => 0 - $balance_remaining,
219             manager_id        => $manager_id,
220             note              => $note,
221         }
222     )->store();
223
224     foreach my $o ( @account_offsets ) {
225         $o->credit_id( $payment->id() );
226         $o->store();
227     }
228
229     $library_id ||= $userenv ? $userenv->{'branch'} : undef;
230
231     UpdateStats(
232         {
233             branch         => $library_id,
234             type           => $type,
235             amount         => $amount,
236             borrowernumber => $self->{patron_id},
237             accountno      => $accountno,
238         }
239     );
240
241     if ( C4::Context->preference("FinesLog") ) {
242         logaction(
243             "FINES", 'CREATE',
244             $self->{patron_id},
245             Dumper(
246                 {
247                     action            => "create_$type",
248                     borrowernumber    => $self->{patron_id},
249                     accountno         => $accountno,
250                     amount            => 0 - $amount,
251                     amountoutstanding => 0 - $balance_remaining,
252                     accounttype       => $account_type,
253                     accountlines_paid => \@fines_paid,
254                     manager_id        => $manager_id,
255                 }
256             )
257         );
258     }
259
260     return $payment->id;
261 }
262
263 =head3 balance
264
265 my $balance = $self->balance
266
267 Return the balance (sum of amountoutstanding columns)
268
269 =cut
270
271 sub balance {
272     my ($self) = @_;
273     my $fines = Koha::Account::Lines->search(
274         {
275             borrowernumber => $self->{patron_id},
276         },
277         {
278             select => [ { sum => 'amountoutstanding' } ],
279             as => ['total_amountoutstanding'],
280         }
281     );
282     return $fines->count
283       ? $fines->next->get_column('total_amountoutstanding')
284       : 0;
285 }
286
287 1;
288
289 =head1 AUTHOR
290
291 Kyle M Hall <kyle.m.hall@gmail.com>
292
293 =cut