Bug 24300: Add payout_amount method to Koha::Account
[koha-ffzg.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 use List::MoreUtils qw( uniq );
25 use Try::Tiny;
26
27 use C4::Circulation qw( ReturnLostItem CanBookBeRenewed AddRenewal );
28 use C4::Letters;
29 use C4::Log qw( logaction );
30 use C4::Stats qw( UpdateStats );
31 use C4::Overdues qw(GetFine);
32
33 use Koha::Patrons;
34 use Koha::Account::Lines;
35 use Koha::Account::Offsets;
36 use Koha::Account::DebitTypes;
37 use Koha::DateUtils qw( dt_from_string );
38 use Koha::Exceptions;
39 use Koha::Exceptions::Account;
40
41 =head1 NAME
42
43 Koha::Accounts - Module for managing payments and fees for patrons
44
45 =cut
46
47 sub new {
48     my ( $class, $params ) = @_;
49
50     Carp::croak("No patron id passed in!") unless $params->{patron_id};
51
52     return bless( $params, $class );
53 }
54
55 =head2 pay
56
57 This method allows payments to be made against fees/fines
58
59 Koha::Account->new( { patron_id => $borrowernumber } )->pay(
60     {
61         amount      => $amount,
62         note        => $note,
63         description => $description,
64         library_id  => $branchcode,
65         lines       => $lines, # Arrayref of Koha::Account::Line objects to pay
66         credit_type => $type,  # credit_type_code code
67         offset_type => $offset_type,    # offset type code
68         item_id     => $itemnumber,     # pass the itemnumber if this is a credit pertianing to a specific item (i.e LOST_FOUND)
69     }
70 );
71
72 =cut
73
74 sub pay {
75     my ( $self, $params ) = @_;
76
77     my $amount        = $params->{amount};
78     my $description   = $params->{description};
79     my $note          = $params->{note} || q{};
80     my $library_id    = $params->{library_id};
81     my $lines         = $params->{lines};
82     my $type          = $params->{type} || 'PAYMENT';
83     my $payment_type  = $params->{payment_type} || undef;
84     my $credit_type   = $params->{credit_type};
85     my $offset_type   = $params->{offset_type} || $type eq 'WRITEOFF' ? 'Writeoff' : 'Payment';
86     my $cash_register = $params->{cash_register};
87     my $item_id       = $params->{item_id};
88
89     my $userenv = C4::Context->userenv;
90
91     $credit_type ||=
92       $type eq 'WRITEOFF'
93       ? 'WRITEOFF'
94       : 'PAYMENT';
95
96     my $patron = Koha::Patrons->find( $self->{patron_id} );
97
98     my $manager_id = $userenv ? $userenv->{number} : undef;
99     my $interface = $params ? ( $params->{interface} || C4::Context->interface ) : C4::Context->interface;
100     Koha::Exceptions::Account::RegisterRequired->throw()
101       if ( C4::Context->preference("UseCashRegisters")
102         && defined($payment_type)
103         && ( $payment_type eq 'CASH' )
104         && !defined($cash_register) );
105
106     my @fines_paid; # List of account lines paid on with this payment
107
108     # The outcome of any attempted item renewals as a result of fines being
109     # paid off
110     my $renew_outcomes = [];
111
112     my $balance_remaining = $amount; # Set it now so we can adjust the amount if necessary
113     $balance_remaining ||= 0;
114
115     my @account_offsets;
116
117     # We were passed a specific line to pay
118     foreach my $fine ( @$lines ) {
119         my $amount_to_pay =
120             $fine->amountoutstanding > $balance_remaining
121           ? $balance_remaining
122           : $fine->amountoutstanding;
123
124         my $old_amountoutstanding = $fine->amountoutstanding;
125         my $new_amountoutstanding = $old_amountoutstanding - $amount_to_pay;
126         $fine->amountoutstanding($new_amountoutstanding)->store();
127         $balance_remaining = $balance_remaining - $amount_to_pay;
128
129         # Attempt to renew the item associated with this debit if
130         # appropriate
131         if ($fine->is_renewable) {
132             # We're ignoring the definition of $interface above, by all
133             # accounts we can't rely on C4::Context::interface, so here
134             # we're only using what we've been explicitly passed
135             my $outcome = $fine->renew_item({ interface => $interface });
136             push @{$renew_outcomes}, $outcome if $outcome;
137         }
138
139         # Same logic exists in Koha::Account::Line::apply
140         if ( C4::Context->preference('MarkLostItemsAsReturned') =~ m|onpayment|
141             && $fine->debit_type_code
142             && $fine->debit_type_code eq 'LOST'
143             && $new_amountoutstanding == 0
144             && $fine->itemnumber
145             && !(  $credit_type eq 'LOST_FOUND'
146                 && $item_id == $fine->itemnumber ) )
147         {
148             C4::Circulation::ReturnLostItem( $self->{patron_id},
149                 $fine->itemnumber );
150         }
151
152         my $account_offset = Koha::Account::Offset->new(
153             {
154                 debit_id => $fine->id,
155                 type     => $offset_type,
156                 amount   => $amount_to_pay * -1,
157             }
158         );
159         push( @account_offsets, $account_offset );
160
161         if ( C4::Context->preference("FinesLog") ) {
162             logaction(
163                 "FINES", 'MODIFY',
164                 $self->{patron_id},
165                 Dumper(
166                     {
167                         action                => 'fee_payment',
168                         borrowernumber        => $fine->borrowernumber,
169                         old_amountoutstanding => $old_amountoutstanding,
170                         new_amountoutstanding => 0,
171                         amount_paid           => $old_amountoutstanding,
172                         accountlines_id       => $fine->id,
173                         manager_id            => $manager_id,
174                         note                  => $note,
175                     }
176                 ),
177                 $interface
178             );
179             push( @fines_paid, $fine->id );
180         }
181     }
182
183     # Were not passed a specific line to pay, or the payment was for more
184     # than the what was owed on the given line. In that case pay down other
185     # lines with remaining balance.
186     my @outstanding_fines;
187     @outstanding_fines = $self->lines->search(
188         {
189             amountoutstanding => { '>' => 0 },
190         }
191     ) if $balance_remaining > 0;
192
193     foreach my $fine (@outstanding_fines) {
194         my $amount_to_pay =
195             $fine->amountoutstanding > $balance_remaining
196           ? $balance_remaining
197           : $fine->amountoutstanding;
198
199         my $old_amountoutstanding = $fine->amountoutstanding;
200         $fine->amountoutstanding( $old_amountoutstanding - $amount_to_pay );
201         $fine->store();
202
203         # If we need to make a note of the item associated with this line,
204         # in order that we can potentially renew it, do so.
205         my $amt = $old_amountoutstanding - $amount_to_pay;
206         if ( $fine->is_renewable ) {
207             my $outcome = $fine->renew_item({ interface => $interface });
208             push @{$renew_outcomes}, $outcome if $outcome;
209         }
210
211         if ( C4::Context->preference('MarkLostItemsAsReturned') =~ m|onpayment|
212             && $fine->debit_type_code
213             && $fine->debit_type_code eq 'LOST'
214             && $fine->amountoutstanding == 0
215             && $fine->itemnumber
216             && !(  $credit_type eq 'LOST_FOUND'
217                 && $item_id == $fine->itemnumber ) )
218         {
219             C4::Circulation::ReturnLostItem( $self->{patron_id},
220                 $fine->itemnumber );
221         }
222
223         my $account_offset = Koha::Account::Offset->new(
224             {
225                 debit_id => $fine->id,
226                 type     => $offset_type,
227                 amount   => $amount_to_pay * -1,
228             }
229         );
230         push( @account_offsets, $account_offset );
231
232         if ( C4::Context->preference("FinesLog") ) {
233             logaction(
234                 "FINES", 'MODIFY',
235                 $self->{patron_id},
236                 Dumper(
237                     {
238                         action                => "fee_$type",
239                         borrowernumber        => $fine->borrowernumber,
240                         old_amountoutstanding => $old_amountoutstanding,
241                         new_amountoutstanding => $fine->amountoutstanding,
242                         amount_paid           => $amount_to_pay,
243                         accountlines_id       => $fine->id,
244                         manager_id            => $manager_id,
245                         note                  => $note,
246                     }
247                 ),
248                 $interface
249             );
250             push( @fines_paid, $fine->id );
251         }
252
253         $balance_remaining = $balance_remaining - $amount_to_pay;
254         last unless $balance_remaining > 0;
255     }
256
257     $description ||= $type eq 'WRITEOFF' ? 'Writeoff' : q{};
258
259     my $payment = Koha::Account::Line->new(
260         {
261             borrowernumber    => $self->{patron_id},
262             date              => dt_from_string(),
263             amount            => 0 - $amount,
264             description       => $description,
265             credit_type_code  => $credit_type,
266             payment_type      => $payment_type,
267             amountoutstanding => 0 - $balance_remaining,
268             manager_id        => $manager_id,
269             interface         => $interface,
270             branchcode        => $library_id,
271             register_id       => $cash_register,
272             note              => $note,
273             itemnumber        => $item_id,
274         }
275     )->store();
276
277     foreach my $o ( @account_offsets ) {
278         $o->credit_id( $payment->id() );
279         $o->store();
280     }
281
282     C4::Stats::UpdateStats(
283         {
284             branch         => $library_id,
285             type           => lc($type),
286             amount         => $amount,
287             borrowernumber => $self->{patron_id},
288         }
289     );
290
291     if ( C4::Context->preference("FinesLog") ) {
292         logaction(
293             "FINES", 'CREATE',
294             $self->{patron_id},
295             Dumper(
296                 {
297                     action            => "create_$type",
298                     borrowernumber    => $self->{patron_id},
299                     amount            => 0 - $amount,
300                     amountoutstanding => 0 - $balance_remaining,
301                     credit_type_code  => $credit_type,
302                     accountlines_paid => \@fines_paid,
303                     manager_id        => $manager_id,
304                 }
305             ),
306             $interface
307         );
308     }
309
310     if ( C4::Context->preference('UseEmailReceipts') ) {
311         if (
312             my $letter = C4::Letters::GetPreparedLetter(
313                 module                 => 'circulation',
314                 letter_code            => uc("ACCOUNT_$type"),
315                 message_transport_type => 'email',
316                 lang    => $patron->lang,
317                 tables => {
318                     borrowers       => $self->{patron_id},
319                     branches        => $library_id,
320                 },
321                 substitute => {
322                     credit => $payment,
323                     offsets => \@account_offsets,
324                 },
325               )
326           )
327         {
328             C4::Letters::EnqueueLetter(
329                 {
330                     letter                 => $letter,
331                     borrowernumber         => $self->{patron_id},
332                     message_transport_type => 'email',
333                 }
334             ) or warn "can't enqueue letter $letter";
335         }
336     }
337
338     return { payment_id => $payment->id, renew_result => $renew_outcomes };
339 }
340
341 =head3 add_credit
342
343 This method allows adding credits to a patron's account
344
345 my $credit_line = Koha::Account->new({ patron_id => $patron_id })->add_credit(
346     {
347         amount       => $amount,
348         description  => $description,
349         note         => $note,
350         user_id      => $user_id,
351         interface    => $interface,
352         library_id   => $library_id,
353         payment_type => $payment_type,
354         type         => $credit_type,
355         item_id      => $item_id
356     }
357 );
358
359 $credit_type can be any of:
360   - 'CREDIT'
361   - 'PAYMENT'
362   - 'FORGIVEN'
363   - 'LOST_FOUND'
364   - 'OVERPAYMENT'
365   - 'PAYMENT'
366   - 'WRITEOFF'
367
368 =cut
369
370 sub add_credit {
371
372     my ( $self, $params ) = @_;
373
374     # check for mandatory params
375     my @mandatory = ( 'interface', 'amount' );
376     for my $param (@mandatory) {
377         unless ( defined( $params->{$param} ) ) {
378             Koha::Exceptions::MissingParameter->throw(
379                 error => "The $param parameter is mandatory" );
380         }
381     }
382
383     # amount should always be passed as a positive value
384     my $amount = $params->{amount} * -1;
385     unless ( $amount < 0 ) {
386         Koha::Exceptions::Account::AmountNotPositive->throw(
387             error => 'Debit amount passed is not positive' );
388     }
389
390     my $description   = $params->{description} // q{};
391     my $note          = $params->{note} // q{};
392     my $user_id       = $params->{user_id};
393     my $interface     = $params->{interface};
394     my $library_id    = $params->{library_id};
395     my $cash_register = $params->{cash_register};
396     my $payment_type  = $params->{payment_type};
397     my $credit_type   = $params->{type} || 'PAYMENT';
398     my $item_id       = $params->{item_id};
399
400     Koha::Exceptions::Account::RegisterRequired->throw()
401       if ( C4::Context->preference("UseCashRegisters")
402         && defined($payment_type)
403         && ( $payment_type eq 'CASH' )
404         && !defined($cash_register) );
405
406     my $line;
407     my $schema = Koha::Database->new->schema;
408     try {
409         $schema->txn_do(
410             sub {
411
412                 # Insert the account line
413                 $line = Koha::Account::Line->new(
414                     {
415                         borrowernumber    => $self->{patron_id},
416                         date              => \'NOW()',
417                         amount            => $amount,
418                         description       => $description,
419                         credit_type_code  => $credit_type,
420                         amountoutstanding => $amount,
421                         payment_type      => $payment_type,
422                         note              => $note,
423                         manager_id        => $user_id,
424                         interface         => $interface,
425                         branchcode        => $library_id,
426                         register_id       => $cash_register,
427                         itemnumber        => $item_id,
428                     }
429                 )->store();
430
431                 # Record the account offset
432                 my $account_offset = Koha::Account::Offset->new(
433                     {
434                         credit_id => $line->id,
435                         type   => $Koha::Account::offset_type->{$credit_type} // $Koha::Account::offset_type->{CREDIT},
436                         amount => $amount
437                     }
438                 )->store();
439
440                 C4::Stats::UpdateStats(
441                     {
442                         branch         => $library_id,
443                         type           => lc($credit_type),
444                         amount         => $amount,
445                         borrowernumber => $self->{patron_id},
446                     }
447                 ) if grep { $credit_type eq $_ } ( 'PAYMENT', 'WRITEOFF' );
448
449                 if ( C4::Context->preference("FinesLog") ) {
450                     logaction(
451                         "FINES", 'CREATE',
452                         $self->{patron_id},
453                         Dumper(
454                             {
455                                 action            => "create_$credit_type",
456                                 borrowernumber    => $self->{patron_id},
457                                 amount            => $amount,
458                                 description       => $description,
459                                 amountoutstanding => $amount,
460                                 credit_type_code  => $credit_type,
461                                 note              => $note,
462                                 itemnumber        => $item_id,
463                                 manager_id        => $user_id,
464                                 branchcode        => $library_id,
465                             }
466                         ),
467                         $interface
468                     );
469                 }
470             }
471         );
472     }
473     catch {
474         if ( ref($_) eq 'Koha::Exceptions::Object::FKConstraint' ) {
475             if ( $_->broken_fk eq 'credit_type_code' ) {
476                 Koha::Exceptions::Account::UnrecognisedType->throw(
477                     error => 'Type of credit not recognised' );
478             }
479             else {
480                 $_->rethrow;
481             }
482         }
483     };
484
485     return $line;
486 }
487
488 =head3 add_debit
489
490 This method allows adding debits to a patron's account
491
492 my $debit_line = Koha::Account->new({ patron_id => $patron_id })->add_debit(
493     {
494         amount       => $amount,
495         description  => $description,
496         note         => $note,
497         user_id      => $user_id,
498         interface    => $interface,
499         library_id   => $library_id,
500         type         => $debit_type,
501         item_id      => $item_id,
502         issue_id     => $issue_id
503     }
504 );
505
506 $debit_type can be any of:
507   - ACCOUNT
508   - ACCOUNT_RENEW
509   - RESERVE_EXPIRED
510   - LOST
511   - sundry
512   - NEW_CARD
513   - OVERDUE
514   - PROCESSING
515   - RENT
516   - RENT_DAILY
517   - RENT_RENEW
518   - RENT_DAILY_RENEW
519   - RESERVE
520
521 =cut
522
523 sub add_debit {
524
525     my ( $self, $params ) = @_;
526
527     # check for mandatory params
528     my @mandatory = ( 'interface', 'type', 'amount' );
529     for my $param (@mandatory) {
530         unless ( defined( $params->{$param} ) ) {
531             Koha::Exceptions::MissingParameter->throw(
532                 error => "The $param parameter is mandatory" );
533         }
534     }
535
536     # amount should always be a positive value
537     my $amount = $params->{amount};
538     unless ( $amount > 0 ) {
539         Koha::Exceptions::Account::AmountNotPositive->throw(
540             error => 'Debit amount passed is not positive' );
541     }
542
543     my $description = $params->{description} // q{};
544     my $note        = $params->{note} // q{};
545     my $user_id     = $params->{user_id};
546     my $interface   = $params->{interface};
547     my $library_id  = $params->{library_id};
548     my $debit_type  = $params->{type};
549     my $item_id     = $params->{item_id};
550     my $issue_id    = $params->{issue_id};
551     my $offset_type = $Koha::Account::offset_type->{$debit_type} // 'Manual Debit';
552
553     my $line;
554     my $schema = Koha::Database->new->schema;
555     try {
556         $schema->txn_do(
557             sub {
558
559                 # Insert the account line
560                 $line = Koha::Account::Line->new(
561                     {
562                         borrowernumber    => $self->{patron_id},
563                         date              => \'NOW()',
564                         amount            => $amount,
565                         description       => $description,
566                         debit_type_code   => $debit_type,
567                         amountoutstanding => $amount,
568                         payment_type      => undef,
569                         note              => $note,
570                         manager_id        => $user_id,
571                         interface         => $interface,
572                         itemnumber        => $item_id,
573                         issue_id          => $issue_id,
574                         branchcode        => $library_id,
575                         (
576                             $debit_type eq 'OVERDUE'
577                             ? ( status => 'UNRETURNED' )
578                             : ()
579                         ),
580                     }
581                 )->store();
582
583                 # Record the account offset
584                 my $account_offset = Koha::Account::Offset->new(
585                     {
586                         debit_id => $line->id,
587                         type     => $offset_type,
588                         amount   => $amount
589                     }
590                 )->store();
591
592                 if ( C4::Context->preference("FinesLog") ) {
593                     logaction(
594                         "FINES", 'CREATE',
595                         $self->{patron_id},
596                         Dumper(
597                             {
598                                 action            => "create_$debit_type",
599                                 borrowernumber    => $self->{patron_id},
600                                 amount            => $amount,
601                                 description       => $description,
602                                 amountoutstanding => $amount,
603                                 debit_type_code   => $debit_type,
604                                 note              => $note,
605                                 itemnumber        => $item_id,
606                                 manager_id        => $user_id,
607                             }
608                         ),
609                         $interface
610                     );
611                 }
612             }
613         );
614     }
615     catch {
616         if ( ref($_) eq 'Koha::Exceptions::Object::FKConstraint' ) {
617             if ( $_->broken_fk eq 'debit_type_code' ) {
618                 Koha::Exceptions::Account::UnrecognisedType->throw(
619                     error => 'Type of debit not recognised' );
620             }
621             else {
622                 $_->rethrow;
623             }
624         }
625     };
626
627     return $line;
628 }
629
630 =head3 payout_amount
631
632     my $debit = $account->payout_amount(
633         {
634             payout_type => $payout_type,
635             register_id => $register_id,
636             staff_id    => $staff_id,
637             interface   => 'intranet',
638             amount      => $amount,
639             credits     => $credit_lines
640         }
641     );
642
643 This method allows an amount to be paid out from a patrons account against outstanding credits.
644
645 $payout_type can be any of the defined payment_types:
646
647 =cut
648
649 sub payout_amount {
650     my ( $self, $params ) = @_;
651
652     # Check for mandatory parameters
653     my @mandatory =
654       ( 'interface', 'staff_id', 'branch', 'payout_type', 'amount' );
655     for my $param (@mandatory) {
656         unless ( defined( $params->{$param} ) ) {
657             Koha::Exceptions::MissingParameter->throw(
658                 error => "The $param parameter is mandatory" );
659         }
660     }
661
662     # Check for mandatory register
663     Koha::Exceptions::Account::RegisterRequired->throw()
664       if ( C4::Context->preference("UseCashRegisters")
665         && ( $params->{payout_type} eq 'CASH' )
666         && !defined($params->{cash_register}) );
667
668     # Amount should always be passed as a positive value
669     my $amount = $params->{amount};
670     unless ( $amount > 0 ) {
671         Koha::Exceptions::Account::AmountNotPositive->throw(
672             error => 'Debit amount passed is not positive' );
673     }
674
675     # Amount should always be less than or equal to outstanding credit
676     my $outstanding = 0;
677     my $outstanding_credits =
678       exists( $params->{credits} )
679       ? $params->{credits}
680       : $self->outstanding_credits->as_list;
681     for my $credit ( @{$outstanding_credits} ) {
682         $outstanding += $credit->amountoutstanding;
683     }
684     $outstanding = $outstanding * -1;
685     Koha::Exceptions::ParameterTooHigh->throw( error =>
686 "Amount to payout ($amount) is higher than amountoutstanding ($outstanding)"
687     ) unless ( $outstanding >= $amount );
688
689     my $payout;
690     my $schema = Koha::Database->new->schema;
691     $schema->txn_do(
692         sub {
693
694             # A 'payout' is a 'debit'
695             $payout = Koha::Account::Line->new(
696                 {
697                     date              => \'NOW()',
698                     amount            => $amount,
699                     debit_type_code   => 'PAYOUT',
700                     payment_type      => $params->{payout_type},
701                     amountoutstanding => $amount,
702                     manager_id        => $params->{staff_id},
703                     borrowernumber    => $self->{patron_id},
704                     interface         => $params->{interface},
705                     branchcode        => $params->{branch},
706                     register_id       => $params->{cash_register}
707                 }
708             )->store();
709
710             # Offset against credits
711             for my $credit ( @{$outstanding_credits} ) {
712                 $credit->apply(
713                     { debits => [$payout], offset_type => 'PAYOUT' } );
714                 $payout->discard_changes;
715             }
716
717             # Set payout as paid
718             $payout->status('PAID')->store;
719         }
720     );
721
722     return $payout;
723 }
724
725 =head3 balance
726
727 my $balance = $self->balance
728
729 Return the balance (sum of amountoutstanding columns)
730
731 =cut
732
733 sub balance {
734     my ($self) = @_;
735     return $self->lines->total_outstanding;
736 }
737
738 =head3 outstanding_debits
739
740 my $lines = Koha::Account->new({ patron_id => $patron_id })->outstanding_debits;
741
742 It returns the debit lines with outstanding amounts for the patron.
743
744 In scalar context, it returns a Koha::Account::Lines iterator. In list context, it will
745 return a list of Koha::Account::Line objects.
746
747 =cut
748
749 sub outstanding_debits {
750     my ($self) = @_;
751
752     return $self->lines->search(
753         {
754             amount            => { '>' => 0 },
755             amountoutstanding => { '>' => 0 }
756         }
757     );
758 }
759
760 =head3 outstanding_credits
761
762 my $lines = Koha::Account->new({ patron_id => $patron_id })->outstanding_credits;
763
764 It returns the credit lines with outstanding amounts for the patron.
765
766 In scalar context, it returns a Koha::Account::Lines iterator. In list context, it will
767 return a list of Koha::Account::Line objects.
768
769 =cut
770
771 sub outstanding_credits {
772     my ($self) = @_;
773
774     return $self->lines->search(
775         {
776             amount            => { '<' => 0 },
777             amountoutstanding => { '<' => 0 }
778         }
779     );
780 }
781
782 =head3 non_issues_charges
783
784 my $non_issues_charges = $self->non_issues_charges
785
786 Calculates amount immediately owing by the patron - non-issue charges.
787
788 Charges exempt from non-issue are:
789 * Res (holds) if HoldsInNoissuesCharge syspref is set to false
790 * Rent (rental) if RentalsInNoissuesCharge syspref is set to false
791 * Manual invoices if ManInvInNoissuesCharge syspref is set to false
792
793 =cut
794
795 sub non_issues_charges {
796     my ($self) = @_;
797
798     #NOTE: With bug 23049 these preferences could be moved to being attached
799     #to individual debit types to give more flexability and specificity.
800     my @not_fines;
801     push @not_fines, 'RESERVE'
802       unless C4::Context->preference('HoldsInNoissuesCharge');
803     push @not_fines, ( 'RENT', 'RENT_DAILY', 'RENT_RENEW', 'RENT_DAILY_RENEW' )
804       unless C4::Context->preference('RentalsInNoissuesCharge');
805     unless ( C4::Context->preference('ManInvInNoissuesCharge') ) {
806         my @man_inv = Koha::Account::DebitTypes->search({ is_system => 0 })->get_column('code');
807         push @not_fines, @man_inv;
808     }
809
810     return $self->lines->search(
811         {
812             debit_type_code => { -not_in => \@not_fines }
813         },
814     )->total_outstanding;
815 }
816
817 =head3 lines
818
819 my $lines = $self->lines;
820
821 Return all credits and debits for the user, outstanding or otherwise
822
823 =cut
824
825 sub lines {
826     my ($self) = @_;
827
828     return Koha::Account::Lines->search(
829         {
830             borrowernumber => $self->{patron_id},
831         }
832     );
833 }
834
835 =head3 reconcile_balance
836
837 $account->reconcile_balance();
838
839 Find outstanding credits and use them to pay outstanding debits.
840 Currently, this implicitly uses the 'First In First Out' rule for
841 applying credits against debits.
842
843 =cut
844
845 sub reconcile_balance {
846     my ($self) = @_;
847
848     my $outstanding_debits  = $self->outstanding_debits;
849     my $outstanding_credits = $self->outstanding_credits;
850
851     while (     $outstanding_debits->total_outstanding > 0
852             and my $credit = $outstanding_credits->next )
853     {
854         # there's both outstanding debits and credits
855         $credit->apply( { debits => [ $outstanding_debits->as_list ] } );    # applying credit, no special offset
856
857         $outstanding_debits = $self->outstanding_debits;
858
859     }
860
861     return $self;
862 }
863
864 1;
865
866 =head2 Name mappings
867
868 =head3 $offset_type
869
870 =cut
871
872 our $offset_type = {
873     'CREDIT'           => 'Manual Credit',
874     'FORGIVEN'         => 'Writeoff',
875     'LOST_FOUND'       => 'Lost Item Found',
876     'OVERPAYMENT'      => 'Overpayment',
877     'PAYMENT'          => 'Payment',
878     'WRITEOFF'         => 'Writeoff',
879     'ACCOUNT'          => 'Account Fee',
880     'ACCOUNT_RENEW'    => 'Account Fee',
881     'RESERVE'          => 'Reserve Fee',
882     'PROCESSING'       => 'Processing Fee',
883     'LOST'             => 'Lost Item',
884     'RENT'             => 'Rental Fee',
885     'RENT_DAILY'       => 'Rental Fee',
886     'RENT_RENEW'       => 'Rental Fee',
887     'RENT_DAILY_RENEW' => 'Rental Fee',
888     'OVERDUE'          => 'OVERDUE',
889     'RESERVE_EXPIRED'  => 'Hold Expired'
890 };
891
892 =head1 AUTHORS
893
894 =encoding utf8
895
896 Kyle M Hall <kyle.m.hall@gmail.com>
897 Tomás Cohen Arazi <tomascohen@gmail.com>
898 Martin Renvoize <martin.renvoize@ptfs-europe.com>
899
900 =cut