use Data::Dumper;
use List::MoreUtils qw( uniq );
+use C4::Circulation qw( ReturnLostItem );
+use C4::Letters;
use C4::Log qw( logaction );
use C4::Stats qw( UpdateStats );
use Koha::Account::Lines;
use Koha::Account::Offsets;
use Koha::DateUtils qw( dt_from_string );
+use Koha::Exceptions;
+use Koha::Exceptions::Account;
=head1 NAME
my $patron = Koha::Patrons->find( $self->{patron_id} );
- # 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;
-
my $manager_id = $userenv ? $userenv->{number} : 0;
+ my $interface = $params ? ( $params->{interface} || C4::Context->interface ) : C4::Context->interface;
my @fines_paid; # List of account lines paid on with this payment
$fine->amountoutstanding($new_amountoutstanding)->store();
$balance_remaining = $balance_remaining - $amount_to_pay;
- if ( $fine->itemnumber && $fine->accounttype && ( $fine->accounttype eq 'Rep' || $fine->accounttype eq 'L' ) )
+ if ( $fine->itemnumber && $fine->accounttype && ( $fine->accounttype eq 'L' ) )
{
C4::Circulation::ReturnLostItem( $self->{patron_id}, $fine->itemnumber );
}
new_amountoutstanding => 0,
amount_paid => $old_amountoutstanding,
accountlines_id => $fine->id,
- accountno => $fine->accountno,
manager_id => $manager_id,
note => $note,
}
- )
+ ),
+ $interface
);
push( @fines_paid, $fine->id );
}
# 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(
+ @outstanding_fines = $self->lines->search(
{
- borrowernumber => $self->{patron_id},
amountoutstanding => { '>' => 0 },
}
) if $balance_remaining > 0;
new_amountoutstanding => $fine->amountoutstanding,
amount_paid => $amount_to_pay,
accountlines_id => $fine->id,
- accountno => $fine->accountno,
manager_id => $manager_id,
note => $note,
}
- )
+ ),
+ $interface
);
push( @fines_paid, $fine->id );
}
my $payment = Koha::Account::Line->new(
{
borrowernumber => $self->{patron_id},
- accountno => $accountno,
date => dt_from_string(),
amount => 0 - $amount,
description => $description,
payment_type => $payment_type,
amountoutstanding => 0 - $balance_remaining,
manager_id => $manager_id,
+ interface => $interface,
+ branchcode => $library_id,
note => $note,
}
)->store();
$o->store();
}
- $library_id ||= $userenv ? $userenv->{'branch'} : undef;
-
UpdateStats(
{
branch => $library_id,
type => $type,
amount => $amount,
borrowernumber => $self->{patron_id},
- accountno => $accountno,
}
);
{
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,
}
- )
+ ),
+ $interface
);
}
- require C4::Letters;
- if (
- my $letter = C4::Letters::GetPreparedLetter(
- module => 'circulation',
- letter_code => uc("ACCOUNT_$type"),
- message_transport_type => 'email',
- lang => Koha::Patrons->find( $self->{patron_id} )->lang,
- objects => {
- patron => scalar Koha::Patrons->find( $self->{patron_id} ),
- library => scalar Koha::Libraries->find( $self->{library_id} ),
- offsets => \@account_offsets,
- credit => $payment,
- },
- )
- )
- {
- C4::Letters::EnqueueLetter(
- {
- letter => $letter,
- borrowernumber => $self->{patron_id},
+ if ( C4::Context->preference('UseEmailReceipts') ) {
+ if (
+ my $letter = C4::Letters::GetPreparedLetter(
+ module => 'circulation',
+ letter_code => uc("ACCOUNT_$type"),
message_transport_type => 'email',
- }
- ) or warn "can't enqueue letter $letter";
+ lang => $patron->lang,
+ tables => {
+ borrowers => $self->{patron_id},
+ branches => $self->{library_id},
+ },
+ substitute => {
+ credit => $payment,
+ offsets => \@account_offsets,
+ },
+ )
+ )
+ {
+ C4::Letters::EnqueueLetter(
+ {
+ letter => $letter,
+ borrowernumber => $self->{patron_id},
+ message_transport_type => 'email',
+ }
+ ) or warn "can't enqueue letter $letter";
+ }
}
return $payment->id;
description => $description,
note => $note,
user_id => $user_id,
+ interface => $interface,
library_id => $library_id,
sip => $sip,
payment_type => $payment_type,
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 $sip = $params->{sip};
my $payment_type = $params->{payment_type};
my $type = $params->{type} || 'payment';
my $item_id = $params->{item_id};
+ unless ( $interface ) {
+ Koha::Exceptions::MissingParameter->throw(
+ error => 'The interface parameter is mandatory'
+ );
+ }
+
my $schema = Koha::Database->new->schema;
- my $account_type = $Koha::Account::account_type->{$type};
+ my $account_type = $Koha::Account::account_type_credit->{$type};
$account_type .= $sip
if defined $sip &&
$type eq 'payment';
$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(
payment_type => $payment_type,
note => $note,
manager_id => $user_id,
- itemnumber => $item_id
+ interface => $interface,
+ branchcode => $library_id,
+ itemnumber => $item_id,
}
)->store();
type => $type,
amount => $amount,
borrowernumber => $self->{patron_id},
- accountno => $accountno,
}
) if grep { $type eq $_ } ('payment', 'writeoff') ;
Dumper(
{ action => "create_$type",
borrowernumber => $self->{patron_id},
- accountno => $accountno,
amount => $amount,
description => $description,
amountoutstanding => $amount,
note => $note,
itemnumber => $item_id,
manager_id => $user_id,
+ branchcode => $library_id,
}
- )
+ ),
+ $interface
+ );
+ }
+ }
+ );
+
+ return $line;
+}
+
+=head3 add_debit
+
+This method allows adding debits to a patron's account
+
+my $debit_line = Koha::Account->new({ patron_id => $patron_id })->add_debit(
+ {
+ amount => $amount,
+ description => $description,
+ note => $note,
+ user_id => $user_id,
+ interface => $interface,
+ library_id => $library_id,
+ type => $debit_type,
+ item_id => $item_id,
+ issue_id => $issue_id
+ }
+);
+
+$debit_type can be any of:
+ - overdue
+ - lost_item
+ - new_card
+ - account
+ - sundry
+ - processing
+ - rent
+ - reserve
+ - manual
+
+=cut
+
+sub add_debit {
+
+ my ( $self, $params ) = @_;
+
+ # 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 $type = $params->{type};
+ my $item_id = $params->{item_id};
+ my $issue_id = $params->{issue_id};
+
+ unless ( $interface ) {
+ Koha::Exceptions::MissingParameter->throw(
+ error => 'The interface parameter is mandatory'
+ );
+ }
+
+ my $schema = Koha::Database->new->schema;
+
+ unless ( exists($Koha::Account::account_type_debit->{$type}) ) {
+ Koha::Exceptions::Account::UnrecognisedType->throw(
+ error => 'Type of debit not recognised'
+ );
+ }
+
+ my $account_type = $Koha::Account::account_type_debit->{$type};
+
+ my $line;
+
+ $schema->txn_do(
+ sub {
+
+ # 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 => undef,
+ note => $note,
+ manager_id => $user_id,
+ interface => $interface,
+ itemnumber => $item_id,
+ issue_id => $issue_id,
+ branchcode => $library_id,
+ ( $type eq 'overdue' ? ( status => 'UNRETURNED' ) : ()),
+ }
+ )->store();
+
+ # Record the account offset
+ my $account_offset = Koha::Account::Offset->new(
+ { debit_id => $line->id,
+ type => $Koha::Account::offset_type->{$type},
+ amount => $amount
+ }
+ )->store();
+
+ if ( C4::Context->preference("FinesLog") ) {
+ logaction(
+ "FINES", 'CREATE',
+ $self->{patron_id},
+ Dumper(
+ { action => "create_$type",
+ borrowernumber => $self->{patron_id},
+ amount => $amount,
+ description => $description,
+ amountoutstanding => $amount,
+ accounttype => $account_type,
+ note => $note,
+ itemnumber => $item_id,
+ manager_id => $user_id,
+ }
+ ),
+ $interface
);
}
}
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.
+
+In scalar context, it returns a Koha::Account::Lines iterator. In list context, it will
+return a list of Koha::Account::Line objects.
+
=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 $lines = Koha::Account->new({ patron_id => $patron_id })->outstanding_credits;
+It returns the credit lines with outstanding amounts for the patron.
+
+In scalar context, it returns a Koha::Account::Lines iterator. In list context, it will
+return a list of Koha::Account::Line objects.
+
=cut
sub outstanding_credits {
my ($self) = @_;
- my $lines = Koha::Account::Lines->search(
+ return $self->lines->search(
{
- borrowernumber => $self->{patron_id},
+ amount => { '<' => 0 },
amountoutstanding => { '<' => 0 }
}
);
-
- return $lines;
}
=head3 non_issues_charges
}
@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 }
},
+ )->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;
+}
+
+=head3 reconcile_balance
+
+$account->reconcile_balance();
+
+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
+
+sub reconcile_balance {
+ my ($self) = @_;
+
+ my $outstanding_debits = $self->outstanding_debits;
+ my $outstanding_credits = $self->outstanding_credits;
+
+ while ( $outstanding_debits->total_outstanding > 0
+ and my $credit = $outstanding_credits->next )
+ {
+ # there's both outstanding debits and credits
+ $credit->apply( { debits => $outstanding_debits } ); # applying credit, no special offset
+
+ $outstanding_debits = $self->outstanding_debits;
+
+ }
+
+ return $self;
}
1;
our $offset_type = {
'credit' => 'Manual Credit',
'forgiven' => 'Writeoff',
- 'lost_item_return' => 'Lost Item Return',
+ 'lost_item_return' => 'Lost Item',
'payment' => 'Payment',
- 'writeoff' => 'Writeoff'
+ 'writeoff' => 'Writeoff',
+ 'account' => 'Account Fee',
+ 'reserve' => 'Reserve Fee',
+ 'processing' => 'Processing Fee',
+ 'lost_item' => 'Lost Item',
+ 'rent' => 'Rental Fee',
+ 'overdue' => 'OVERDUE',
+ 'manual_debit' => 'Manual Debit',
+ 'hold_expired' => 'Hold Expired'
};
-=head3 $account_type
+=head3 $account_type_credit
=cut
-our $account_type = {
+our $account_type_credit = {
'credit' => 'C',
'forgiven' => 'FOR',
'lost_item_return' => 'CR',
'writeoff' => 'W'
};
-=head1 AUTHOR
+=head3 $account_type_debit
+
+=cut
+
+our $account_type_debit = {
+ 'account' => 'A',
+ 'overdue' => 'OVERDUE',
+ 'lost_item' => 'L',
+ 'new_card' => 'N',
+ 'sundry' => 'M',
+ 'processing' => 'PF',
+ 'rent' => 'Rent',
+ 'reserve' => 'Res',
+ 'manual_debit' => 'M',
+ 'hold_expired' => 'HE'
+};
+
+=head1 AUTHORS
+
+=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