Bug 23866: Prompt for HEA configuration
[koha.git] / Koha / Account / Line.pm
blob9004f97df1c142d2bf50668e4d7d2a4de954c115
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
8 # version.
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.
18 use Modern::Perl;
20 use Carp;
21 use Data::Dumper;
23 use C4::Log qw(logaction);
25 use Koha::Account::DebitType;
26 use Koha::Account::Offsets;
27 use Koha::Database;
28 use Koha::Exceptions::Account;
29 use Koha::Items;
31 use base qw(Koha::Object);
33 =encoding utf8
35 =head1 NAME
37 Koha::Account::Line - Koha accountline Object class
39 =head1 API
41 =head2 Class methods
43 =cut
45 =head3 patron
47 Return the patron linked to this account line
49 =cut
51 sub patron {
52 my ( $self ) = @_;
53 my $rs = $self->_result->borrowernumber;
54 return unless $rs;
55 return Koha::Patron->_new_from_dbic( $rs );
58 =head3 item
60 Return the item linked to this account line if exists
62 =cut
64 sub item {
65 my ( $self ) = @_;
66 my $rs = $self->_result->itemnumber;
67 return unless $rs;
68 return Koha::Item->_new_from_dbic( $rs );
71 =head3 checkout
73 Return the checkout linked to this account line if exists
75 =cut
77 sub checkout {
78 my ( $self ) = @_;
79 return unless $self->issue_id ;
81 $self->{_checkout} ||= Koha::Checkouts->find( $self->issue_id );
82 $self->{_checkout} ||= Koha::Old::Checkouts->find( $self->issue_id );
83 return $self->{_checkout};
86 =head3 debit_type
88 Return the debit_type linked to this account line
90 =cut
92 sub debit_type {
93 my ( $self ) = @_;
94 my $rs = $self->_result->debit_type_code;
95 return unless $rs;
96 return Koha::Account::DebitType->_new_from_dbic( $rs );
99 =head3 void
101 $payment_accountline->void();
103 Used to 'void' (or reverse) a payment/credit. It will roll back any offsets
104 created by the application of this credit upon any debits and mark the credit
105 as 'void' by updating it's status to "VOID".
107 =cut
109 sub void {
110 my ($self) = @_;
112 # Make sure it is a payment we are voiding
113 return unless $self->amount < 0;
115 my @account_offsets =
116 Koha::Account::Offsets->search(
117 { credit_id => $self->id, amount => { '<' => 0 } } );
119 $self->_result->result_source->schema->txn_do(
120 sub {
121 foreach my $account_offset (@account_offsets) {
122 my $fee_paid =
123 Koha::Account::Lines->find( $account_offset->debit_id );
125 next unless $fee_paid;
127 my $amount_paid = $account_offset->amount * -1; # amount paid is stored as a negative amount
128 my $new_amount = $fee_paid->amountoutstanding + $amount_paid;
129 $fee_paid->amountoutstanding($new_amount);
130 $fee_paid->store();
132 Koha::Account::Offset->new(
134 credit_id => $self->id,
135 debit_id => $fee_paid->id,
136 amount => $amount_paid,
137 type => 'Void Payment',
139 )->store();
142 if ( C4::Context->preference("FinesLog") ) {
143 logaction(
144 "FINES", 'VOID',
145 $self->borrowernumber,
146 Dumper(
148 action => 'void_payment',
149 borrowernumber => $self->borrowernumber,
150 amount => $self->amount,
151 amountoutstanding => $self->amountoutstanding,
152 description => $self->description,
153 credit_type_code => $self->credit_type_code,
154 payment_type => $self->payment_type,
155 note => $self->note,
156 itemnumber => $self->itemnumber,
157 manager_id => $self->manager_id,
158 offsets =>
159 [ map { $_->unblessed } @account_offsets ],
165 $self->set(
167 status => 'VOID',
168 amountoutstanding => 0,
169 amount => 0,
172 $self->store();
178 =head3 apply
180 my $debits = $account->outstanding_debits;
181 my $outstanding_amount = $credit->apply( { debits => $debits, [ offset_type => $offset_type ] } );
183 Applies the credit to a given debits array reference.
185 =head4 arguments hashref
187 =over 4
189 =item debits - Koha::Account::Lines object set of debits
191 =item offset_type (optional) - a string indicating the offset type (valid values are those from
192 the 'account_offset_types' table)
194 =back
196 =cut
198 sub apply {
199 my ( $self, $params ) = @_;
201 my $debits = $params->{debits};
202 my $offset_type = $params->{offset_type} // 'Credit Applied';
204 unless ( $self->is_credit ) {
205 Koha::Exceptions::Account::IsNotCredit->throw(
206 error => 'Account line ' . $self->id . ' is not a credit'
210 my $available_credit = $self->amountoutstanding * -1;
212 unless ( $available_credit > 0 ) {
213 Koha::Exceptions::Account::NoAvailableCredit->throw(
214 error => 'Outstanding credit is ' . $available_credit . ' and cannot be applied'
218 my $schema = Koha::Database->new->schema;
220 $schema->txn_do( sub {
221 for my $debit ( @{$debits} ) {
223 unless ( $debit->is_debit ) {
224 Koha::Exceptions::Account::IsNotDebit->throw(
225 error => 'Account line ' . $debit->id . 'is not a debit'
228 my $amount_to_cancel;
229 my $owed = $debit->amountoutstanding;
231 if ( $available_credit >= $owed ) {
232 $amount_to_cancel = $owed;
234 else { # $available_credit < $debit->amountoutstanding
235 $amount_to_cancel = $available_credit;
238 # record the account offset
239 Koha::Account::Offset->new(
240 { credit_id => $self->id,
241 debit_id => $debit->id,
242 amount => $amount_to_cancel * -1,
243 type => $offset_type,
245 )->store();
247 $available_credit -= $amount_to_cancel;
249 $self->amountoutstanding( $available_credit * -1 )->store;
250 $debit->amountoutstanding( $owed - $amount_to_cancel )->store;
252 # Same logic exists in Koha::Account::pay
253 if ( $debit->amountoutstanding == 0
254 && $debit->itemnumber
255 && $debit->debit_type_code
256 && $debit->debit_type_code eq 'LOST' )
258 C4::Circulation::ReturnLostItem( $self->borrowernumber, $debit->itemnumber );
264 return $available_credit;
267 =head3 adjust
269 This method allows updating a debit or credit on a patron's account
271 $account_line->adjust(
273 amount => $amount,
274 type => $update_type,
275 interface => $interface
279 $update_type can be any of:
280 - overdue_update
282 Authors Note: The intention here is that this method is only used
283 to adjust accountlines where the final amount is not yet known/fixed.
284 Incrementing fines are the only existing case at the time of writing,
285 all other forms of 'adjustment' should be recorded as distinct credits
286 or debits and applied, via an offset, to the corresponding debit or credit.
288 =cut
290 sub adjust {
291 my ( $self, $params ) = @_;
293 my $amount = $params->{amount};
294 my $update_type = $params->{type};
295 my $interface = $params->{interface};
297 unless ( exists($Koha::Account::Line::allowed_update->{$update_type}) ) {
298 Koha::Exceptions::Account::UnrecognisedType->throw(
299 error => 'Update type not recognised'
303 my $debit_type_code = $self->debit_type_code;
304 my $account_status = $self->status;
305 unless (
307 exists(
308 $Koha::Account::Line::allowed_update->{$update_type}
309 ->{$debit_type_code}
311 && ( $Koha::Account::Line::allowed_update->{$update_type}
312 ->{$debit_type_code} eq $account_status )
316 Koha::Exceptions::Account::UnrecognisedType->throw(
317 error => 'Update type not allowed on this debit_type' );
320 my $schema = Koha::Database->new->schema;
322 $schema->txn_do(
323 sub {
325 my $amount_before = $self->amount;
326 my $amount_outstanding_before = $self->amountoutstanding;
327 my $difference = $amount - $amount_before;
328 my $new_outstanding = $amount_outstanding_before + $difference;
330 my $offset_type = $debit_type_code;
331 $offset_type .= ( $difference > 0 ) ? "_INCREASE" : "_DECREASE";
333 # Catch cases that require patron refunds
334 if ( $new_outstanding < 0 ) {
335 my $account =
336 Koha::Patrons->find( $self->borrowernumber )->account;
337 my $credit = $account->add_credit(
339 amount => $new_outstanding * -1,
340 description => 'Overpayment refund',
341 type => 'CREDIT',
342 interface => $interface,
343 ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : ()),
346 $new_outstanding = 0;
349 # Update the account line
350 $self->set(
352 date => \'NOW()',
353 amount => $amount,
354 amountoutstanding => $new_outstanding,
356 )->store();
358 # Record the account offset
359 my $account_offset = Koha::Account::Offset->new(
361 debit_id => $self->id,
362 type => $offset_type,
363 amount => $difference
365 )->store();
367 if ( C4::Context->preference("FinesLog") ) {
368 logaction(
369 "FINES", 'UPDATE', #undef becomes UPDATE in UpdateFine
370 $self->borrowernumber,
371 Dumper(
372 { action => $update_type,
373 borrowernumber => $self->borrowernumber,
374 amount => $amount,
375 description => undef,
376 amountoutstanding => $new_outstanding,
377 debit_type_code => $self->debit_type_code,
378 note => undef,
379 itemnumber => $self->itemnumber,
380 manager_id => undef,
383 ) if ( $update_type eq 'overdue_update' );
388 return $self;
391 =head3 is_credit
393 my $bool = $line->is_credit;
395 =cut
397 sub is_credit {
398 my ($self) = @_;
400 return ( $self->amount < 0 );
403 =head3 is_debit
405 my $bool = $line->is_debit;
407 =cut
409 sub is_debit {
410 my ($self) = @_;
412 return !$self->is_credit;
415 =head3 to_api_mapping
417 This method returns the mapping for representing a Koha::Account::Line object
418 on the API.
420 =cut
422 sub to_api_mapping {
423 return {
424 accountlines_id => 'account_line_id',
425 credit_type_code => 'credit_type',
426 debit_type_code => 'debit_type',
427 amountoutstanding => 'amount_outstanding',
428 borrowernumber => 'patron_id',
429 branchcode => 'library_id',
430 issue_id => 'checkout_id',
431 itemnumber => 'item_id',
432 manager_id => 'user_id',
433 note => 'internal_note',
437 =head2 Internal methods
439 =cut
441 =head3 _type
443 =cut
445 sub _type {
446 return 'Accountline';
451 =head2 Name mappings
453 =head3 $allowed_update
455 =cut
457 our $allowed_update = { 'overdue_update' => { 'OVERDUE' => 'UNRETURNED' } };
459 =head1 AUTHORS
461 Kyle M Hall <kyle@bywatersolutions.com >
462 Tomás Cohen Arazi <tomascohen@theke.io>
463 Martin Renvoize <martin.renvoize@ptfs-europe.com>
465 =cut