1 package Koha::Account::Line;
3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 3 of the License, or (at your option) any later
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License along
15 # with Koha; if not, write to the Free Software Foundation, Inc.,
16 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 use C4::Log qw(logaction);
25 use Koha::Account::CreditType;
26 use Koha::Account::DebitType;
27 use Koha::Account::Offsets;
29 use Koha::Exceptions::Account;
32 use base qw(Koha::Object);
38 Koha::Account::Line - Koha accountline Object class
48 Return the patron linked to this account line
54 my $rs = $self->_result->borrowernumber;
56 return Koha::Patron->_new_from_dbic( $rs );
61 Return the item linked to this account line if exists
67 my $rs = $self->_result->itemnumber;
69 return Koha::Item->_new_from_dbic( $rs );
74 Return the checkout linked to this account line if exists
80 return unless $self->issue_id ;
82 $self->{_checkout} ||= Koha::Checkouts->find( $self->issue_id );
83 $self->{_checkout} ||= Koha::Old::Checkouts->find( $self->issue_id );
84 return $self->{_checkout};
89 Return the credit_type linked to this account line
95 my $rs = $self->_result->credit_type_code;
97 return Koha::Account::CreditType->_new_from_dbic( $rs );
102 Return the debit_type linked to this account line
108 my $rs = $self->_result->debit_type_code;
110 return Koha::Account::DebitType->_new_from_dbic( $rs );
115 $payment_accountline->void();
117 Used to 'void' (or reverse) a payment/credit. It will roll back any offsets
118 created by the application of this credit upon any debits and mark the credit
119 as 'void' by updating it's status to "VOID".
126 # Make sure it is a payment we are voiding
127 return unless $self->amount < 0;
129 my @account_offsets =
130 Koha::Account::Offsets->search(
131 { credit_id => $self->id, amount => { '<' => 0 } } );
133 $self->_result->result_source->schema->txn_do(
135 foreach my $account_offset (@account_offsets) {
137 Koha::Account::Lines->find( $account_offset->debit_id );
139 next unless $fee_paid;
141 my $amount_paid = $account_offset->amount * -1; # amount paid is stored as a negative amount
142 my $new_amount = $fee_paid->amountoutstanding + $amount_paid;
143 $fee_paid->amountoutstanding($new_amount);
146 Koha::Account::Offset->new(
148 credit_id => $self->id,
149 debit_id => $fee_paid->id,
150 amount => $amount_paid,
151 type => 'Void Payment',
156 if ( C4::Context->preference("FinesLog") ) {
159 $self->borrowernumber,
162 action => 'void_payment',
163 borrowernumber => $self->borrowernumber,
164 amount => $self->amount,
165 amountoutstanding => $self->amountoutstanding,
166 description => $self->description,
167 credit_type_code => $self->credit_type_code,
168 payment_type => $self->payment_type,
170 itemnumber => $self->itemnumber,
171 manager_id => $self->manager_id,
173 [ map { $_->unblessed } @account_offsets ],
182 amountoutstanding => 0,
194 my $debits = $account->outstanding_debits;
195 my $outstanding_amount = $credit->apply( { debits => $debits, [ offset_type => $offset_type ] } );
197 Applies the credit to a given debits array reference.
199 =head4 arguments hashref
203 =item debits - Koha::Account::Lines object set of debits
205 =item offset_type (optional) - a string indicating the offset type (valid values are those from
206 the 'account_offset_types' table)
213 my ( $self, $params ) = @_;
215 my $debits = $params->{debits};
216 my $offset_type = $params->{offset_type} // 'Credit Applied';
218 unless ( $self->is_credit ) {
219 Koha::Exceptions::Account::IsNotCredit->throw(
220 error => 'Account line ' . $self->id . ' is not a credit'
224 my $available_credit = $self->amountoutstanding * -1;
226 unless ( $available_credit > 0 ) {
227 Koha::Exceptions::Account::NoAvailableCredit->throw(
228 error => 'Outstanding credit is ' . $available_credit . ' and cannot be applied'
232 my $schema = Koha::Database->new->schema;
234 $schema->txn_do( sub {
235 for my $debit ( @{$debits} ) {
237 unless ( $debit->is_debit ) {
238 Koha::Exceptions::Account::IsNotDebit->throw(
239 error => 'Account line ' . $debit->id . 'is not a debit'
242 my $amount_to_cancel;
243 my $owed = $debit->amountoutstanding;
245 if ( $available_credit >= $owed ) {
246 $amount_to_cancel = $owed;
248 else { # $available_credit < $debit->amountoutstanding
249 $amount_to_cancel = $available_credit;
252 # record the account offset
253 Koha::Account::Offset->new(
254 { credit_id => $self->id,
255 debit_id => $debit->id,
256 amount => $amount_to_cancel * -1,
257 type => $offset_type,
261 $available_credit -= $amount_to_cancel;
263 $self->amountoutstanding( $available_credit * -1 )->store;
264 $debit->amountoutstanding( $owed - $amount_to_cancel )->store;
266 # Same logic exists in Koha::Account::pay
267 if ( $debit->amountoutstanding == 0
268 && $debit->itemnumber
269 && $debit->debit_type_code
270 && $debit->debit_type_code eq 'LOST' )
272 C4::Circulation::ReturnLostItem( $self->borrowernumber, $debit->itemnumber );
278 return $available_credit;
283 $credit_accountline->payout(
285 payout_type => $payout_type,
286 register_id => $register_id,
287 staff_id => $staff_id,
288 interface => 'intranet',
293 Used to 'pay out' a credit to a user.
295 Payout type may be one of any existing payment types
297 Returns the payout debit line that is created via this transaction.
302 my ( $self, $params ) = @_;
304 # Make sure it is a credit we are paying out
305 unless ( $self->is_credit ) {
306 Koha::Exceptions::Account::IsNotCredit->throw(
307 error => 'Account line ' . $self->id . ' is not a credit' );
310 # Check for mandatory parameters
312 ( 'interface', 'staff_id', 'branch', 'payout_type', 'amount' );
313 for my $param (@mandatory) {
314 unless ( defined( $params->{$param} ) ) {
315 Koha::Exceptions::MissingParameter->throw(
316 error => "The $param parameter is mandatory" );
320 # Make sure there is outstanding credit to pay out
321 my $outstanding = -1 * $self->amountoutstanding;
323 $params->{amount} ? $params->{amount} : $outstanding;
324 Koha::Exceptions::Account::AmountNotPositive->throw(
325 error => 'Payout amount passed is not positive' )
326 unless ( $amount > 0 );
327 Koha::Exceptions::ParameterTooHigh->throw(
328 error => "Amount to payout ($amount) is higher than amountoutstanding ($outstanding)" )
329 unless ($outstanding >= $amount );
331 # Make sure we record the cash register for cash transactions
332 Koha::Exceptions::Account::RegisterRequired->throw()
333 if ( C4::Context->preference("UseCashRegisters")
334 && defined( $params->{payout_type} )
335 && ( $params->{payout_type} eq 'CASH' )
336 && !defined( $params->{cash_register} ) );
339 $self->_result->result_source->schema->txn_do(
342 # A 'payout' is a 'debit'
343 $payout = Koha::Account::Line->new(
347 debit_type_code => 'PAYOUT',
348 payment_type => $params->{payout_type},
349 amountoutstanding => $amount,
350 manager_id => $params->{staff_id},
351 borrowernumber => $self->borrowernumber,
352 interface => $params->{interface},
353 branchcode => $params->{branch},
354 register_id => $params->{cash_register}
358 my $payout_offset = Koha::Account::Offset->new(
360 debit_id => $payout->accountlines_id,
366 $self->apply( { debits => [$payout], offset_type => 'PAYOUT' } );
367 $self->status('PAID')->store;
376 This method allows updating a debit or credit on a patron's account
378 $account_line->adjust(
381 type => $update_type,
382 interface => $interface
386 $update_type can be any of:
389 Authors Note: The intention here is that this method is only used
390 to adjust accountlines where the final amount is not yet known/fixed.
391 Incrementing fines are the only existing case at the time of writing,
392 all other forms of 'adjustment' should be recorded as distinct credits
393 or debits and applied, via an offset, to the corresponding debit or credit.
398 my ( $self, $params ) = @_;
400 my $amount = $params->{amount};
401 my $update_type = $params->{type};
402 my $interface = $params->{interface};
404 unless ( exists($Koha::Account::Line::allowed_update->{$update_type}) ) {
405 Koha::Exceptions::Account::UnrecognisedType->throw(
406 error => 'Update type not recognised'
410 my $debit_type_code = $self->debit_type_code;
411 my $account_status = $self->status;
415 $Koha::Account::Line::allowed_update->{$update_type}
418 && ( $Koha::Account::Line::allowed_update->{$update_type}
419 ->{$debit_type_code} eq $account_status )
423 Koha::Exceptions::Account::UnrecognisedType->throw(
424 error => 'Update type not allowed on this debit_type' );
427 my $schema = Koha::Database->new->schema;
432 my $amount_before = $self->amount;
433 my $amount_outstanding_before = $self->amountoutstanding;
434 my $difference = $amount - $amount_before;
435 my $new_outstanding = $amount_outstanding_before + $difference;
437 my $offset_type = $debit_type_code;
438 $offset_type .= ( $difference > 0 ) ? "_INCREASE" : "_DECREASE";
440 # Catch cases that require patron refunds
441 if ( $new_outstanding < 0 ) {
443 Koha::Patrons->find( $self->borrowernumber )->account;
444 my $credit = $account->add_credit(
446 amount => $new_outstanding * -1,
447 description => 'Overpayment refund',
449 interface => $interface,
450 ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : ()),
453 $new_outstanding = 0;
456 # Update the account line
461 amountoutstanding => $new_outstanding,
465 # Record the account offset
466 my $account_offset = Koha::Account::Offset->new(
468 debit_id => $self->id,
469 type => $offset_type,
470 amount => $difference
474 if ( C4::Context->preference("FinesLog") ) {
476 "FINES", 'UPDATE', #undef becomes UPDATE in UpdateFine
477 $self->borrowernumber,
479 { action => $update_type,
480 borrowernumber => $self->borrowernumber,
482 description => undef,
483 amountoutstanding => $new_outstanding,
484 debit_type_code => $self->debit_type_code,
486 itemnumber => $self->itemnumber,
490 ) if ( $update_type eq 'overdue_update' );
500 my $bool = $line->is_credit;
507 return ( $self->amount < 0 );
512 my $bool = $line->is_debit;
519 return !$self->is_credit;
522 =head3 to_api_mapping
524 This method returns the mapping for representing a Koha::Account::Line object
531 accountlines_id => 'account_line_id',
532 credit_type_code => 'credit_type',
533 debit_type_code => 'debit_type',
534 amountoutstanding => 'amount_outstanding',
535 borrowernumber => 'patron_id',
536 branchcode => 'library_id',
537 issue_id => 'checkout_id',
538 itemnumber => 'item_id',
539 manager_id => 'user_id',
540 note => 'internal_note',
544 =head2 Internal methods
553 return 'Accountline';
560 =head3 $allowed_update
564 our $allowed_update = { 'overdue_update' => { 'OVERDUE' => 'UNRETURNED' } };
568 Kyle M Hall <kyle@bywatersolutions.com >
569 Tomás Cohen Arazi <tomascohen@theke.io>
570 Martin Renvoize <martin.renvoize@ptfs-europe.com>