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
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.
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.
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>.
20 use Data::Dumper qw( Dumper );
22 use C4::Log qw( logaction );
23 use C4::Overdues qw( UpdateFine );
25 use Koha::Account::CreditType;
26 use Koha::Account::DebitType;
27 use Koha::Account::Offsets;
29 use Koha::DateUtils qw( dt_from_string );
30 use Koha::Exceptions::Account;
33 use base qw(Koha::Object);
39 Koha::Account::Line - Koha accountline Object class
49 Return the patron linked to this account line
55 my $rs = $self->_result->borrowernumber;
57 return Koha::Patron->_new_from_dbic( $rs );
62 Return the item linked to this account line if exists
68 my $rs = $self->_result->itemnumber;
70 return Koha::Item->_new_from_dbic( $rs );
75 Return the checkout linked to this account line if exists
81 return unless $self->issue_id ;
83 $self->{_checkout} ||= Koha::Checkouts->find( $self->issue_id );
84 $self->{_checkout} ||= Koha::Old::Checkouts->find( $self->issue_id );
85 return $self->{_checkout};
90 Returns a Koha::Library object representing where the accountline was recorded
96 my $rs = $self->_result->library;
98 return Koha::Library->_new_from_dbic($rs);
103 Return the credit_type linked to this account line
109 my $rs = $self->_result->credit_type_code;
111 return Koha::Account::CreditType->_new_from_dbic( $rs );
116 Return the debit_type linked to this account line
122 my $rs = $self->_result->debit_type_code;
124 return Koha::Account::DebitType->_new_from_dbic( $rs );
127 =head3 credit_offsets
129 Return the credit_offsets linked to this account line if some exist
135 my $rs = $self->_result->account_offsets_credits;
137 return Koha::Account::Offsets->_new_from_dbic($rs);
142 Return the debit_offsets linked to this account line if some exist
148 my $rs = $self->_result->account_offsets_debits;
150 return Koha::Account::Offsets->_new_from_dbic($rs);
156 my $credits = $accountline->credits;
157 my $credits = $accountline->credits( $cond, $attr );
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.
166 my ( $self, $cond, $attr ) = @_;
168 unless ( $self->is_debit ) {
169 Koha::Exceptions::Account::IsNotDebit->throw(
170 error => 'Account line ' . $self->id . ' is not a debit'
174 my $cond_m = { map { "credit.".$_ => $cond->{$_} } keys %{$cond}};
176 $self->_result->search_related('account_offsets_debits')
177 ->search_related( 'credit', $cond_m, $attr );
179 return Koha::Account::Lines->_new_from_dbic($rs);
184 my $debits = $accountline->debits;
185 my $debits = $accountline->debits( $cond, $attr );
187 Return the debits linked to this account line if some exist.
188 Search conditions and attributes may be passed if you wish to filter
189 the resultant resultant resultset.
194 my ( $self, $cond, $attr ) = @_;
196 unless ( $self->is_credit ) {
197 Koha::Exceptions::Account::IsNotCredit->throw(
198 error => 'Account line ' . $self->id . ' is not a credit'
202 my $cond_m = { map { "debit.".$_ => $cond->{$_} } keys %{$cond}};
204 $self->_result->search_related('account_offsets_credits')
205 ->search_related( 'debit', $cond_m, $attr );
207 return Koha::Account::Lines->_new_from_dbic($rs);
212 $payment_accountline->void({
213 interface => $interface,
214 [ staff_id => $staff_id, branch => $branchcode ]
217 Used to 'void' (or reverse) a payment/credit. It will roll back any offsets
218 created by the application of this credit upon any debits and mark the credit
219 as 'void' by updating it's status to "VOID".
224 my ($self, $params) = @_;
226 # Make sure it is a credit we are voiding
227 unless ( $self->is_credit ) {
228 Koha::Exceptions::Account::IsNotCredit->throw(
229 error => 'Account line ' . $self->id . 'is not a credit' );
232 # Make sure it is not already voided
233 if ( $self->status && $self->status eq 'VOID' ) {
234 Koha::Exceptions::Account->throw(
235 error => 'Account line ' . $self->id . 'is already void' );
238 # Check for mandatory parameters
239 my @mandatory = ( 'interface' );
240 for my $param (@mandatory) {
241 unless ( defined( $params->{$param} ) ) {
242 Koha::Exceptions::MissingParameter->throw(
243 error => "The $param parameter is mandatory" );
247 # More mandatory parameters
248 if ( $params->{interface} eq 'intranet' ) {
249 my @optional = ( 'staff_id', 'branch' );
250 for my $param (@optional) {
251 unless ( defined( $params->{$param} ) ) {
252 Koha::Exceptions::MissingParameter->throw( error =>
253 "The $param parameter is mandatory when interface is set to 'intranet'"
259 # Find any applied offsets for the credit so we may reverse them
260 my @account_offsets =
261 Koha::Account::Offsets->search(
262 { credit_id => $self->id, amount => { '<' => 0 } } );
265 $self->_result->result_source->schema->txn_do(
268 # A 'void' is a 'debit'
269 $void = Koha::Account::Line->new(
271 borrowernumber => $self->borrowernumber,
273 debit_type_code => 'VOID',
274 amount => $self->amount * -1,
275 amountoutstanding => $self->amount * -1,
276 manager_id => $params->{staff_id},
277 interface => $params->{interface},
278 branchcode => $params->{branch},
282 # Record the creation offset
283 Koha::Account::Offset->new(
285 debit_id => $void->id,
287 amount => $self->amount * -1
291 # Link void to payment
293 amountoutstanding => $self->amount,
296 $self->apply( { debits => [$void] } );
298 # Reverse any applied payments
299 foreach my $account_offset (@account_offsets) {
301 Koha::Account::Lines->find( $account_offset->debit_id );
303 next unless $fee_paid;
305 my $amount_paid = $account_offset->amount * -1; # amount paid is stored as a negative amount
306 my $new_amount = $fee_paid->amountoutstanding + $amount_paid;
307 $fee_paid->amountoutstanding($new_amount);
310 Koha::Account::Offset->new(
312 credit_id => $self->id,
313 debit_id => $fee_paid->id,
314 amount => $amount_paid,
320 if ( C4::Context->preference("FinesLog") ) {
323 $self->borrowernumber,
326 action => 'void_payment',
327 borrowernumber => $self->borrowernumber,
328 amount => $self->amount,
329 amountoutstanding => $self->amountoutstanding,
330 description => $self->description,
331 credit_type_code => $self->credit_type_code,
332 payment_type => $self->payment_type,
334 itemnumber => $self->itemnumber,
335 manager_id => $self->manager_id,
337 [ map { $_->unblessed } @account_offsets ],
345 $void->discard_changes;
351 $debit_accountline->cancel();
353 Cancel a charge. It will mark the debit as 'cancelled' by updating its
354 status to 'CANCELLED'.
356 Charges that have been fully or partially paid cannot be cancelled.
358 Returns the cancellation accountline.
363 my ( $self, $params ) = @_;
365 # Make sure it is a charge we are reducing
366 unless ( $self->is_debit ) {
367 Koha::Exceptions::Account::IsNotDebit->throw(
368 error => 'Account line ' . $self->id . 'is not a debit' );
370 if ( $self->debit_type_code eq 'PAYOUT' ) {
371 Koha::Exceptions::Account::IsNotDebit->throw(
372 error => 'Account line ' . $self->id . 'is a payout' );
375 # Make sure it is not already cancelled
376 if ( $self->status && $self->status eq 'CANCELLED' ) {
377 Koha::Exceptions::Account->throw(
378 error => 'Account line ' . $self->id . 'is already cancelled' );
381 # Make sure it has not be paid yet
382 if ( $self->amount != $self->amountoutstanding ) {
383 Koha::Exceptions::Account->throw(
384 error => 'Account line ' . $self->id . 'is already offset' );
387 # Check for mandatory parameters
388 my @mandatory = ( 'staff_id', 'branch' );
389 for my $param (@mandatory) {
390 unless ( defined( $params->{$param} ) ) {
391 Koha::Exceptions::MissingParameter->throw(
392 error => "The $param parameter is mandatory" );
397 $self->_result->result_source->schema->txn_do(
400 # A 'cancellation' is a 'credit'
401 $cancellation = Koha::Account::Line->new(
404 amount => 0 - $self->amount,
405 credit_type_code => 'CANCELLATION',
407 amountoutstanding => 0 - $self->amount,
408 manager_id => $params->{staff_id},
409 borrowernumber => $self->borrowernumber,
410 interface => 'intranet',
411 branchcode => $params->{branch},
415 my $cancellation_offset = Koha::Account::Offset->new(
417 credit_id => $cancellation->accountlines_id,
419 amount => 0 - $self->amount
423 # Link cancellation to charge
424 $cancellation->apply( { debits => [$self] } );
425 $cancellation->status('APPLIED')->store();
427 # Update status of original debit
428 $self->status('CANCELLED')->store;
432 $cancellation->discard_changes;
433 return $cancellation;
438 $charge_accountline->reduce({
439 reduction_type => $reduction_type
442 Used to 'reduce' a charge/debit by adding a credit to offset against the amount
445 May be used to apply a discount whilst retaining the original debit amounts or
446 to apply a full or partial refund for example when a lost item is found and
449 It will immediately be applied to the given debit unless the debit has already
450 been paid, in which case a 'zero' offset will be added to maintain a link to
451 the debit but the outstanding credit will be left so it may be applied to other
454 Reduction type may be one of:
459 Returns the reduction accountline (which will be a credit)
464 my ( $self, $params ) = @_;
466 # Make sure it is a charge we are reducing
467 unless ( $self->is_debit ) {
468 Koha::Exceptions::Account::IsNotDebit->throw(
469 error => 'Account line ' . $self->id . 'is not a debit' );
471 if ( $self->debit_type_code eq 'PAYOUT' ) {
472 Koha::Exceptions::Account::IsNotDebit->throw(
473 error => 'Account line ' . $self->id . 'is a payout' );
476 # Check for mandatory parameters
477 my @mandatory = ( 'interface', 'reduction_type', 'amount' );
478 for my $param (@mandatory) {
479 unless ( defined( $params->{$param} ) ) {
480 Koha::Exceptions::MissingParameter->throw(
481 error => "The $param parameter is mandatory" );
485 # More mandatory parameters
486 if ( $params->{interface} eq 'intranet' ) {
487 my @optional = ( 'staff_id', 'branch' );
488 for my $param (@optional) {
489 unless ( defined( $params->{$param} ) ) {
490 Koha::Exceptions::MissingParameter->throw( error =>
491 "The $param parameter is mandatory when interface is set to 'intranet'"
497 # Make sure the reduction isn't more than the original
498 my $original = $self->amount;
499 Koha::Exceptions::Account::AmountNotPositive->throw(
500 error => 'Reduce amount passed is not positive' )
501 unless ( $params->{amount} > 0 );
502 Koha::Exceptions::ParameterTooHigh->throw( error =>
503 "Amount to reduce ($params->{amount}) is higher than original amount ($original)"
504 ) unless ( $original >= $params->{amount} );
506 $self->credits( { credit_type_code => [ 'DISCOUNT', 'REFUND' ] } )->total;
507 Koha::Exceptions::ParameterTooHigh->throw( error =>
508 "Combined reduction ($params->{amount} + $reduced) is higher than original amount ("
511 unless ( $original >= ( $params->{amount} + abs($reduced) ) );
513 my $status = { 'REFUND' => 'REFUNDED', 'DISCOUNT' => 'DISCOUNTED' };
516 $self->_result->result_source->schema->txn_do(
519 # A 'reduction' is a 'credit'
520 $reduction = Koha::Account::Line->new(
523 amount => 0 - $params->{amount},
524 credit_type_code => $params->{reduction_type},
526 amountoutstanding => 0 - $params->{amount},
527 manager_id => $params->{staff_id},
528 borrowernumber => $self->borrowernumber,
529 interface => $params->{interface},
530 branchcode => $params->{branch},
534 my $reduction_offset = Koha::Account::Offset->new(
536 credit_id => $reduction->accountlines_id,
538 amount => 0 - $params->{amount}
542 # Link reduction to charge (and apply as required)
543 my $debit_outstanding = $self->amountoutstanding;
544 if ( $debit_outstanding >= $params->{amount} ) {
546 $reduction->apply( { debits => [$self] } );
547 $reduction->status('APPLIED')->store();
551 # Zero amount offset used to link original 'debit' to
553 my $link_reduction_offset = Koha::Account::Offset->new(
555 credit_id => $reduction->accountlines_id,
556 debit_id => $self->accountlines_id,
563 # Update status of original debit
564 $self->status( $status->{ $params->{reduction_type} } )->store;
568 $reduction->discard_changes;
574 my $debits = $account->outstanding_debits;
575 my $credit = $credit->apply( { debits => $debits } );
577 Applies the credit to a given debits array reference.
579 =head4 arguments hashref
583 =item debits - Koha::Account::Lines object set of debits
590 my ( $self, $params ) = @_;
592 my $debits = $params->{debits};
594 unless ( $self->is_credit ) {
595 Koha::Exceptions::Account::IsNotCredit->throw(
596 error => 'Account line ' . $self->id . ' is not a credit'
600 my $available_credit = $self->amountoutstanding * -1;
602 unless ( $available_credit > 0 ) {
603 Koha::Exceptions::Account::NoAvailableCredit->throw(
604 error => 'Outstanding credit is ' . $available_credit . ' and cannot be applied'
608 my $schema = Koha::Database->new->schema;
610 $schema->txn_do( sub {
611 for my $debit ( @{$debits} ) {
613 unless ( $debit->is_debit ) {
614 Koha::Exceptions::Account::IsNotDebit->throw(
615 error => 'Account line ' . $debit->id . 'is not a debit'
618 my $amount_to_cancel;
619 my $owed = $debit->amountoutstanding;
621 if ( $available_credit >= $owed ) {
622 $amount_to_cancel = $owed;
624 else { # $available_credit < $debit->amountoutstanding
625 $amount_to_cancel = $available_credit;
628 # record the account offset
629 Koha::Account::Offset->new(
630 { credit_id => $self->id,
631 debit_id => $debit->id,
632 amount => $amount_to_cancel * -1,
637 $available_credit -= $amount_to_cancel;
639 $self->amountoutstanding( $available_credit * -1 )->store;
640 $debit->amountoutstanding( $owed - $amount_to_cancel )->store;
642 # Attempt to renew the item associated with this debit if
644 if ( $self->credit_type_code ne 'FORGIVEN' && $debit->is_renewable ) {
645 my $outcome = $debit->renew_item( { interface => $params->{interface} } );
649 message => 'renewal',
654 $debit->discard_changes; # Refresh values from DB to clear floating point remainders
656 # Same logic exists in Koha::Account::pay
658 C4::Context->preference('MarkLostItemsAsReturned') =~
660 && $debit->debit_type_code
661 && $debit->debit_type_code eq 'LOST'
662 && $debit->amountoutstanding == 0
663 && $debit->itemnumber
665 $self->credit_type_code eq 'LOST_FOUND'
666 && $self->itemnumber == $debit->itemnumber
670 C4::Circulation::ReturnLostItem( $self->borrowernumber,
671 $debit->itemnumber );
674 last if $available_credit == 0;
683 $credit_accountline->payout(
685 payout_type => $payout_type,
686 register_id => $register_id,
687 staff_id => $staff_id,
688 interface => 'intranet',
693 Used to 'pay out' a credit to a user.
695 Payout type may be one of any existing payment types
697 Returns the payout debit line that is created via this transaction.
702 my ( $self, $params ) = @_;
704 # Make sure it is a credit we are paying out
705 unless ( $self->is_credit ) {
706 Koha::Exceptions::Account::IsNotCredit->throw(
707 error => 'Account line ' . $self->id . ' is not a credit' );
710 # Check for mandatory parameters
712 ( 'interface', 'staff_id', 'branch', 'payout_type', 'amount' );
713 for my $param (@mandatory) {
714 unless ( defined( $params->{$param} ) ) {
715 Koha::Exceptions::MissingParameter->throw(
716 error => "The $param parameter is mandatory" );
720 # Make sure there is outstanding credit to pay out
721 my $outstanding = -1 * $self->amountoutstanding;
723 $params->{amount} ? $params->{amount} : $outstanding;
724 Koha::Exceptions::Account::AmountNotPositive->throw(
725 error => 'Payout amount passed is not positive' )
726 unless ( $amount > 0 );
727 Koha::Exceptions::ParameterTooHigh->throw(
728 error => "Amount to payout ($amount) is higher than amountoutstanding ($outstanding)" )
729 unless ($outstanding >= $amount );
731 # Make sure we record the cash register for cash transactions
732 Koha::Exceptions::Account::RegisterRequired->throw()
733 if ( C4::Context->preference("UseCashRegisters")
734 && defined( $params->{payout_type} )
735 && ( $params->{payout_type} eq 'CASH' )
736 && !defined( $params->{cash_register} ) );
739 $self->_result->result_source->schema->txn_do(
742 # A 'payout' is a 'debit'
743 $payout = Koha::Account::Line->new(
747 debit_type_code => 'PAYOUT',
748 payment_type => $params->{payout_type},
749 amountoutstanding => $amount,
750 manager_id => $params->{staff_id},
751 borrowernumber => $self->borrowernumber,
752 interface => $params->{interface},
753 branchcode => $params->{branch},
754 register_id => $params->{cash_register}
758 my $payout_offset = Koha::Account::Offset->new(
760 debit_id => $payout->accountlines_id,
766 $self->apply( { debits => [$payout] } );
767 $self->status('PAID')->store;
771 $payout->discard_changes;
777 This method allows updating a debit or credit on a patron's account
779 $account_line->adjust(
782 type => $update_type,
783 interface => $interface
787 $update_type can be any of:
790 Authors Note: The intention here is that this method is only used
791 to adjust accountlines where the final amount is not yet known/fixed.
792 Incrementing fines are the only existing case at the time of writing,
793 all other forms of 'adjustment' should be recorded as distinct credits
794 or debits and applied, via an offset, to the corresponding debit or credit.
799 my ( $self, $params ) = @_;
801 my $amount = $params->{amount};
802 my $update_type = $params->{type};
803 my $interface = $params->{interface};
805 unless ( exists($Koha::Account::Line::allowed_update->{$update_type}) ) {
806 Koha::Exceptions::Account::UnrecognisedType->throw(
807 error => 'Update type not recognised'
811 my $debit_type_code = $self->debit_type_code;
812 my $account_status = $self->status;
816 $Koha::Account::Line::allowed_update->{$update_type}
819 && ( $Koha::Account::Line::allowed_update->{$update_type}
820 ->{$debit_type_code} eq $account_status )
824 Koha::Exceptions::Account::UnrecognisedType->throw(
825 error => 'Update type not allowed on this debit_type' );
828 my $schema = Koha::Database->new->schema;
833 my $amount_before = $self->amount;
834 my $amount_outstanding_before = $self->amountoutstanding;
835 my $difference = $amount - $amount_before;
836 my $new_outstanding = $amount_outstanding_before + $difference;
838 my $offset_type = $debit_type_code;
839 $offset_type .= ( $difference > 0 ) ? "_INCREASE" : "_DECREASE";
841 # Catch cases that require patron refunds
842 if ( $new_outstanding < 0 ) {
844 Koha::Patrons->find( $self->borrowernumber )->account;
845 my $credit = $account->add_credit(
847 amount => $new_outstanding * -1,
848 type => 'OVERPAYMENT',
849 interface => $interface,
850 ( $update_type eq 'overdue_update' ? ( item_id => $self->itemnumber ) : ()),
853 $new_outstanding = 0;
856 # Update the account line
861 amountoutstanding => $new_outstanding,
865 # Record the account offset
866 my $account_offset = Koha::Account::Offset->new(
868 debit_id => $self->id,
869 type => $offset_type,
870 amount => $difference
874 if ( C4::Context->preference("FinesLog") ) {
876 "FINES", 'UPDATE', #undef becomes UPDATE in UpdateFine
877 $self->borrowernumber,
879 { action => $update_type,
880 borrowernumber => $self->borrowernumber,
882 description => undef,
883 amountoutstanding => $new_outstanding,
884 debit_type_code => $self->debit_type_code,
886 itemnumber => $self->itemnumber,
890 ) if ( $update_type eq 'overdue_update' );
900 my $bool = $line->is_credit;
907 return defined $self->credit_type_code;
912 my $bool = $line->is_debit;
919 return !$self->is_credit;
922 =head3 to_api_mapping
924 This method returns the mapping for representing a Koha::Account::Line object
931 accountlines_id => 'account_line_id',
932 credit_number => undef,
933 credit_type_code => 'credit_type',
934 debit_type_code => 'debit_type',
935 amountoutstanding => 'amount_outstanding',
936 borrowernumber => 'patron_id',
937 branchcode => 'library_id',
938 issue_id => 'checkout_id',
939 itemnumber => 'item_id',
940 manager_id => 'user_id',
941 note => 'internal_note',
942 register_id => 'cash_register_id',
949 my $bool = $line->is_renewable;
957 $self->amountoutstanding == 0 &&
958 $self->debit_type_code &&
959 $self->debit_type_code eq 'OVERDUE' &&
961 $self->status eq 'UNRETURNED' &&
969 my $renew_result = $line->renew_item;
971 Conditionally attempt to renew an item and return the outcome. This is
972 as a consequence of the fine on an item being fully paid off.
973 Caller must call is_renewable before.
978 my ($self, $params) = @_;
982 # We want to reject the call to renew if:
983 # - The RenewAccruingItemWhenPaid syspref is off
985 # - The RenewAccruingItemInOpac syspref is off
986 # - There is an interface param passed and it's value is 'opac'
989 !C4::Context->preference('RenewAccruingItemWhenPaid') ||
991 !C4::Context->preference('RenewAccruingItemInOpac') &&
992 $params->{interface} &&
993 $params->{interface} eq 'opac'
999 my $itemnumber = $self->item->itemnumber;
1000 my $borrowernumber = $self->patron->borrowernumber;
1001 my ( $can_renew, $error ) = C4::Circulation::CanBookBeRenewed(
1006 my $due_date = C4::Circulation::AddRenewal(
1009 $self->{branchcode},
1015 itemnumber => $itemnumber,
1016 due_date => $due_date,
1021 itemnumber => $itemnumber,
1031 Specific store method to generate credit number before saving
1038 my $AutoCreditNumber = C4::Context->preference('AutoCreditNumber');
1039 my $credit_number_enabled = $self->is_credit && $self->credit_type->credit_number_enabled;
1041 if ($AutoCreditNumber && $credit_number_enabled && !$self->in_storage) {
1042 if (defined $self->credit_number) {
1043 Koha::Exceptions::Account->throw('AutoCreditNumber is enabled but credit_number is already defined');
1046 my $rs = Koha::Database->new->schema->resultset($self->_type);
1048 if ($AutoCreditNumber eq 'incremental') {
1049 my $max = $rs->search({
1050 credit_number => { -regexp => '^[0-9]+$' }
1052 select => \'CAST(credit_number AS UNSIGNED)',
1053 as => ['credit_number'],
1054 })->get_column('credit_number')->max;
1056 $self->credit_number($max + 1);
1057 } elsif ($AutoCreditNumber eq 'annual') {
1058 my $now = dt_from_string;
1059 my $prefix = sprintf('%d-', $now->year);
1060 my $max = $rs->search({
1062 credit_number => { -regexp => '[0-9]{4}$' },
1063 credit_number => { -like => "$prefix%" },
1065 })->get_column('credit_number')->max;
1066 $max //= $prefix . '0000';
1067 my $incr = substr($max, length $prefix);
1068 $self->credit_number(sprintf('%s%04d', $prefix, $incr + 1));
1069 } elsif ($AutoCreditNumber eq 'branchyyyymmincr') {
1070 my $userenv = C4::Context->userenv;
1072 my $branch = $userenv->{branch};
1073 my $now = dt_from_string;
1074 my $prefix = sprintf('%s%d%02d', $branch, $now->year, $now->month);
1075 my $pattern = $prefix;
1076 $pattern =~ s/([\?%_])/\\$1/g;
1077 my $max = $rs->search({
1079 credit_number => { -regexp => '[0-9]{4}$' },
1080 credit_number => { -like => "$pattern%" },
1082 })->get_column('credit_number')->max;
1083 $max //= $prefix . '0000';
1084 my $incr = substr($max, length $prefix);
1085 $self->credit_number(sprintf('%s%04d', $prefix, $incr + 1));
1090 return $self->SUPER::store();
1093 =head2 Internal methods
1102 return 'Accountline';
1107 =head2 Name mappings
1109 =head3 $allowed_update
1113 our $allowed_update = { 'overdue_update' => { 'OVERDUE' => 'UNRETURNED' } };
1117 Kyle M Hall <kyle@bywatersolutions.com >
1118 Tomás Cohen Arazi <tomascohen@theke.io>
1119 Martin Renvoize <martin.renvoize@ptfs-europe.com>