use Modern::Perl;
use Carp;
-use Data::Dumper;
-use List::MoreUtils qw( uniq );
+use Data::Dumper qw( Dumper );
+use Try::Tiny qw( catch try );
+use C4::Circulation qw( ReturnLostItem CanBookBeRenewed AddRenewal );
+use C4::Letters;
use C4::Log qw( logaction );
use C4::Stats qw( UpdateStats );
+use C4::Overdues qw(GetFine);
+use Koha::Patrons;
use Koha::Account::Lines;
use Koha::Account::Offsets;
-use Koha::DateUtils qw( dt_from_string );
+use Koha::Account::DebitTypes;
+use Koha::Exceptions;
+use Koha::Exceptions::Account;
+use Koha::Plugins;
=head1 NAME
Koha::Account->new( { patron_id => $borrowernumber } )->pay(
{
amount => $amount,
- sip => $sipmode,
note => $note,
description => $description,
library_id => $branchcode,
- lines => $lines, # Arrayref of Koha::Account::Line objects to pay
- account_type => $type, # accounttype code
- offset_type => $offset_type, # offset type code
+ lines => $lines, # Arrayref of Koha::Account::Line objects to pay
+ credit_type => $type, # credit_type_code code
+ item_id => $itemnumber, # pass the itemnumber if this is a credit pertianing to a specific item (i.e LOST_FOUND)
}
);
sub pay {
my ( $self, $params ) = @_;
- my $amount = $params->{amount};
- my $sip = $params->{sip};
- my $description = $params->{description};
- my $note = $params->{note} || q{};
- my $library_id = $params->{library_id};
- my $lines = $params->{lines};
- my $type = $params->{type} || 'payment';
- my $payment_type = $params->{payment_type} || undef;
- my $account_type = $params->{account_type};
- my $offset_type = $params->{offset_type} || $type eq 'writeoff' ? 'Writeoff' : 'Payment';
+ my $amount = $params->{amount};
+ my $description = $params->{description};
+ my $note = $params->{note} || q{};
+ my $library_id = $params->{library_id};
+ my $lines = $params->{lines};
+ my $type = $params->{type} || 'PAYMENT';
+ my $payment_type = $params->{payment_type} || undef;
+ my $cash_register = $params->{cash_register};
+ my $item_id = $params->{item_id};
my $userenv = C4::Context->userenv;
- # We should remove accountno, it is no longer needed
- my $last = Koha::Account::Lines->search(
+ my $manager_id = $userenv ? $userenv->{number} : undef;
+ my $interface = $params ? ( $params->{interface} || C4::Context->interface ) : C4::Context->interface;
+ my $payment = $self->payin_amount(
{
- borrowernumber => $self->{patron_id}
- },
+ interface => $interface,
+ type => $type,
+ amount => $amount,
+ payment_type => $payment_type,
+ cash_register => $cash_register,
+ user_id => $manager_id,
+ library_id => $library_id,
+ item_id => $item_id,
+ description => $description,
+ note => $note,
+ debits => $lines
+ }
+ );
+
+ # NOTE: Pay historically always applied as much credit as it could to all
+ # existing outstanding debits, whether passed specific debits or otherwise.
+ if ( $payment->amountoutstanding ) {
+ $payment =
+ $payment->apply(
+ { debits => [ $self->outstanding_debits->as_list ] } );
+ }
+
+ my $patron = Koha::Patrons->find( $self->{patron_id} );
+ my @account_offsets = $payment->credit_offsets({ type => 'APPLY' })->as_list;
+ if ( C4::Context->preference('UseEmailReceipts') ) {
+ if (
+ my $letter = C4::Letters::GetPreparedLetter(
+ module => 'circulation',
+ letter_code => uc("ACCOUNT_$type"),
+ message_transport_type => 'email',
+ lang => $patron->lang,
+ tables => {
+ borrowers => $self->{patron_id},
+ branches => $library_id,
+ },
+ substitute => {
+ credit => $payment,
+ offsets => \@account_offsets,
+ },
+ )
+ )
{
- order_by => 'accountno'
+ C4::Letters::EnqueueLetter(
+ {
+ letter => $letter,
+ borrowernumber => $self->{patron_id},
+ message_transport_type => 'email',
+ }
+ ) or warn "can't enqueue letter $letter";
}
- )->next();
- my $accountno = $last ? $last->accountno + 1 : 1;
+ }
+
+ my $renew_outcomes = [];
+ for my $message ( @{$payment->object_messages} ) {
+ push @{$renew_outcomes}, $message->payload;
+ }
- my $manager_id = $userenv ? $userenv->{number} : 0;
+ return { payment_id => $payment->id, renew_result => $renew_outcomes };
+}
- my @fines_paid; # List of account lines paid on with this payment
+=head3 add_credit
+
+This method allows adding credits to a patron's account
+
+my $credit_line = Koha::Account->new({ patron_id => $patron_id })->add_credit(
+ {
+ amount => $amount,
+ description => $description,
+ note => $note,
+ user_id => $user_id,
+ interface => $interface,
+ library_id => $library_id,
+ payment_type => $payment_type,
+ type => $credit_type,
+ item_id => $item_id
+ }
+);
- my $balance_remaining = $amount; # Set it now so we can adjust the amount if necessary
- $balance_remaining ||= 0;
+$credit_type can be any of:
+ - 'CREDIT'
+ - 'PAYMENT'
+ - 'FORGIVEN'
+ - 'LOST_FOUND'
+ - 'OVERPAYMENT'
+ - 'PAYMENT'
+ - 'WRITEOFF'
+ - 'PROCESSING_FOUND'
- my @account_offsets;
+=cut
- # We were passed a specific line to pay
- foreach my $fine ( @$lines ) {
- my $amount_to_pay =
- $fine->amountoutstanding > $balance_remaining
- ? $balance_remaining
- : $fine->amountoutstanding;
+sub add_credit {
- my $old_amountoutstanding = $fine->amountoutstanding;
- my $new_amountoutstanding = $old_amountoutstanding - $amount_to_pay;
- $fine->amountoutstanding($new_amountoutstanding)->store();
- $balance_remaining = $balance_remaining - $amount_to_pay;
+ my ( $self, $params ) = @_;
- if ( $fine->itemnumber && $fine->accounttype && ( $fine->accounttype eq 'Rep' || $fine->accounttype eq 'L' ) )
- {
- C4::Circulation::ReturnLostItem( $self->{patron_id}, $fine->itemnumber );
+ # check for mandatory params
+ my @mandatory = ( 'interface', 'amount' );
+ for my $param (@mandatory) {
+ unless ( defined( $params->{$param} ) ) {
+ Koha::Exceptions::MissingParameter->throw(
+ error => "The $param parameter is mandatory" );
}
+ }
- my $account_offset = Koha::Account::Offset->new(
- {
- debit_id => $fine->id,
- type => $offset_type,
- amount => $amount_to_pay * -1,
- }
- );
- push( @account_offsets, $account_offset );
+ # amount should always be passed as a positive value
+ my $amount = $params->{amount} * -1;
+ unless ( $amount < 0 ) {
+ Koha::Exceptions::Account::AmountNotPositive->throw(
+ error => 'Debit amount passed is not positive' );
+ }
- if ( C4::Context->preference("FinesLog") ) {
- logaction(
- "FINES", 'MODIFY',
- $self->{patron_id},
- Dumper(
+ my $description = $params->{description} // q{};
+ my $note = $params->{note} // q{};
+ my $user_id = $params->{user_id};
+ my $interface = $params->{interface};
+ my $library_id = $params->{library_id};
+ my $cash_register = $params->{cash_register};
+ my $payment_type = $params->{payment_type};
+ my $credit_type = $params->{type} || 'PAYMENT';
+ my $item_id = $params->{item_id};
+
+ Koha::Exceptions::Account::RegisterRequired->throw()
+ if ( C4::Context->preference("UseCashRegisters")
+ && defined($payment_type)
+ && ( $payment_type eq 'CASH' || $payment_type eq 'SIP00' )
+ && !defined($cash_register) );
+
+ my $line;
+ my $schema = Koha::Database->new->schema;
+ try {
+ $schema->txn_do(
+ sub {
+
+ # Insert the account line
+ $line = Koha::Account::Line->new(
{
- action => 'fee_payment',
- borrowernumber => $fine->borrowernumber,
- old_amountoutstanding => $old_amountoutstanding,
- new_amountoutstanding => 0,
- amount_paid => $old_amountoutstanding,
- accountlines_id => $fine->id,
- accountno => $fine->accountno,
- manager_id => $manager_id,
- note => $note,
+ borrowernumber => $self->{patron_id},
+ date => \'NOW()',
+ amount => $amount,
+ description => $description,
+ credit_type_code => $credit_type,
+ amountoutstanding => $amount,
+ payment_type => $payment_type,
+ note => $note,
+ manager_id => $user_id,
+ interface => $interface,
+ branchcode => $library_id,
+ register_id => $cash_register,
+ itemnumber => $item_id,
}
- )
- );
- push( @fines_paid, $fine->id );
- }
+ )->store();
+
+ # Record the account offset
+ my $account_offset = Koha::Account::Offset->new(
+ {
+ credit_id => $line->id,
+ type => 'CREATE',
+ amount => $amount * -1
+ }
+ )->store();
+
+ C4::Stats::UpdateStats(
+ {
+ branch => $library_id,
+ type => lc($credit_type),
+ amount => $amount,
+ borrowernumber => $self->{patron_id},
+ }
+ ) if grep { $credit_type eq $_ } ( 'PAYMENT', 'WRITEOFF' );
+
+ Koha::Plugins->call(
+ 'after_account_action',
+ {
+ action => "add_credit",
+ payload => {
+ type => lc($credit_type),
+ line => $line->get_from_storage, #TODO Seems unneeded
+ }
+ }
+ );
+
+ if ( C4::Context->preference("FinesLog") ) {
+ logaction(
+ "FINES", 'CREATE',
+ $self->{patron_id},
+ Dumper(
+ {
+ action => "create_$credit_type",
+ borrowernumber => $self->{patron_id},
+ amount => $amount,
+ description => $description,
+ amountoutstanding => $amount,
+ credit_type_code => $credit_type,
+ note => $note,
+ itemnumber => $item_id,
+ manager_id => $user_id,
+ branchcode => $library_id,
+ }
+ ),
+ $interface
+ );
+ }
+ }
+ );
}
+ catch {
+ if ( ref($_) eq 'Koha::Exceptions::Object::FKConstraint' ) {
+ if ( $_->broken_fk eq 'credit_type_code' ) {
+ Koha::Exceptions::Account::UnrecognisedType->throw(
+ error => 'Type of credit not recognised' );
+ }
+ else {
+ $_->rethrow;
+ }
+ }
+ };
- # Were not passed a specific line to pay, or the payment was for more
- # than the what was owed on the given line. In that case pay down other
- # lines with remaining balance.
- my @outstanding_fines;
- @outstanding_fines = Koha::Account::Lines->search(
+ return $line;
+}
+
+=head3 payin_amount
+
+ my $credit = $account->payin_amount(
{
- borrowernumber => $self->{patron_id},
- amountoutstanding => { '>' => 0 },
+ amount => $amount,
+ type => $credit_type,
+ payment_type => $payment_type,
+ cash_register => $register_id,
+ interface => $interface,
+ library_id => $branchcode,
+ user_id => $staff_id,
+ debits => $debit_lines,
+ description => $description,
+ note => $note
}
- ) if $balance_remaining > 0;
+ );
- foreach my $fine (@outstanding_fines) {
- my $amount_to_pay =
- $fine->amountoutstanding > $balance_remaining
- ? $balance_remaining
- : $fine->amountoutstanding;
+This method allows an amount to be paid into a patrons account and immediately applied against debts.
- my $old_amountoutstanding = $fine->amountoutstanding;
- $fine->amountoutstanding( $old_amountoutstanding - $amount_to_pay );
- $fine->store();
+You can optionally pass a debts parameter which consists of an arrayref of Koha::Account::Line debit lines.
- my $account_offset = Koha::Account::Offset->new(
- {
- debit_id => $fine->id,
- type => $offset_type,
- amount => $amount_to_pay * -1,
+$credit_type can be any of:
+ - 'PAYMENT'
+ - 'WRITEOFF'
+ - 'FORGIVEN'
+
+=cut
+
+sub payin_amount {
+ my ( $self, $params ) = @_;
+
+ # check for mandatory params
+ my @mandatory = ( 'interface', 'amount', 'type' );
+ for my $param (@mandatory) {
+ unless ( defined( $params->{$param} ) ) {
+ Koha::Exceptions::MissingParameter->throw(
+ error => "The $param parameter is mandatory" );
+ }
+ }
+
+ # Check for mandatory register
+ Koha::Exceptions::Account::RegisterRequired->throw()
+ if ( C4::Context->preference("UseCashRegisters")
+ && defined( $params->{payment_type} )
+ && ( $params->{payment_type} eq 'CASH' || $params->{payment_type} eq 'SIP00' )
+ && !defined($params->{cash_register}) );
+
+ # amount should always be passed as a positive value
+ my $amount = $params->{amount};
+ unless ( $amount > 0 ) {
+ Koha::Exceptions::Account::AmountNotPositive->throw(
+ error => 'Payin amount passed is not positive' );
+ }
+
+ my $credit;
+ my $schema = Koha::Database->new->schema;
+ $schema->txn_do(
+ sub {
+
+ # Add payin credit
+ $credit = $self->add_credit($params);
+
+ # Offset debts passed first
+ if ( exists( $params->{debits} ) ) {
+ $credit = $credit->apply(
+ {
+ debits => $params->{debits}
+ }
+ );
}
- );
- push( @account_offsets, $account_offset );
- if ( C4::Context->preference("FinesLog") ) {
- logaction(
- "FINES", 'MODIFY',
- $self->{patron_id},
- Dumper(
+ # Offset against remaining balance if AutoReconcile
+ if ( C4::Context->preference("AccountAutoReconcile")
+ && $credit->amountoutstanding != 0 )
+ {
+ $credit = $credit->apply(
{
- action => "fee_$type",
- borrowernumber => $fine->borrowernumber,
- old_amountoutstanding => $old_amountoutstanding,
- new_amountoutstanding => $fine->amountoutstanding,
- amount_paid => $amount_to_pay,
- accountlines_id => $fine->id,
- accountno => $fine->accountno,
- manager_id => $manager_id,
- note => $note,
+ debits => [ $self->outstanding_debits->as_list ]
}
- )
- );
- push( @fines_paid, $fine->id );
+ );
+ }
}
+ );
- $balance_remaining = $balance_remaining - $amount_to_pay;
- last unless $balance_remaining > 0;
- }
+ return $credit;
+}
- $account_type ||=
- $type eq 'writeoff' ? 'W'
- : defined($sip) ? "Pay$sip"
- : 'Pay';
+=head3 add_debit
- $description ||= $type eq 'writeoff' ? 'Writeoff' : q{};
+This method allows adding debits to a patron's account
- my $payment = Koha::Account::Line->new(
+ my $debit_line = Koha::Account->new({ patron_id => $patron_id })->add_debit(
{
- borrowernumber => $self->{patron_id},
- accountno => $accountno,
- date => dt_from_string(),
- amount => 0 - $amount,
- description => $description,
- accounttype => $account_type,
- payment_type => $payment_type,
- amountoutstanding => 0 - $balance_remaining,
- manager_id => $manager_id,
- note => $note,
+ amount => $amount,
+ description => $description,
+ note => $note,
+ user_id => $user_id,
+ interface => $interface,
+ library_id => $library_id,
+ type => $debit_type,
+ transaction_type => $transaction_type,
+ cash_register => $register_id,
+ item_id => $item_id,
+ issue_id => $issue_id
}
- )->store();
+ );
- foreach my $o ( @account_offsets ) {
- $o->credit_id( $payment->id() );
- $o->store();
- }
+$debit_type can be any of:
+ - ACCOUNT
+ - ACCOUNT_RENEW
+ - RESERVE_EXPIRED
+ - LOST
+ - sundry
+ - NEW_CARD
+ - OVERDUE
+ - PROCESSING
+ - RENT
+ - RENT_DAILY
+ - RENT_RENEW
+ - RENT_DAILY_RENEW
+ - RESERVE
+ - PAYOUT
- $library_id ||= $userenv ? $userenv->{'branch'} : undef;
+=cut
- UpdateStats(
- {
- branch => $library_id,
- type => $type,
- amount => $amount,
- borrowernumber => $self->{patron_id},
- accountno => $accountno,
+sub add_debit {
+
+ my ( $self, $params ) = @_;
+
+ # check for mandatory params
+ my @mandatory = ( 'interface', 'type', 'amount' );
+ for my $param (@mandatory) {
+ unless ( defined( $params->{$param} ) ) {
+ Koha::Exceptions::MissingParameter->throw(
+ error => "The $param parameter is mandatory" );
}
- );
+ }
- if ( C4::Context->preference("FinesLog") ) {
- logaction(
- "FINES", 'CREATE',
- $self->{patron_id},
- Dumper(
- {
- action => "create_$type",
- borrowernumber => $self->{patron_id},
- accountno => $accountno,
- amount => 0 - $amount,
- amountoutstanding => 0 - $balance_remaining,
- accounttype => $account_type,
- accountlines_paid => \@fines_paid,
- manager_id => $manager_id,
+ # check for cash register if using cash
+ Koha::Exceptions::Account::RegisterRequired->throw()
+ if ( C4::Context->preference("UseCashRegisters")
+ && defined( $params->{transaction_type} )
+ && ( $params->{transaction_type} eq 'CASH' || $params->{payment_type} eq 'SIP00' )
+ && !defined( $params->{cash_register} ) );
+
+ # amount should always be a positive value
+ my $amount = $params->{amount};
+ unless ( $amount > 0 ) {
+ Koha::Exceptions::Account::AmountNotPositive->throw(
+ error => 'Debit amount passed is not positive' );
+ }
+
+ my $description = $params->{description} // q{};
+ my $note = $params->{note} // q{};
+ my $user_id = $params->{user_id};
+ my $interface = $params->{interface};
+ my $library_id = $params->{library_id};
+ my $cash_register = $params->{cash_register};
+ my $debit_type = $params->{type};
+ my $transaction_type = $params->{transaction_type};
+ my $item_id = $params->{item_id};
+ my $issue_id = $params->{issue_id};
+
+ my $line;
+ my $schema = Koha::Database->new->schema;
+ try {
+ $schema->txn_do(
+ sub {
+
+ # Insert the account line
+ $line = Koha::Account::Line->new(
+ {
+ borrowernumber => $self->{patron_id},
+ date => \'NOW()',
+ amount => $amount,
+ description => $description,
+ debit_type_code => $debit_type,
+ amountoutstanding => $amount,
+ payment_type => $transaction_type,
+ note => $note,
+ manager_id => $user_id,
+ interface => $interface,
+ itemnumber => $item_id,
+ issue_id => $issue_id,
+ branchcode => $library_id,
+ register_id => $cash_register,
+ (
+ $debit_type eq 'OVERDUE'
+ ? ( status => 'UNRETURNED' )
+ : ()
+ ),
+ }
+ )->store();
+
+ # Record the account offset
+ my $account_offset = Koha::Account::Offset->new(
+ {
+ debit_id => $line->id,
+ type => 'CREATE',
+ amount => $amount
+ }
+ )->store();
+
+ if ( C4::Context->preference("FinesLog") ) {
+ logaction(
+ "FINES", 'CREATE',
+ $self->{patron_id},
+ Dumper(
+ {
+ action => "create_$debit_type",
+ borrowernumber => $self->{patron_id},
+ amount => $amount,
+ description => $description,
+ amountoutstanding => $amount,
+ debit_type_code => $debit_type,
+ note => $note,
+ itemnumber => $item_id,
+ manager_id => $user_id,
+ }
+ ),
+ $interface
+ );
}
- )
+ }
);
}
+ catch {
+ if ( ref($_) eq 'Koha::Exceptions::Object::FKConstraint' ) {
+ if ( $_->broken_fk eq 'debit_type_code' ) {
+ Koha::Exceptions::Account::UnrecognisedType->throw(
+ error => 'Type of debit not recognised' );
+ }
+ else {
+ $_->rethrow;
+ }
+ }
+ };
- return $payment->id;
+ return $line;
}
-=head3 add_credit
+=head3 payout_amount
-This method allows adding credits to a patron's account
+ my $debit = $account->payout_amount(
+ {
+ payout_type => $payout_type,
+ register_id => $register_id,
+ staff_id => $staff_id,
+ interface => 'intranet',
+ amount => $amount,
+ credits => $credit_lines
+ }
+ );
-my $credit_line = Koha::Account->new({ patron_id => $patron_id })->add_credit(
- {
- amount => $amount,
- description => $description,
- note => $note,
- user_id => $user_id,
- library_id => $library_id,
- sip => $sip,
- payment_type => $payment_type,
- type => $credit_type,
- item_id => $item_id
- }
-);
+This method allows an amount to be paid out from a patrons account against outstanding credits.
-$credit_type can be any of:
- - 'credit'
- - 'payment'
- - 'forgiven'
- - 'lost_item_return'
- - 'writeoff'
+$payout_type can be any of the defined payment_types:
=cut
-sub add_credit {
-
+sub payout_amount {
my ( $self, $params ) = @_;
- # amount is passed as a positive value, but we store credit as negative values
- my $amount = $params->{amount} * -1;
- my $description = $params->{description} // q{};
- my $note = $params->{note} // q{};
- my $user_id = $params->{user_id};
- my $library_id = $params->{library_id};
- my $sip = $params->{sip};
- my $payment_type = $params->{payment_type};
- my $type = $params->{type} || 'payment';
- my $item_id = $params->{item_id};
-
- my $schema = Koha::Database->new->schema;
+ # Check for mandatory parameters
+ my @mandatory =
+ ( 'interface', 'staff_id', 'branch', 'payout_type', 'amount' );
+ for my $param (@mandatory) {
+ unless ( defined( $params->{$param} ) ) {
+ Koha::Exceptions::MissingParameter->throw(
+ error => "The $param parameter is mandatory" );
+ }
+ }
- my $account_type = $Koha::Account::account_type->{$type};
- $account_type .= $sip
- if defined $sip &&
- $type eq 'payment';
+ # Check for mandatory register
+ Koha::Exceptions::Account::RegisterRequired->throw()
+ if ( C4::Context->preference("UseCashRegisters")
+ && ( $params->{payout_type} eq 'CASH' || $params->{payout_type} eq 'SIP00' )
+ && !defined($params->{cash_register}) );
+
+ # Amount should always be passed as a positive value
+ my $amount = $params->{amount};
+ unless ( $amount > 0 ) {
+ Koha::Exceptions::Account::AmountNotPositive->throw(
+ error => 'Payout amount passed is not positive' );
+ }
- my $line;
+ # Amount should always be less than or equal to outstanding credit
+ my $outstanding = 0;
+ my $outstanding_credits =
+ exists( $params->{credits} )
+ ? $params->{credits}
+ : $self->outstanding_credits->as_list;
+ for my $credit ( @{$outstanding_credits} ) {
+ $outstanding += $credit->amountoutstanding;
+ }
+ $outstanding = $outstanding * -1;
+ Koha::Exceptions::ParameterTooHigh->throw( error =>
+"Amount to payout ($amount) is higher than amountoutstanding ($outstanding)"
+ ) unless ( $outstanding >= $amount );
+ my $payout;
+ my $schema = Koha::Database->new->schema;
$schema->txn_do(
sub {
- # We should remove accountno, it is no longer needed
- my $last = Koha::Account::Lines->search( { borrowernumber => $self->{patron_id} },
- { order_by => 'accountno' } )->next();
- my $accountno = $last ? $last->accountno + 1 : 1;
-
- # Insert the account line
- $line = Koha::Account::Line->new(
- { borrowernumber => $self->{patron_id},
- date => \'NOW()',
- amount => $amount,
- description => $description,
- accounttype => $account_type,
- amountoutstanding => $amount,
- payment_type => $payment_type,
- note => $note,
- manager_id => $user_id,
- itemnumber => $item_id
- }
- )->store();
- # Record the account offset
- my $account_offset = Koha::Account::Offset->new(
- { credit_id => $line->id,
- type => $Koha::Account::offset_type->{$type},
- amount => $amount
- }
- )->store();
-
- UpdateStats(
- { branch => $library_id,
- type => $type,
- amount => $amount,
- borrowernumber => $self->{patron_id},
- accountno => $accountno,
+ # A 'payout' is a 'debit'
+ $payout = $self->add_debit(
+ {
+ amount => $params->{amount},
+ type => 'PAYOUT',
+ transaction_type => $params->{payout_type},
+ amountoutstanding => $params->{amount},
+ user_id => $params->{staff_id},
+ interface => $params->{interface},
+ branchcode => $params->{branch},
+ cash_register => $params->{cash_register}
}
- ) if grep { $type eq $_ } ('payment', 'writeoff') ;
-
- if ( C4::Context->preference("FinesLog") ) {
- logaction(
- "FINES", 'CREATE',
- $self->{patron_id},
- Dumper(
- { action => "create_$type",
- borrowernumber => $self->{patron_id},
- accountno => $accountno,
- amount => $amount,
- description => $description,
- amountoutstanding => $amount,
- accounttype => $account_type,
- note => $note,
- itemnumber => $item_id,
- manager_id => $user_id,
- }
- )
- );
+ );
+
+ # Offset against credits
+ for my $credit ( @{$outstanding_credits} ) {
+ $credit->apply( { debits => [$payout] } );
+ $payout->discard_changes;
+ last if $payout->amountoutstanding == 0;
}
+
+ # Set payout as paid
+ $payout->status('PAID')->store;
}
);
- return $line;
+ return $payout;
}
=head3 balance
sub balance {
my ($self) = @_;
- my $fines = Koha::Account::Lines->search(
- {
- borrowernumber => $self->{patron_id},
- },
- {
- select => [ { sum => 'amountoutstanding' } ],
- as => ['total_amountoutstanding'],
- }
- );
-
- return ( $fines->count )
- ? $fines->next->get_column('total_amountoutstanding') + 0
- : 0;
+ return $self->lines->total_outstanding;
}
=head3 outstanding_debits
my $lines = Koha::Account->new({ patron_id => $patron_id })->outstanding_debits;
+It returns the debit lines with outstanding amounts for the patron.
+
+It returns a Koha::Account::Lines iterator.
+
=cut
sub outstanding_debits {
my ($self) = @_;
- my $lines = Koha::Account::Lines->search(
+ return $self->lines->search(
{
- borrowernumber => $self->{patron_id},
+ amount => { '>' => 0 },
amountoutstanding => { '>' => 0 }
}
);
-
- return $lines;
}
=head3 outstanding_credits
-my ( $total, $lines ) = Koha::Account->new({ patron_id => $patron_id })->outstanding_credits;
+my $lines = Koha::Account->new({ patron_id => $patron_id })->outstanding_credits;
+
+It returns the credit lines with outstanding amounts for the patron.
+
+It returns a Koha::Account::Lines iterator.
=cut
sub outstanding_credits {
my ($self) = @_;
- my $outstanding_credits = Koha::Account::Lines->search(
- { borrowernumber => $self->{patron_id},
- amountoutstanding => { '<' => 0 }
- },
- { select => [ { sum => 'amountoutstanding' } ],
- as => ['outstanding_credits_total'],
- }
- );
- my $total
- = ( $outstanding_credits->count )
- ? $outstanding_credits->next->get_column('outstanding_credits_total') + 0
- : 0;
-
- my $lines = Koha::Account::Lines->search(
+ return $self->lines->search(
{
- borrowernumber => $self->{patron_id},
+ amount => { '<' => 0 },
amountoutstanding => { '<' => 0 }
}
);
-
- return ( $total, $lines );
}
=head3 non_issues_charges
sub non_issues_charges {
my ($self) = @_;
- # FIXME REMOVE And add a warning in the about page + update DB if length(MANUAL_INV) > 5
- my $ACCOUNT_TYPE_LENGTH = 5; # this is plain ridiculous...
-
+ #NOTE: With bug 23049 these preferences could be moved to being attached
+ #to individual debit types to give more flexability and specificity.
my @not_fines;
- push @not_fines, 'Res'
+ push @not_fines, 'RESERVE'
unless C4::Context->preference('HoldsInNoissuesCharge');
- push @not_fines, 'Rent'
+ push @not_fines, ( 'RENT', 'RENT_DAILY', 'RENT_RENEW', 'RENT_DAILY_RENEW' )
unless C4::Context->preference('RentalsInNoissuesCharge');
unless ( C4::Context->preference('ManInvInNoissuesCharge') ) {
- my $dbh = C4::Context->dbh;
- push @not_fines,
- @{
- $dbh->selectcol_arrayref(q|
- SELECT authorised_value FROM authorised_values WHERE category = 'MANUAL_INV'
- |)
- };
+ my @man_inv = Koha::Account::DebitTypes->search({ is_system => 0 })->get_column('code');
+ push @not_fines, @man_inv;
}
- @not_fines = map { substr( $_, 0, $ACCOUNT_TYPE_LENGTH ) } uniq(@not_fines);
- my $non_issues_charges = Koha::Account::Lines->search(
+ return $self->lines->search(
{
- borrowernumber => $self->{patron_id},
- accounttype => { -not_in => \@not_fines }
+ debit_type_code => { -not_in => \@not_fines }
},
+ )->total_outstanding;
+}
+
+=head3 lines
+
+my $lines = $self->lines;
+
+Return all credits and debits for the user, outstanding or otherwise
+
+=cut
+
+sub lines {
+ my ($self) = @_;
+
+ return Koha::Account::Lines->search(
{
- select => [ { sum => 'amountoutstanding' } ],
- as => ['non_issues_charges'],
+ borrowernumber => $self->{patron_id},
}
);
- return $non_issues_charges->count
- ? $non_issues_charges->next->get_column('non_issues_charges') + 0
- : 0;
}
-1;
+=head3 reconcile_balance
-=head2 Name mappings
+$account->reconcile_balance();
-=head3 $offset_type
+Find outstanding credits and use them to pay outstanding debits.
+Currently, this implicitly uses the 'First In First Out' rule for
+applying credits against debits.
=cut
-our $offset_type = {
- 'credit' => 'Manual Credit',
- 'forgiven' => 'Writeoff',
- 'lost_item_return' => 'Lost Item Return',
- 'payment' => 'Payment',
- 'writeoff' => 'Writeoff'
-};
+sub reconcile_balance {
+ my ($self) = @_;
+
+ my $outstanding_debits = $self->outstanding_debits;
+ my $outstanding_credits = $self->outstanding_credits;
-=head3 $account_type
+ while ( $outstanding_debits->total_outstanding > 0
+ and my $credit = $outstanding_credits->next )
+ {
+ # there's both outstanding debits and credits
+ $credit->apply( { debits => [ $outstanding_debits->as_list ] } ); # applying credit, no special offset
-=cut
+ $outstanding_debits = $self->outstanding_debits;
+
+ }
+
+ return $self;
+}
+
+1;
-our $account_type = {
- 'credit' => 'C',
- 'forgiven' => 'FOR',
- 'lost_item_return' => 'CR',
- 'payment' => 'Pay',
- 'writeoff' => 'W'
-};
+=head1 AUTHORS
-=head1 AUTHOR
+=encoding utf8
Kyle M Hall <kyle.m.hall@gmail.com>
+Tomás Cohen Arazi <tomascohen@gmail.com>
+Martin Renvoize <martin.renvoize@ptfs-europe.com>
=cut