Bug 25998: (QA follow-up) Add DBIC level relation
[koha-equinox.git] / Koha / Account / Line.pm
1 package Koha::Account::Line;
2
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18 use Modern::Perl;
19
20 use Carp;
21 use Data::Dumper;
22
23 use C4::Log qw(logaction);
24 use C4::Overdues qw(GetFine);
25
26 use Koha::Account::CreditType;
27 use Koha::Account::DebitType;
28 use Koha::Account::Offsets;
29 use Koha::Database;
30 use Koha::Exceptions::Account;
31 use Koha::Items;
32
33 use base qw(Koha::Object);
34
35 =encoding utf8
36
37 =head1 NAME
38
39 Koha::Account::Line - Koha accountline Object class
40
41 =head1 API
42
43 =head2 Class methods
44
45 =cut
46
47 =head3 patron
48
49 Return the patron linked to this account line
50
51 =cut
52
53 sub patron {
54     my ( $self ) = @_;
55     my $rs = $self->_result->borrowernumber;
56     return unless $rs;
57     return Koha::Patron->_new_from_dbic( $rs );
58 }
59
60 =head3 item
61
62 Return the item linked to this account line if exists
63
64 =cut
65
66 sub item {
67     my ( $self ) = @_;
68     my $rs = $self->_result->itemnumber;
69     return unless $rs;
70     return Koha::Item->_new_from_dbic( $rs );
71 }
72
73 =head3 checkout
74
75 Return the checkout linked to this account line if exists
76
77 =cut
78
79 sub checkout {
80     my ( $self ) = @_;
81     return unless $self->issue_id ;
82
83     $self->{_checkout} ||= Koha::Checkouts->find( $self->issue_id );
84     $self->{_checkout} ||= Koha::Old::Checkouts->find( $self->issue_id );
85     return $self->{_checkout};
86 }
87
88 =head3 library
89
90 Returns a Koha::Library object representing where the accountline was recorded
91
92 =cut
93
94 sub library {
95     my ( $self ) = @_;
96     my $rs = $self->_result->library;
97     return unless $rs;
98     return Koha::Library->_new_from_dbic($rs);
99 }
100
101 =head3 credit_type
102
103 Return the credit_type linked to this account line
104
105 =cut
106
107 sub credit_type {
108     my ( $self ) = @_;
109     my $rs = $self->_result->credit_type_code;
110     return unless $rs;
111     return Koha::Account::CreditType->_new_from_dbic( $rs );
112 }
113
114 =head3 debit_type
115
116 Return the debit_type linked to this account line
117
118 =cut
119
120 sub debit_type {
121     my ( $self ) = @_;
122     my $rs = $self->_result->debit_type_code;
123     return unless $rs;
124     return Koha::Account::DebitType->_new_from_dbic( $rs );
125 }
126
127 =head3 credit_offsets
128
129 Return the credit_offsets linked to this account line if some exist
130
131 =cut
132
133 sub credit_offsets {
134     my ( $self ) = @_;
135     my $rs = $self->_result->account_offsets_credits;
136     return unless $rs;
137     return Koha::Account::Offsets->_new_from_dbic($rs);
138 }
139
140 =head3 debit_offsets
141
142 Return the debit_offsets linked to this account line if some exist
143
144 =cut
145
146 sub debit_offsets {
147     my ( $self ) = @_;
148     my $rs = $self->_result->account_offsets_debits;
149     return unless $rs;
150     return Koha::Account::Offsets->_new_from_dbic($rs);
151 }
152
153
154 =head3 credits
155
156   my $credits = $accountline->credits;
157   my $credits = $accountline->credits( $cond, $attr );
158
159 Return the credits linked to this account line if some exist.
160 Search conditions and attributes may be passed if you wish to filter
161 the resultant resultant resultset.
162
163 =cut
164
165 sub credits {
166     my ( $self, $cond, $attr ) = @_;
167
168     unless ( $self->is_debit ) {
169         Koha::Exceptions::Account::IsNotCredit->throw(
170             error => 'Account line ' . $self->id . ' is not a debit'
171         );
172     }
173
174     my $rs =
175       $self->_result->search_related('account_offsets_debits')
176       ->search_related( 'credit', $cond, $attr );
177     return unless $rs;
178     return Koha::Account::Lines->_new_from_dbic($rs);
179 }
180
181 =head3 debits
182
183   my $debits = $accountline->debits;
184   my $debits = $accountline->debits( $cond, $attr );
185
186 Return the debits linked to this account line if some exist.
187 Search conditions and attributes may be passed if you wish to filter
188 the resultant resultant resultset.
189
190 =cut
191
192 sub debits {
193     my ( $self, $cond, $attr ) = @_;
194
195     unless ( $self->is_credit ) {
196         Koha::Exceptions::Account::IsNotCredit->throw(
197             error => 'Account line ' . $self->id . ' is not a credit'
198         );
199     }
200
201     my $rs =
202       $self->_result->search_related('account_offsets_credits')
203       ->search_related( 'debit', $cond, $attr );
204     return unless $rs;
205     return Koha::Account::Lines->_new_from_dbic($rs);
206 }
207
208 =head3 void
209
210   $payment_accountline->void();
211
212 Used to 'void' (or reverse) a payment/credit. It will roll back any offsets
213 created by the application of this credit upon any debits and mark the credit
214 as 'void' by updating it's status to "VOID".
215
216 =cut
217
218 sub void {
219     my ($self) = @_;
220
221     # Make sure it is a payment we are voiding
222     return unless $self->amount < 0;
223
224     my @account_offsets =
225       Koha::Account::Offsets->search(
226         { credit_id => $self->id, amount => { '<' => 0 }  } );
227
228     $self->_result->result_source->schema->txn_do(
229         sub {
230             foreach my $account_offset (@account_offsets) {
231                 my $fee_paid =
232                   Koha::Account::Lines->find( $account_offset->debit_id );
233
234                 next unless $fee_paid;
235
236                 my $amount_paid = $account_offset->amount * -1; # amount paid is stored as a negative amount
237                 my $new_amount = $fee_paid->amountoutstanding + $amount_paid;
238                 $fee_paid->amountoutstanding($new_amount);
239                 $fee_paid->store();
240
241                 Koha::Account::Offset->new(
242                     {
243                         credit_id => $self->id,
244                         debit_id  => $fee_paid->id,
245                         amount    => $amount_paid,
246                         type      => 'Void Payment',
247                     }
248                 )->store();
249             }
250
251             if ( C4::Context->preference("FinesLog") ) {
252                 logaction(
253                     "FINES", 'VOID',
254                     $self->borrowernumber,
255                     Dumper(
256                         {
257                             action         => 'void_payment',
258                             borrowernumber => $self->borrowernumber,
259                             amount            => $self->amount,
260                             amountoutstanding => $self->amountoutstanding,
261                             description       => $self->description,
262                             credit_type_code  => $self->credit_type_code,
263                             payment_type      => $self->payment_type,
264                             note              => $self->note,
265                             itemnumber        => $self->itemnumber,
266                             manager_id        => $self->manager_id,
267                             offsets =>
268                               [ map { $_->unblessed } @account_offsets ],
269                         }
270                     )
271                 );
272             }
273
274             $self->set(
275                 {
276                     status            => 'VOID',
277                     amountoutstanding => 0,
278                     amount            => 0,
279                 }
280             );
281             $self->store();
282         }
283     );
284
285 }
286
287 =head3 reduce
288
289   $charge_accountline->reduce({
290       reduction_type => $reduction_type
291   });
292
293 Used to 'reduce' a charge/debit by adding a credit to offset against the amount
294 outstanding.
295
296 May be used to apply a discount whilst retaining the original debit amounts or
297 to apply a full or partial refund for example when a lost item is found and
298 returned.
299
300 It will immediately be applied to the given debit unless the debit has already
301 been paid, in which case a 'zero' offset will be added to maintain a link to
302 the debit but the outstanding credit will be left so it may be applied to other
303 debts.
304
305 Reduction type may be one of:
306
307 * REFUND
308 * DISCOUNT
309
310 Returns the reduction accountline (which will be a credit)
311
312 =cut
313
314 sub reduce {
315     my ( $self, $params ) = @_;
316
317     # Make sure it is a charge we are reducing
318     unless ( $self->is_debit ) {
319         Koha::Exceptions::Account::IsNotDebit->throw(
320             error => 'Account line ' . $self->id . 'is not a debit' );
321     }
322     if ( $self->debit_type_code eq 'PAYOUT' ) {
323         Koha::Exceptions::Account::IsNotDebit->throw(
324             error => 'Account line ' . $self->id . 'is a payout' );
325     }
326
327     # Check for mandatory parameters
328     my @mandatory = ( 'interface', 'reduction_type', 'amount' );
329     for my $param (@mandatory) {
330         unless ( defined( $params->{$param} ) ) {
331             Koha::Exceptions::MissingParameter->throw(
332                 error => "The $param parameter is mandatory" );
333         }
334     }
335
336     # More mandatory parameters
337     if ( $params->{interface} eq 'intranet' ) {
338         my @optional = ( 'staff_id', 'branch' );
339         for my $param (@optional) {
340             unless ( defined( $params->{$param} ) ) {
341                 Koha::Exceptions::MissingParameter->throw( error =>
342 "The $param parameter is mandatory when interface is set to 'intranet'"
343                 );
344             }
345         }
346     }
347
348     # Make sure the reduction isn't more than the original
349     my $original = $self->amount;
350     Koha::Exceptions::Account::AmountNotPositive->throw(
351         error => 'Reduce amount passed is not positive' )
352       unless ( $params->{amount} > 0 );
353     Koha::Exceptions::ParameterTooHigh->throw( error =>
354 "Amount to reduce ($params->{amount}) is higher than original amount ($original)"
355     ) unless ( $original >= $params->{amount} );
356     my $reduced =
357       $self->credits( { credit_type_code => [ 'DISCOUNT', 'REFUND' ] } )->total;
358     Koha::Exceptions::ParameterTooHigh->throw( error =>
359 "Combined reduction ($params->{amount} + $reduced) is higher than original amount ("
360           . abs($original)
361           . ")" )
362       unless ( $original >= ( $params->{amount} + abs($reduced) ) );
363
364     my $status = { 'REFUND' => 'REFUNDED', 'DISCOUNT' => 'DISCOUNTED' };
365
366     my $reduction;
367     $self->_result->result_source->schema->txn_do(
368         sub {
369
370             # A 'reduction' is a 'credit'
371             $reduction = Koha::Account::Line->new(
372                 {
373                     date              => \'NOW()',
374                     amount            => 0 - $params->{amount},
375                     credit_type_code  => $params->{reduction_type},
376                     status            => 'ADDED',
377                     amountoutstanding => 0 - $params->{amount},
378                     manager_id        => $params->{staff_id},
379                     borrowernumber    => $self->borrowernumber,
380                     interface         => $params->{interface},
381                     branchcode        => $params->{branch},
382                 }
383             )->store();
384
385             my $reduction_offset = Koha::Account::Offset->new(
386                 {
387                     credit_id => $reduction->accountlines_id,
388                     type      => uc( $params->{reduction_type} ),
389                     amount    => $params->{amount}
390                 }
391             )->store();
392
393             # Link reduction to charge (and apply as required)
394             my $debit_outstanding = $self->amountoutstanding;
395             if ( $debit_outstanding >= $params->{amount} ) {
396
397                 $reduction->apply(
398                     {
399                         debits      => [$self],
400                         offset_type => uc( $params->{reduction_type} )
401                     }
402                 );
403                 $reduction->status('APPLIED')->store();
404             }
405             else {
406
407         # Zero amount offset used to link original 'debit' to reduction 'credit'
408                 my $link_reduction_offset = Koha::Account::Offset->new(
409                     {
410                         credit_id => $reduction->accountlines_id,
411                         debit_id  => $self->accountlines_id,
412                         type      => uc( $params->{reduction_type} ),
413                         amount    => 0
414                     }
415                 )->store();
416             }
417
418             # Update status of original debit
419             $self->status( $status->{ $params->{reduction_type} } )->store;
420         }
421     );
422
423     $reduction->discard_changes;
424     return $reduction;
425 }
426
427 =head3 apply
428
429     my $debits = $account->outstanding_debits;
430     my $outstanding_amount = $credit->apply( { debits => $debits, [ offset_type => $offset_type ] } );
431
432 Applies the credit to a given debits array reference.
433
434 =head4 arguments hashref
435
436 =over 4
437
438 =item debits - Koha::Account::Lines object set of debits
439
440 =item offset_type (optional) - a string indicating the offset type (valid values are those from
441 the 'account_offset_types' table)
442
443 =back
444
445 =cut
446
447 sub apply {
448     my ( $self, $params ) = @_;
449
450     my $debits      = $params->{debits};
451     my $offset_type = $params->{offset_type} // 'Credit Applied';
452
453     unless ( $self->is_credit ) {
454         Koha::Exceptions::Account::IsNotCredit->throw(
455             error => 'Account line ' . $self->id . ' is not a credit'
456         );
457     }
458
459     my $available_credit = $self->amountoutstanding * -1;
460
461     unless ( $available_credit > 0 ) {
462         Koha::Exceptions::Account::NoAvailableCredit->throw(
463             error => 'Outstanding credit is ' . $available_credit . ' and cannot be applied'
464         );
465     }
466
467     my $schema = Koha::Database->new->schema;
468
469     $schema->txn_do( sub {
470         for my $debit ( @{$debits} ) {
471
472             unless ( $debit->is_debit ) {
473                 Koha::Exceptions::Account::IsNotDebit->throw(
474                     error => 'Account line ' . $debit->id . 'is not a debit'
475                 );
476             }
477             my $amount_to_cancel;
478             my $owed = $debit->amountoutstanding;
479
480             if ( $available_credit >= $owed ) {
481                 $amount_to_cancel = $owed;
482             }
483             else {    # $available_credit < $debit->amountoutstanding
484                 $amount_to_cancel = $available_credit;
485             }
486
487             # record the account offset
488             Koha::Account::Offset->new(
489                 {   credit_id => $self->id,
490                     debit_id  => $debit->id,
491                     amount    => $amount_to_cancel * -1,
492                     type      => $offset_type,
493                 }
494             )->store();
495
496             $available_credit -= $amount_to_cancel;
497
498             $self->amountoutstanding( $available_credit * -1 )->store;
499             $debit->amountoutstanding( $owed - $amount_to_cancel )->store;
500
501             # Attempt to renew the item associated with this debit if
502             # appropriate
503             if ($debit->renewable) {
504                 $debit->renew_item($params->{interface});
505             }
506
507             # Same logic exists in Koha::Account::pay
508             if (
509                 C4::Context->preference('MarkLostItemsAsReturned') =~
510                 m|onpayment|
511                 && $debit->debit_type_code
512                 && $debit->debit_type_code eq 'LOST'
513                 && $debit->amountoutstanding == 0
514                 && $debit->itemnumber
515                 && !(
516                        $self->credit_type_code eq 'LOST_FOUND'
517                     && $self->itemnumber == $debit->itemnumber
518                 )
519               )
520             {
521                 C4::Circulation::ReturnLostItem( $self->borrowernumber,
522                     $debit->itemnumber );
523             }
524         }
525     });
526
527     return $available_credit;
528 }
529
530 =head3 payout
531
532   $credit_accountline->payout(
533     {
534         payout_type => $payout_type,
535         register_id => $register_id,
536         staff_id    => $staff_id,
537         interface   => 'intranet',
538         amount      => $amount
539     }
540   );
541
542 Used to 'pay out' a credit to a user.
543
544 Payout type may be one of any existing payment types
545
546 Returns the payout debit line that is created via this transaction.
547
548 =cut
549
550 sub payout {
551     my ( $self, $params ) = @_;
552
553     # Make sure it is a credit we are paying out
554     unless ( $self->is_credit ) {
555         Koha::Exceptions::Account::IsNotCredit->throw(
556             error => 'Account line ' . $self->id . ' is not a credit' );
557     }
558
559     # Check for mandatory parameters
560     my @mandatory =
561       ( 'interface', 'staff_id', 'branch', 'payout_type', 'amount' );
562     for my $param (@mandatory) {
563         unless ( defined( $params->{$param} ) ) {
564             Koha::Exceptions::MissingParameter->throw(
565                 error => "The $param parameter is mandatory" );
566         }
567     }
568
569     # Make sure there is outstanding credit to pay out
570     my $outstanding = -1 * $self->amountoutstanding;
571     my $amount =
572       $params->{amount} ? $params->{amount} : $outstanding;
573     Koha::Exceptions::Account::AmountNotPositive->throw(
574         error => 'Payout amount passed is not positive' )
575       unless ( $amount > 0 );
576     Koha::Exceptions::ParameterTooHigh->throw(
577         error => "Amount to payout ($amount) is higher than amountoutstanding ($outstanding)" )
578       unless ($outstanding >= $amount );
579
580     # Make sure we record the cash register for cash transactions
581     Koha::Exceptions::Account::RegisterRequired->throw()
582       if ( C4::Context->preference("UseCashRegisters")
583         && defined( $params->{payout_type} )
584         && ( $params->{payout_type} eq 'CASH' )
585         && !defined( $params->{cash_register} ) );
586
587     my $payout;
588     $self->_result->result_source->schema->txn_do(
589         sub {
590
591             # A 'payout' is a 'debit'
592             $payout = Koha::Account::Line->new(
593                 {
594                     date              => \'NOW()',
595                     amount            => $amount,
596                     debit_type_code   => 'PAYOUT',
597                     payment_type      => $params->{payout_type},
598                     amountoutstanding => $amount,
599                     manager_id        => $params->{staff_id},
600                     borrowernumber    => $self->borrowernumber,
601                     interface         => $params->{interface},
602                     branchcode        => $params->{branch},
603                     register_id       => $params->{cash_register}
604                 }
605             )->store();
606
607             my $payout_offset = Koha::Account::Offset->new(
608                 {
609                     debit_id => $payout->accountlines_id,
610                     type     => 'PAYOUT',
611                     amount   => $amount
612                 }
613             )->store();
614
615             $self->apply( { debits => [$payout], offset_type => 'PAYOUT' } );
616             $self->status('PAID')->store;
617         }
618     );
619
620     $payout->discard_changes;
621     return $payout;
622 }
623
624 =head3 adjust
625
626 This method allows updating a debit or credit on a patron's account
627
628     $account_line->adjust(
629         {
630             amount    => $amount,
631             type      => $update_type,
632             interface => $interface
633         }
634     );
635
636 $update_type can be any of:
637   - overdue_update
638
639 Authors Note: The intention here is that this method is only used
640 to adjust accountlines where the final amount is not yet known/fixed.
641 Incrementing fines are the only existing case at the time of writing,
642 all other forms of 'adjustment' should be recorded as distinct credits
643 or debits and applied, via an offset, to the corresponding debit or credit.
644
645 =cut
646
647 sub adjust {
648     my ( $self, $params ) = @_;
649
650     my $amount       = $params->{amount};
651     my $update_type  = $params->{type};
652     my $interface    = $params->{interface};
653
654     unless ( exists($Koha::Account::Line::allowed_update->{$update_type}) ) {
655         Koha::Exceptions::Account::UnrecognisedType->throw(
656             error => 'Update type not recognised'
657         );
658     }
659
660     my $debit_type_code = $self->debit_type_code;
661     my $account_status  = $self->status;
662     unless (
663         (
664             exists(
665                 $Koha::Account::Line::allowed_update->{$update_type}
666                   ->{$debit_type_code}
667             )
668             && ( $Koha::Account::Line::allowed_update->{$update_type}
669                 ->{$debit_type_code} eq $account_status )
670         )
671       )
672     {
673         Koha::Exceptions::Account::UnrecognisedType->throw(
674             error => 'Update type not allowed on this debit_type' );
675     }
676
677     my $schema = Koha::Database->new->schema;
678
679     $schema->txn_do(
680         sub {
681
682             my $amount_before             = $self->amount;
683             my $amount_outstanding_before = $self->amountoutstanding;
684             my $difference                = $amount - $amount_before;
685             my $new_outstanding           = $amount_outstanding_before + $difference;
686
687             my $offset_type = $debit_type_code;
688             $offset_type .= ( $difference > 0 ) ? "_INCREASE" : "_DECREASE";
689
690             # Catch cases that require patron refunds
691             if ( $new_outstanding < 0 ) {
692                 my $account =
693                   Koha::Patrons->find( $self->borrowernumber )->account;
694                 my $credit = $account->add_credit(
695                     {
696                         amount      => $new_outstanding * -1,
697                         description => 'Overpayment refund',
698                         type        => 'CREDIT',
699                         interface   => $interface,
700                         ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : ()),
701                     }
702                 );
703                 $new_outstanding = 0;
704             }
705
706             # Update the account line
707             $self->set(
708                 {
709                     date              => \'NOW()',
710                     amount            => $amount,
711                     amountoutstanding => $new_outstanding,
712                 }
713             )->store();
714
715             # Record the account offset
716             my $account_offset = Koha::Account::Offset->new(
717                 {
718                     debit_id => $self->id,
719                     type     => $offset_type,
720                     amount   => $difference
721                 }
722             )->store();
723
724             if ( C4::Context->preference("FinesLog") ) {
725                 logaction(
726                     "FINES", 'UPDATE', #undef becomes UPDATE in UpdateFine
727                     $self->borrowernumber,
728                     Dumper(
729                         {   action            => $update_type,
730                             borrowernumber    => $self->borrowernumber,
731                             amount            => $amount,
732                             description       => undef,
733                             amountoutstanding => $new_outstanding,
734                             debit_type_code   => $self->debit_type_code,
735                             note              => undef,
736                             itemnumber        => $self->itemnumber,
737                             manager_id        => undef,
738                         }
739                     )
740                 ) if ( $update_type eq 'overdue_update' );
741             }
742         }
743     );
744
745     return $self;
746 }
747
748 =head3 is_credit
749
750     my $bool = $line->is_credit;
751
752 =cut
753
754 sub is_credit {
755     my ($self) = @_;
756
757     return ( $self->amount < 0 );
758 }
759
760 =head3 is_debit
761
762     my $bool = $line->is_debit;
763
764 =cut
765
766 sub is_debit {
767     my ($self) = @_;
768
769     return !$self->is_credit;
770 }
771
772 =head3 to_api_mapping
773
774 This method returns the mapping for representing a Koha::Account::Line object
775 on the API.
776
777 =cut
778
779 sub to_api_mapping {
780     return {
781         accountlines_id   => 'account_line_id',
782         credit_type_code  => 'credit_type',
783         debit_type_code   => 'debit_type',
784         amountoutstanding => 'amount_outstanding',
785         borrowernumber    => 'patron_id',
786         branchcode        => 'library_id',
787         issue_id          => 'checkout_id',
788         itemnumber        => 'item_id',
789         manager_id        => 'user_id',
790         note              => 'internal_note',
791     };
792
793 }
794
795 =head3 renewable
796
797     my $bool = $line->renewable;
798
799 =cut
800
801 sub renewable {
802     my ($self) = @_;
803
804     return (
805         $self->amountoutstanding == 0 &&
806         $self->debit_type_code &&
807         $self->debit_type_code eq 'OVERDUE' &&
808         $self->status &&
809         $self->status eq 'UNRETURNED'
810     ) ? 1 : 0;
811 }
812
813 =head3 renew_item
814
815     my $renew_result = $line->renew_item;
816
817 Conditionally attempt to renew an item and return the outcome. This is
818 as a consequence of the fine on an item being fully paid off
819
820 =cut
821
822 sub renew_item {
823     my ($self, $params) = @_;
824
825     my $outcome = {};
826
827     # We want to reject the call to renew if any of these apply:
828     # - The RenewAccruingItemWhenPaid syspref is off
829     # - The line item doesn't have an item attached to it
830     # - The line item doesn't have a patron attached to it
831     #
832     # - The RenewAccruingItemInOpac syspref is off
833     # AND
834     # - There is an interface param passed and it's value is 'opac'
835
836     if (
837         !C4::Context->preference('RenewAccruingItemWhenPaid') ||
838         !$self->item ||
839         !$self->patron ||
840         (
841             !C4::Context->preference('RenewAccruingItemInOpac') &&
842             $params->{interface} &&
843             $params->{interface} eq 'opac'
844         )
845     ) {
846         return;
847     }
848
849     my $itemnumber = $self->item->itemnumber;
850     my $borrowernumber = $self->patron->borrowernumber;
851     my ( $can_renew, $error ) = C4::Circulation::CanBookBeRenewed(
852         $borrowernumber,
853         $itemnumber
854     );
855     if ( $can_renew ) {
856         my $due_date = C4::Circulation::AddRenewal(
857             $borrowernumber,
858             $itemnumber,
859             $self->{branchcode},
860             undef,
861             undef,
862             1
863         );
864         return {
865             itemnumber => $itemnumber,
866             due_date   => $due_date,
867             success    => 1
868         };
869     } else {
870         return {
871             itemnumber => $itemnumber,
872             error      => $error,
873             success    => 0
874         };
875     }
876
877 }
878
879 =head2 Internal methods
880
881 =cut
882
883 =head3 _type
884
885 =cut
886
887 sub _type {
888     return 'Accountline';
889 }
890
891 1;
892
893 =head2 Name mappings
894
895 =head3 $allowed_update
896
897 =cut
898
899 our $allowed_update = { 'overdue_update' => { 'OVERDUE' => 'UNRETURNED' } };
900
901 =head1 AUTHORS
902
903 Kyle M Hall <kyle@bywatersolutions.com >
904 Tomás Cohen Arazi <tomascohen@theke.io>
905 Martin Renvoize <martin.renvoize@ptfs-europe.com>
906
907 =cut