package Koha::Account; # Copyright 2016 ByWater Solutions # # This file is part of Koha. # # Koha is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # Koha is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Koha; if not, see . use Modern::Perl; use Carp; use Data::Dumper; use List::MoreUtils qw( uniq ); use C4::Log qw( logaction ); use C4::Stats qw( UpdateStats ); use Koha::Account::Lines; use Koha::Account::Offsets; use Koha::DateUtils qw( dt_from_string ); =head1 NAME Koha::Accounts - Module for managing payments and fees for patrons =cut sub new { my ( $class, $params ) = @_; Carp::croak("No patron id passed in!") unless $params->{patron_id}; return bless( $params, $class ); } =head2 pay This method allows payments to be made against fees/fines Koha::Account->new( { patron_id => $borrowernumber } )->pay( { amount => $amount, sip => $sipmode, note => $note, description => $description, library_id => $branchcode, lines => $lines, # Arrayref of Koha::Account::Line objects to pay account_type => $type, # accounttype code offset_type => $offset_type, # offset type code } ); =cut sub pay { my ( $self, $params ) = @_; my $amount = $params->{amount}; my $sip = $params->{sip}; my $description = $params->{description}; my $note = $params->{note} || q{}; my $library_id = $params->{library_id}; my $lines = $params->{lines}; my $type = $params->{type} || 'payment'; my $payment_type = $params->{payment_type} || undef; my $account_type = $params->{account_type}; my $offset_type = $params->{offset_type} || $type eq 'writeoff' ? 'Writeoff' : 'Payment'; my $userenv = C4::Context->userenv; # We should remove accountno, it is no longer needed my $last = Koha::Account::Lines->search( { borrowernumber => $self->{patron_id} }, { order_by => 'accountno' } )->next(); my $accountno = $last ? $last->accountno + 1 : 1; my $manager_id = $userenv ? $userenv->{number} : 0; my @fines_paid; # List of account lines paid on with this payment my $balance_remaining = $amount; # Set it now so we can adjust the amount if necessary $balance_remaining ||= 0; my @account_offsets; # We were passed a specific line to pay foreach my $fine ( @$lines ) { my $amount_to_pay = $fine->amountoutstanding > $balance_remaining ? $balance_remaining : $fine->amountoutstanding; my $old_amountoutstanding = $fine->amountoutstanding; my $new_amountoutstanding = $old_amountoutstanding - $amount_to_pay; $fine->amountoutstanding($new_amountoutstanding)->store(); $balance_remaining = $balance_remaining - $amount_to_pay; if ( $fine->itemnumber && $fine->accounttype && ( $fine->accounttype eq 'Rep' || $fine->accounttype eq 'L' ) ) { C4::Circulation::ReturnLostItem( $self->{patron_id}, $fine->itemnumber ); } my $account_offset = Koha::Account::Offset->new( { debit_id => $fine->id, type => $offset_type, amount => $amount_to_pay * -1, } ); push( @account_offsets, $account_offset ); if ( C4::Context->preference("FinesLog") ) { logaction( "FINES", 'MODIFY', $self->{patron_id}, Dumper( { action => 'fee_payment', borrowernumber => $fine->borrowernumber, old_amountoutstanding => $old_amountoutstanding, new_amountoutstanding => 0, amount_paid => $old_amountoutstanding, accountlines_id => $fine->id, accountno => $fine->accountno, manager_id => $manager_id, note => $note, } ) ); push( @fines_paid, $fine->id ); } } # Were not passed a specific line to pay, or the payment was for more # than the what was owed on the given line. In that case pay down other # lines with remaining balance. my @outstanding_fines; @outstanding_fines = Koha::Account::Lines->search( { borrowernumber => $self->{patron_id}, amountoutstanding => { '>' => 0 }, } ) if $balance_remaining > 0; foreach my $fine (@outstanding_fines) { my $amount_to_pay = $fine->amountoutstanding > $balance_remaining ? $balance_remaining : $fine->amountoutstanding; my $old_amountoutstanding = $fine->amountoutstanding; $fine->amountoutstanding( $old_amountoutstanding - $amount_to_pay ); $fine->store(); my $account_offset = Koha::Account::Offset->new( { debit_id => $fine->id, type => $offset_type, amount => $amount_to_pay * -1, } ); push( @account_offsets, $account_offset ); if ( C4::Context->preference("FinesLog") ) { logaction( "FINES", 'MODIFY', $self->{patron_id}, Dumper( { action => "fee_$type", borrowernumber => $fine->borrowernumber, old_amountoutstanding => $old_amountoutstanding, new_amountoutstanding => $fine->amountoutstanding, amount_paid => $amount_to_pay, accountlines_id => $fine->id, accountno => $fine->accountno, manager_id => $manager_id, note => $note, } ) ); push( @fines_paid, $fine->id ); } $balance_remaining = $balance_remaining - $amount_to_pay; last unless $balance_remaining > 0; } $account_type ||= $type eq 'writeoff' ? 'W' : defined($sip) ? "Pay$sip" : 'Pay'; $description ||= $type eq 'writeoff' ? 'Writeoff' : q{}; my $payment = Koha::Account::Line->new( { borrowernumber => $self->{patron_id}, accountno => $accountno, date => dt_from_string(), amount => 0 - $amount, description => $description, accounttype => $account_type, payment_type => $payment_type, amountoutstanding => 0 - $balance_remaining, manager_id => $manager_id, note => $note, } )->store(); foreach my $o ( @account_offsets ) { $o->credit_id( $payment->id() ); $o->store(); } $library_id ||= $userenv ? $userenv->{'branch'} : undef; UpdateStats( { branch => $library_id, type => $type, amount => $amount, borrowernumber => $self->{patron_id}, accountno => $accountno, } ); if ( C4::Context->preference("FinesLog") ) { logaction( "FINES", 'CREATE', $self->{patron_id}, Dumper( { action => "create_$type", borrowernumber => $self->{patron_id}, accountno => $accountno, amount => 0 - $amount, amountoutstanding => 0 - $balance_remaining, accounttype => $account_type, accountlines_paid => \@fines_paid, manager_id => $manager_id, } ) ); } return $payment->id; } =head3 balance my $balance = $self->balance Return the balance (sum of amountoutstanding columns) =cut sub balance { my ($self) = @_; my $fines = Koha::Account::Lines->search( { borrowernumber => $self->{patron_id}, }, { select => [ { sum => 'amountoutstanding' } ], as => ['total_amountoutstanding'], } ); return ( $fines->count ) ? $fines->next->get_column('total_amountoutstanding') + 0 : 0; } =head3 non_issues_charges my $non_issues_charges = $self->non_issues_charges Calculates amount immediately owing by the patron - non-issue charges. Charges exempt from non-issue are: * Res (holds) if HoldsInNoissuesCharge syspref is set to false * Rent (rental) if RentalsInNoissuesCharge syspref is set to false * Manual invoices if ManInvInNoissuesCharge syspref is set to false =cut sub non_issues_charges { my ($self) = @_; # FIXME REMOVE And add a warning in the about page + update DB if length(MANUAL_INV) > 5 my $ACCOUNT_TYPE_LENGTH = 5; # this is plain ridiculous... my @not_fines; push @not_fines, 'Res' unless C4::Context->preference('HoldsInNoissuesCharge'); push @not_fines, 'Rent' unless C4::Context->preference('RentalsInNoissuesCharge'); unless ( C4::Context->preference('ManInvInNoissuesCharge') ) { my $dbh = C4::Context->dbh; push @not_fines, @{ $dbh->selectcol_arrayref(q| SELECT authorised_value FROM authorised_values WHERE category = 'MANUAL_INV' |) }; } @not_fines = map { substr( $_, 0, $ACCOUNT_TYPE_LENGTH ) } uniq(@not_fines); my $non_issues_charges = Koha::Account::Lines->search( { borrowernumber => $self->{patron_id}, accounttype => { -not_in => \@not_fines } }, { select => [ { sum => 'amountoutstanding' } ], as => ['non_issues_charges'], } ); return $non_issues_charges->count ? $non_issues_charges->next->get_column('non_issues_charges') + 0 : 0; } 1; =head1 AUTHOR Kyle M Hall =cut