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 );
113 =head3 credit_offsets
115 Return the credit_offsets linked to this account line if some exist
121 my $rs = $self->_result->account_offsets_credits;
123 return Koha::Account::Offsets->_new_from_dbic($rs);
128 Return the debit_offsets linked to this account line if some exist
134 my $rs = $self->_result->account_offsets_debits;
136 return Koha::Account::Offsets->_new_from_dbic($rs);
142 my $credits = $accountline->credits;
143 my $credits = $accountline->credits( $cond, $attr );
145 Return the credits linked to this account line if some exist.
146 Search conditions and attributes may be passed if you wish to filter
147 the resultant resultant resultset.
152 my ( $self, $cond, $attr ) = @_;
154 unless ( $self->is_debit ) {
155 Koha::Exceptions::Account::IsNotCredit->throw(
156 error => 'Account line ' . $self->id . ' is not a debit'
161 $self->_result->search_related('account_offsets_debits')
162 ->search_related( 'credit', $cond, $attr );
164 return Koha::Account::Lines->_new_from_dbic($rs);
169 my $debits = $accountline->debits;
170 my $debits = $accountline->debits( $cond, $attr );
172 Return the debits linked to this account line if some exist.
173 Search conditions and attributes may be passed if you wish to filter
174 the resultant resultant resultset.
179 my ( $self, $cond, $attr ) = @_;
181 unless ( $self->is_credit ) {
182 Koha::Exceptions::Account::IsNotCredit->throw(
183 error => 'Account line ' . $self->id . ' is not a credit'
188 $self->_result->search_related('account_offsets_credits')
189 ->search_related( 'debit', $cond, $attr );
191 return Koha::Account::Lines->_new_from_dbic($rs);
196 $payment_accountline->void();
198 Used to 'void' (or reverse) a payment/credit. It will roll back any offsets
199 created by the application of this credit upon any debits and mark the credit
200 as 'void' by updating it's status to "VOID".
207 # Make sure it is a payment we are voiding
208 return unless $self->amount < 0;
210 my @account_offsets =
211 Koha::Account::Offsets->search(
212 { credit_id => $self->id, amount => { '<' => 0 } } );
214 $self->_result->result_source->schema->txn_do(
216 foreach my $account_offset (@account_offsets) {
218 Koha::Account::Lines->find( $account_offset->debit_id );
220 next unless $fee_paid;
222 my $amount_paid = $account_offset->amount * -1; # amount paid is stored as a negative amount
223 my $new_amount = $fee_paid->amountoutstanding + $amount_paid;
224 $fee_paid->amountoutstanding($new_amount);
227 Koha::Account::Offset->new(
229 credit_id => $self->id,
230 debit_id => $fee_paid->id,
231 amount => $amount_paid,
232 type => 'Void Payment',
237 if ( C4::Context->preference("FinesLog") ) {
240 $self->borrowernumber,
243 action => 'void_payment',
244 borrowernumber => $self->borrowernumber,
245 amount => $self->amount,
246 amountoutstanding => $self->amountoutstanding,
247 description => $self->description,
248 credit_type_code => $self->credit_type_code,
249 payment_type => $self->payment_type,
251 itemnumber => $self->itemnumber,
252 manager_id => $self->manager_id,
254 [ map { $_->unblessed } @account_offsets ],
263 amountoutstanding => 0,
275 my $debits = $account->outstanding_debits;
276 my $outstanding_amount = $credit->apply( { debits => $debits, [ offset_type => $offset_type ] } );
278 Applies the credit to a given debits array reference.
280 =head4 arguments hashref
284 =item debits - Koha::Account::Lines object set of debits
286 =item offset_type (optional) - a string indicating the offset type (valid values are those from
287 the 'account_offset_types' table)
294 my ( $self, $params ) = @_;
296 my $debits = $params->{debits};
297 my $offset_type = $params->{offset_type} // 'Credit Applied';
299 unless ( $self->is_credit ) {
300 Koha::Exceptions::Account::IsNotCredit->throw(
301 error => 'Account line ' . $self->id . ' is not a credit'
305 my $available_credit = $self->amountoutstanding * -1;
307 unless ( $available_credit > 0 ) {
308 Koha::Exceptions::Account::NoAvailableCredit->throw(
309 error => 'Outstanding credit is ' . $available_credit . ' and cannot be applied'
313 my $schema = Koha::Database->new->schema;
315 $schema->txn_do( sub {
316 for my $debit ( @{$debits} ) {
318 unless ( $debit->is_debit ) {
319 Koha::Exceptions::Account::IsNotDebit->throw(
320 error => 'Account line ' . $debit->id . 'is not a debit'
323 my $amount_to_cancel;
324 my $owed = $debit->amountoutstanding;
326 if ( $available_credit >= $owed ) {
327 $amount_to_cancel = $owed;
329 else { # $available_credit < $debit->amountoutstanding
330 $amount_to_cancel = $available_credit;
333 # record the account offset
334 Koha::Account::Offset->new(
335 { credit_id => $self->id,
336 debit_id => $debit->id,
337 amount => $amount_to_cancel * -1,
338 type => $offset_type,
342 $available_credit -= $amount_to_cancel;
344 $self->amountoutstanding( $available_credit * -1 )->store;
345 $debit->amountoutstanding( $owed - $amount_to_cancel )->store;
347 # Same logic exists in Koha::Account::pay
348 if ( $debit->amountoutstanding == 0
349 && $debit->itemnumber
350 && $debit->debit_type_code
351 && $debit->debit_type_code eq 'LOST' )
353 C4::Circulation::ReturnLostItem( $self->borrowernumber, $debit->itemnumber );
359 return $available_credit;
364 $credit_accountline->payout(
366 payout_type => $payout_type,
367 register_id => $register_id,
368 staff_id => $staff_id,
369 interface => 'intranet',
374 Used to 'pay out' a credit to a user.
376 Payout type may be one of any existing payment types
378 Returns the payout debit line that is created via this transaction.
383 my ( $self, $params ) = @_;
385 # Make sure it is a credit we are paying out
386 unless ( $self->is_credit ) {
387 Koha::Exceptions::Account::IsNotCredit->throw(
388 error => 'Account line ' . $self->id . ' is not a credit' );
391 # Check for mandatory parameters
393 ( 'interface', 'staff_id', 'branch', 'payout_type', 'amount' );
394 for my $param (@mandatory) {
395 unless ( defined( $params->{$param} ) ) {
396 Koha::Exceptions::MissingParameter->throw(
397 error => "The $param parameter is mandatory" );
401 # Make sure there is outstanding credit to pay out
402 my $outstanding = -1 * $self->amountoutstanding;
404 $params->{amount} ? $params->{amount} : $outstanding;
405 Koha::Exceptions::Account::AmountNotPositive->throw(
406 error => 'Payout amount passed is not positive' )
407 unless ( $amount > 0 );
408 Koha::Exceptions::ParameterTooHigh->throw(
409 error => "Amount to payout ($amount) is higher than amountoutstanding ($outstanding)" )
410 unless ($outstanding >= $amount );
412 # Make sure we record the cash register for cash transactions
413 Koha::Exceptions::Account::RegisterRequired->throw()
414 if ( C4::Context->preference("UseCashRegisters")
415 && defined( $params->{payout_type} )
416 && ( $params->{payout_type} eq 'CASH' )
417 && !defined( $params->{cash_register} ) );
420 $self->_result->result_source->schema->txn_do(
423 # A 'payout' is a 'debit'
424 $payout = Koha::Account::Line->new(
428 debit_type_code => 'PAYOUT',
429 payment_type => $params->{payout_type},
430 amountoutstanding => $amount,
431 manager_id => $params->{staff_id},
432 borrowernumber => $self->borrowernumber,
433 interface => $params->{interface},
434 branchcode => $params->{branch},
435 register_id => $params->{cash_register}
439 my $payout_offset = Koha::Account::Offset->new(
441 debit_id => $payout->accountlines_id,
447 $self->apply( { debits => [$payout], offset_type => 'PAYOUT' } );
448 $self->status('PAID')->store;
457 This method allows updating a debit or credit on a patron's account
459 $account_line->adjust(
462 type => $update_type,
463 interface => $interface
467 $update_type can be any of:
470 Authors Note: The intention here is that this method is only used
471 to adjust accountlines where the final amount is not yet known/fixed.
472 Incrementing fines are the only existing case at the time of writing,
473 all other forms of 'adjustment' should be recorded as distinct credits
474 or debits and applied, via an offset, to the corresponding debit or credit.
479 my ( $self, $params ) = @_;
481 my $amount = $params->{amount};
482 my $update_type = $params->{type};
483 my $interface = $params->{interface};
485 unless ( exists($Koha::Account::Line::allowed_update->{$update_type}) ) {
486 Koha::Exceptions::Account::UnrecognisedType->throw(
487 error => 'Update type not recognised'
491 my $debit_type_code = $self->debit_type_code;
492 my $account_status = $self->status;
496 $Koha::Account::Line::allowed_update->{$update_type}
499 && ( $Koha::Account::Line::allowed_update->{$update_type}
500 ->{$debit_type_code} eq $account_status )
504 Koha::Exceptions::Account::UnrecognisedType->throw(
505 error => 'Update type not allowed on this debit_type' );
508 my $schema = Koha::Database->new->schema;
513 my $amount_before = $self->amount;
514 my $amount_outstanding_before = $self->amountoutstanding;
515 my $difference = $amount - $amount_before;
516 my $new_outstanding = $amount_outstanding_before + $difference;
518 my $offset_type = $debit_type_code;
519 $offset_type .= ( $difference > 0 ) ? "_INCREASE" : "_DECREASE";
521 # Catch cases that require patron refunds
522 if ( $new_outstanding < 0 ) {
524 Koha::Patrons->find( $self->borrowernumber )->account;
525 my $credit = $account->add_credit(
527 amount => $new_outstanding * -1,
528 description => 'Overpayment refund',
530 interface => $interface,
531 ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : ()),
534 $new_outstanding = 0;
537 # Update the account line
542 amountoutstanding => $new_outstanding,
546 # Record the account offset
547 my $account_offset = Koha::Account::Offset->new(
549 debit_id => $self->id,
550 type => $offset_type,
551 amount => $difference
555 if ( C4::Context->preference("FinesLog") ) {
557 "FINES", 'UPDATE', #undef becomes UPDATE in UpdateFine
558 $self->borrowernumber,
560 { action => $update_type,
561 borrowernumber => $self->borrowernumber,
563 description => undef,
564 amountoutstanding => $new_outstanding,
565 debit_type_code => $self->debit_type_code,
567 itemnumber => $self->itemnumber,
571 ) if ( $update_type eq 'overdue_update' );
581 my $bool = $line->is_credit;
588 return ( $self->amount < 0 );
593 my $bool = $line->is_debit;
600 return !$self->is_credit;
603 =head3 to_api_mapping
605 This method returns the mapping for representing a Koha::Account::Line object
612 accountlines_id => 'account_line_id',
613 credit_type_code => 'credit_type',
614 debit_type_code => 'debit_type',
615 amountoutstanding => 'amount_outstanding',
616 borrowernumber => 'patron_id',
617 branchcode => 'library_id',
618 issue_id => 'checkout_id',
619 itemnumber => 'item_id',
620 manager_id => 'user_id',
621 note => 'internal_note',
625 =head2 Internal methods
634 return 'Accountline';
641 =head3 $allowed_update
645 our $allowed_update = { 'overdue_update' => { 'OVERDUE' => 'UNRETURNED' } };
649 Kyle M Hall <kyle@bywatersolutions.com >
650 Tomás Cohen Arazi <tomascohen@theke.io>
651 Martin Renvoize <martin.renvoize@ptfs-europe.com>