X-Git-Url: http://koha-dev.rot13.org:8081/gitweb/?a=blobdiff_plain;f=Koha%2FAccount.pm;h=5f4da3f11efe9152fc225c58f44ed4c359cf275b;hb=241cbea6912d0ecc0232e57f0b09d0a72a9926e4;hp=bffc98bd6539b2362d4c40e7910003ab58739d9b;hpb=b3cd3855b912cd7bfc9b10f1163a34d09be4f5e0;p=srvgit diff --git a/Koha/Account.pm b/Koha/Account.pm index bffc98bd65..5f4da3f11e 100644 --- a/Koha/Account.pm +++ b/Koha/Account.pm @@ -20,16 +20,21 @@ package Koha::Account; use Modern::Perl; use Carp; -use Data::Dumper; -use List::MoreUtils qw( uniq ); -use List::Util qw( sum ); +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; =head1 NAME @@ -52,13 +57,12 @@ This method allows payments to be made against fees/fines 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) } ); @@ -67,201 +71,561 @@ Koha::Account->new( { patron_id => $borrowernumber } )->pay( 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->messages} ) { + push @{$renew_outcomes}, $message->payload; + } + + return { payment_id => $payment->id, renew_result => $renew_outcomes }; +} - my $manager_id = $userenv ? $userenv->{number} : 0; +=head3 add_credit - my @fines_paid; # List of account lines paid on with this payment +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' - 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' ) + && !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' ); + + 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; + } + } + }; + + return $line; +} + +=head3 payin_amount - # 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( + 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' ) + && !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 + } + ); + +$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 + +=cut + +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" ); } - )->store(); + } - foreach my $o ( @account_offsets ) { - $o->credit_id( $payment->id() ); - $o->store(); + # 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' ) + && !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' ); } - $library_id ||= $userenv ? $userenv->{'branch'} : undef; + 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(); - UpdateStats( + # 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 $line; +} + +=head3 payout_amount + + my $debit = $account->payout_amount( { - branch => $library_id, - type => $type, - amount => $amount, - borrowernumber => $self->{patron_id}, - accountno => $accountno, + payout_type => $payout_type, + register_id => $register_id, + staff_id => $staff_id, + interface => 'intranet', + amount => $amount, + credits => $credit_lines } ); - if ( C4::Context->preference("FinesLog") ) { - logaction( - "FINES", 'CREATE', - $self->{patron_id}, - Dumper( +This method allows an amount to be paid out from a patrons account against outstanding credits. + +$payout_type can be any of the defined payment_types: + +=cut + +sub payout_amount { + my ( $self, $params ) = @_; + + # 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" ); + } + } + + # Check for mandatory register + Koha::Exceptions::Account::RegisterRequired->throw() + if ( C4::Context->preference("UseCashRegisters") + && ( $params->{payout_type} eq 'CASH' ) + && !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' ); + } + + # 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 { + + # A 'payout' is a 'debit' + $payout = $self->add_debit( { - 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, + amount => $params->{amount}, + type => 'PAYOUT', + transaction_type => $params->{payout_type}, + amountoutstanding => $params->{amount}, + manager_id => $params->{staff_id}, + interface => $params->{interface}, + branchcode => $params->{branch}, + cash_register => $params->{cash_register} } - ) - ); - } + ); + + # 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 $payment->id; + return $payout; } =head3 balance @@ -274,41 +638,51 @@ Return the balance (sum of amountoutstanding columns) 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 ( $total, $lines ) = Koha::Account->new({ patron_id => $patron_id })->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 } } ); +} + +=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. - # sum returns undef it list is empty - my $total = sum( $lines->get_column('amountoutstanding') ) + 0; +=cut + +sub outstanding_credits { + my ($self) = @_; - return ( $total, $lines ); + return $self->lines->search( + { + amount => { '<' => 0 }, + amountoutstanding => { '<' => 0 } + } + ); } =head3 non_issues_charges @@ -327,44 +701,80 @@ Charges exempt from non-issue are: 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; +} + +=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->as_list ] } ); # applying credit, no special offset + + $outstanding_debits = $self->outstanding_debits; + + } + + return $self; } 1; -=head1 AUTHOR +=head1 AUTHORS + +=encoding utf8 Kyle M Hall +Tomás Cohen Arazi +Martin Renvoize =cut