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::Offsets;
27 use Koha::Exceptions::Account;
30 use base qw(Koha::Object);
36 Koha::Account::Line - Koha accountline Object class
46 Return the patron linked to this account line
52 my $rs = $self->_result->borrowernumber;
54 return Koha::Patron->_new_from_dbic( $rs );
59 Return the item linked to this account line if exists
65 my $rs = $self->_result->itemnumber;
67 return Koha::Item->_new_from_dbic( $rs );
72 Return the checkout linked to this account line if exists
78 return unless $self->issue_id ;
80 $self->{_checkout} ||= Koha::Checkouts->find( $self->issue_id );
81 $self->{_checkout} ||= Koha::Old::Checkouts->find( $self->issue_id );
82 return $self->{_checkout};
87 $payment_accountline->void();
89 Used to 'void' (or reverse) a payment/credit. It will roll back any offsets
90 created by the application of this credit upon any debits and mark the credit
91 as 'void' by updating it's status to "VOID".
98 # Make sure it is a payment we are voiding
99 return unless $self->amount < 0;
101 my @account_offsets =
102 Koha::Account::Offsets->search(
103 { credit_id => $self->id, amount => { '<' => 0 } } );
105 $self->_result->result_source->schema->txn_do(
107 foreach my $account_offset (@account_offsets) {
109 Koha::Account::Lines->find( $account_offset->debit_id );
111 next unless $fee_paid;
113 my $amount_paid = $account_offset->amount * -1; # amount paid is stored as a negative amount
114 my $new_amount = $fee_paid->amountoutstanding + $amount_paid;
115 $fee_paid->amountoutstanding($new_amount);
118 Koha::Account::Offset->new(
120 credit_id => $self->id,
121 debit_id => $fee_paid->id,
122 amount => $amount_paid,
123 type => 'Void Payment',
128 if ( C4::Context->preference("FinesLog") ) {
131 $self->borrowernumber,
134 action => 'void_payment',
135 borrowernumber => $self->borrowernumber,
136 amount => $self->amount,
137 amountoutstanding => $self->amountoutstanding,
138 description => $self->description,
139 accounttype => $self->accounttype,
140 payment_type => $self->payment_type,
142 itemnumber => $self->itemnumber,
143 manager_id => $self->manager_id,
145 [ map { $_->unblessed } @account_offsets ],
154 amountoutstanding => 0,
166 my $debits = $account->outstanding_debits;
167 my $outstanding_amount = $credit->apply( { debits => $debits, [ offset_type => $offset_type ] } );
169 Applies the credit to a given debits set.
171 =head4 arguments hashref
175 =item debits - Koha::Account::Lines object set of debits
177 =item offset_type (optional) - a string indicating the offset type (valid values are those from
178 the 'account_offset_types' table)
185 my ( $self, $params ) = @_;
187 my $debits = $params->{debits};
188 my $offset_type = $params->{offset_type} // 'Credit Applied';
190 unless ( $self->is_credit ) {
191 Koha::Exceptions::Account::IsNotCredit->throw(
192 error => 'Account line ' . $self->id . ' is not a credit'
196 my $available_credit = $self->amountoutstanding * -1;
198 unless ( $available_credit > 0 ) {
199 Koha::Exceptions::Account::NoAvailableCredit->throw(
200 error => 'Outstanding credit is ' . $available_credit . ' and cannot be applied'
204 my $schema = Koha::Database->new->schema;
206 $schema->txn_do( sub {
207 while ( my $debit = $debits->next ) {
209 unless ( $debit->is_debit ) {
210 Koha::Exceptions::Account::IsNotDebit->throw(
211 error => 'Account line ' . $debit->id . 'is not a debit'
214 my $amount_to_cancel;
215 my $owed = $debit->amountoutstanding;
217 if ( $available_credit >= $owed ) {
218 $amount_to_cancel = $owed;
220 else { # $available_credit < $debit->amountoutstanding
221 $amount_to_cancel = $available_credit;
224 # record the account offset
225 Koha::Account::Offset->new(
226 { credit_id => $self->id,
227 debit_id => $debit->id,
228 amount => $amount_to_cancel * -1,
229 type => $offset_type,
233 $available_credit -= $amount_to_cancel;
235 $self->amountoutstanding( $available_credit * -1 )->store;
236 $debit->amountoutstanding( $owed - $amount_to_cancel )->store;
238 # Same logic exists in Koha::Account::pay
239 if ( $debit->amountoutstanding == 0
240 && $debit->itemnumber
241 && $debit->accounttype
242 && $debit->accounttype eq 'LOST' )
244 C4::Circulation::ReturnLostItem( $self->borrowernumber, $debit->itemnumber );
250 return $available_credit;
255 This method allows updating a debit or credit on a patron's account
257 $account_line->adjust(
260 type => $update_type,
261 interface => $interface
265 $update_type can be any of:
268 Authors Note: The intention here is that this method is only used
269 to adjust accountlines where the final amount is not yet known/fixed.
270 Incrementing fines are the only existing case at the time of writing,
271 all other forms of 'adjustment' should be recorded as distinct credits
272 or debits and applied, via an offset, to the corresponding debit or credit.
277 my ( $self, $params ) = @_;
279 my $amount = $params->{amount};
280 my $update_type = $params->{type};
281 my $interface = $params->{interface};
283 unless ( exists($Koha::Account::Line::allowed_update->{$update_type}) ) {
284 Koha::Exceptions::Account::UnrecognisedType->throw(
285 error => 'Update type not recognised'
289 my $account_type = $self->accounttype;
290 my $account_status = $self->status;
294 $Koha::Account::Line::allowed_update->{$update_type}
297 && ( $Koha::Account::Line::allowed_update->{$update_type}
298 ->{$account_type} eq $account_status )
302 Koha::Exceptions::Account::UnrecognisedType->throw(
303 error => 'Update type not allowed on this accounttype' );
306 my $schema = Koha::Database->new->schema;
311 my $amount_before = $self->amount;
312 my $amount_outstanding_before = $self->amountoutstanding;
313 my $difference = $amount - $amount_before;
314 my $new_outstanding = $amount_outstanding_before + $difference;
316 my $offset_type = $account_type;
317 $offset_type .= ( $difference > 0 ) ? "_INCREASE" : "_DECREASE";
319 # Catch cases that require patron refunds
320 if ( $new_outstanding < 0 ) {
322 Koha::Patrons->find( $self->borrowernumber )->account;
323 my $credit = $account->add_credit(
325 amount => $new_outstanding * -1,
326 description => 'Overpayment refund',
328 interface => $interface,
329 ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : ()),
332 $new_outstanding = 0;
335 # Update the account line
340 amountoutstanding => $new_outstanding,
344 # Record the account offset
345 my $account_offset = Koha::Account::Offset->new(
347 debit_id => $self->id,
348 type => $offset_type,
349 amount => $difference
353 if ( C4::Context->preference("FinesLog") ) {
355 "FINES", 'UPDATE', #undef becomes UPDATE in UpdateFine
356 $self->borrowernumber,
358 { action => $update_type,
359 borrowernumber => $self->borrowernumber,
361 description => undef,
362 amountoutstanding => $new_outstanding,
363 accounttype => $self->accounttype,
365 itemnumber => $self->itemnumber,
369 ) if ( $update_type eq 'overdue_update' );
379 my $bool = $line->is_credit;
386 return ( $self->amount < 0 );
391 my $bool = $line->is_debit;
398 return !$self->is_credit;
401 =head2 Internal methods
410 return 'Accountline';
417 =head3 $allowed_update
421 our $allowed_update = { 'overdue_update' => { 'OVERDUE' => 'UNRETURNED' } };
425 Kyle M Hall <kyle@bywatersolutions.com >
426 Tomás Cohen Arazi <tomascohen@theke.io>
427 Martin Renvoize <martin.renvoize@ptfs-europe.com>