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